[TASK] Add recreate button to slug field 19/58119/2
authorChristian Kuhn <lolli@schwarzbu.ch>
Sat, 1 Sep 2018 21:15:37 +0000 (23:15 +0200)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Sat, 1 Sep 2018 21:51:10 +0000 (23:51 +0200)
Adds a second button next to the 'manually edit slug' button
in pages: If the title is changed, this button can be used
to recalculate a new slug from the changed title.

Resolves: #86077
Releases: master
Change-Id: I862413423cefa1f391321f6d5e5c3d5dba68397e
Reviewed-on: https://review.typo3.org/58119
Tested-by: TYPO3com <no-reply@typo3.com>
Tested-by: Kevin Appelt <kevin.appelt@icloud.com>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
typo3/sysext/backend/Classes/Controller/FormSlugAjaxController.php
typo3/sysext/backend/Classes/Form/Element/InputSlugElement.php
typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SlugElement.js
typo3/sysext/core/Resources/Private/Language/locallang_core.xlf

index f523042..2bdc8d5 100644 (file)
@@ -60,9 +60,10 @@ class FormSlugAjaxController extends AbstractFormEngineAjaxController
 
         $queryParameters = $request->getParsedBody() ?? [];
         $values = $queryParameters['values'];
-        $autoGeneration = $queryParameters['autoGeneration'] === 'true' ? true : false;
+        $mode = $queryParameters['mode'];
         $tableName = $queryParameters['tableName'];
         $pid = (int)$queryParameters['pageId'];
+        $parentPageId = (int)$queryParameters['parentPageId'];
         $recordId = (int)$queryParameters['recordId'];
         $languageId = (int)$queryParameters['language'];
         $fieldName = $queryParameters['fieldName'];
@@ -82,15 +83,22 @@ class FormSlugAjaxController extends AbstractFormEngineAjaxController
         $hasConflict = false;
 
         $slug = GeneralUtility::makeInstance(SlugHelper::class, $tableName, $fieldName, $fieldConfig);
-        if ($autoGeneration) {
+        if ($mode === 'auto') {
             // New page - Feed incoming values to generator
             $recordData = $values;
             $recordData['pid'] = $pid;
             $recordData[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']] = $languageId;
             $proposal = $slug->generate($recordData, $pid);
-        } else {
+        } elseif ($mode === 'recreate') {
+            $recordData = $values;
+            $recordData['pid'] = $pid;
+            $recordData[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']] = $languageId;
+            $proposal = $slug->generate($recordData, $parentPageId);
+        } elseif ($mode === 'manual') {
             // Existing record - Fetch full record and only validate against the new "slug" field.
             $proposal = $slug->sanitize($values['manual']);
+        } else {
+            throw new \RuntimeException('mode must be either "auto", "recreate" or "manual"', 1535835666);
         }
 
         if ($hasToBeUniqueInSite && !$slug->isUniqueInSite($proposal, $recordId, $pid, $languageId)) {
@@ -103,7 +111,7 @@ class FormSlugAjaxController extends AbstractFormEngineAjaxController
         }
 
         return new JsonResponse([
-            'hasConflicts' => !$autoGeneration && $hasConflict,
+            'hasConflicts' => !$mode && $hasConflict,
             'manual' => $values['manual'] ?? '',
             'proposal' => $proposal,
         ]);
@@ -126,6 +134,7 @@ class FormSlugAjaxController extends AbstractFormEngineAjaxController
                     $queryParameters['language'],
                     $queryParameters['fieldName'],
                     $queryParameters['command'],
+                    $queryParameters['parentPageId'],
                 ]
             ),
             __CLASS__
index 879df50..a0ab76b 100644 (file)
@@ -88,6 +88,7 @@ class InputSlugElement extends AbstractFormElement
         $fieldWizardHtml = $fieldWizardResult['html'];
         $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
         $toggleButtonTitle = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.toggleSlugExplanation');
+        $recreateButtonTitle = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.recreateSlugExplanation');
 
         $thisSlugId = 't3js-form-field-slug-id' . StringUtility::getUniqueId();
         $mainFieldHtml = [];
@@ -119,6 +120,9 @@ class InputSlugElement extends AbstractFormElement
         $mainFieldHtml[] =                      '<button class="btn btn-default t3js-form-field-slug-toggle" type="button" title="' . htmlspecialchars($toggleButtonTitle) . '">';
         $mainFieldHtml[] =                          $this->iconFactory->getIcon('actions-version-workspaces-preview-link', Icon::SIZE_SMALL)->render();
         $mainFieldHtml[] =                      '</button>';
+        $mainFieldHtml[] =                      '<button class="btn btn-default t3js-form-field-slug-recreate" type="button" title="' . htmlspecialchars($recreateButtonTitle) . '">';
+        $mainFieldHtml[] =                          $this->iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL)->render();
+        $mainFieldHtml[] =                      '</button>';
         $mainFieldHtml[] =                  '</span>';
         $mainFieldHtml[] =                  '<input type="hidden"';
         $mainFieldHtml[] =                      ' class="t3js-form-field-slug-hidden"';
@@ -150,6 +154,7 @@ class InputSlugElement extends AbstractFormElement
         foreach ($config['generatorOptions']['fields'] ?? [] as $listenerFieldName) {
             $validInputNamesToListenTo[$listenerFieldName] = $commonElementPrefix . '[' . htmlspecialchars($listenerFieldName) . ']';
         }
+        $parentPageId = $this->data['parentPageRow']['uid'] ?? 0;
         $signature = GeneralUtility::hmac(
             implode(
                 '',
@@ -160,6 +165,7 @@ class InputSlugElement extends AbstractFormElement
                     $languageId,
                     $this->data['fieldName'],
                     $this->data['command'],
+                    $parentPageId
                 ]
             ),
             FormSlugAjaxController::class
@@ -174,7 +180,8 @@ class InputSlugElement extends AbstractFormElement
             'language' => $languageId,
             'originalValue' => $itemValue,
             'signature' => $signature,
-            'command' => $this->data['command']
+            'command' => $this->data['command'],
+            'parentPageId' => $parentPageId,
         ];
         $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SlugElement' => '
             function(SlugElement) {
index 7636398..274db09 100644 (file)
@@ -47,6 +47,7 @@ define(['jquery'], function ($) {
   SlugElement.initialize = function (selector, options) {
     var self = this;
     var toggleButtonClass = '.t3js-form-field-slug-toggle';
+    var recreateButtonClass = '.t3js-form-field-slug-recreate';
     var inputFieldClass = '.t3js-form-field-slug-input';
     var readOnlyFieldClass = '.t3js-form-field-slug-readonly';
     var hiddenFieldClass = '.t3js-form-field-slug-hidden';
@@ -67,36 +68,30 @@ define(['jquery'], function ($) {
         $(document).on('keyup', '[data-formengine-input-name="' + field + '"]', function (e) {
           if (!self.manuallyChanged) {
             // manuallyChanged = true stops slug generation as soon as editor set a slug manually
-            SlugElement.sendSlugProposal(true);
+            SlugElement.sendSlugProposal('auto');
           }
         });
       });
     }
 
-    if (options.command === 'edit' && this.$hiddenField.val() === '') {
-      // If we're editing a page and the slug is currently empty for whatever reason,
-      // auto-generate the slug once.
-      // @todo: This currently happens if a page is localized via page / list module
-      // @todo: "Make new translation of this page" - DataHandler does not create slug in this case
-      // @todo: "justLocalized" This should probably be fixed in the DataHandler
-      SlugElement.sendSlugProposal(true);
-      // And also listen on the listener fields so slug is modified in this case, too
-      $.each(fieldsToListenOn, function (fieldName, field) {
-        $(document).on('keyup', '[data-formengine-input-name="' + field + '"]', function (e) {
-          if (!self.manuallyChanged) {
-            // manuallyChanged = true stops slug generation as soon as editor set a slug manually
-            SlugElement.sendSlugProposal(true);
-          }
-        });
-      });
-    }
+    // Clicking the recreate button makes new slug proposal created from 'title' field
+    $(document).on('click', recreateButtonClass, function (e) {
+      e.preventDefault();
+      if (self.$readOnlyField.hasClass('hidden')) {
+        // Switch to readonly version - similar to 'new' page where field is
+        // written on the fly with title change
+        self.$readOnlyField.toggleClass('hidden', false);
+        self.$inputField.toggleClass('hidden', true);
+      }
+      SlugElement.sendSlugProposal('recreate');
+    });
 
     // Scenario for new pages: Usually, slug is created from the page title. However, if user toggles the
     // input field and feeds an own slug, and then changes title again, the slug should stay. manuallyChanged
     // is used to track this.
     $(this.$inputField).on('keyup', function (e) {
       self.manuallyChanged = true;
-      SlugElement.sendSlugProposal(false);
+      SlugElement.sendSlugProposal('manual');
     });
 
     // Clicking the toggle button toggles the read only field and the input field.
@@ -117,9 +112,9 @@ define(['jquery'], function ($) {
     });
   };
 
-  SlugElement.sendSlugProposal = function (autoGeneration) {
+  SlugElement.sendSlugProposal = function (mode) {
     var input = {};
-    if (autoGeneration) {
+    if (mode === 'auto' || mode === 'recreate') {
       var fieldsToListenOn = SlugElement.options.listenerFieldNames || {};
       $.each(fieldsToListenOn, function (fieldName, field) {
         input[fieldName] = $('[data-formengine-input-name="' + field + '"]').val();
@@ -131,9 +126,10 @@ define(['jquery'], function ($) {
       TYPO3.settings.ajaxUrls['record_slug_suggest'],
       {
         values: input,
-        autoGeneration: autoGeneration,
+        mode: mode,
         tableName: SlugElement.options.tableName,
         pageId: SlugElement.options.pageId,
+        parentPageId: SlugElement.options.parentPageId,
         recordId: SlugElement.options.recordId,
         language: SlugElement.options.language,
         fieldName: SlugElement.options.fieldName,
@@ -147,7 +143,7 @@ define(['jquery'], function ($) {
           SlugElement.$fullElement.find('.t3js-form-proposal-accepted').removeClass('hidden').find('span').text(response.proposal);
           SlugElement.$fullElement.find('.t3js-form-proposal-different').addClass('hidden');
         }
-        if (autoGeneration) {
+        if (mode === 'auto' || mode === 'recreate') {
           SlugElement.$readOnlyField.val(response.proposal);
           SlugElement.$hiddenField.val(response.proposal);
         } else {
index 8871428..66b8c6c 100644 (file)
@@ -991,6 +991,9 @@ Do you want to refresh it now?</source>
                        <trans-unit id="buttons.toggleSlugExplanation">
                                <source>Toggle manual URL segment</source>
                        </trans-unit>
+                       <trans-unit id="buttons.recreateSlugExplanation">
+                               <source>Recalculate URL segment from page title</source>
+                       </trans-unit>
                        <trans-unit id="cm.copy">
                                <source>Copy</source>
                        </trans-unit>