oxen_media 0.0.6 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/components/dropzone.js.jsx.coffee +298 -0
- data/app/assets/javascripts/components/photocard.js.jsx.coffee +77 -0
- data/app/assets/javascripts/components/photos.js.jsx.coffee +68 -0
- data/app/assets/javascripts/oxen_media.js +1 -3
- data/app/assets/stylesheets/oxen_media/drop_zone.css +86 -0
- data/app/assets/stylesheets/oxen_media.css +3 -0
- data/app/controllers/photos_controller.rb +26 -0
- data/app/models/ox_photo.rb +17 -0
- data/app/policies/ox_photo_policy.rb +3 -0
- data/app/uploaders/photo_uploader.rb +53 -0
- data/app/views/photos/_photo.html.haml +11 -0
- data/app/views/photos/create.js.haml +1 -0
- data/app/views/photos/index.json.jbuilder +5 -0
- data/lib/oxen_media/version.rb +1 -1
- data/lib/oxen_media.rb +1 -1
- data/oxen_media.gemspec +4 -4
- metadata +15 -86
- data/app/assets/javascripts/jsrender.min.js +0 -4
- data/app/assets/javascripts/jsrender.min.js.map +0 -463
- data/app/assets/javascripts/oxen_media/carrier_wave_cropper.js.coffee +0 -22
- data/app/assets/javascripts/oxen_media/media.js.coffee +0 -469
- data/app/assets/javascripts/oxen_media/medium_pane.js.coffee +0 -107
- data/app/assets/javascripts/templates/selected_files.html +0 -35
- data/app/assets/stylesheets/media.css +0 -10
- data/app/assets/stylesheets/scaffold.css +0 -56
- data/app/controllers/media_controller.rb +0 -106
- data/app/helpers/media_helper.rb +0 -2
- data/app/models/medium.rb +0 -25
- data/app/policies/oxen_medium_policy.rb +0 -12
- data/app/uploaders/medium_uploader.rb +0 -153
- data/app/views/media/_fields.html.haml +0 -42
- data/app/views/media/_form.html.haml +0 -48
- data/app/views/media/_media.html.haml +0 -18
- data/app/views/media/_medium.html.haml +0 -25
- data/app/views/media/_medium_fields.html.haml +0 -4
- data/app/views/media/create.js.haml +0 -2
- data/app/views/media/crop.html.haml +0 -3
- data/app/views/media/edit.html.haml +0 -2
- data/app/views/media/index.html.haml +0 -29
- data/app/views/media/index.json.jbuilder +0 -4
- data/app/views/media/new.html.erb +0 -5
- data/app/views/media/show.html.haml +0 -21
- data/app/views/media/show.json.jbuilder +0 -1
- data/app/views/media/update_crop.js.haml +0 -1
- /data/app/assets/javascripts/{oxen_media/.keep → components/.gitkeep} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 969f6e62d5ffc5306313af425df4ad912e833635
|
4
|
+
data.tar.gz: 38d1dab4be570218ac019416769bbc0b0bfd6db4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f36442f070349750f997d8579c6900551572c0ca1dbf22b29d81544efad8ef441ff8f337ff11c1d7e645e2204d4f4a2d4a04efe24beb4f2de5915dcda71e6a25
|
7
|
+
data.tar.gz: 97ae113d18282e2d85dd2e9a61f6a1c875c1058c2f2369d1ec252d28f5058151b9d3643ecd7ff7c26527d572362bc035f943d98703d2b93f74c70891955925c2
|
@@ -0,0 +1,298 @@
|
|
1
|
+
{ div, input } = React.DOM
|
2
|
+
|
3
|
+
BLANK_FUNCTION = () ->
|
4
|
+
|
5
|
+
|
6
|
+
class @DropZone extends React.Component
|
7
|
+
constructor: (props) ->
|
8
|
+
super props
|
9
|
+
@state =
|
10
|
+
entity: props.entity
|
11
|
+
key: props.key
|
12
|
+
record: props.record
|
13
|
+
reader: null
|
14
|
+
shared: new App.Shared()
|
15
|
+
url: props.url
|
16
|
+
|
17
|
+
# filesID: {}
|
18
|
+
|
19
|
+
@propTypes =
|
20
|
+
# record: React.PropTypes.node
|
21
|
+
entity: React.PropTypes.string
|
22
|
+
key: React.PropTypes.number
|
23
|
+
id: React.PropTypes.string
|
24
|
+
classes: React.PropTypes.string
|
25
|
+
url: React.PropTypes.string
|
26
|
+
onDragStart: React.PropTypes.func
|
27
|
+
onDragEnter: React.PropTypes.func
|
28
|
+
onDrop: React.PropTypes.func
|
29
|
+
disabled: React.PropTypes.bool
|
30
|
+
|
31
|
+
@defaultProps: ->
|
32
|
+
disabled: true
|
33
|
+
entity: 'stock_item'
|
34
|
+
record: ''
|
35
|
+
classes: ''
|
36
|
+
id: 'dropzone'
|
37
|
+
key: 0
|
38
|
+
onDragStart: BLANK_FUNCTION
|
39
|
+
onDragEnter: BLANK_FUNCTION
|
40
|
+
onDrop: BLANK_FUNCTION
|
41
|
+
|
42
|
+
# reader = () ->
|
43
|
+
#
|
44
|
+
# currentFile: (f) =>
|
45
|
+
# #
|
46
|
+
# # if (i>-1)
|
47
|
+
# # # remove element
|
48
|
+
# # ids = @state.filesID.slice() # copy array
|
49
|
+
# # ids.splice(i, 1) # remove element
|
50
|
+
# # @setState filesID: ids # update state
|
51
|
+
# # else
|
52
|
+
# # @setState filesID:
|
53
|
+
|
54
|
+
progressPercentElement = ''
|
55
|
+
|
56
|
+
abortRead: () =>
|
57
|
+
@reader.abort()
|
58
|
+
|
59
|
+
preventDefaultClick: (e) =>
|
60
|
+
e.preventDefault()
|
61
|
+
e.stopPropagation()
|
62
|
+
|
63
|
+
onDragStart: (e) =>
|
64
|
+
@preventDefaultClick(e)
|
65
|
+
|
66
|
+
onDragOver: (e) =>
|
67
|
+
@preventDefaultClick(e)
|
68
|
+
# if(this.containerAcceptsDropData(e.dataTransfer.types)) { e.preventDefault(); }
|
69
|
+
# var over = parseInt(e.currentTarget.dataset.key);
|
70
|
+
# if(e.clientY - e.currentTarget.offsetTop > e.currentTarget.offsetHeight / 2) { over++; }
|
71
|
+
# if(over !== this.state.hoverOver) { this.setState({ hoverOver: over }); }
|
72
|
+
e.dataTransfer.dropEffect = 'copy';
|
73
|
+
return false;
|
74
|
+
|
75
|
+
onDragEnter: (e) =>
|
76
|
+
@preventDefaultClick(e)
|
77
|
+
$('#drop_zone_upload_bay').addClass('hovering_files')
|
78
|
+
|
79
|
+
onDragLeave: (e) =>
|
80
|
+
@preventDefaultClick(e)
|
81
|
+
$('#drop_zone_upload_bay').removeClass('hovering_files')
|
82
|
+
|
83
|
+
onDrop: (e) =>
|
84
|
+
@preventDefaultClick(e)
|
85
|
+
dt = e.dataTransfer
|
86
|
+
@handleFiles(dt.files)
|
87
|
+
|
88
|
+
clickDropZone: (e) =>
|
89
|
+
$('#file_browser').trigger('click')
|
90
|
+
|
91
|
+
changeDropZone: (e) =>
|
92
|
+
dt = e.target
|
93
|
+
@handleFiles(dt.files)
|
94
|
+
|
95
|
+
|
96
|
+
# .drop_zone.delimiter
|
97
|
+
# .drop_zone.upload_bay.stitched
|
98
|
+
# .drop_zone.file_browser
|
99
|
+
# %input{ type: "file", multiple: true, id: "file_browser" }
|
100
|
+
# .drop_zone.user_notice
|
101
|
+
# %span
|
102
|
+
# træk dine filer her; eller tryk og vælg
|
103
|
+
# .drop_zone.file_hangar
|
104
|
+
#
|
105
|
+
# :coffeescript
|
106
|
+
# $('.drop_zone.user_notice').on 'dragenter', (e) =>
|
107
|
+
# $('.drop_zone.upload_bay').addClass 'hovering_files'
|
108
|
+
# $('.drop_zone.user_notice').on 'dragleave', (e) =>
|
109
|
+
# $('.drop_zone.upload_bay').removeClass 'hovering_files'
|
110
|
+
# $('.drop_zone.user_notice').on 'click', (e) =>
|
111
|
+
# $('#file_browser').trigger 'click'
|
112
|
+
|
113
|
+
render: ->
|
114
|
+
|
115
|
+
photos = React.createElement( Photos, url: @props.url, pollInterval: 2000, id: 'existing_photos', classes: 'drop_zone existing_file_hangar', entity: @props.entity )
|
116
|
+
|
117
|
+
if @props.disabled
|
118
|
+
div
|
119
|
+
className: 'drop_zone file_hangar row'
|
120
|
+
id: 'photos'
|
121
|
+
photos
|
122
|
+
else
|
123
|
+
try
|
124
|
+
|
125
|
+
div
|
126
|
+
className: 'drop_zone delimiter'
|
127
|
+
div
|
128
|
+
className: 'drop_zone upload_bay row stitched'
|
129
|
+
id: 'drop_zone_upload_bay'
|
130
|
+
onClick: @clickDropZone
|
131
|
+
div
|
132
|
+
className: 'drop_zone file_browser'
|
133
|
+
input
|
134
|
+
className: "file_browser"
|
135
|
+
type: "file"
|
136
|
+
id: "file_browser"
|
137
|
+
name: "files"
|
138
|
+
multiple: 'true'
|
139
|
+
onChange: @changeDropZone
|
140
|
+
div
|
141
|
+
className: 'drop_zone user_notice'
|
142
|
+
onDragOver: @onDragOver
|
143
|
+
onDragEnter: @onDragEnter
|
144
|
+
onDragLeave: @onDragLeave
|
145
|
+
# onClick: @clickDropZone
|
146
|
+
onDrop: @onDrop
|
147
|
+
'træk dine billeder herover og slip dem; eller klik her og vælg dem!'
|
148
|
+
div
|
149
|
+
className: "progress"
|
150
|
+
id: "progress_bar"
|
151
|
+
div
|
152
|
+
className: "percent"
|
153
|
+
'0%'
|
154
|
+
div
|
155
|
+
className: 'drop_zone file_hangar row'
|
156
|
+
id: 'photos'
|
157
|
+
photos
|
158
|
+
|
159
|
+
catch
|
160
|
+
div
|
161
|
+
className: ''
|
162
|
+
@state.err
|
163
|
+
|
164
|
+
handleFiles: (files) =>
|
165
|
+
@bindDeleteActions()
|
166
|
+
count = 0
|
167
|
+
for file in files
|
168
|
+
count=count+1
|
169
|
+
# Only process image files.
|
170
|
+
continue if (!file.type.match('image.*'))
|
171
|
+
|
172
|
+
file.id=Date.now() + count
|
173
|
+
|
174
|
+
# place file in the dropzone
|
175
|
+
@placeFileInDOM(file)
|
176
|
+
|
177
|
+
# start uploading it
|
178
|
+
# @uploadFile(file)
|
179
|
+
|
180
|
+
|
181
|
+
bindDeleteActions: =>
|
182
|
+
$(document.body).unbind('click.delete_photo')
|
183
|
+
$(document.body).on 'click.delete_photo', 'i.delete.file_upload', @handleDelete
|
184
|
+
|
185
|
+
handleDelete: (e) =>
|
186
|
+
e.preventDefault()
|
187
|
+
id=$(e.target).closest('a').next('input').val()
|
188
|
+
|
189
|
+
jqxhr = $.ajax
|
190
|
+
method: 'DELETE'
|
191
|
+
url: "/photos/" + id + ".js"
|
192
|
+
dataType: 'html'
|
193
|
+
jqxhr.done (r) =>
|
194
|
+
$(e.target).closest('.card').fadeOut 'slow', () => #animate({ "opacity": "0" }, "slow" )
|
195
|
+
$(e.target).closest('.card').remove()
|
196
|
+
jqxhr.fail (e,msg) =>
|
197
|
+
swal "Fejl!", "Det var ikke muligt at slette billedet - fejlen er:\n" + msg, "error"
|
198
|
+
|
199
|
+
uploadFile: (f) =>
|
200
|
+
|
201
|
+
formData = new FormData()
|
202
|
+
formData.append 'photo[image]', f
|
203
|
+
$('#drop_zone_upload_bay').removeClass('hovering_files').addClass('loading_files')
|
204
|
+
jqxhr = $.ajax
|
205
|
+
url : '/photos.js'
|
206
|
+
type : 'POST'
|
207
|
+
data : formData
|
208
|
+
processData: false
|
209
|
+
contentType: false
|
210
|
+
dataType: 'html'
|
211
|
+
|
212
|
+
.done (r) =>
|
213
|
+
$('#drop_zone_upload_bay').removeClass('loading_files')
|
214
|
+
$('#photos_'+f.id+'_id').val(r.replace(/\s/g, ""))
|
215
|
+
|
216
|
+
.fail (e,msg) =>
|
217
|
+
$('#photos_'+f.id+'_id').closest('.card').remove()
|
218
|
+
$('#drop_zone_upload_bay').removeClass('loading_files')
|
219
|
+
$('#drop_zone_upload_bay').addClass('error_loading_files')
|
220
|
+
setTimeout(@removeLoadError,2000)
|
221
|
+
swal "Fejl!", "Der opstod desværre en fejl - beskrivelsen er:\n" + msg, "error"
|
222
|
+
|
223
|
+
errorHandler: (evt) =>
|
224
|
+
switch evt.target.error.code
|
225
|
+
when evt.target.error.NOT_FOUND_ERR then alert('File Not Found!')
|
226
|
+
when evt.target.error.NOT_READABLE_ERR then alert('File is not readable')
|
227
|
+
when evt.target.error.ABORT_ERR then console.log 'aborted!! line 227 in dropzone.js.jsx.coffee'
|
228
|
+
else alert('An error occurred reading this file.')
|
229
|
+
|
230
|
+
removeLoadError: (e) =>
|
231
|
+
$('#drop_zone_upload_bay').removeClass('error_loading_files')
|
232
|
+
|
233
|
+
showProgress: (p) =>
|
234
|
+
progressPercentElement = document.querySelector('.percent')
|
235
|
+
progressPercentElement.style.width = p + '%'
|
236
|
+
progressPercentElement.textContent = p + '%'
|
237
|
+
if p>99
|
238
|
+
@state.shared.fadeItOut document.getElementById('progress_bar')
|
239
|
+
|
240
|
+
updateProgress: (e) =>
|
241
|
+
# e is an ProgressEvent.
|
242
|
+
percentLoaded = 0
|
243
|
+
if (e.lengthComputable)
|
244
|
+
percentLoaded = Math.round((e.loaded / e.total) * 100)
|
245
|
+
# Increase the progress bar length.
|
246
|
+
@showProgress(percentLoaded) if (percentLoaded < 100)
|
247
|
+
@showProgress(100) if (percentLoaded > 99)
|
248
|
+
|
249
|
+
|
250
|
+
placeFileInDOM: (file) =>
|
251
|
+
# Reset progress indicator on new file selection.
|
252
|
+
# @updateProgress ''
|
253
|
+
|
254
|
+
@reader = new FileReader()
|
255
|
+
# @currentFile(file)
|
256
|
+
|
257
|
+
@reader.onerror = @errorHandler
|
258
|
+
@reader.onprogress = @updateProgress
|
259
|
+
@reader.onabort = (e) =>
|
260
|
+
alert('File read cancelled')
|
261
|
+
|
262
|
+
@reader.onloadstart = (e) =>
|
263
|
+
document.getElementById('progress_bar').className = 'loading'
|
264
|
+
|
265
|
+
@reader.onload = (e) =>
|
266
|
+
|
267
|
+
# Render thumbnail.
|
268
|
+
img = document.createElement('div')
|
269
|
+
img.className = "col sl12 m6 l3"
|
270
|
+
img.innerHTML = [ '<div class="card">',
|
271
|
+
'<div class="card-image waves-effect waves-block waves-light">',
|
272
|
+
'<div class="progress fileupload-progress fade" style="display: none">',
|
273
|
+
'<div class="determinate" style="width: 0%"></div>',
|
274
|
+
'</div>',
|
275
|
+
'<img class="thumb" src="', e.target.result, '" title="', escape(file.name), '"/>',
|
276
|
+
'<div class="row">',
|
277
|
+
'<div class="col s2">',
|
278
|
+
'<a class="delete file-upload" href="#!">',
|
279
|
+
'<i class="material-icons small delete file_upload red-text">',
|
280
|
+
'delete',
|
281
|
+
'</i> </a>',
|
282
|
+
'</div>',
|
283
|
+
'<div class="col s10">',
|
284
|
+
window.build_drop_zone_photo file, @props.entity
|
285
|
+
'</div>',
|
286
|
+
'</div>',
|
287
|
+
'</div>'
|
288
|
+
].join('')
|
289
|
+
|
290
|
+
document.getElementById('photos').appendChild(img, null)
|
291
|
+
@uploadFile file
|
292
|
+
|
293
|
+
# Ensure that the progress bar displays 100% at the end.
|
294
|
+
@showProgress(100)
|
295
|
+
|
296
|
+
|
297
|
+
# Read in the image file as a data URL.
|
298
|
+
@reader.readAsDataURL(file)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
{ div, img, input, a, i, label } = React.DOM
|
2
|
+
|
3
|
+
class @PhotoCard extends React.Component
|
4
|
+
constructor: (props) ->
|
5
|
+
super props
|
6
|
+
@state =
|
7
|
+
key: props.key
|
8
|
+
entity: props.entity
|
9
|
+
record: props.record
|
10
|
+
|
11
|
+
@propTypes =
|
12
|
+
# record: React.PropTypes.node
|
13
|
+
key: React.PropTypes.number
|
14
|
+
entity: React.PropTypes.string
|
15
|
+
|
16
|
+
@defaultProps: ->
|
17
|
+
record: ''
|
18
|
+
key: 0
|
19
|
+
entity: 'stock_item'
|
20
|
+
|
21
|
+
show_on_new: () =>
|
22
|
+
if @props.record.id == 0
|
23
|
+
''
|
24
|
+
else
|
25
|
+
'hide'
|
26
|
+
|
27
|
+
handleDelete: (e) =>
|
28
|
+
e.preventDefault()
|
29
|
+
jqxhr = $.ajax
|
30
|
+
method: 'DELETE'
|
31
|
+
url: "/photos/#{ @state.record.id }"
|
32
|
+
dataType: 'html'
|
33
|
+
jqxhr.done () =>
|
34
|
+
$(e.target).closest('.card').fadeOut 'slow', () => #animate({ "opacity": "0" }, "slow" )
|
35
|
+
@props.deleteRecord @state.record
|
36
|
+
jqxhr.fail (e,msg) =>
|
37
|
+
swal "Fejl!", "Det var ikke muligt at slette billedet - fejlen er:\n" + msg, "error"
|
38
|
+
|
39
|
+
render: ->
|
40
|
+
|
41
|
+
div
|
42
|
+
className: "col s12 m6 l3"
|
43
|
+
div
|
44
|
+
className: "card"
|
45
|
+
div
|
46
|
+
className: "card-image waves-effect waves-block waves-light"
|
47
|
+
div
|
48
|
+
className: "progress fileupload-progress fade"
|
49
|
+
style: display: 'none'
|
50
|
+
div
|
51
|
+
className: "determinate"
|
52
|
+
style: width: '0%'
|
53
|
+
img
|
54
|
+
className: "activator"
|
55
|
+
id: @props.key
|
56
|
+
src: @props.record.image.url
|
57
|
+
div
|
58
|
+
className: "row"
|
59
|
+
div
|
60
|
+
className: "col s2"
|
61
|
+
a
|
62
|
+
className: "delete file-upload"
|
63
|
+
href: "#!"
|
64
|
+
i
|
65
|
+
className: "material-icons small file_upload red-text"
|
66
|
+
onClick: @handleDelete
|
67
|
+
'delete'
|
68
|
+
div
|
69
|
+
className: "col s10"
|
70
|
+
input
|
71
|
+
className: "files_uploaded"
|
72
|
+
id: "photos_#{@props.record.id}_id"
|
73
|
+
type: "hidden"
|
74
|
+
name: "#{@props.entity}[ps][#{@props.record.id}][id]"
|
75
|
+
value: "#{@props.record.id}"
|
76
|
+
@props.record.purpose || ""
|
77
|
+
@props.record.title || ""
|
@@ -0,0 +1,68 @@
|
|
1
|
+
{ div, a, i } = React.DOM
|
2
|
+
|
3
|
+
class @Photos extends React.Component
|
4
|
+
constructor: (props) ->
|
5
|
+
super props
|
6
|
+
@state =
|
7
|
+
url: props.url
|
8
|
+
err: 'Indlæser billeder...'
|
9
|
+
entity: props.entity
|
10
|
+
|
11
|
+
@propTypes =
|
12
|
+
url: React.PropTypes.string
|
13
|
+
id: React.PropTypes.string
|
14
|
+
classes: React.PropTypes.string
|
15
|
+
entity: React.PropTypes.string
|
16
|
+
|
17
|
+
@defaultProps: ->
|
18
|
+
records: []
|
19
|
+
url: ''
|
20
|
+
id: 'oldphotos'
|
21
|
+
classes: 'drop_zone old_files_hangar'
|
22
|
+
entity: 'stock_item'
|
23
|
+
|
24
|
+
componentDidMount: ->
|
25
|
+
@loadPhotosFromServer()
|
26
|
+
# setInterval(@loadPhotosFromServer, @props.pollInterval); # use polling, can be other eg. WebSockets
|
27
|
+
|
28
|
+
addRecord: (record) ->
|
29
|
+
records = React.addons.update(@state.records, { $push: [record] })
|
30
|
+
@setState records: records
|
31
|
+
|
32
|
+
deleteRecord: (record) =>
|
33
|
+
index = @state.records.indexOf record
|
34
|
+
records = React.addons.update(@state.records, { $splice: [[index, 1]] })
|
35
|
+
@setState records: records
|
36
|
+
|
37
|
+
render: ->
|
38
|
+
try
|
39
|
+
cards = []
|
40
|
+
for record in @state.records
|
41
|
+
cards.push React.createElement PhotoCard, { key: record.id, record: record, deleteRecord: @deleteRecord, entity: @props.entity }
|
42
|
+
|
43
|
+
div
|
44
|
+
className: @props.classes
|
45
|
+
id: @props.id
|
46
|
+
div
|
47
|
+
cards
|
48
|
+
|
49
|
+
catch
|
50
|
+
div
|
51
|
+
className: ''
|
52
|
+
@state.err
|
53
|
+
|
54
|
+
loadPhotosFromServer: ->
|
55
|
+
$.ajax
|
56
|
+
url: @state.url,
|
57
|
+
dataType: 'json',
|
58
|
+
cache: false,
|
59
|
+
success: (data) =>
|
60
|
+
@setState {records: data}
|
61
|
+
error: (xhr, status, err) =>
|
62
|
+
@setState { err: err.toString() }
|
63
|
+
|
64
|
+
# $.ajax {
|
65
|
+
# url: @props.url,
|
66
|
+
# dataType: 'json',
|
67
|
+
# type: 'POST',
|
68
|
+
# data: comment,
|
@@ -0,0 +1,86 @@
|
|
1
|
+
/* dropzone styling */
|
2
|
+
.thumb {
|
3
|
+
width: 100%;
|
4
|
+
}
|
5
|
+
|
6
|
+
#progress_bar {
|
7
|
+
margin: 50px 40px 0px 30px;
|
8
|
+
padding: 3px;
|
9
|
+
border: 1px solid grey;
|
10
|
+
font-size: 7px;
|
11
|
+
clear: both;
|
12
|
+
opacity: 0;
|
13
|
+
-moz-transition: opacity 1s linear;
|
14
|
+
-o-transition: opacity 1s linear;
|
15
|
+
-webkit-transition: opacity 1s linear;
|
16
|
+
}
|
17
|
+
#progress_bar.loading {
|
18
|
+
opacity: 1.0;
|
19
|
+
}
|
20
|
+
#progress_bar .percent {
|
21
|
+
background-color: #99ccff;
|
22
|
+
height: auto;
|
23
|
+
width: 0;
|
24
|
+
}
|
25
|
+
|
26
|
+
.drop_zone.delimiter {
|
27
|
+
height: 150px;
|
28
|
+
width: 100%;
|
29
|
+
padding: 0px;
|
30
|
+
margin: 0px;
|
31
|
+
position: relative
|
32
|
+
}
|
33
|
+
.drop_zone.upload_bay {
|
34
|
+
width: 100%;
|
35
|
+
height: 80px;
|
36
|
+
position: relative;
|
37
|
+
padding: 0px;
|
38
|
+
margin: 0px;
|
39
|
+
background-color: #26a69a;
|
40
|
+
border-radius: 8px;
|
41
|
+
}
|
42
|
+
.drop_zone.user_notice {
|
43
|
+
position: absolute;
|
44
|
+
top: 0px;
|
45
|
+
border: 0px;
|
46
|
+
width: 100%;
|
47
|
+
height: 100%;
|
48
|
+
padding: 30px;
|
49
|
+
}
|
50
|
+
.drop_zone.file_browser {
|
51
|
+
width: 100%;
|
52
|
+
height: 100%;
|
53
|
+
position: absolute;
|
54
|
+
top: 0px;
|
55
|
+
opacity: 0;
|
56
|
+
}
|
57
|
+
.drop_zone.file_hangar {
|
58
|
+
min-height: 100px;
|
59
|
+
height: 100%;
|
60
|
+
background-color: transparent;
|
61
|
+
}
|
62
|
+
.hovering_files {
|
63
|
+
background-color: #0ABFBC !important;
|
64
|
+
}
|
65
|
+
.loading_files {
|
66
|
+
background-color: #f57c00 !important;
|
67
|
+
}
|
68
|
+
.error_loading_files {
|
69
|
+
background-color: red !important;
|
70
|
+
}
|
71
|
+
.stitched {
|
72
|
+
padding: 20px;
|
73
|
+
margin: 10px;
|
74
|
+
color: #fff;
|
75
|
+
font-size: 12px;
|
76
|
+
font-weight: 400;
|
77
|
+
line-height: 1.3em;
|
78
|
+
border: 1px dashed #fff;
|
79
|
+
border-radius: 8px;
|
80
|
+
box-shadow: 0 0 0 4px #26a69a, 2px 2px 6px 4px rgba(1, 1, 1, 0.25);
|
81
|
+
font-weight: normal;
|
82
|
+
}
|
83
|
+
.drop_zone .card .row {
|
84
|
+
margin-bottom: 0px
|
85
|
+
}
|
86
|
+
/* end of dropzone styling */
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class PhotosController < AbstractResourcesController
|
2
|
+
|
3
|
+
private
|
4
|
+
|
5
|
+
def resource_params
|
6
|
+
params.require(:photo).permit( :id, :image )
|
7
|
+
end
|
8
|
+
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# find all resources
|
12
|
+
def find_all_resources options
|
13
|
+
return false unless policy_scope(resource_class)
|
14
|
+
policy_scope(resource_class)
|
15
|
+
end
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# find queried resources collection - implement on each controller to customize
|
19
|
+
def find_resources_queried options={}
|
20
|
+
lot = parent? ? parent.send(resource_name) : resource_class
|
21
|
+
res = case params[:subtype]
|
22
|
+
when nil; Photo.search policy_scope(lot), params[:q]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class OxPhoto < AbstractResource
|
2
|
+
self.table_name = "photos"
|
3
|
+
|
4
|
+
has_paper_trail
|
5
|
+
#photo belongs to album
|
6
|
+
# belongs_to :stock_item
|
7
|
+
# belongs_to :account
|
8
|
+
|
9
|
+
#validations
|
10
|
+
# validates :stock_item, presence: true
|
11
|
+
|
12
|
+
include Rails.application.routes.url_helpers
|
13
|
+
|
14
|
+
# Photo uploader using carrierwave
|
15
|
+
mount_uploader :image, PhotoUploader
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class PhotoUploader < CarrierWave::Uploader::Base
|
4
|
+
|
5
|
+
# Include RMagick or MiniMagick support:
|
6
|
+
# include CarrierWave::RMagick
|
7
|
+
include CarrierWave::MiniMagick
|
8
|
+
|
9
|
+
# Choose what kind of storage to use for this uploader:
|
10
|
+
storage :file
|
11
|
+
#storage :fog
|
12
|
+
# Override the directory where uploaded files will be stored.
|
13
|
+
# This is a sensible default for uploaders that are meant to be mounted:
|
14
|
+
def store_dir
|
15
|
+
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Provide a default URL as a default if there hasn't been a file uploaded:
|
19
|
+
def default_url
|
20
|
+
# For Rails 3.1+ asset pipeline compatibility:
|
21
|
+
# ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
|
22
|
+
|
23
|
+
#{}"/images/fallback/" + [version_name, "default.png"].compact.join('_')
|
24
|
+
'default_photo.png' #rails will look at 'app/assets/images/default_avatar.png'
|
25
|
+
end
|
26
|
+
|
27
|
+
# Process files as they are uploaded:
|
28
|
+
# process :scale => [200, 300]
|
29
|
+
#
|
30
|
+
# def scale(width, height)
|
31
|
+
# # do something
|
32
|
+
# end
|
33
|
+
|
34
|
+
# Create different versions of your uploaded files:
|
35
|
+
version :large_photo do
|
36
|
+
# returns a 150x150 image
|
37
|
+
process :resize_to_fill => [150, 150]
|
38
|
+
end
|
39
|
+
version :medium_photo do
|
40
|
+
# returns a 50x50 image
|
41
|
+
process :resize_to_fill => [50, 50]
|
42
|
+
end
|
43
|
+
version :small_photo do
|
44
|
+
# returns a 35x35 image
|
45
|
+
process :resize_to_fill => [35, 35]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add a white list of extensions which are allowed to be uploaded.
|
49
|
+
# For images you might use something like this:
|
50
|
+
def extension_white_list
|
51
|
+
%w(jpg jpeg gif png)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
.col.s12.m6.l3
|
2
|
+
.card
|
3
|
+
.card-image
|
4
|
+
= image_tag photo.image_url
|
5
|
+
%span.card-title{ style: "padding: 0px"}
|
6
|
+
%a.start.file-upload{ href: '#!'}
|
7
|
+
%i.material-icons.medium.file_upload.green-text file_upload
|
8
|
+
/%a.cancel.file-upload{ href: '#!'}
|
9
|
+
/ %i.material-icons.cancel.orange-text cancel
|
10
|
+
%a.delete.file-upload{ href: '#!'}
|
11
|
+
%i.material-icons.medium.delete.red-text delete
|
@@ -0,0 +1 @@
|
|
1
|
+
= resource.id
|
data/lib/oxen_media/version.rb
CHANGED