mix-rails-albums 0.16.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,7 @@ class Application.Models.Photo extends Backbone.Model
3
3
  idAttribute: '_id'
4
4
  defaults:
5
5
  image: null
6
- description: null
6
+ description: ''
7
7
  progress: 0
8
8
 
9
9
  initialize: ->
@@ -1,16 +1,20 @@
1
1
  class Application.Routers.PhotosRouter extends Backbone.Router
2
2
  initialize: (options) ->
3
3
  @photos = new Application.Collections.PhotosCollection()
4
+ @upload_index()
5
+
4
6
  @photos.url = options.collectionUrl
5
7
  @photos.reset options.photos
6
8
 
9
+
7
10
  routes:
8
- "new" : "newPhoto"
9
- "index" : "index"
10
- "upload_index" : "upload_index"
11
- ":id/edit" : "edit"
12
- ":id" : "show"
13
- ".*" : "upload_index"
11
+ "new" : "newPhoto"
12
+ "index" : "index"
13
+ "upload_index" : "upload_index"
14
+ "photo_edited" : "photo_edited"
15
+ ":id/edit" : "edit"
16
+ ":id" : "show"
17
+ ".*" : "upload_index"
14
18
 
15
19
  newPhoto: ->
16
20
  @view = new Application.Views.Photos.NewView(collection: @photos)
@@ -32,6 +36,10 @@ class Application.Routers.PhotosRouter extends Backbone.Router
32
36
 
33
37
  edit: (id) ->
34
38
  photo = @photos.get(id)
35
- console.log 'Editando a foto!' + photo
39
+ console.log 'Editando a foto!', id
40
+ $("#photo-modal-#{id}").modal()
36
41
  #@view = new Application.Views.Photos.EditView(model: photo)
37
42
  #$("#upload-container").html(@view.render().el)
43
+
44
+ photo_edited: () ->
45
+
@@ -0,0 +1,14 @@
1
+ .modal.hide.fade.admix-photo-modal{id: "photo-modal-#{@_id}"}
2
+ .modal-header
3
+ %button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} ×
4
+ %h3 Detalhes da foto
5
+ .modal-body
6
+ .edit-area.pull-right
7
+ %textarea.span5{id: "input_photo_description_#{@_id}",placeholder: 'Adicione uma descrição para esta foto.'}
8
+ #{@description}
9
+ .photo
10
+ %img{src: @image.medium.url}
11
+ %p One fine body…
12
+ .modal-footer
13
+ %a.btn{href:"#", "data-dismiss": "modal"} Cancelar
14
+ %a.btn.btn-primary.save{:href => "#", "data-dismiss": "modal"} Salvar mudanças
@@ -1,18 +1,21 @@
1
1
  .thumbnail
2
- .file-title
3
- = @title
4
2
  %a.link-popover{"data-content" => "<i class=icon-edit></i> Edit"}
5
3
  .loading-file
6
- - if @image.url
7
- %a.photo-link{href: "#"}
8
- %img{src: ROOT_PATH + @image.medium.url}
4
+ - if @get('image').url
5
+ %a.edit{href: "#"}
6
+ %img{src: ROOT_PATH + @get('image').medium.url}
9
7
  - else
10
8
  %img{src: "http://placehold.it/130x90&text=Carregando"}
11
9
  -if (@progress != null && @progress < 100)
12
10
  %div{class: (@progress < 100) ? 'progress progress-striped active' : 'progress'}
13
11
  .bar{style: "width:#{@progress}%;"}
12
+ %textarea.span12{style: 'margin-top:5px;', placeholder:'Adicione uma descrição'}
13
+ = @get('description')
14
14
  .caption
15
15
  %a.file-action.btn.destroy{href: "#", tooltip: 'Excluir'}
16
16
  <%= image_tag "admix/icons/delete.png" %>
17
- %a.file-action.btn.edit{href: "#", tooltip: 'Editar'}
18
- <%= image_tag "admix/icons/picture_edit.png" %>
17
+ -#%a.file-action.btn.edit{href: "#", tooltip: 'Editar'}
18
+ <%= image_tag "admix/icons/picture_edit.png" %>
19
+
20
+ %a.file-action.btn.btn-primary.save{style: 'display:none', href: "#", tooltip: 'Salvar mudanças'}
21
+ = 'Salvar mudanças'
@@ -0,0 +1,26 @@
1
+ Application.Views.Photos ||= {}
2
+
3
+ class Application.Views.Photos.PhotoModalEditView extends Backbone.View
4
+ template: JST["backbone/templates/photos/photo_modal_edit"]
5
+ tagName: "div"
6
+ #className: "span2"
7
+ events:
8
+ "click .save": "save_changes"
9
+
10
+ initialize: ->
11
+ @model.on "change", @render, this
12
+
13
+ render: ->
14
+ data = _.defaults(@model.attributes,
15
+ id: null
16
+ )
17
+ html = @template(@model.attributes)
18
+ $(@el).html html
19
+ this
20
+
21
+ save_changes: (e) ->
22
+ e.preventDefault()
23
+ description = $("#input_photo_description_#{@model.id}").val()
24
+ @model.set(description: description)
25
+ $.post @model.url() + '/update_description', @model.attributes, (data) ->
26
+ Backbone.history.navigate('photo_edited')
@@ -6,11 +6,14 @@ class Application.Views.Photos.PhotoView extends Backbone.View
6
6
  events:
7
7
  "click .destroy" : "destroy"
8
8
  "click .edit": "edit",
9
+ "keydown textarea": 'update_description'
10
+ "click .save": 'save_description'
9
11
 
10
12
  tagName: "li"
11
- className: 'span4'
13
+ className: 'span3'
12
14
 
13
15
  initialize: ->
16
+ @tmpDescription = null
14
17
  @model.on "change", @render, this
15
18
  @model.on "uploadCompleted", @removeProgressBar, this
16
19
  self = @
@@ -27,12 +30,32 @@ class Application.Views.Photos.PhotoView extends Backbone.View
27
30
  destroy: ->
28
31
  if confirm('Tem certeza que deseja deletar?')
29
32
  @model.destroy()
30
- this.remove()
33
+ @$el.fadeOut 500, () =>
34
+ @remove()
31
35
  $(".qtip-#{@model.get('id')}").remove()
32
36
  false
33
37
  edit: () ->
34
38
  Backbone.history.navigate("#/#{@model.id}/edit")
35
39
  false
40
+ update_description: () ->
41
+ self = @
42
+ $(@el).find('textarea').livequery ->
43
+ new_description = $(this).val()
44
+ self.tmpDescription = new_description
45
+ $(self.el).find('.save').show()
46
+
47
+ save_description: (e) ->
48
+ e.preventDefault()
49
+ @doHighlighter = true
50
+ new_description = $(@el).find('textarea').val()
51
+ @model.set(description : new_description)
52
+ @tmpDescription = null
53
+ $.post @model.url() + '/update_description', @model.attributes, (data) =>
54
+ $(@el).find('.save').hide()
55
+
36
56
  render: ->
37
- @$el.html(@template(@model.toJSON() ))
57
+ @$el.html(@template(@model))
58
+ if @doHighlighter?
59
+ @$el.find('textarea').effect('highlight', {}, 3000)
60
+ @doHighlighter = undefined
38
61
  return this
@@ -9,9 +9,16 @@ class Admix::PhotosController < Admix::AdmixController
9
9
 
10
10
  polymorphic_belongs_to :album, param: :album_id
11
11
 
12
- # load_and_authorize_resource :album
13
- # load_and_authorize_resource :photo, :through => :album_id
14
- skip_load_and_authorize_resource
12
+ # TODO: Maybe we need to skip authorization here. But not authetication.
13
+
14
+ def update_description
15
+ @album = Album.find(params[:album_id])
16
+ @photo = @album.photos.find(params[:id])
17
+ @photo.description = params[:description]
18
+ @photo.save()
19
+ render :json => @photo
20
+ end
21
+
15
22
 
16
23
  def upload
17
24
  render inline: params.to_s
@@ -2,18 +2,10 @@ class AlbumsController < ApplicationController
2
2
 
3
3
  def index
4
4
  @albums = Album.published.paginate(:page => params[:page], :per_page => 12)
5
- # @albums.each do |a|
6
- # a.photos.each do |p|
7
- # p.image.recreate_versions!
8
- # end
9
- # end
10
-
11
5
  end
12
6
 
13
7
  def show
14
8
  @album = Album.find(params[:id])
15
9
  end
16
-
17
10
 
18
-
19
11
  end
@@ -0,0 +1,12 @@
1
+ class PhotosController < ApplicationController
2
+
3
+ def index
4
+ #@albums = Album.published.paginate(:page => params[:page], :per_page => 12)
5
+ end
6
+
7
+ def show
8
+ @album = Album.find(params[:album_id])
9
+ @photo = @album.photos.find(params[:id])
10
+ end
11
+
12
+ end
@@ -2,30 +2,28 @@ class Admix::AlbumsDatagrid
2
2
 
3
3
  include Datagrid
4
4
 
5
+ extend AdmixHelper
6
+
5
7
 
6
8
  scope do
7
9
  Album.desc(:date)
8
10
  end
9
11
 
10
- filter :name, :string
12
+ filter :title, :string
11
13
  filter :date do |value|
12
14
  value.to_s(:created_at)
13
15
  end
14
16
 
15
- column :name, header: I18n.t('albums.name')
16
- column :date, header: I18n.t('albums.date') do |album|
17
+ column :title, header: input_label(:albums, :title)
18
+ column :date, header: input_label(:albums, :date) do |album|
17
19
  album.date.strftime("%d/%m/%Y") #album.date.to_s(:created_at)
18
20
  end
19
- column :photos_quantity, header: I18n.t('albums.photos_quantity') do |album|
21
+ column :photos_quantity, header: input_label(:albums, :photos_quantity) do |album|
20
22
  album.photos.count
21
23
  end
22
24
 
23
- column :published, header: I18n.t('albums.published') do |album|
24
- if album.published
25
- I18n.t("albums.published_yes")
26
- else
27
- I18n.t("albums.published_no")
28
- end
25
+ column :published, header: input_label(:albums, :status) do |album|
26
+ album.status.text
29
27
  end
30
28
 
31
29
 
data/app/models/album.rb CHANGED
@@ -1,27 +1,3 @@
1
1
  class Album
2
-
3
- include Mongoid::Document
4
- include Mongoid::Timestamps
5
- include Mongoid::Slug
6
-
7
- field :name, type: String
8
- field :description, type: String
9
- field :date, type: Date
10
- field :published, type: Boolean, default: true
11
-
12
-
13
- validates_presence_of :name
14
- validates_presence_of :date
15
-
16
- #slug :name, history: true
17
-
18
- slug :name, history: true do |current_object|
19
- current_object.name.parameterize
20
- end
21
-
22
- embeds_many :photos, cascade_callbacks: true
23
-
24
- scope :published, where(published: true, :photos.ne => nil, :photos.exists => true)
25
-
26
-
2
+ include MixRailsAlbums::Concerns::Models::Album
27
3
  end
data/app/models/photo.rb CHANGED
@@ -2,8 +2,6 @@ class Photo
2
2
 
3
3
  include Mongoid::Document
4
4
  include Mongoid::Timestamps
5
-
6
- attr_accessible :description, :image, :image_cache
7
5
 
8
6
  field :description
9
7
 
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require 'carrierwave/processing/mini_magick'
3
+ require 'mix-rails-core/gridfs'
3
4
 
4
5
  class Albums::ImageUploader < CarrierWave::Uploader::Base
5
6
 
@@ -22,7 +23,7 @@ class Albums::ImageUploader < CarrierWave::Uploader::Base
22
23
  end
23
24
 
24
25
  def cache_dir
25
- "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
26
+ "system/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
26
27
  end
27
28
 
28
29
  # Provide a default URL as a default if there hasn't been a file uploaded:
@@ -45,8 +46,35 @@ class Albums::ImageUploader < CarrierWave::Uploader::Base
45
46
  # process :resize_to_fit => [80, 80]
46
47
  # end
47
48
 
49
+ def watermark
50
+ manipulate! do |img|
51
+
52
+ albums_watermark = Setting.albums_watermark
53
+
54
+ watermark_image = if albums_watermark
55
+ gridfs_file = MixRailsCore::Gridfs::read_file(albums_watermark.image.url)
56
+ MiniMagick::Image.read(gridfs_file, "png")
57
+ else
58
+ # We can read a file from system too.
59
+ #MiniMagick::Image.open("#{Rails.root}/app/assets/images/watermarks/watermark.png", "png")
60
+ false
61
+ end
62
+
63
+ if watermark_image
64
+ result = img.composite(watermark_image) do |c|
65
+ c.gravity "SouthEast"
66
+ end
67
+ result
68
+ else
69
+ img
70
+ end
71
+
72
+ end
73
+ end
74
+
48
75
  version :medium do
49
76
  process :resize_to_fill => [640,420]
77
+ process :watermark
50
78
  end
51
79
 
52
80
  version :thumb do
@@ -1,4 +1,4 @@
1
- = f.input :name, as: 'string'
1
+ = f.input :title, as: 'string'
2
2
  = f.input :description, as: 'text'
3
3
  = f.input :date, as: 'datepicker'
4
- = f.input :published, as: 'boolean'
4
+ = f.input :status
@@ -1,7 +1,7 @@
1
1
  %dl
2
- %dt= t('albums.name')
3
- %dd= resource.name
4
- %dt= t('albums.date')
2
+ %dt= input_label(:albums, :title)
3
+ %dd= resource.title
4
+ %dt= input_label(:albums, :date)
5
5
  %dd= resource.date
6
6
 
7
7
  = render('admix/photos/upload')
@@ -0,0 +1,4 @@
1
+ - content_for(:photos_content) do
2
+ = render @photos
3
+ = will_paginate @photos
4
+ = render template: 'templates/photos/index'
@@ -0,0 +1,3 @@
1
+ - content_for(:photo_content) do
2
+ = render partial: 'photo_show', locals: {album: @album, photo: @photo}
3
+ = render template: 'templates/photos/show'
@@ -1,6 +1,7 @@
1
1
  pt-BR:
2
2
  resources:
3
- albums: 'fotos'
3
+ albums: 'albuns'
4
+ photos: 'fotos'
4
5
  will_paginate:
5
6
  previous_label: "<< admix"
6
7
  next_label: "próximo >>"
@@ -14,4 +15,15 @@ pt-BR:
14
15
  name: 'Nome'
15
16
  date: 'Data'
16
17
  published: 'Publicado?'
17
- photos_quantity: "Quantidade de fotos"
18
+ photos_quantity: "Quantidade de fotos"
19
+
20
+ photos:
21
+ photos: 'Fotos'
22
+ photo: 'Foto'
23
+ legend: 'Legenda'
24
+ or: 'ou'
25
+ upload:
26
+ explanation: 'Clique no botão abaixo para adicionar as fotos.'
27
+ btn:
28
+ upload_pictures: 'Enviar arquivos de imagem'
29
+ upload_zip: 'Enviar arquivo ZIP'
data/config/routes.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  Rails.application.routes.draw do
2
2
 
3
3
  localized do
4
- resources :albums
4
+ resources :albums, :only => [:index, :show] do
5
+ resources :photos, :only => [:index, :show]
6
+ end
5
7
  end
6
8
 
7
9
  scope Admix::namespace_path, as: :admix, module: :admix do
@@ -9,6 +11,7 @@ Rails.application.routes.draw do
9
11
  resources :photos do
10
12
  member do
11
13
  get :edit_ajax
14
+ post :update_description
12
15
  end
13
16
  end
14
17
  end
@@ -0,0 +1,43 @@
1
+ module MixRailsAlbums::Concerns::Models::Album
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include Mongoid::Slug
9
+
10
+ extend Enumerize
11
+
12
+ field :title, type: String
13
+ field :description, type: String
14
+ field :date, type: Date
15
+ field :status, type: String
16
+ enumerize :status, in: [:published, :unpublished], default: :published, predicates: true
17
+
18
+ validation_for_title
19
+ validation_for_date
20
+
21
+ slug :title, history: true do |current_object|
22
+ current_object.title.parameterize
23
+ end
24
+
25
+ embeds_many :photos, cascade_callbacks: true
26
+
27
+ scope :published, where(status: :published, :photos.ne => nil, :photos.exists => true)
28
+
29
+ end
30
+
31
+ module ClassMethods
32
+
33
+ def validation_for_title
34
+ validates_presence_of :title
35
+ end
36
+
37
+ def validation_for_date
38
+ validates_presence_of :date
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -7,7 +7,7 @@ module MixRailsAlbums
7
7
  Admix::Navigation::NavBar.find(:content) do |menu|
8
8
  menu.submenu do |submenu|
9
9
  submenu.key = :albums
10
- submenu.title = I18n.t 'albums.albums'
10
+ submenu.title = I18n.t('albums.albums')
11
11
  submenu.url = 'admix_albums_url'
12
12
  end
13
13
  end
@@ -1,7 +1,7 @@
1
1
  module MixRailsAlbums
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 16
4
+ MINOR = 22
5
5
  TINY = 0
6
6
  PRE = nil
7
7
 
@@ -7,4 +7,10 @@ require "mini_magick"
7
7
  require "haml_coffee_assets"
8
8
 
9
9
  module MixRailsAlbums
10
- end
10
+ module Concerns
11
+ module Models
12
+ end
13
+ end
14
+ end
15
+
16
+ require 'concerns/models/album'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mix-rails-albums
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.22.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-27 00:00:00.000000000 Z
12
+ date: 2013-02-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -114,87 +114,89 @@ executables: []
114
114
  extensions: []
115
115
  extra_rdoc_files: []
116
116
  files:
117
- - app/assets/stylesheets/skins/tango/next-vertical.png
118
- - app/assets/stylesheets/skins/tango/skin.css
117
+ - app/uploaders/albums/image_uploader.rb
118
+ - app/controllers/admix/albums_controller.rb
119
+ - app/controllers/admix/photos_controller.rb
120
+ - app/controllers/albums_controller.rb
121
+ - app/controllers/photos_controller.rb
119
122
  - app/assets/stylesheets/skins/tango/next-horizontal.png
123
+ - app/assets/stylesheets/skins/tango/skin.css
120
124
  - app/assets/stylesheets/skins/tango/prev-vertical.png
125
+ - app/assets/stylesheets/skins/tango/next-vertical.png
121
126
  - app/assets/stylesheets/skins/tango/credits.txt
122
127
  - app/assets/stylesheets/skins/tango/prev-horizontal.png
123
- - app/assets/stylesheets/skins/ie7/loading-small.gif
124
- - app/assets/stylesheets/skins/ie7/skin.css
125
- - app/assets/stylesheets/skins/ie7/next-horizontal.gif
126
- - app/assets/stylesheets/skins/ie7/credits.txt
127
- - app/assets/stylesheets/skins/ie7/loading_small.gif
128
128
  - app/assets/stylesheets/skins/ie7/loading.gif
129
129
  - app/assets/stylesheets/skins/ie7/prev-horizontal.gif
130
+ - app/assets/stylesheets/skins/ie7/skin.css
131
+ - app/assets/stylesheets/skins/ie7/loading_small.gif
132
+ - app/assets/stylesheets/skins/ie7/credits.txt
133
+ - app/assets/stylesheets/skins/ie7/loading-small.gif
134
+ - app/assets/stylesheets/skins/ie7/next-horizontal.gif
130
135
  - app/assets/stylesheets/colorbox/colorbox.css.scss
131
- - app/assets/images/colorbox/ie6/borderMiddleLeft.png
132
- - app/assets/images/colorbox/ie6/borderTopRight.png
133
- - app/assets/images/colorbox/ie6/borderBottomLeft.png
134
- - app/assets/images/colorbox/ie6/borderTopLeft.png
135
- - app/assets/images/colorbox/ie6/borderBottomCenter.png
136
- - app/assets/images/colorbox/ie6/borderTopCenter.png
137
- - app/assets/images/colorbox/ie6/borderMiddleRight.png
138
- - app/assets/images/colorbox/ie6/borderBottomRight.png
136
+ - app/assets/images/albums/photos.png
137
+ - app/assets/images/albums/delete.png
138
+ - app/assets/images/albums/rotate_cw.png
139
+ - app/assets/images/albums/crop.png
140
+ - app/assets/images/albums/rotate_ccw.png
139
141
  - app/assets/images/colorbox/border.png
142
+ - app/assets/images/colorbox/loading.gif
140
143
  - app/assets/images/colorbox/loading_background.png
141
144
  - app/assets/images/colorbox/controls.png
142
- - app/assets/images/colorbox/loading.gif
145
+ - app/assets/images/colorbox/ie6/borderTopCenter.png
146
+ - app/assets/images/colorbox/ie6/borderMiddleRight.png
147
+ - app/assets/images/colorbox/ie6/borderBottomRight.png
148
+ - app/assets/images/colorbox/ie6/borderBottomCenter.png
149
+ - app/assets/images/colorbox/ie6/borderTopLeft.png
150
+ - app/assets/images/colorbox/ie6/borderTopRight.png
151
+ - app/assets/images/colorbox/ie6/borderBottomLeft.png
152
+ - app/assets/images/colorbox/ie6/borderMiddleLeft.png
143
153
  - app/assets/images/colorbox/overlay.png
144
- - app/assets/images/albums/delete.png
145
- - app/assets/images/albums/photos.png
146
- - app/assets/images/albums/rotate_cw.png
147
- - app/assets/images/albums/rotate_ccw.png
148
- - app/assets/images/albums/crop.png
149
- - app/assets/javascripts/application.js
150
- - app/assets/javascripts/backbone/views/photos/upload_index_view.js.coffee
151
- - app/assets/javascripts/backbone/views/photos/thumbnail_view.js.coffee
152
- - app/assets/javascripts/backbone/models/photo.js.coffee
153
- - app/assets/javascripts/backbone/templates/photos/thumbnail.hamlc.erb
154
+ - app/assets/javascripts/admix/photos.js.erb
155
+ - app/assets/javascripts/templates/photo_modal_edit.hamlc.erb
156
+ - app/assets/javascripts/templates/photo_view.hamlc.erb
157
+ - app/assets/javascripts/backbone/templates/photos/photo_modal_edit.hamlc.erb
154
158
  - app/assets/javascripts/backbone/templates/photos/upload_index.hamlc.erb
159
+ - app/assets/javascripts/backbone/templates/photos/thumbnail.hamlc.erb
155
160
  - app/assets/javascripts/backbone/application.js.coffee
156
161
  - app/assets/javascripts/backbone/routers/photos_router.js.coffee
157
- - app/assets/javascripts/admix/photos.js.erb
158
- - app/assets/javascripts/views/fileview.js
159
- - app/assets/javascripts/views/filezipview.js
160
- - app/assets/javascripts/views/photo_modal_view.js.coffee
161
- - app/assets/javascripts/views/filelistview.js
162
+ - app/assets/javascripts/backbone/models/photo.js.coffee
163
+ - app/assets/javascripts/backbone/views/photos/upload_index_view.js.coffee
164
+ - app/assets/javascripts/backbone/views/photos/photo_modal_view.js.coffee
165
+ - app/assets/javascripts/backbone/views/photos/thumbnail_view.js.coffee
162
166
  - app/assets/javascripts/photos/upload.js.coffee.erb
163
- - app/assets/javascripts/models/filezip.js
164
- - app/assets/javascripts/models/fileitem.js
165
167
  - app/assets/javascripts/collections/filelist.js
166
- - app/assets/javascripts/templates/photo_view.hamlc.erb
167
- - app/assets/javascripts/templates/photo_modal_edit.hamlc.erb
168
168
  - app/assets/javascripts/routers/photo_router.js.coffee.erb
169
- - app/assets/javascripts/albums/show.js.coffee
170
- - app/assets/javascripts/albums/jquery.jcarousel.min.js
169
+ - app/assets/javascripts/models/fileitem.js
170
+ - app/assets/javascripts/models/filezip.js
171
+ - app/assets/javascripts/application.js
172
+ - app/assets/javascripts/views/filelistview.js
173
+ - app/assets/javascripts/views/photo_modal_view.js.coffee
174
+ - app/assets/javascripts/views/filezipview.js
175
+ - app/assets/javascripts/views/fileview.js
176
+ - app/helpers/admix/albums_helper.rb
177
+ - app/models/admix/photos_datagrid.rb
178
+ - app/models/admix/albums_datagrid.rb
179
+ - app/models/album.rb
180
+ - app/models/photo.rb
181
+ - app/views/admix/albums/_show.html.haml
182
+ - app/views/admix/albums/_table_actions.html.haml
183
+ - app/views/admix/albums/_form_fields.html.haml
171
184
  - app/views/admix/photos/index.html.haml
172
185
  - app/views/admix/photos/_upload.html.haml
173
- - app/views/admix/albums/_form_fields.html.haml
174
- - app/views/admix/albums/_table_actions.html.haml
175
- - app/views/admix/albums/_show.html.haml
176
186
  - app/views/albums/index.html.haml
177
187
  - app/views/albums/show.html.haml
178
- - app/controllers/albums_controller.rb
179
- - app/controllers/admix/albums_controller.rb
180
- - app/controllers/admix/photos_controller.rb
181
- - app/models/album.rb
182
- - app/models/admix/photos_datagrid.rb
183
- - app/models/admix/albums_datagrid.rb
184
- - app/models/photo.rb
185
- - app/helpers/admix/albums_helper.rb
186
- - app/uploaders/albums/image_uploader.rb
187
- - config/initializers/carrierwave.rb
188
+ - app/views/photos/index.html.haml
189
+ - app/views/photos/show.html.haml
190
+ - config/locales/albums.pt-BR.yml
191
+ - config/locales/albums.en.yml
188
192
  - config/initializers/albums.rb
189
193
  - config/routes.rb
190
194
  - config/application.rb
191
- - config/locales/photos.pt-BR.yml
192
- - config/locales/albums.pt-BR.yml
193
- - config/locales/albums.en.yml
194
195
  - lib/tasks/albums_tasks.rake
196
+ - lib/mix-rails-albums.rb
197
+ - lib/concerns/models/album.rb
195
198
  - lib/mix-rails-albums/version.rb
196
199
  - lib/mix-rails-albums/engine.rb
197
- - lib/mix-rails-albums.rb
198
200
  - MIT-LICENSE
199
201
  - Rakefile
200
202
  - README.rdoc
@@ -212,7 +214,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
212
214
  version: '0'
213
215
  segments:
214
216
  - 0
215
- hash: -3421642337988709305
217
+ hash: -3474028709906897679
216
218
  required_rubygems_version: !ruby/object:Gem::Requirement
217
219
  none: false
218
220
  requirements:
@@ -221,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
223
  version: '0'
222
224
  segments:
223
225
  - 0
224
- hash: -3421642337988709305
226
+ hash: -3474028709906897679
225
227
  requirements: []
226
228
  rubyforge_project:
227
229
  rubygems_version: 1.8.24
@@ -1,16 +0,0 @@
1
- /*!
2
- * jCarousel - Riding carousels with jQuery
3
- * http://sorgalla.com/jcarousel/
4
- *
5
- * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
6
- * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
7
- * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
8
- *
9
- * Built on top of the jQuery library
10
- * http://jquery.com
11
- *
12
- * Inspired by the "Carousel Component" by Bill Scott
13
- * http://billwscott.com/carousel/
14
- */
15
-
16
- (function(g){var q={vertical:!1,rtl:!1,start:1,offset:1,size:null,scroll:3,visible:null,animation:"normal",easing:"swing",auto:0,wrap:null,initCallback:null,setupCallback:null,reloadCallback:null,itemLoadCallback:null,itemFirstInCallback:null,itemFirstOutCallback:null,itemLastInCallback:null,itemLastOutCallback:null,itemVisibleInCallback:null,itemVisibleOutCallback:null,animationStepCallback:null,buttonNextHTML:"<div></div>",buttonPrevHTML:"<div></div>",buttonNextEvent:"click",buttonPrevEvent:"click", buttonNextCallback:null,buttonPrevCallback:null,itemFallbackDimension:null},m=!1;g(window).bind("load.jcarousel",function(){m=!0});g.jcarousel=function(a,c){this.options=g.extend({},q,c||{});this.autoStopped=this.locked=!1;this.buttonPrevState=this.buttonNextState=this.buttonPrev=this.buttonNext=this.list=this.clip=this.container=null;if(!c||c.rtl===void 0)this.options.rtl=(g(a).attr("dir")||g("html").attr("dir")||"").toLowerCase()=="rtl";this.wh=!this.options.vertical?"width":"height";this.lt=!this.options.vertical? this.options.rtl?"right":"left":"top";for(var b="",d=a.className.split(" "),f=0;f<d.length;f++)if(d[f].indexOf("jcarousel-skin")!=-1){g(a).removeClass(d[f]);b=d[f];break}a.nodeName.toUpperCase()=="UL"||a.nodeName.toUpperCase()=="OL"?(this.list=g(a),this.clip=this.list.parents(".jcarousel-clip"),this.container=this.list.parents(".jcarousel-container")):(this.container=g(a),this.list=this.container.find("ul,ol").eq(0),this.clip=this.container.find(".jcarousel-clip"));if(this.clip.size()===0)this.clip= this.list.wrap("<div></div>").parent();if(this.container.size()===0)this.container=this.clip.wrap("<div></div>").parent();b!==""&&this.container.parent()[0].className.indexOf("jcarousel-skin")==-1&&this.container.wrap('<div class=" '+b+'"></div>');this.buttonPrev=g(".jcarousel-prev",this.container);if(this.buttonPrev.size()===0&&this.options.buttonPrevHTML!==null)this.buttonPrev=g(this.options.buttonPrevHTML).appendTo(this.container);this.buttonPrev.addClass(this.className("jcarousel-prev"));this.buttonNext= g(".jcarousel-next",this.container);if(this.buttonNext.size()===0&&this.options.buttonNextHTML!==null)this.buttonNext=g(this.options.buttonNextHTML).appendTo(this.container);this.buttonNext.addClass(this.className("jcarousel-next"));this.clip.addClass(this.className("jcarousel-clip")).css({position:"relative"});this.list.addClass(this.className("jcarousel-list")).css({overflow:"hidden",position:"relative",top:0,margin:0,padding:0}).css(this.options.rtl?"right":"left",0);this.container.addClass(this.className("jcarousel-container")).css({position:"relative"}); !this.options.vertical&&this.options.rtl&&this.container.addClass("jcarousel-direction-rtl").attr("dir","rtl");var j=this.options.visible!==null?Math.ceil(this.clipping()/this.options.visible):null,b=this.list.children("li"),e=this;if(b.size()>0){var h=0,i=this.options.offset;b.each(function(){e.format(this,i++);h+=e.dimension(this,j)});this.list.css(this.wh,h+100+"px");if(!c||c.size===void 0)this.options.size=b.size()}this.container.css("display","block");this.buttonNext.css("display","block");this.buttonPrev.css("display", "block");this.funcNext=function(){e.next()};this.funcPrev=function(){e.prev()};this.funcResize=function(){e.resizeTimer&&clearTimeout(e.resizeTimer);e.resizeTimer=setTimeout(function(){e.reload()},100)};this.options.initCallback!==null&&this.options.initCallback(this,"init");!m&&g.browser.safari?(this.buttons(!1,!1),g(window).bind("load.jcarousel",function(){e.setup()})):this.setup()};var f=g.jcarousel;f.fn=f.prototype={jcarousel:"0.2.8"};f.fn.extend=f.extend=g.extend;f.fn.extend({setup:function(){this.prevLast= this.prevFirst=this.last=this.first=null;this.animating=!1;this.tail=this.resizeTimer=this.timer=null;this.inTail=!1;if(!this.locked){this.list.css(this.lt,this.pos(this.options.offset)+"px");var a=this.pos(this.options.start,!0);this.prevFirst=this.prevLast=null;this.animate(a,!1);g(window).unbind("resize.jcarousel",this.funcResize).bind("resize.jcarousel",this.funcResize);this.options.setupCallback!==null&&this.options.setupCallback(this)}},reset:function(){this.list.empty();this.list.css(this.lt, "0px");this.list.css(this.wh,"10px");this.options.initCallback!==null&&this.options.initCallback(this,"reset");this.setup()},reload:function(){this.tail!==null&&this.inTail&&this.list.css(this.lt,f.intval(this.list.css(this.lt))+this.tail);this.tail=null;this.inTail=!1;this.options.reloadCallback!==null&&this.options.reloadCallback(this);if(this.options.visible!==null){var a=this,c=Math.ceil(this.clipping()/this.options.visible),b=0,d=0;this.list.children("li").each(function(f){b+=a.dimension(this, c);f+1<a.first&&(d=b)});this.list.css(this.wh,b+"px");this.list.css(this.lt,-d+"px")}this.scroll(this.first,!1)},lock:function(){this.locked=!0;this.buttons()},unlock:function(){this.locked=!1;this.buttons()},size:function(a){if(a!==void 0)this.options.size=a,this.locked||this.buttons();return this.options.size},has:function(a,c){if(c===void 0||!c)c=a;if(this.options.size!==null&&c>this.options.size)c=this.options.size;for(var b=a;b<=c;b++){var d=this.get(b);if(!d.length||d.hasClass("jcarousel-item-placeholder"))return!1}return!0}, get:function(a){return g(">.jcarousel-item-"+a,this.list)},add:function(a,c){var b=this.get(a),d=0,p=g(c);if(b.length===0)for(var j,e=f.intval(a),b=this.create(a);;){if(j=this.get(--e),e<=0||j.length){e<=0?this.list.prepend(b):j.after(b);break}}else d=this.dimension(b);p.get(0).nodeName.toUpperCase()=="LI"?(b.replaceWith(p),b=p):b.empty().append(c);this.format(b.removeClass(this.className("jcarousel-item-placeholder")),a);p=this.options.visible!==null?Math.ceil(this.clipping()/this.options.visible): null;d=this.dimension(b,p)-d;a>0&&a<this.first&&this.list.css(this.lt,f.intval(this.list.css(this.lt))-d+"px");this.list.css(this.wh,f.intval(this.list.css(this.wh))+d+"px");return b},remove:function(a){var c=this.get(a);if(c.length&&!(a>=this.first&&a<=this.last)){var b=this.dimension(c);a<this.first&&this.list.css(this.lt,f.intval(this.list.css(this.lt))+b+"px");c.remove();this.list.css(this.wh,f.intval(this.list.css(this.wh))-b+"px")}},next:function(){this.tail!==null&&!this.inTail?this.scrollTail(!1): this.scroll((this.options.wrap=="both"||this.options.wrap=="last")&&this.options.size!==null&&this.last==this.options.size?1:this.first+this.options.scroll)},prev:function(){this.tail!==null&&this.inTail?this.scrollTail(!0):this.scroll((this.options.wrap=="both"||this.options.wrap=="first")&&this.options.size!==null&&this.first==1?this.options.size:this.first-this.options.scroll)},scrollTail:function(a){if(!this.locked&&!this.animating&&this.tail){this.pauseAuto();var c=f.intval(this.list.css(this.lt)), c=!a?c-this.tail:c+this.tail;this.inTail=!a;this.prevFirst=this.first;this.prevLast=this.last;this.animate(c)}},scroll:function(a,c){!this.locked&&!this.animating&&(this.pauseAuto(),this.animate(this.pos(a),c))},pos:function(a,c){var b=f.intval(this.list.css(this.lt));if(this.locked||this.animating)return b;this.options.wrap!="circular"&&(a=a<1?1:this.options.size&&a>this.options.size?this.options.size:a);for(var d=this.first>a,g=this.options.wrap!="circular"&&this.first<=1?1:this.first,j=d?this.get(g): this.get(this.last),e=d?g:g-1,h=null,i=0,k=!1,l=0;d?--e>=a:++e<a;){h=this.get(e);k=!h.length;if(h.length===0&&(h=this.create(e).addClass(this.className("jcarousel-item-placeholder")),j[d?"before":"after"](h),this.first!==null&&this.options.wrap=="circular"&&this.options.size!==null&&(e<=0||e>this.options.size)))j=this.get(this.index(e)),j.length&&(h=this.add(e,j.clone(!0)));j=h;l=this.dimension(h);k&&(i+=l);if(this.first!==null&&(this.options.wrap=="circular"||e>=1&&(this.options.size===null||e<= this.options.size)))b=d?b+l:b-l}for(var g=this.clipping(),m=[],o=0,n=0,j=this.get(a-1),e=a;++o;){h=this.get(e);k=!h.length;if(h.length===0){h=this.create(e).addClass(this.className("jcarousel-item-placeholder"));if(j.length===0)this.list.prepend(h);else j[d?"before":"after"](h);if(this.first!==null&&this.options.wrap=="circular"&&this.options.size!==null&&(e<=0||e>this.options.size))j=this.get(this.index(e)),j.length&&(h=this.add(e,j.clone(!0)))}j=h;l=this.dimension(h);if(l===0)throw Error("jCarousel: No width/height set for items. This will cause an infinite loop. Aborting..."); this.options.wrap!="circular"&&this.options.size!==null&&e>this.options.size?m.push(h):k&&(i+=l);n+=l;if(n>=g)break;e++}for(h=0;h<m.length;h++)m[h].remove();i>0&&(this.list.css(this.wh,this.dimension(this.list)+i+"px"),d&&(b-=i,this.list.css(this.lt,f.intval(this.list.css(this.lt))-i+"px")));i=a+o-1;if(this.options.wrap!="circular"&&this.options.size&&i>this.options.size)i=this.options.size;if(e>i){o=0;e=i;for(n=0;++o;){h=this.get(e--);if(!h.length)break;n+=this.dimension(h);if(n>=g)break}}e=i-o+ 1;this.options.wrap!="circular"&&e<1&&(e=1);if(this.inTail&&d)b+=this.tail,this.inTail=!1;this.tail=null;if(this.options.wrap!="circular"&&i==this.options.size&&i-o+1>=1&&(d=f.intval(this.get(i).css(!this.options.vertical?"marginRight":"marginBottom")),n-d>g))this.tail=n-g-d;if(c&&a===this.options.size&&this.tail)b-=this.tail,this.inTail=!0;for(;a-- >e;)b+=this.dimension(this.get(a));this.prevFirst=this.first;this.prevLast=this.last;this.first=e;this.last=i;return b},animate:function(a,c){if(!this.locked&& !this.animating){this.animating=!0;var b=this,d=function(){b.animating=!1;a===0&&b.list.css(b.lt,0);!b.autoStopped&&(b.options.wrap=="circular"||b.options.wrap=="both"||b.options.wrap=="last"||b.options.size===null||b.last<b.options.size||b.last==b.options.size&&b.tail!==null&&!b.inTail)&&b.startAuto();b.buttons();b.notify("onAfterAnimation");if(b.options.wrap=="circular"&&b.options.size!==null)for(var c=b.prevFirst;c<=b.prevLast;c++)c!==null&&!(c>=b.first&&c<=b.last)&&(c<1||c>b.options.size)&&b.remove(c)}; this.notify("onBeforeAnimation");if(!this.options.animation||c===!1)this.list.css(this.lt,a+"px"),d();else{var f=!this.options.vertical?this.options.rtl?{right:a}:{left:a}:{top:a},d={duration:this.options.animation,easing:this.options.easing,complete:d};if(g.isFunction(this.options.animationStepCallback))d.step=this.options.animationStepCallback;this.list.animate(f,d)}}},startAuto:function(a){if(a!==void 0)this.options.auto=a;if(this.options.auto===0)return this.stopAuto();if(this.timer===null){this.autoStopped= !1;var c=this;this.timer=window.setTimeout(function(){c.next()},this.options.auto*1E3)}},stopAuto:function(){this.pauseAuto();this.autoStopped=!0},pauseAuto:function(){if(this.timer!==null)window.clearTimeout(this.timer),this.timer=null},buttons:function(a,c){if(a==null&&(a=!this.locked&&this.options.size!==0&&(this.options.wrap&&this.options.wrap!="first"||this.options.size===null||this.last<this.options.size),!this.locked&&(!this.options.wrap||this.options.wrap=="first")&&this.options.size!==null&& this.last>=this.options.size))a=this.tail!==null&&!this.inTail;if(c==null&&(c=!this.locked&&this.options.size!==0&&(this.options.wrap&&this.options.wrap!="last"||this.first>1),!this.locked&&(!this.options.wrap||this.options.wrap=="last")&&this.options.size!==null&&this.first==1))c=this.tail!==null&&this.inTail;var b=this;this.buttonNext.size()>0?(this.buttonNext.unbind(this.options.buttonNextEvent+".jcarousel",this.funcNext),a&&this.buttonNext.bind(this.options.buttonNextEvent+".jcarousel",this.funcNext), this.buttonNext[a?"removeClass":"addClass"](this.className("jcarousel-next-disabled")).attr("disabled",a?!1:!0),this.options.buttonNextCallback!==null&&this.buttonNext.data("jcarouselstate")!=a&&this.buttonNext.each(function(){b.options.buttonNextCallback(b,this,a)}).data("jcarouselstate",a)):this.options.buttonNextCallback!==null&&this.buttonNextState!=a&&this.options.buttonNextCallback(b,null,a);this.buttonPrev.size()>0?(this.buttonPrev.unbind(this.options.buttonPrevEvent+".jcarousel",this.funcPrev), c&&this.buttonPrev.bind(this.options.buttonPrevEvent+".jcarousel",this.funcPrev),this.buttonPrev[c?"removeClass":"addClass"](this.className("jcarousel-prev-disabled")).attr("disabled",c?!1:!0),this.options.buttonPrevCallback!==null&&this.buttonPrev.data("jcarouselstate")!=c&&this.buttonPrev.each(function(){b.options.buttonPrevCallback(b,this,c)}).data("jcarouselstate",c)):this.options.buttonPrevCallback!==null&&this.buttonPrevState!=c&&this.options.buttonPrevCallback(b,null,c);this.buttonNextState= a;this.buttonPrevState=c},notify:function(a){var c=this.prevFirst===null?"init":this.prevFirst<this.first?"next":"prev";this.callback("itemLoadCallback",a,c);this.prevFirst!==this.first&&(this.callback("itemFirstInCallback",a,c,this.first),this.callback("itemFirstOutCallback",a,c,this.prevFirst));this.prevLast!==this.last&&(this.callback("itemLastInCallback",a,c,this.last),this.callback("itemLastOutCallback",a,c,this.prevLast));this.callback("itemVisibleInCallback",a,c,this.first,this.last,this.prevFirst, this.prevLast);this.callback("itemVisibleOutCallback",a,c,this.prevFirst,this.prevLast,this.first,this.last)},callback:function(a,c,b,d,f,j,e){if(!(this.options[a]==null||typeof this.options[a]!="object"&&c!="onAfterAnimation")){var h=typeof this.options[a]=="object"?this.options[a][c]:this.options[a];if(g.isFunction(h)){var i=this;if(d===void 0)h(i,b,c);else if(f===void 0)this.get(d).each(function(){h(i,this,d,b,c)});else for(var a=function(a){i.get(a).each(function(){h(i,this,a,b,c)})},k=d;k<=f;k++)k!== null&&!(k>=j&&k<=e)&&a(k)}}},create:function(a){return this.format("<li></li>",a)},format:function(a,c){for(var a=g(a),b=a.get(0).className.split(" "),d=0;d<b.length;d++)b[d].indexOf("jcarousel-")!=-1&&a.removeClass(b[d]);a.addClass(this.className("jcarousel-item")).addClass(this.className("jcarousel-item-"+c)).css({"float":this.options.rtl?"right":"left","list-style":"none"}).attr("jcarouselindex",c);return a},className:function(a){return a+" "+a+(!this.options.vertical?"-horizontal":"-vertical")}, dimension:function(a,c){var b=g(a);if(c==null)return!this.options.vertical?b.outerWidth(!0)||f.intval(this.options.itemFallbackDimension):b.outerHeight(!0)||f.intval(this.options.itemFallbackDimension);else{var d=!this.options.vertical?c-f.intval(b.css("marginLeft"))-f.intval(b.css("marginRight")):c-f.intval(b.css("marginTop"))-f.intval(b.css("marginBottom"));g(b).css(this.wh,d+"px");return this.dimension(b)}},clipping:function(){return!this.options.vertical?this.clip[0].offsetWidth-f.intval(this.clip.css("borderLeftWidth"))- f.intval(this.clip.css("borderRightWidth")):this.clip[0].offsetHeight-f.intval(this.clip.css("borderTopWidth"))-f.intval(this.clip.css("borderBottomWidth"))},index:function(a,c){if(c==null)c=this.options.size;return Math.round(((a-1)/c-Math.floor((a-1)/c))*c)+1}});f.extend({defaults:function(a){return g.extend(q,a||{})},intval:function(a){a=parseInt(a,10);return isNaN(a)?0:a},windowLoaded:function(){m=!0}});g.fn.jcarousel=function(a){if(typeof a=="string"){var c=g(this).data("jcarousel"),b=Array.prototype.slice.call(arguments, 1);return c[a].apply(c,b)}else return this.each(function(){var b=g(this).data("jcarousel");b?(a&&g.extend(b.options,a),b.reload()):g(this).data("jcarousel",new f(this,a))})}})(jQuery);
@@ -1,62 +0,0 @@
1
- photos_initCallback = (carousel) ->
2
- jQuery(".jcarousel-control a").bind "click", ->
3
- carousel.scroll jQuery.jcarousel.intval(jQuery(this).text())
4
- false
5
-
6
- jQuery(".jcarousel-scroll select").bind "change", ->
7
- carousel.options.scroll = jQuery.jcarousel.intval(@options[@selectedIndex].value)
8
- false
9
-
10
- jQuery(".s-next").bind "click", (e) ->
11
- e.preventDefault()
12
- carousel.next()
13
- false
14
-
15
- jQuery(".s-prev").bind "click", (e)->
16
- e.preventDefault()
17
- carousel.prev()
18
- false
19
-
20
- $("a.trigger").click (e)->
21
- e.preventDefault()
22
- index = jQuery(this).attr('href').replace('#', "")
23
- carousel.scroll jQuery.jcarousel.intval(index)
24
-
25
-
26
- mycarousel_initCallback = (carousel) ->
27
- jQuery(".jcarousel-control a").bind "click", ->
28
- carousel.scroll jQuery.jcarousel.intval(jQuery(this).text())
29
- false
30
-
31
- jQuery(".jcarousel-scroll select").bind "change", ->
32
- carousel.options.scroll = jQuery.jcarousel.intval(@options[@selectedIndex].value)
33
- false
34
-
35
- jQuery(".next-thumbs").bind "click", (e) ->
36
- e.preventDefault()
37
- carousel.next()
38
- false
39
-
40
- jQuery(".prev-thumbs").bind "click", (e) ->
41
- e.preventDefault()
42
- carousel.prev()
43
- false
44
-
45
- $ ->
46
-
47
- photos = $("#photos-carousel").jcarousel
48
- visible: 1
49
- scroll: 1
50
- initCallback: photos_initCallback
51
- # This tells jCarousel NOT to autobuild prev/next buttons
52
- buttonNextHTML: null
53
- buttonPrevHTML: null
54
-
55
-
56
- $(".mycarousel").jcarousel
57
- visible: 6
58
- initCallback: mycarousel_initCallback
59
- # This tells jCarousel NOT to autobuild prev/next buttons
60
- buttonNextHTML: null
61
- buttonPrevHTML: null
62
-
@@ -1,9 +0,0 @@
1
- require 'mongoid/railtie'
2
-
3
- CarrierWave.configure do |config|
4
-
5
- config.storage = :grid_fs
6
- config.grid_fs_access_url = "/files"
7
- config.cache_dir = "uploads"
8
- config.root = Rails.root.join('public')
9
- end
@@ -1,11 +0,0 @@
1
- pt-BR:
2
- photos:
3
- photos: 'Fotos'
4
- photo: 'Foto'
5
- legend: 'Legenda'
6
- or: 'ou'
7
- upload:
8
- explanation: 'Clique no botão abaixo para adicionar as fotos.'
9
- btn:
10
- upload_pictures: 'Enviar arquivos de imagem'
11
- upload_zip: 'Enviar arquivo ZIP'