]> BookStack Code Mirror - bookstack/commitdiff
converted image picker to blade-based component
authorDan Brown <redacted>
Sat, 24 Dec 2016 15:21:19 +0000 (15:21 +0000)
committerDan Brown <redacted>
Sat, 24 Dec 2016 15:21:19 +0000 (15:21 +0000)
Also updated some other JS translations

15 files changed:
app/Http/Controllers/AttachmentController.php
app/Http/Controllers/Controller.php
config/setting-defaults.php
resources/assets/js/controllers.js
resources/assets/js/directives.js
resources/assets/js/global.js
resources/assets/sass/_components.scss
resources/lang/en/common.php
resources/lang/en/components.php
resources/lang/en/entities.php
resources/views/components/image-picker.blade.php [new file with mode: 0644]
resources/views/pages/form-toolbox.blade.php
resources/views/partials/image-manager.blade.php
resources/views/settings/index.blade.php
resources/views/users/edit.blade.php

index e61a488ce9e3b2d56a4887cb533dd465c8f07053..a81cb8a68167182be8afeaae606ed9b3b31e133f 100644 (file)
@@ -117,7 +117,7 @@ class AttachmentController extends Controller
         }
 
         $attachment = $this->attachmentService->updateFile($attachment, $request->all());
-        return $attachment;
+        return $this->jsonSuccess($attachment, trans('entities.attachments_updated_success'));
     }
 
     /**
index 2b6c88fe0b73748ab5cbdf61a64138dcdc4d5c49..c5255c0ba53715436d09cf82c16b963ca201e8ad 100644 (file)
@@ -117,6 +117,17 @@ abstract class Controller extends BaseController
         return true;
     }
 
+    /**
+     * Send a json respons with a message attached as a header.
+     * @param $data
+     * @param string $successMessage
+     * @return $this
+     */
+    protected function jsonSuccess($data, $successMessage = "")
+    {
+        return response()->json($data)->header('message-success', $successMessage);
+    }
+
     /**
      * Send back a json error message.
      * @param string $messageText
index c681bb7f55ddbe97e6ba73b29154373e18d2db55..db35023d5e727f926a63f7858d28cc64cb2a34b2 100644 (file)
@@ -6,6 +6,7 @@
 return [
 
     'app-name'        => 'BookStack',
+    'app-logo' => '',
     'app-name-header' => true,
     'app-editor'      => 'wysiwyg',
     'app-color'       => '#0288D1',
index 3a465da97221710c312eb768452cb8ef36a39e7e..af740f508e9585fa83daf971d328c96bc5392bba 100644 (file)
@@ -576,7 +576,7 @@ export default function (ngApp, events) {
              * Get files for the current page from the server.
              */
             function getFiles() {
-                let url = window.baseUrl(`/attachments/get/page/${pageId}`)
+                let url = window.baseUrl(`/attachments/get/page/${pageId}`);
                 $http.get(url).then(resp => {
                     $scope.files = resp.data;
                     currentOrder = resp.data.map(file => {return file.id}).join(':');
@@ -672,7 +672,7 @@ export default function (ngApp, events) {
                         $scope.editFile.link = '';
                     }
                     $scope.editFile = false;
-                    events.emit('success', 'Attachment details updated');
+                    events.emit('success', resp.headers('message-success'));
                 }, checkError('edit'));
             };
 
index 0ff7c0fcca8c3e920df87f8edcebb09ad73ad443..68c3150e02a48fb4f6ee5c1829bc421cb41bc7be 100644 (file)
@@ -35,7 +35,7 @@ export default function (ngApp, events) {
     });
 
     /**
-     * Sub form component to allow inner-form sections to act like thier own forms.
+     * Sub form component to allow inner-form sections to act like their own forms.
      */
     ngApp.directive('subForm', function() {
         return {
@@ -50,96 +50,13 @@ export default function (ngApp, events) {
                 element.find('button[type="submit"]').click(submitEvent);
 
                 function submitEvent(e) {
-                    e.preventDefault()
+                    e.preventDefault();
                     if (attrs.subForm) scope.$eval(attrs.subForm);
                 }
             }
         };
     });
 
-
-    /**
-     * Image Picker
-     * Is a simple front-end interface that connects to an ImageManager if present.
-     */
-    ngApp.directive('imagePicker', ['$http', 'imageManagerService', function ($http, imageManagerService) {
-        return {
-            restrict: 'E',
-            template: `
-            <div class="image-picker">
-                <div>
-                    <img ng-if="image && image !== 'none'" ng-src="{{image}}" ng-class="{{imageClass}}" alt="Image Preview">
-                    <img ng-if="image === '' && defaultImage" ng-src="{{defaultImage}}" ng-class="{{imageClass}}" alt="Image Preview">
-                </div>
-                <button class="button" type="button" ng-click="showImageManager()">Select Image</button>
-                <br>
-
-                <button class="text-button" ng-click="reset()" type="button">Reset</button>
-                <span ng-show="showRemove" class="sep">|</span>
-                <button ng-show="showRemove" class="text-button neg" ng-click="remove()" type="button">Remove</button>
-
-                <input type="hidden" ng-attr-name="{{name}}" ng-attr-id="{{name}}" ng-attr-value="{{value}}">
-            </div>
-            `,
-            scope: {
-                name: '@',
-                resizeHeight: '@',
-                resizeWidth: '@',
-                resizeCrop: '@',
-                showRemove: '=',
-                currentImage: '@',
-                currentId: '@',
-                defaultImage: '@',
-                imageClass: '@'
-            },
-            link: function (scope, element, attrs) {
-                let usingIds = typeof scope.currentId !== 'undefined' || scope.currentId === 'false';
-                scope.image = scope.currentImage;
-                scope.value = scope.currentImage || '';
-                if (usingIds) scope.value = scope.currentId;
-
-                function setImage(imageModel, imageUrl) {
-                    scope.image = imageUrl;
-                    scope.value = usingIds ? imageModel.id : imageUrl;
-                }
-
-                scope.reset = function () {
-                    setImage({id: 0}, scope.defaultImage);
-                };
-
-                scope.remove = function () {
-                    scope.image = 'none';
-                    scope.value = 'none';
-                };
-
-                scope.showImageManager = function () {
-                    imageManagerService.show((image) => {
-                        scope.updateImageFromModel(image);
-                    });
-                };
-
-                scope.updateImageFromModel = function (model) {
-                    let isResized = scope.resizeWidth && scope.resizeHeight;
-
-                    if (!isResized) {
-                        scope.$apply(() => {
-                            setImage(model, model.url);
-                        });
-                        return;
-                    }
-
-                    let cropped = scope.resizeCrop ? 'true' : 'false';
-                    let requestString = '/images/thumb/' + model.id + '/' + scope.resizeWidth + '/' + scope.resizeHeight + '/' + cropped;
-                    requestString = window.baseUrl(requestString);
-                    $http.get(requestString).then((response) => {
-                        setImage(model, response.data.url);
-                    });
-                };
-
-            }
-        };
-    }]);
-
     /**
      * DropZone
      * Used for uploading images
@@ -149,16 +66,17 @@ export default function (ngApp, events) {
             restrict: 'E',
             template: `
             <div class="dropzone-container">
-                <div class="dz-message">Drop files or click here to upload</div>
+                <div class="dz-message">{{message}}</div>
             </div>
             `,
             scope: {
                 uploadUrl: '@',
                 eventSuccess: '=',
                 eventError: '=',
-                uploadedTo: '@'
+                uploadedTo: '@',
             },
             link: function (scope, element, attrs) {
+                scope.message = attrs.message;
                 if (attrs.placeholder) element[0].querySelector('.dz-message').textContent = attrs.placeholder;
                 let dropZone = new DropZone(element[0].querySelector('.dropzone-container'), {
                     url: scope.uploadUrl,
index 537a43a4eff3a555f6cfafe4f24a26a276196e74..7a7cc592efca1eb1963ecd6f0e659ec27be23246 100644 (file)
@@ -161,6 +161,51 @@ $(function () {
         });
     }
 
+    // Image pickers
+    $('.image-picker').on('click', 'button', event => {
+        let button = event.target;
+        let picker = $(button).closest('.image-picker')[0];
+        let action = button.getAttribute('data-action');
+        let resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width');
+        let usingIds = picker.getAttribute('data-current-id') !== '';
+        let resizeCrop = picker.getAttribute('data-resize-crop') !== '';
+        let imageElem = picker.querySelector('img');
+        let input = picker.querySelector('input');
+
+        function setImage(image) {
+
+            if (image === 'none') {
+                imageElem.src = picker.getAttribute('data-default-image');
+                imageElem.classList.add('none');
+                input.value = 'none';
+                return;
+            }
+
+            imageElem.src = image.url;
+            input.value = usingIds ? image.id : image.url;
+            imageElem.classList.remove('none');
+        }
+
+        if (action === 'show-image-manager') {
+            window.ImageManager.showExternal((image) => {
+                if (!resize) {
+                    setImage(image);
+                    return;
+                }
+                let requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false');
+                $.get(window.baseUrl(requestString), resp => {
+                    image.url = resp.url;
+                    setImage(image);
+                });
+            });
+        } else if (action === 'reset-image') {
+            setImage({id: 0, url: picker.getAttribute('data-default-image')});
+        } else if (action === 'remove-image') {
+            setImage('none');
+        }
+
+    });
+
     // Detect IE for css
     if(navigator.userAgent.indexOf('MSIE')!==-1
         || navigator.appVersion.indexOf('Trident/') > 0
index 2f9051a5258e94d9a821fd71c73aac30569c1501..c8fd8bcfabd56235bf392fed177898c830115d0b 100644 (file)
@@ -465,4 +465,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
       border-bottom-width: 3px;
     }
   }
+}
+
+.image-picker .none {
+  display: none;
 }
\ No newline at end of file
index 674ed32dc0fba0d76d6cebe9bf776bbc73443364..31ef42e97e0045e57ff5de563b71d40c40b35869 100644 (file)
@@ -31,6 +31,8 @@ return [
     'delete' => 'Delete',
     'search' => 'Search',
     'search_clear' => 'Clear Search',
+    'reset' => 'Reset',
+    'remove' => 'Remove',
 
 
     /**
index 9fe277059583731cc7f7ad7ecf689ab1d386512a..cc023799a9cf4eb6093c0611e9c69aef6de198cb 100644 (file)
@@ -15,5 +15,7 @@ return [
     'imagem_image_name' => 'Image Name',
     'imagem_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.',
     'imagem_select_image' => 'Select Image',
+    'imagem_dropzone' => 'Drop images or click here to upload',
     'images_deleted' => 'Images Deleted',
+    'image_preview' => 'Image Preview',
 ];
\ No newline at end of file
index 855cea20c223381fbc8def4a8503d8786f3dea39..87ffb49856b003351bfb43aa1bfcff5a82179b75 100644 (file)
@@ -193,6 +193,7 @@ return [
     'attachments_link' => 'Attach Link',
     'attachments_set_link' => 'Set Link',
     'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_dropzone' => 'Drop files or click here to attach a file',
     'attachments_no_files' => 'No files have been uploaded',
     'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
     'attachments_link_name' => 'Link Name',
@@ -204,6 +205,7 @@ return [
     'attachments_edit_file_name' => 'File Name',
     'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
     'attachments_order_updated' => 'Attachment order updated',
+    'attachments_updated_success' => 'Attachment details updated',
     'attachments_deleted' => 'Attachment deleted',
 
     /**
diff --git a/resources/views/components/image-picker.blade.php b/resources/views/components/image-picker.blade.php
new file mode 100644 (file)
index 0000000..c2c56ee
--- /dev/null
@@ -0,0 +1,17 @@
+<div class="image-picker" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId or '' }}" data-resize-crop="{{ $resizeCrop or '' }}">
+
+    <div>
+        <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif  class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}">
+    </div>
+
+    <button class="button" type="button" data-action="show-image-manager">{{ trans('components.imagem_select_image') }}</button>
+    <br>
+    <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button>
+
+    @if ($showRemove)
+        <span class="sep">|</span>
+        <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button>
+    @endif
+
+    <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{$currentId or $currentImage or ''}}">
+</div>
\ No newline at end of file
index cd9b7ed80013ed547aa887b0a96843e6231af494..ecf7619b766c29e99ea3ffb503e978cd7fe6a9c2 100644 (file)
@@ -75,7 +75,7 @@
                             </p>
                         </div>
                         <div tab-content="file">
-                            <drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
+                            <drop-zone message="{{ trans('entities.attachments_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
                         </div>
                         <div tab-content="link" sub-form="attachLinkSubmit(file)">
                             <p class="muted small">{{ trans('entities.attachments_explain_link') }}</p>
                         </div>
                     </div>
 
-                    <button type="button" class="button" ng-click="cancelEdit()">{{ trans('entities.back') }}</button>
-                    <button type="submit" class="button pos">{{ trans('entities.save') }}</button>
+                    <button type="button" class="button" ng-click="cancelEdit()">{{ trans('common.back') }}</button>
+                    <button type="submit" class="button pos">{{ trans('common.save') }}</button>
                 </div>
 
             </div>
index bed269fbec069070e5aced07a392eeedf49e5d79..0d3626f5eeb32febe4e3deb0795e7b4839dcb121 100644 (file)
@@ -78,7 +78,7 @@
 
                         </div>
 
-                        <drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
+                        <drop-zone message="{{ trans('components.imagem_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
 
 
                     </div>
index 2e9d5ce8ce9da31d0586e184dd7216f2fec3a662..1645fdfd0e063f8cc29533bc00cba4a9b975fc32 100644 (file)
@@ -8,7 +8,7 @@
 
     <h1>{{ trans('settings.settings') }}</h1>
 
-    <form action="{{ baseUrl("/settings") }}" method="POST" ng-cloak>
+    <form action="{{ baseUrl("/settings") }}" method="POST">
         {!! csrf_field() !!}
 
         <h3>{{ trans('settings.app_settings') }}</h3>
                 <div class="form-group" id="logo-control">
                     <label for="setting-app-logo">{{ trans('settings.app_logo') }}</label>
                     <p class="small">{!! trans('settings.app_logo_desc') !!}</p>
-                    <image-picker resize-height="43" show-remove="true" resize-width="200" current-image="{{ setting('app-logo', '') }}" default-image="{{ baseUrl('/logo.png') }}" name="setting-app-logo" image-class="logo-image"></image-picker>
+
+                    @include('components.image-picker', [
+                        'resizeHeight' => '43',
+                        'resizeWidth' => '200',
+                        'showRemove' => true,
+                        'defaultImage' => baseUrl('/logo.png'),
+                        'currentImage' => setting('app-logo'),
+                        'name' => 'setting-app-logo',
+                        'imageClass' => 'logo-image'
+                    ])
+
                 </div>
                 <div class="form-group" id="color-control">
                     <label for="setting-app-color">{{ trans('settings.app_primary_color') }}</label>
index b753dfc455b2d9409b0ea369a8d0698f58bd097f..5b55a228395590c19a445d3342a01f4f6324f3bb 100644 (file)
                 <div class="form-group" id="logo-control">
                     <label for="user-avatar">{{ trans('settings.users_avatar') }}</label>
                     <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
-                    <image-picker resize-height="512" resize-width="512" current-image="{{ $user->getAvatar(80) }}" current-id="{{ $user->image_id }}" default-image="{{ baseUrl("/user_avatar.png") }}" name="image_id" show-remove="false" image-class="['avatar' ,'large']"></image-picker>
+
+                    @include('components.image-picker', [
+                          'resizeHeight' => '512',
+                          'resizeWidth' => '512',
+                          'showRemove' => false,
+                          'defaultImage' => baseUrl('/user_avatar.png'),
+                          'currentImage' => $user->getAvatar(80),
+                          'currentId' => $user->image_id,
+                          'name' => 'image_id',
+                          'imageClass' => 'avatar large'
+                      ])
+
                 </div>
             </div>
         </div>