Skip to content

Commit f405175

Browse files
committed
image field type
1 parent bb3d72a commit f405175

File tree

7 files changed

+243
-8
lines changed

7 files changed

+243
-8
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ All Notable changes to `Backpack CRUD` will be documented in this file
2020
- Nothing
2121

2222

23+
## [3.1.7] - 2016-09-15
24+
25+
### Added
26+
- image field type - stores a base64 image from the front-end into a jpg/png file using Intervention/Image;
27+
2328

2429
## [3.1.6] - 2016-09-15
2530

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"laravelcollective/html": "~5.0",
2727
"barryvdh/laravel-elfinder": "^0.3.5",
2828
"livecontrol/eloquent-datatable": "^0.1.5",
29-
"doctrine/dbal": "^2.5"
29+
"doctrine/dbal": "^2.5",
30+
"intervention/image": "^2.3"
3031
},
3132
"require-dev": {
3233
"phpunit/phpunit" : "4.*",

src/CrudServiceProvider.php

+2
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ public function register()
7373
$this->app->register(\Backpack\Base\BaseServiceProvider::class);
7474
$this->app->register(\Collective\Html\HtmlServiceProvider::class);
7575
$this->app->register(\Barryvdh\Elfinder\ElfinderServiceProvider::class);
76+
$this->app->register(\Intervention\Image\ImageServiceProvider::class);
7677

7778
// register their aliases
7879
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
7980
$loader->alias('CRUD', \Backpack\CRUD\CrudServiceProvider::class);
8081
$loader->alias('Form', \Collective\Html\FormFacade::class);
8182
$loader->alias('Html', \Collective\Html\HtmlFacade::class);
83+
$loader->alias('Image', \Intervention\Image\Facades\Image::class);
8284
}
8385

8486
public static function resource($name, $controller, array $options = [])

src/resources/lang/en/crud.php

+1
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,6 @@
9696
'internal_link' => 'Internal link',
9797
'internal_link_placeholder' => 'Internal slug. Ex: \'admin/page\' (no quotes) for \':url\'',
9898
'external_link' => 'External link',
99+
'choose_file' => 'Choose file',
99100

100101
];

src/resources/lang/ro/crud.php

+10
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,14 @@
8585
'reorder_success_message' => 'Ordinea a fost salvată.',
8686
'reorder_error_title' => 'Eroare',
8787
'reorder_error_message' => 'Ordinea nu a fost salvată.',
88+
89+
// Fields
90+
'browse_uploads' => 'Alege din fișierele urcate',
91+
'clear' => 'Curăță',
92+
'page_link' => 'Link către pagină',
93+
'page_link_placeholder' => 'http://example.com/pagina-dorita-de-tine',
94+
'internal_link' => 'Link intern',
95+
'internal_link_placeholder' => 'Rută internă. De ex: \'admin/page\' (fără ghilimele) pentru \':url\'',
96+
'external_link' => 'Link extern',
97+
'choose_file' => 'Alege fișier',
8898
];

src/resources/views/fields/base64_image.blade.php

+7-7
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@
2020
</div>
2121
<div class="btn-group">
2222
<label class="btn btn-primary btn-file">
23-
Upload <input type="file" accept="image/*" id="uploadImage" class="hide">
23+
Choose file <input type="file" accept="image/*" id="uploadImage" class="hide">
2424
<input type="hidden" id="hiddenImage" name="{{ $field['name'] }}">
2525
</label>
2626
@if($field['crop'])
27-
<button class="btn btn-default" id="rotateLeft" type="button" style="display: none;">Rotate Left</button>
28-
<button class="btn btn-default" id="rotateRight" type="button" style="display: none;">Rotate Right</button>
29-
<button class="btn btn-default" id="zoomIn" type="button" style="display: none;">Zoom In</button>
30-
<button class="btn btn-default" id="zoomOut" type="button" style="display: none;">Zoom Out</button>
31-
<button class="btn btn-warning" id="reset" type="button" style="display: none;">Reset</button>
27+
<button class="btn btn-default" id="rotateLeft" type="button" style="display: none;"><i class="fa fa-rotate-left"></i></button>
28+
<button class="btn btn-default" id="rotateRight" type="button" style="display: none;"><i class="fa fa-rotate-right"></i></button>
29+
<button class="btn btn-default" id="zoomIn" type="button" style="display: none;"><i class="fa fa-search-plus"></i></button>
30+
<button class="btn btn-default" id="zoomOut" type="button" style="display: none;"><i class="fa fa-search-minus"></i></button>
31+
<button class="btn btn-warning" id="reset" type="button" style="display: none;"><i class="fa fa-times"></i></button>
3232
@endif
33-
<button class="btn btn-danger" id="remove" type="button">Remove</button>
33+
<button class="btn btn-danger" id="remove" type="button"><i class="fa fa-trash"></i></button>
3434
</div>
3535
</div>
3636

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
<div class="form-group col-md-12 image" data-preview="#{{ $field['name'] }}" data-aspectRatio="{{ isset($field['aspect_ratio'])?$field['aspect_ratio']:0 }}" data-crop="{{ $field['crop'] }}">
2+
<div>
3+
<label>{!! $field['label'] !!}</label>
4+
</div>
5+
<!-- Wrap the image or canvas element with a block element (container) -->
6+
<div class="row">
7+
<div class="col-sm-6" style="margin-bottom: 20px;">
8+
<img id="mainImage" src="{{ (isset($field['value']))?asset($field['value']):'' }}">
9+
</div>
10+
@if($field['crop'])
11+
<div class="col-sm-3">
12+
<div class="docs-preview clearfix">
13+
<div id="{{ $field['name'] }}" class="img-preview preview-lg">
14+
<img src="" style="display: block; min-width: 0px !important; min-height: 0px !important; max-width: none !important; max-height: none !important; margin-left: -32.875px; margin-top: -18.4922px; transform: none;">
15+
</div>
16+
</div>
17+
</div>
18+
@endif
19+
</div>
20+
<div class="btn-group">
21+
<label class="btn btn-primary btn-file">
22+
{{ trans('backpack::crud.choose_file') }} <input type="file" accept="image/*" id="uploadImage" class="hide">
23+
<input type="hidden" id="hiddenImage" name="{{ $field['name'] }}">
24+
</label>
25+
@if($field['crop'])
26+
<button class="btn btn-default" id="rotateLeft" type="button" style="display: none;"><i class="fa fa-rotate-left"></i></button>
27+
<button class="btn btn-default" id="rotateRight" type="button" style="display: none;"><i class="fa fa-rotate-right"></i></button>
28+
<button class="btn btn-default" id="zoomIn" type="button" style="display: none;"><i class="fa fa-search-plus"></i></button>
29+
<button class="btn btn-default" id="zoomOut" type="button" style="display: none;"><i class="fa fa-search-minus"></i></button>
30+
<button class="btn btn-warning" id="reset" type="button" style="display: none;"><i class="fa fa-times"></i></button>
31+
@endif
32+
<button class="btn btn-danger" id="remove" type="button"><i class="fa fa-trash"></i></button>
33+
</div>
34+
</div>
35+
36+
37+
{{-- ########################################## --}}
38+
{{-- Extra CSS and JS for this particular field --}}
39+
{{-- If a field type is shown multiple times on a form, the CSS and JS will only be loaded once --}}
40+
@if ($crud->checkIfFieldIsFirstOfItsType($field, $fields))
41+
42+
{{-- FIELD CSS - will be loaded in the after_styles section --}}
43+
@push('crud_fields_styles')
44+
{{-- YOUR CSS HERE --}}
45+
<link href="{{ asset('vendor/backpack/cropper/dist/cropper.min.css') }}" rel="stylesheet" type="text/css" />
46+
<style>
47+
.hide {
48+
display: none;
49+
}
50+
.btn-group {
51+
margin-top: 10px;
52+
}
53+
img {
54+
max-width: 100%; /* This rule is very important, please do not ignore this! */
55+
}
56+
.img-container, .img-preview {
57+
width: 100%;
58+
text-align: center;
59+
}
60+
.img-preview {
61+
float: left;
62+
margin-right: 10px;
63+
margin-bottom: 10px;
64+
overflow: hidden;
65+
}
66+
.preview-lg {
67+
width: 263px;
68+
height: 148px;
69+
}
70+
71+
.btn-file {
72+
position: relative;
73+
overflow: hidden;
74+
}
75+
.btn-file input[type=file] {
76+
position: absolute;
77+
top: 0;
78+
right: 0;
79+
min-width: 100%;
80+
min-height: 100%;
81+
font-size: 100px;
82+
text-align: right;
83+
filter: alpha(opacity=0);
84+
opacity: 0;
85+
outline: none;
86+
background: white;
87+
cursor: inherit;
88+
display: block;
89+
}
90+
</style>
91+
@endpush
92+
93+
{{-- FIELD JS - will be loaded in the after_scripts section --}}
94+
@push('crud_fields_scripts')
95+
{{-- YOUR JS HERE --}}
96+
<script src="{{ asset('vendor/backpack/cropper/dist/cropper.min.js') }}"></script>
97+
<script>
98+
jQuery(document).ready(function($) {
99+
// Loop through all instances of the image field
100+
$('.form-group.image').each(function(index){
101+
// Find DOM elements under this form-group element
102+
var $mainImage = $(this).find('#mainImage');
103+
var $uploadImage = $(this).find("#uploadImage");
104+
var $hiddenImage = $(this).find("#hiddenImage");
105+
var $rotateLeft = $(this).find("#rotateLeft")
106+
var $rotateRight = $(this).find("#rotateRight")
107+
var $zoomIn = $(this).find("#zoomIn")
108+
var $zoomOut = $(this).find("#zoomOut")
109+
var $reset = $(this).find("#reset")
110+
var $remove = $(this).find("#remove")
111+
// Options either global for all image type fields, or use 'data-*' elements for options passed in via the CRUD controller
112+
var options = {
113+
viewMode: 2,
114+
checkOrientation: false,
115+
autoCropArea: 1,
116+
responsive: true,
117+
preview : $(this).attr('data-preview'),
118+
aspectRatio : $(this).attr('data-aspectRatio')
119+
};
120+
var crop = $(this).attr('data-crop');
121+
122+
// Hide 'Remove' button if there is no image saved
123+
if (!$mainImage.attr('src')){
124+
$remove.hide();
125+
}
126+
// Initialise hidden form input in case we submit with no change
127+
$hiddenImage.val($mainImage.attr('src'));
128+
129+
130+
// Only initialize cropper plugin if crop is set to true
131+
if(crop){
132+
133+
$remove.click(function() {
134+
$mainImage.cropper("destroy");
135+
$mainImage.attr('src','');
136+
$hiddenImage.val('');
137+
$rotateLeft.hide();
138+
$rotateRight.hide();
139+
$zoomIn.hide();
140+
$zoomOut.hide();
141+
$reset.hide();
142+
$remove.hide();
143+
});
144+
} else {
145+
146+
$(this).find("#remove").click(function() {
147+
$mainImage.attr('src','');
148+
$hiddenImage.val('');
149+
$remove.hide();
150+
});
151+
}
152+
153+
$uploadImage.change(function() {
154+
var fileReader = new FileReader(),
155+
files = this.files,
156+
file;
157+
158+
if (!files.length) {
159+
return;
160+
}
161+
file = files[0];
162+
163+
if (/^image\/\w+$/.test(file.type)) {
164+
fileReader.readAsDataURL(file);
165+
fileReader.onload = function () {
166+
$uploadImage.val("");
167+
if(crop){
168+
$mainImage.cropper(options).cropper("reset", true).cropper("replace", this.result);
169+
// Override form submit to copy canvas to hidden input before submitting
170+
$('form').submit(function() {
171+
var imageURL = $mainImage.cropper('getCroppedCanvas').toDataURL();
172+
$hiddenImage.val(imageURL);
173+
return true; // return false to cancel form action
174+
});
175+
$rotateLeft.click(function() {
176+
$mainImage.cropper("rotate", 90);
177+
});
178+
$rotateRight.click(function() {
179+
$mainImage.cropper("rotate", -90);
180+
});
181+
$zoomIn.click(function() {
182+
$mainImage.cropper("zoom", 0.1);
183+
});
184+
$zoomOut.click(function() {
185+
$mainImage.cropper("zoom", -0.1);
186+
});
187+
$reset.click(function() {
188+
$mainImage.cropper("reset");
189+
});
190+
$rotateLeft.show();
191+
$rotateRight.show();
192+
$zoomIn.show();
193+
$zoomOut.show();
194+
$reset.show();
195+
$remove.show();
196+
197+
} else {
198+
$mainImage.attr('src',this.result);
199+
$hiddenImage.val(this.result);
200+
$remove.show();
201+
}
202+
};
203+
} else {
204+
alert("Please choose an image file.");
205+
}
206+
});
207+
208+
});
209+
});
210+
</script>
211+
212+
213+
@endpush
214+
@endif
215+
{{-- End of Extra CSS and JS --}}
216+
{{-- ########################################## --}}

0 commit comments

Comments
 (0)