Image uploads now quicker, and image sized reduced with links to originals
Showing
4 changed files
with
104 additions
and
20 deletions
| ... | @@ -39,27 +39,40 @@ class ImageController extends Controller | ... | @@ -39,27 +39,40 @@ class ImageController extends Controller |
| 39 | $images = $this->image->orderBy('created_at', 'desc') | 39 | $images = $this->image->orderBy('created_at', 'desc') |
| 40 | ->skip($page * $pageSize)->take($pageSize)->get(); | 40 | ->skip($page * $pageSize)->take($pageSize)->get(); |
| 41 | foreach ($images as $image) { | 41 | foreach ($images as $image) { |
| 42 | - $image->thumbnail = $this->getThumbnail($image, 150, 150); | 42 | + $this->loadSizes($image); |
| 43 | } | 43 | } |
| 44 | $hasMore = $this->image->orderBy('created_at', 'desc') | 44 | $hasMore = $this->image->orderBy('created_at', 'desc') |
| 45 | ->skip(($page + 1) * $pageSize)->take($pageSize)->count() > 0; | 45 | ->skip(($page + 1) * $pageSize)->take($pageSize)->count() > 0; |
| 46 | return response()->json([ | 46 | return response()->json([ |
| 47 | - 'images' => $images, | 47 | + 'images' => $images, |
| 48 | 'hasMore' => $hasMore | 48 | 'hasMore' => $hasMore |
| 49 | ]); | 49 | ]); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | /** | 52 | /** |
| 53 | + * Loads the standard thumbnail sizes for an image. | ||
| 54 | + * @param Image $image | ||
| 55 | + */ | ||
| 56 | + private function loadSizes(Image $image) | ||
| 57 | + { | ||
| 58 | + $image->thumbnail = $this->getThumbnail($image, 150, 150); | ||
| 59 | + $image->display = $this->getThumbnail($image, 840, 0, true); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + /** | ||
| 53 | * Get the thumbnail for an image. | 63 | * Get the thumbnail for an image. |
| 54 | - * @param $image | 64 | + * If $keepRatio is true only the width will be used. |
| 55 | - * @param int $width | 65 | + * @param $image |
| 56 | - * @param int $height | 66 | + * @param int $width |
| 67 | + * @param int $height | ||
| 68 | + * @param bool $keepRatio | ||
| 57 | * @return string | 69 | * @return string |
| 58 | */ | 70 | */ |
| 59 | - public function getThumbnail($image, $width = 220, $height = 220) | 71 | + public function getThumbnail($image, $width = 220, $height = 220, $keepRatio = false) |
| 60 | { | 72 | { |
| 61 | $explodedPath = explode('/', $image->url); | 73 | $explodedPath = explode('/', $image->url); |
| 62 | - array_splice($explodedPath, 4, 0, ['thumbs-' . $width . '-' . $height]); | 74 | + $dirPrefix = $keepRatio ? 'scaled-' : 'thumbs-'; |
| 75 | + array_splice($explodedPath, 4, 0, [$dirPrefix . $width . '-' . $height]); | ||
| 63 | $thumbPath = implode('/', $explodedPath); | 76 | $thumbPath = implode('/', $explodedPath); |
| 64 | $thumbFilePath = public_path() . $thumbPath; | 77 | $thumbFilePath = public_path() . $thumbPath; |
| 65 | 78 | ||
| ... | @@ -70,7 +83,14 @@ class ImageController extends Controller | ... | @@ -70,7 +83,14 @@ class ImageController extends Controller |
| 70 | 83 | ||
| 71 | // Otherwise create the thumbnail | 84 | // Otherwise create the thumbnail |
| 72 | $thumb = ImageTool::make(public_path() . $image->url); | 85 | $thumb = ImageTool::make(public_path() . $image->url); |
| 73 | - $thumb->fit($width, $height); | 86 | + if($keepRatio) { |
| 87 | + $thumb->resize($width, null, function ($constraint) { | ||
| 88 | + $constraint->aspectRatio(); | ||
| 89 | + $constraint->upsize(); | ||
| 90 | + }); | ||
| 91 | + } else { | ||
| 92 | + $thumb->fit($width, $height); | ||
| 93 | + } | ||
| 74 | 94 | ||
| 75 | // Create thumbnail folder if it does not exist | 95 | // Create thumbnail folder if it does not exist |
| 76 | if (!file_exists(dirname($thumbFilePath))) { | 96 | if (!file_exists(dirname($thumbFilePath))) { |
| ... | @@ -107,7 +127,7 @@ class ImageController extends Controller | ... | @@ -107,7 +127,7 @@ class ImageController extends Controller |
| 107 | $this->image->created_by = auth()->user()->id; | 127 | $this->image->created_by = auth()->user()->id; |
| 108 | $this->image->updated_by = auth()->user()->id; | 128 | $this->image->updated_by = auth()->user()->id; |
| 109 | $this->image->save(); | 129 | $this->image->save(); |
| 110 | - $this->image->thumbnail = $this->getThumbnail($this->image, 150, 150); | 130 | + $this->loadSizes($this->image); |
| 111 | return response()->json($this->image); | 131 | return response()->json($this->image); |
| 112 | } | 132 | } |
| 113 | 133 | ||
| ... | @@ -126,6 +146,7 @@ class ImageController extends Controller | ... | @@ -126,6 +146,7 @@ class ImageController extends Controller |
| 126 | $image = $this->image->findOrFail($imageId); | 146 | $image = $this->image->findOrFail($imageId); |
| 127 | $image->fill($request->all()); | 147 | $image->fill($request->all()); |
| 128 | $image->save(); | 148 | $image->save(); |
| 149 | + $this->loadSizes($image); | ||
| 129 | return response()->json($this->image); | 150 | return response()->json($this->image); |
| 130 | } | 151 | } |
| 131 | 152 | ... | ... |
| ... | @@ -14,7 +14,7 @@ | ... | @@ -14,7 +14,7 @@ |
| 14 | "tests" | 14 | "tests" |
| 15 | ], | 15 | ], |
| 16 | "dependencies": { | 16 | "dependencies": { |
| 17 | - "tinymce-dist": "~4.2.1", | 17 | + "tinymce-dist": "~4.2.6", |
| 18 | "bootstrap": "~3.3.5", | 18 | "bootstrap": "~3.3.5", |
| 19 | "jquery-sortable": "~0.9.13", | 19 | "jquery-sortable": "~0.9.13", |
| 20 | "material-design-iconic-font": "~2.1.1" | 20 | "material-design-iconic-font": "~2.1.1" | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | <div id="image-manager"> | 2 | <div id="image-manager"> |
| 3 | - <div class="overlay" v-el="overlay" v-on="click: overlayClick" > | 3 | + <div class="overlay" v-el="overlay" v-on="click: overlayClick"> |
| 4 | <div class="image-manager-body"> | 4 | <div class="image-manager-body"> |
| 5 | <div class="image-manager-content"> | 5 | <div class="image-manager-content"> |
| 6 | <div class="image-manager-list"> | 6 | <div class="image-manager-list"> |
| ... | @@ -32,7 +32,8 @@ | ... | @@ -32,7 +32,8 @@ |
| 32 | <hr class="even"> | 32 | <hr class="even"> |
| 33 | <div v-show="dependantPages"> | 33 | <div v-show="dependantPages"> |
| 34 | <p class="text-neg text-small"> | 34 | <p class="text-neg text-small"> |
| 35 | - This image is used in the pages below, Click delete again to confirm you want to delete this image. | 35 | + This image is used in the pages below, Click delete again to confirm you want to delete |
| 36 | + this image. | ||
| 36 | </p> | 37 | </p> |
| 37 | <ul class="text-neg"> | 38 | <ul class="text-neg"> |
| 38 | <li v-repeat="page: dependantPages"> | 39 | <li v-repeat="page: dependantPages"> |
| ... | @@ -46,7 +47,9 @@ | ... | @@ -46,7 +47,9 @@ |
| 46 | </form> | 47 | </form> |
| 47 | </div> | 48 | </div> |
| 48 | <div class="image-manager-bottom"> | 49 | <div class="image-manager-bottom"> |
| 49 | - <button class="button pos anim fadeIn" v-show="selectedImage" v-on="click:selectButtonClick"><i class="zmdi zmdi-square-right"></i>Select Image</button> | 50 | + <button class="button pos anim fadeIn" v-show="selectedImage" v-on="click:selectButtonClick"><i |
| 51 | + class="zmdi zmdi-square-right"></i>Select Image | ||
| 52 | + </button> | ||
| 50 | </div> | 53 | </div> |
| 51 | </div> | 54 | </div> |
| 52 | </div> | 55 | </div> |
| ... | @@ -59,7 +62,7 @@ | ... | @@ -59,7 +62,7 @@ |
| 59 | var Dropzone = require('dropzone'); | 62 | var Dropzone = require('dropzone'); |
| 60 | 63 | ||
| 61 | module.exports = { | 64 | module.exports = { |
| 62 | - data: function(){ | 65 | + data: function () { |
| 63 | return { | 66 | return { |
| 64 | images: [], | 67 | images: [], |
| 65 | hasMore: false, | 68 | hasMore: false, |
| ... | @@ -68,13 +71,12 @@ | ... | @@ -68,13 +71,12 @@ |
| 68 | selectedImage: false, | 71 | selectedImage: false, |
| 69 | dependantPages: false, | 72 | dependantPages: false, |
| 70 | deleteForm: {}, | 73 | deleteForm: {}, |
| 71 | - token: document.querySelector('meta[name=token]').getAttribute('content') | 74 | + token: document.querySelector('meta[name=token]').getAttribute('content'), |
| 75 | + dataLoaded: false | ||
| 72 | } | 76 | } |
| 73 | }, | 77 | }, |
| 74 | 78 | ||
| 75 | created: function () { | 79 | created: function () { |
| 76 | - // Get initial images | ||
| 77 | - this.fetchData(this.page); | ||
| 78 | window.ImageManager = this; | 80 | window.ImageManager = this; |
| 79 | }, | 81 | }, |
| 80 | 82 | ||
| ... | @@ -139,6 +141,11 @@ | ... | @@ -139,6 +141,11 @@ |
| 139 | show: function (callback) { | 141 | show: function (callback) { |
| 140 | this.callback = callback; | 142 | this.callback = callback; |
| 141 | this.$$.overlay.style.display = 'block'; | 143 | this.$$.overlay.style.display = 'block'; |
| 144 | + // Get initial images if they have not yet been loaded in. | ||
| 145 | + if (!this.dataLoaded) { | ||
| 146 | + this.fetchData(this.page); | ||
| 147 | + this.dataLoaded = true; | ||
| 148 | + } | ||
| 142 | }, | 149 | }, |
| 143 | 150 | ||
| 144 | overlayClick: function (e) { | 151 | overlayClick: function (e) { |
| ... | @@ -178,9 +185,9 @@ | ... | @@ -178,9 +185,9 @@ |
| 178 | _this.images.splice(_this.images.indexOf(_this.selectedImage), 1); | 185 | _this.images.splice(_this.images.indexOf(_this.selectedImage), 1); |
| 179 | _this.selectedImage = false; | 186 | _this.selectedImage = false; |
| 180 | $(_this.$$.imageTitle).showSuccess('Image Deleted'); | 187 | $(_this.$$.imageTitle).showSuccess('Image Deleted'); |
| 181 | - }).fail(function(jqXHR, textStatus) { | 188 | + }).fail(function (jqXHR, textStatus) { |
| 182 | // Pages failure | 189 | // Pages failure |
| 183 | - if(jqXHR.status === 400) { | 190 | + if (jqXHR.status === 400) { |
| 184 | _this.dependantPages = jqXHR.responseJSON; | 191 | _this.dependantPages = jqXHR.responseJSON; |
| 185 | } | 192 | } |
| 186 | }); | 193 | }); | ... | ... |
| ... | @@ -15,7 +15,7 @@ module.exports = { | ... | @@ -15,7 +15,7 @@ module.exports = { |
| 15 | automatic_uploads: false, | 15 | automatic_uploads: false, |
| 16 | valid_children: "-div[p|pre|h1|h2|h3|h4|h5|h6|blockquote]", | 16 | valid_children: "-div[p|pre|h1|h2|h3|h4|h5|h6|blockquote]", |
| 17 | plugins: "image table textcolor paste link imagetools fullscreen code hr", | 17 | plugins: "image table textcolor paste link imagetools fullscreen code hr", |
| 18 | - toolbar: "undo redo | styleselect | bold italic underline strikethrough superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image link hr | removeformat code fullscreen", | 18 | + toolbar: "undo redo | styleselect | bold italic underline strikethrough superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image-insert link hr | removeformat code fullscreen", |
| 19 | content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}", | 19 | content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}", |
| 20 | style_formats: [ | 20 | style_formats: [ |
| 21 | {title: "Header 1", format: "h1"}, | 21 | {title: "Header 1", format: "h1"}, |
| ... | @@ -51,6 +51,62 @@ module.exports = { | ... | @@ -51,6 +51,62 @@ module.exports = { |
| 51 | } | 51 | } |
| 52 | }, | 52 | }, |
| 53 | setup: function(editor) { | 53 | setup: function(editor) { |
| 54 | + | ||
| 55 | + ( function() { | ||
| 56 | + var wrap; | ||
| 57 | + | ||
| 58 | + function hasTextContent( node ) { | ||
| 59 | + return node && !! ( node.textContent || node.innerText ); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + editor.on( 'dragstart', function() { | ||
| 63 | + var node = editor.selection.getNode(); | ||
| 64 | + | ||
| 65 | + if ( node.nodeName === 'IMG' ) { | ||
| 66 | + wrap = editor.dom.getParent( node, '.mceTemp' ); | ||
| 67 | + | ||
| 68 | + if ( ! wrap && node.parentNode.nodeName === 'A' && ! hasTextContent( node.parentNode ) ) { | ||
| 69 | + wrap = node.parentNode; | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | + } ); | ||
| 73 | + | ||
| 74 | + editor.on( 'drop', function( event ) { | ||
| 75 | + var dom = editor.dom, | ||
| 76 | + rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint( event.clientX, event.clientY, editor.getDoc() ); | ||
| 77 | + | ||
| 78 | + // Don't allow anything to be dropped in a captioned image. | ||
| 79 | + if ( dom.getParent( rng.startContainer, '.mceTemp' ) ) { | ||
| 80 | + event.preventDefault(); | ||
| 81 | + } else if ( wrap ) { | ||
| 82 | + event.preventDefault(); | ||
| 83 | + | ||
| 84 | + editor.undoManager.transact( function() { | ||
| 85 | + editor.selection.setRng( rng ); | ||
| 86 | + editor.selection.setNode( wrap ); | ||
| 87 | + dom.remove( wrap ); | ||
| 88 | + } ); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + wrap = null; | ||
| 92 | + } ); | ||
| 93 | + } )(); | ||
| 94 | + | ||
| 95 | + // Image picker button | ||
| 96 | + editor.addButton('image-insert', { | ||
| 97 | + title: 'My title', | ||
| 98 | + icon: 'image', | ||
| 99 | + tooltip: 'Insert an image', | ||
| 100 | + onclick: function() { | ||
| 101 | + ImageManager.show(function(image) { | ||
| 102 | + var html = '<p><a href="'+image.url+'" target="_blank">'; | ||
| 103 | + html += '<img src="'+image.display+'" alt="'+image.name+'">'; | ||
| 104 | + html += '</a></p>'; | ||
| 105 | + console.log(image); | ||
| 106 | + editor.execCommand('mceInsertContent', false, html); | ||
| 107 | + }); | ||
| 108 | + } | ||
| 109 | + }); | ||
| 54 | // Paste image-uploads | 110 | // Paste image-uploads |
| 55 | editor.on('paste', function(e) { | 111 | editor.on('paste', function(e) { |
| 56 | if(e.clipboardData) { | 112 | if(e.clipboardData) { | ... | ... |
-
Please register or sign in to post a comment