Improved ACP cover photo UI
authorAlexander Ebert <ebert@woltlab.com>
Thu, 30 Nov 2017 11:28:18 +0000 (12:28 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 30 Nov 2017 11:28:18 +0000 (12:28 +0100)
See #2484

wcfsetup/install/files/acp/style/layout.scss
wcfsetup/install/files/acp/templates/styleAdd.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Delete.js [new file with mode: 0644]
wcfsetup/install/files/lib/data/style/StyleAction.class.php
wcfsetup/install/files/lib/data/style/StyleEditor.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index a7efb2992789fa0150d8ba9be05401db927e7580..c03fb69b19e63456a92271d993d639f3e724a67e 100644 (file)
@@ -382,3 +382,7 @@ $wcfAcpSubMenuWidth: 300px;
                height: 150px;
        }
 }
+
+#uploadCoverPhoto > .button {
+       vertical-align: top;
+}
index 9b206c46b6b8c9691688ea9025845f012f3cba7b..ca0e19c2794acfb8db1199329183545390bc3e7d 100644 (file)
@@ -6,9 +6,11 @@
 {js application='wcf' file='WCF.ColorPicker' bundle='WCF.Combined'}
 <script data-relocate="true">
        require([
-               'WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Upload', 'WoltLabSuite/Core/Acp/Ui/Style/Favicon/Upload', 'WoltLabSuite/Core/Acp/Ui/Style/Image/Upload', 'WoltLabSuite/Core/Acp/Ui/Style/Editor', 'WoltLabSuite/Core/Ui/Toggle/Input', 'Language'
+               'WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Delete', 'WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Upload', 'WoltLabSuite/Core/Acp/Ui/Style/Favicon/Upload', 'WoltLabSuite/Core/Acp/Ui/Style/Image/Upload',
+               'WoltLabSuite/Core/Acp/Ui/Style/Editor', 'WoltLabSuite/Core/Ui/Toggle/Input', 'Language'
        ], function(
-               AcpUiStyleCoverPhotoUpload, AcpUiStyleFaviconUpload, AcpUiStyleImageUpload, AcpUiStyleEditor, UiToggleInput, Language
+               AcpUiStyleCoverPhotoDelete, AcpUiStyleCoverPhotoUpload, AcpUiStyleFaviconUpload, AcpUiStyleImageUpload,
+               AcpUiStyleEditor, UiToggleInput, Language
        ) {
                AcpUiStyleEditor.setup({
                        isTainted: {if $isTainted}true{else}false{/if},
                });
                
                {if $action === 'edit'}
-                       Language.addObject({
-                               'wcf.user.coverPhoto.upload.error.invalidExtension': '{lang}wcf.user.coverPhoto.upload.error.invalidExtension{/lang}',
-                               'wcf.user.coverPhoto.upload.error.minHeight': '{lang}wcf.user.coverPhoto.upload.error.minHeight{/lang}',
-                               'wcf.user.coverPhoto.upload.error.minWidth': '{lang}wcf.user.coverPhoto.upload.error.minWidth{/lang}',
-                               'wcf.user.coverPhoto.upload.error.uploadFailed': '{lang}wcf.user.coverPhoto.upload.error.uploadFailed{/lang}'
-                       });
-                       
                        new AcpUiStyleFaviconUpload({@$style->styleID});
-                       new AcpUiStyleCoverPhotoUpload({@$style->styleID});
+                       
+                       {if MODULE_USER_COVER_PHOTO}
+                               Language.addObject({
+                                       'wcf.acp.style.coverPhoto.delete.confirmMessage': '{lang}wcf.acp.style.coverPhoto.delete.confirmMessage{/lang}',
+                                       'wcf.user.coverPhoto.upload.error.invalidExtension': '{lang}wcf.user.coverPhoto.upload.error.invalidExtension{/lang}',
+                                       'wcf.user.coverPhoto.upload.error.minHeight': '{lang}wcf.user.coverPhoto.upload.error.minHeight{/lang}',
+                                       'wcf.user.coverPhoto.upload.error.minWidth': '{lang}wcf.user.coverPhoto.upload.error.minWidth{/lang}',
+                                       'wcf.user.coverPhoto.upload.error.uploadFailed': '{lang}wcf.user.coverPhoto.upload.error.uploadFailed{/lang}'
+                               });
+                               
+                               AcpUiStyleCoverPhotoDelete.init({@$style->styleID});
+                               new AcpUiStyleCoverPhotoUpload({@$style->styleID});
+                       {/if}
                {/if}
        });
        
                                        
                                        {event name='faviconFields'}
                                </section>
-                               
-                               <section class="section">
-                                       <h2 class="sectionTitle">{lang}wcf.acp.style.general.coverPhoto{/lang}</h2>
-                                       
-                                       <dl>
-                                               <dt><label for="coverPhoto">{lang}wcf.acp.style.coverPhoto{/lang}</label></dt>
-                                               <dd>
-                                                       <div id="coverPhotoPreview" style="background-image: url({@$__wcf->getPath()}images/coverPhotos/{@$style->getCoverPhoto()})"></div>
-                                                       <div id="uploadCoverPhoto"></div>
-                                                       <small>{lang}wcf.acp.style.coverPhoto.description{/lang}</small>
-                                               </dd>
-                                       </dl>
-                                       
-                                       {event name='coverPhotoFields'}
-                               </section>
+                       
+                               {if MODULE_USER_COVER_PHOTO}
+                                       <section class="section">
+                                               <h2 class="sectionTitle">{lang}wcf.acp.style.general.coverPhoto{/lang}</h2>
+                                               
+                                               <dl>
+                                                       <dt><label for="coverPhoto">{lang}wcf.acp.style.coverPhoto{/lang}</label></dt>
+                                                       <dd>
+                                                               <div id="coverPhotoPreview" style="background-image: url({@$__wcf->getPath()}images/coverPhotos/{@$style->getCoverPhoto()})"></div>
+                                                               <div id="uploadCoverPhoto">
+                                                                       <button class="jsButtonDeleteCoverPhoto"{if !$style->coverPhotoExtension} style="display:none"{/if}>{lang}wcf.global.button.delete{/lang}</button>
+                                                               </div>
+                                                               <small>{lang}wcf.acp.style.coverPhoto.description{/lang}</small>
+                                                       </dd>
+                                               </dl>
+                                               
+                                               {event name='coverPhotoFields'}
+                                       </section>
+                               {/if}
                        {/if}
                        
                        {event name='generalFieldsets'}
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Delete.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Delete.js
new file mode 100644 (file)
index 0000000..ddffc21
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Deletes the current style's default cover photo.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Delete
+ */
+define(['Ajax', 'Language', 'Ui/Confirmation', 'Ui/Notification'], function (Ajax, Language, UiConfirmation, UiNotification) {
+       "use strict";
+       
+       var _button, _styleId;
+       
+       /**
+        * @exports     WoltLabSuite/Core/Acp/Ui/Style/CoverPhoto/Delete
+        */
+       return {
+               /**
+                * Initializes the delete handler and enables the delete button on upload.
+                * 
+                * @param       {int}           styleId
+                */
+               init: function (styleId) {
+                       _styleId = styleId;
+                       
+                       _button = elBySel('.jsButtonDeleteCoverPhoto');
+                       _button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+               },
+               
+               /**
+                * Handles clicks on the delete button.
+                * 
+                * @param       {Event}         event
+                * @protected
+                */
+               _click: function (event) {
+                       event.preventDefault();
+                       
+                       UiConfirmation.show({
+                               confirm: Ajax.api.bind(Ajax, this),
+                               message: Language.get('wcf.acp.style.coverPhoto.delete.confirmMessage')
+                       });
+               },
+               
+               _ajaxSuccess: function (data) {
+                       elById('coverPhotoPreview').style.setProperty('background-image', 'url(' + data.returnValues.url + ')', '');
+                       
+                       elHide(_button);
+                       
+                       UiNotification.show();
+               },
+               
+               _ajaxSetup: function () {
+                       return {
+                               data: {
+                                       actionName: 'deleteCoverPhoto',
+                                       className: 'wcf\\data\\style\\StyleAction',
+                                       objectIDs: [_styleId]
+                               }
+                       };
+               }
+       };
+});
index b67d70ebebfeb9bb741b32b3d2c0497a11145398..f5a12254efd53991b87fef2ad4cb4ea4e7fd5226 100644 (file)
@@ -55,19 +55,19 @@ class StyleAction extends AbstractDatabaseObjectAction implements IToggleAction,
        /**
         * @inheritDoc
         */
-       protected $requireACP = ['copy', 'delete', 'markAsTainted', 'setAsDefault', 'toggle', 'update', 'upload', 'uploadLogo', 'uploadLogoMobile'];
+       protected $requireACP = ['copy', 'delete', 'deleteCoverPhoto', 'markAsTainted', 'setAsDefault', 'toggle', 'update', 'upload', 'uploadCoverPhoto', 'uploadLogo', 'uploadLogoMobile'];
        
        /**
         * style object
         * @var Style
         */
-       public $style = null;
+       public $style;
        
        /**
         * style editor object
         * @var StyleEditor
         */
-       public $styleEditor = null;
+       public $styleEditor;
        
        /**
         * @inheritDoc
@@ -667,6 +667,10 @@ BROWSERCONFIG;
         * @since       3.1
         */
        public function validateUploadCoverPhoto() {
+               if (!MODULE_USER_COVER_PHOTO) {
+                       throw new PermissionDeniedException();
+               }
+               
                // ignore tmp hash, uploading is supported for existing styles only
                // and files will be finally processed on form submit
                $this->parameters['tmpHash'] = '@@@WCF_INVALID_TMP_HASH@@@';
@@ -739,6 +743,38 @@ BROWSERCONFIG;
                return ['errorType' => $file->getValidationErrorType()];
        }
        
+       /**
+        * Validates the parameters to delete a style's default cover photo.
+        * 
+        * @throws      PermissionDeniedException
+        * @throws      UserInputException
+        * @since       3.1
+        */
+       public function validateDeleteCoverPhoto() {
+               if (!MODULE_USER_COVER_PHOTO) {
+                       throw new PermissionDeniedException();
+               }
+               
+               $this->styleEditor = $this->getSingleObject();
+               if (!$this->styleEditor->coverPhotoExtension) {
+                       throw new UserInputException('objectIDs');
+               }
+       }
+       
+       /**
+        * Deletes a style's default cover photo.
+        * 
+        * @return      string[]
+        * @since       3.1
+        */
+       public function deleteCoverPhoto() {
+               $this->styleEditor->deleteCoverPhoto();
+               
+               return [
+                       'url' => WCF::getPath().'images/coverPhotos/'.(new Style($this->styleEditor->styleID))->getCoverPhoto()
+               ];
+       }
+       
        /**
         * Validates parameters to assign a new default style.
         */
index c5affcfbc6a2002bb4b3e46a3719d176f43404a3..e92f208ded72c3b84b34f4c8495ed0544a1e4d88 100644 (file)
@@ -124,6 +124,19 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                self::resetCache();
        }
        
+       /**
+        * Deletes the style's default cover photo.
+        */
+       public function deleteCoverPhoto() {
+               if ($this->coverPhotoExtension) {
+                       @unlink(WCF_DIR.'images/coverPhotos/'.$this->styleID.'.'.$this->coverPhotoExtension);
+                       
+                       $this->update([
+                               'coverPhotoExtension' => ''
+                       ]);
+               }
+       }
+       
        /**
         * Reads the data of a style exchange format file.
         * 
index 7f2b33b18d608f7fd4c080e9679bad3eb6c35832..2f7846410c9ab41d8f71bbb0e665d232601f9267 100644 (file)
@@ -1931,7 +1931,8 @@ Als Benachrichtigungs-URL in der Konfiguration der sofortigen Zahlungsbestätigu
                <item name="wcf.acp.style.copyStyle"><![CDATA[Stil kopieren]]></item>
                <item name="wcf.acp.style.copyStyle.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} den Stil <span class="confirmationObject">{$style->styleName}</span> wirklich duplizieren?]]></item>
                <item name="wcf.acp.style.coverPhoto"><![CDATA[Standard-Profilbild]]></item>
-               <item name="wcf.acp.style.coverPhoto.description"><![CDATA[Das Bild muss mindestens {$coverPhotoMinWidth}×{$coverPhotoMinHeight} Pixel groß sein, als Dateiendung sind GIF, JPG, JPEG und PNG zulässig.]]></item>
+               <item name="wcf.acp.style.coverPhoto.delete.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} das Standard-Profilbild dieses Stils wirklich löschen? Nach dem Löschen wird das allgemeine, Stil-unabhängige Standard-Profilbild verwendet.]]></item>
+               <item name="wcf.acp.style.coverPhoto.description"><![CDATA[Das Bild muss mindestens {$coverPhotoMinWidth}×{$coverPhotoMinHeight} Pixel groß sein, als Bildformate sind GIF, JPG, JPEG und PNG zulässig.]]></item>
                <item name="wcf.acp.style.delete.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} den Stil <span class="confirmationObject">{$style->styleName}</span> wirklich löschen?]]></item>
                <item name="wcf.acp.style.edit"><![CDATA[Stil bearbeiten]]></item>
                <item name="wcf.acp.style.exportAsPackage"><![CDATA[Als Paket exportieren]]></item>
index c96f84cb7b826c436d13332e262a56eb0bd8a400..f9a63af49fb49d909bd297bcd5bc6d3cda87eb5d 100644 (file)
@@ -1872,6 +1872,7 @@ When prompted for the notification URL for the instant payment notifications, pl
                <item name="wcf.acp.style.copyStyle"><![CDATA[Duplicate Style]]></item>
                <item name="wcf.acp.style.copyStyle.confirmMessage"><![CDATA[Do you really want to duplicate the style <span class="confirmationObject">{$style->styleName}</span>?]]></item>
                <item name="wcf.acp.style.coverPhoto"><![CDATA[Default Cover Photo]]></item>
+               <item name="wcf.acp.style.coverPhoto.delete.confirmMessage"><![CDATA[Do you really want to delete the default cover photo? Once deleted, the global default photo will be used instead.]]></item>
                <item name="wcf.acp.style.coverPhoto.description"><![CDATA[The image must be at least {$coverPhotoMinWidth}×{$coverPhotoMinHeight} pixels large, acceptable image types are GIF, JPG, JPEG and PNG.]]></item>
                <item name="wcf.acp.style.delete.confirmMessage"><![CDATA[Do you really want to delete the style <span class="confirmationObject">{$style->styleName}</span>?]]></item>
                <item name="wcf.acp.style.edit"><![CDATA[Edit Style]]></item>