Duplicate documentable code and rename for imageable
This commit is contained in:
@@ -63,6 +63,7 @@
|
||||
//= require followable
|
||||
//= require flaggable
|
||||
//= require documentable
|
||||
//= require imageable
|
||||
//= require tree_navigator
|
||||
//= require custom
|
||||
//= require tag_autocomplete
|
||||
@@ -100,6 +101,7 @@ var initialize_modules = function() {
|
||||
App.WatchFormChanges.initialize();
|
||||
App.TreeNavigator.initialize();
|
||||
App.Documentable.initialize();
|
||||
App.Imageable.initialize();
|
||||
App.TagAutocomplete.initialize();
|
||||
};
|
||||
|
||||
|
||||
101
app/assets/javascripts/imageable.js.coffee
Normal file
101
app/assets/javascripts/imageable.js.coffee
Normal file
@@ -0,0 +1,101 @@
|
||||
App.Imageable =
|
||||
|
||||
initialize: ->
|
||||
@initializeDirectUploads()
|
||||
@initializeInterface()
|
||||
|
||||
initializeDirectUploads: ->
|
||||
|
||||
$('input.image_ajax_attachment[type=file]').fileupload
|
||||
|
||||
paramName: "image[attachment]"
|
||||
|
||||
formData: null
|
||||
|
||||
add: (e, data) ->
|
||||
wrapper = $(e.target).closest('.image')
|
||||
index = $(e.target).data('index')
|
||||
is_nested_image = $(e.target).data('nested-image')
|
||||
$(wrapper).find('.progress-bar-placeholder').empty()
|
||||
data.progressBar = $(wrapper).find('.progress-bar-placeholder').html('<div class="progress-bar"><div class="loading-bar uploading"></div></div>')
|
||||
$(wrapper).find('.progress-bar-placeholder').css('display','block')
|
||||
data.formData = {
|
||||
"image[title]": $(wrapper).find('input.image-title').val() || data.files[0].name
|
||||
"index": index,
|
||||
"nested_image": is_nested_image
|
||||
}
|
||||
data.submit()
|
||||
|
||||
change: (e, data) ->
|
||||
wrapper = $(e.target).parent()
|
||||
$.each(data.files, (index, file)->
|
||||
$(wrapper).find('.file-name').text(file.name)
|
||||
)
|
||||
|
||||
progress: (e, data) ->
|
||||
progress = parseInt(data.loaded / data.total * 100, 10)
|
||||
$(data.progressBar).find('.loading-bar').css 'width', progress + '%'
|
||||
return
|
||||
|
||||
initializeInterface: ->
|
||||
input_files = $('input.image_ajax_attachment[type=file]')
|
||||
|
||||
$.each input_files, (index, file) ->
|
||||
wrapper = $(file).parent()
|
||||
App.Imageable.watchRemoveImagebutton(wrapper)
|
||||
|
||||
watchRemoveImagebutton: (wrapper) ->
|
||||
remove_image_button = $(wrapper).find('.remove-image')
|
||||
$(remove_image_button).on 'click', (e) ->
|
||||
e.preventDefault()
|
||||
$(wrapper).remove()
|
||||
$('#new_image_link').show()
|
||||
$('.max-images-notice').hide()
|
||||
|
||||
uploadNestedImage: (id, nested_image, result) ->
|
||||
$('#' + id).replaceWith(nested_image)
|
||||
@updateLoadingBar(id, result)
|
||||
@initialize()
|
||||
|
||||
uploadPlainImage: (id, nested_image, result) ->
|
||||
$('#' + id).replaceWith(nested_image)
|
||||
@updateLoadingBar(id, result)
|
||||
@initialize()
|
||||
|
||||
updateLoadingBar: (id, result) ->
|
||||
if result
|
||||
$('#' + id).find('.loading-bar').addClass 'complete'
|
||||
else
|
||||
$('#' + id).find('.loading-bar').addClass 'errors'
|
||||
$('#' + id).find('.progress-bar-placeholder').css('display','block')
|
||||
|
||||
new: (nested_fields) ->
|
||||
$(".images-list").append(nested_fields)
|
||||
@initialize()
|
||||
|
||||
destroyNestedImage: (id, notice) ->
|
||||
$('#' + id).remove()
|
||||
@updateNotice(notice)
|
||||
|
||||
replacePlainImage: (id, notice, plain_image) ->
|
||||
$('#' + id).replaceWith(plain_image)
|
||||
@updateNotice(notice)
|
||||
@initialize()
|
||||
|
||||
updateNotice: (notice) ->
|
||||
if $('[data-alert]').length > 0
|
||||
$('[data-alert]').replaceWith(notice)
|
||||
else
|
||||
$("body").append(notice)
|
||||
|
||||
updateNewImageButton: (link) ->
|
||||
if $('.image').length >= $('.images').data('max-images')
|
||||
$('#new_image_link').hide()
|
||||
$('.max-images-notice').removeClass('hide')
|
||||
$('.max-images-notice').show()
|
||||
else if $('#new_image_link').length > 0
|
||||
$('#new_image_link').replaceWith(link)
|
||||
$('.max-images-notice').hide()
|
||||
else
|
||||
$('.max-images-notice').hide()
|
||||
$(link).insertBefore('.images hr:last')
|
||||
@@ -19,3 +19,4 @@
|
||||
@import 'jquery-ui/autocomplete';
|
||||
@import 'autocomplete_overrides';
|
||||
@import 'documentable';
|
||||
@import 'imageable';
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
content: '\53';
|
||||
}
|
||||
|
||||
.icon-budget-investment-image::before {
|
||||
.icon-image::before {
|
||||
content: '\68';
|
||||
}
|
||||
|
||||
|
||||
64
app/assets/stylesheets/imageable.scss
Normal file
64
app/assets/stylesheets/imageable.scss
Normal file
@@ -0,0 +1,64 @@
|
||||
.progress-bar-placeholder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.image-form {
|
||||
.image .file-name {
|
||||
margin-top: 0;
|
||||
}
|
||||
.progress-bar-placeholder {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.image .loading-bar.errors {
|
||||
margin-top: $line-height * 2;
|
||||
}
|
||||
}
|
||||
|
||||
.cached-image{
|
||||
max-width: 150px;
|
||||
max-height: 150px;
|
||||
}
|
||||
.image {
|
||||
|
||||
.button {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
background-color: $light-gray;
|
||||
}
|
||||
|
||||
input.image_ajax_attachment[type=file]{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
height: 5px;
|
||||
width: 0;
|
||||
transition: width 500ms ease-out;
|
||||
|
||||
&.uploading {
|
||||
background-color: $dark-gray;
|
||||
}
|
||||
|
||||
&.complete {
|
||||
background-color: $success-color;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.errors {
|
||||
background-color: $alert-color;
|
||||
width: 100%;
|
||||
margin-top: $line-height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-bar.no-transition {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@@ -2282,3 +2282,59 @@ table {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 19. Images
|
||||
.image-form form {
|
||||
|
||||
.source-option-link {
|
||||
input {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-bottom: $line-height;
|
||||
}
|
||||
|
||||
label {
|
||||
&.error {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.source-option-file {
|
||||
.file-name {
|
||||
label {
|
||||
|
||||
@include breakpoint(small medium) {
|
||||
float: none;
|
||||
}
|
||||
|
||||
@include breakpoint(large) {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
@include breakpoint(small medium) {
|
||||
float: none;
|
||||
margin-top: 0;
|
||||
margin-left: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@include breakpoint(large) {
|
||||
float: left;
|
||||
margin-bottom: 0;
|
||||
margin-top: $line-height / 2;
|
||||
margin-left: $line-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-errors {
|
||||
margin-bottom: $line-height;
|
||||
}
|
||||
}
|
||||
@@ -252,13 +252,13 @@
|
||||
.document-form,
|
||||
.topic-new,
|
||||
.topic-form,
|
||||
.budget-investment-image-form {
|
||||
.image-form {
|
||||
|
||||
.icon-debates,
|
||||
.icon-proposals,
|
||||
.icon-budget,
|
||||
.icon-documents,
|
||||
.icon-budget-investment-image {
|
||||
.icon-image {
|
||||
font-size: rem-calc(50);
|
||||
line-height: $line-height;
|
||||
opacity: 0.5;
|
||||
@@ -277,7 +277,7 @@
|
||||
color: $budget;
|
||||
}
|
||||
|
||||
.icon-budget-investment-image {
|
||||
.icon-image {
|
||||
color: $budget-investment-image;
|
||||
}
|
||||
}
|
||||
@@ -315,7 +315,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.budget-investment-image-form {
|
||||
.image-form {
|
||||
|
||||
.recommendations li::before {
|
||||
color: $budget-investment-image;
|
||||
@@ -955,7 +955,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.budget-investment-image-form {
|
||||
.image-form {
|
||||
|
||||
@include image-upload;
|
||||
|
||||
|
||||
@@ -45,11 +45,13 @@ module Budgets
|
||||
load_investment_votes(@investment)
|
||||
@investment_ids = [@investment.id]
|
||||
@document = Document.new(documentable: @investment)
|
||||
@image = Image.new(imageable: @investment)
|
||||
end
|
||||
|
||||
def create
|
||||
@investment.author = current_user
|
||||
recover_documents_from_cache(@investment)
|
||||
recover_image_from_cache(@investment)
|
||||
|
||||
if @investment.save
|
||||
Mailer.budget_investment_created(@investment).deliver_later
|
||||
@@ -80,24 +82,6 @@ module Budgets
|
||||
super
|
||||
end
|
||||
|
||||
def edit_image
|
||||
end
|
||||
|
||||
def update_image
|
||||
if @investment.update(investment_params)
|
||||
redirect_to budget_investment_path(@investment.budget, @investment),
|
||||
notice: t("flash.actions.update_image.budget_investment")
|
||||
else
|
||||
render :edit_image
|
||||
end
|
||||
end
|
||||
|
||||
def remove_image
|
||||
@investment.image.destroy!
|
||||
redirect_to budget_investment_path(@investment.budget, @investment),
|
||||
notice: t("flash.actions.remove_image.budget_investment")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_model
|
||||
@@ -124,9 +108,9 @@ module Budgets
|
||||
|
||||
def investment_params
|
||||
params.require(:budget_investment)
|
||||
.permit(:title, :description, :external_url, :heading_id,
|
||||
:tag_list, :organization_name, :location, :terms_of_service,
|
||||
image_attributes: [:title, :attachment],
|
||||
.permit(:title, :description, :external_url, :heading_id, :tag_list,
|
||||
:organization_name, :location, :terms_of_service,
|
||||
image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id],
|
||||
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id])
|
||||
end
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ module CommentableActions
|
||||
def update
|
||||
resource.assign_attributes(strong_params)
|
||||
recover_documents_from_cache(resource)
|
||||
recover_image_from_cache(resource)
|
||||
|
||||
if resource.save
|
||||
redirect_to resource, notice: t("flash.actions.update.#{resource_name.underscore}")
|
||||
@@ -119,4 +120,10 @@ module CommentableActions
|
||||
end
|
||||
end
|
||||
|
||||
def recover_image_from_cache(resource)
|
||||
return false unless resource.try(:image)
|
||||
|
||||
resource.image.attachment = resource.image.set_attachment_from_cached_attachment if resource.image.cached_attachment.present?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
106
app/controllers/images_controller.rb
Normal file
106
app/controllers/images_controller.rb
Normal file
@@ -0,0 +1,106 @@
|
||||
class ImagesController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
before_filter :find_imageable, except: :destroy
|
||||
before_filter :prepare_new_image, only: [:new, :new_nested]
|
||||
before_filter :prepare_image_for_creation, only: :create
|
||||
before_filter :find_image, only: :destroy
|
||||
|
||||
load_and_authorize_resource except: :upload
|
||||
skip_authorization_check only: :upload
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
def new_nested
|
||||
end
|
||||
|
||||
def create
|
||||
recover_attachments_from_cache
|
||||
|
||||
if @image.save
|
||||
flash[:notice] = t "images.actions.create.notice"
|
||||
redirect_to params[:from]
|
||||
else
|
||||
flash[:alert] = t "images.actions.create.alert"
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if @image.destroy
|
||||
flash[:notice] = t "images.actions.destroy.notice"
|
||||
else
|
||||
flash[:alert] = t "images.actions.destroy.alert"
|
||||
end
|
||||
redirect_to params[:from]
|
||||
end
|
||||
format.js do
|
||||
if @image.destroy
|
||||
flash.now[:notice] = t "images.actions.destroy.notice"
|
||||
else
|
||||
flash.now[:alert] = t "images.actions.destroy.alert"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_upload
|
||||
@image = Image.new(cached_attachment: params[:path])
|
||||
@image.set_attachment_from_cached_attachment
|
||||
@image.cached_attachment = nil
|
||||
@image.imageable = @imageable
|
||||
|
||||
if @image.attachment.destroy
|
||||
flash.now[:notice] = t "images.actions.destroy.notice"
|
||||
else
|
||||
flash.now[:alert] = t "images.actions.destroy.alert"
|
||||
end
|
||||
render :destroy
|
||||
end
|
||||
|
||||
def upload
|
||||
@image = Image.new(image_params.merge(user: current_user))
|
||||
@image.imageable = @imageable
|
||||
|
||||
if @image.valid?
|
||||
@image.attachment.save
|
||||
@image.set_cached_attachment_from_attachment(URI(request.url))
|
||||
else
|
||||
@image.attachment.destroy
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def image_params
|
||||
params.require(:image).permit(:title, :imageable_type, :imageable_id,
|
||||
:attachment, :cached_attachment, :user_id)
|
||||
end
|
||||
|
||||
def find_imageable
|
||||
@imageable = params[:imageable_type].constantize.find_or_initialize_by(id: params[:imageable_id])
|
||||
end
|
||||
|
||||
def find_image
|
||||
@image = Image.find(params[:id])
|
||||
end
|
||||
|
||||
def prepare_new_image
|
||||
@image = Image.new(imageable: @imageable)
|
||||
end
|
||||
|
||||
def prepare_image_for_creation
|
||||
@image = Image.new(image_params)
|
||||
@image.imageable = @imageable
|
||||
@image.user = current_user
|
||||
end
|
||||
|
||||
def recover_attachments_from_cache
|
||||
if @image.attachment.blank? && @image.cached_attachment.present?
|
||||
@image.set_attachment_from_cached_attachment
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -20,12 +20,14 @@ class ProposalsController < ApplicationController
|
||||
super
|
||||
@notifications = @proposal.notifications
|
||||
@document = Document.new(documentable: @proposal)
|
||||
@image = Image.new(imageable: @proposal)
|
||||
redirect_to proposal_path(@proposal), status: :moved_permanently if request.path != proposal_path(@proposal)
|
||||
end
|
||||
|
||||
def create
|
||||
@proposal = Proposal.new(proposal_params.merge(author: current_user))
|
||||
recover_documents_from_cache(@proposal)
|
||||
recover_image_from_cache(@proposal)
|
||||
|
||||
if @proposal.save
|
||||
redirect_to share_proposal_path(@proposal), notice: I18n.t('flash.actions.create.proposal')
|
||||
@@ -78,6 +80,7 @@ class ProposalsController < ApplicationController
|
||||
def proposal_params
|
||||
params.require(:proposal).permit(:title, :question, :summary, :description, :external_url, :video_url,
|
||||
:responsible_name, :tag_list, :terms_of_service, :geozone_id,
|
||||
image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id],
|
||||
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id] )
|
||||
end
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ module DocumentablesHelper
|
||||
.join(",")
|
||||
end
|
||||
|
||||
def humanized_accepted_content_types(documentable)
|
||||
def documentable_humanized_accepted_content_types(documentable)
|
||||
documentable.class.accepted_content_types
|
||||
.collect{ |content_type| content_type.split("/").last }
|
||||
.join(", ")
|
||||
@@ -30,7 +30,7 @@ module DocumentablesHelper
|
||||
|
||||
def documentables_note(documentable)
|
||||
t "documents.form.note", max_documents_allowed: max_documents_allowed(documentable),
|
||||
accepted_content_types: humanized_accepted_content_types(documentable),
|
||||
accepted_content_types: documentable_humanized_accepted_content_types(documentable),
|
||||
max_file_size: max_file_size(documentable)
|
||||
end
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ module DocumentsHelper
|
||||
document.attachment_file_name
|
||||
end
|
||||
|
||||
def errors_on_attachment(document)
|
||||
def document_errors_on_attachment(document)
|
||||
document.errors[:attachment].join(', ') if document.errors.key?(:attachment)
|
||||
end
|
||||
|
||||
@@ -71,7 +71,7 @@ module DocumentsHelper
|
||||
class: "button hollow #{klass}"
|
||||
if document.errors[:attachment].any?
|
||||
html += content_tag :small, class: "error" do
|
||||
errors_on_attachment(document)
|
||||
document_errors_on_attachment(document)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
36
app/helpers/imageables_helper.rb
Normal file
36
app/helpers/imageables_helper.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
module ImageablesHelper
|
||||
|
||||
def imageable_class(imageable)
|
||||
imageable.class.name.parameterize('_')
|
||||
end
|
||||
|
||||
def imageable_max_file_size
|
||||
bytesToMeg(Image::MAX_IMAGE_SIZE)
|
||||
end
|
||||
|
||||
def bytesToMeg(bytes)
|
||||
bytes / Numeric::MEGABYTE
|
||||
end
|
||||
|
||||
def imageable_accepted_content_types
|
||||
Image::ACCEPTED_CONTENT_TYPE
|
||||
end
|
||||
|
||||
def imageable_accepted_content_types_extensions
|
||||
Image::ACCEPTED_CONTENT_TYPE
|
||||
.collect{ |content_type| ".#{content_type.split("/").last}" }
|
||||
.join(",")
|
||||
end
|
||||
|
||||
def imageable_humanized_accepted_content_types
|
||||
Image::ACCEPTED_CONTENT_TYPE
|
||||
.collect{ |content_type| content_type.split("/").last }
|
||||
.join(", ")
|
||||
end
|
||||
|
||||
def imageables_note(imageable)
|
||||
t "images.form.note", accepted_content_types: imageable_humanized_accepted_content_types,
|
||||
max_file_size: max_file_size(imageable)
|
||||
end
|
||||
|
||||
end
|
||||
98
app/helpers/images_helper.rb
Normal file
98
app/helpers/images_helper.rb
Normal file
@@ -0,0 +1,98 @@
|
||||
module ImagesHelper
|
||||
|
||||
def image_attachment_file_name(image)
|
||||
image.attachment_file_name
|
||||
end
|
||||
|
||||
def image_errors_on_attachment(image)
|
||||
image.errors[:attachment].join(', ') if image.errors.key?(:attachment)
|
||||
end
|
||||
|
||||
def image_bytesToMeg(bytes)
|
||||
bytes / Numeric::MEGABYTE
|
||||
end
|
||||
|
||||
def image_nested_field_name(image, field)
|
||||
parent = image.imageable_type.parameterize.underscore
|
||||
"#{parent.parameterize}[image_attributes]#{field}"
|
||||
end
|
||||
|
||||
def image_nested_field_id(image, field)
|
||||
parent = image.imageable_type.parameterize.underscore
|
||||
"#{parent.parameterize}_image_attributes_#{field}"
|
||||
end
|
||||
|
||||
def image_nested_field_wrapper_id
|
||||
"nested_image"
|
||||
end
|
||||
|
||||
def image_class(image)
|
||||
image.persisted? ? "image" : "cached-image"
|
||||
end
|
||||
|
||||
def render_destroy_image_link(image)
|
||||
if image.persisted?
|
||||
link_to t('images.form.delete_button'),
|
||||
image_path(image, nested_image: true),
|
||||
method: :delete,
|
||||
remote: true,
|
||||
data: { confirm: t('images.actions.destroy.confirm') },
|
||||
class: "delete float-right"
|
||||
elsif !image.persisted? && image.cached_attachment.present?
|
||||
link_to t('images.form.delete_button'),
|
||||
destroy_upload_images_path(path: image.cached_attachment,
|
||||
nested_image: true,
|
||||
imageable_type: image.imageable_type,
|
||||
imageable_id: image.imageable_id),
|
||||
method: :delete,
|
||||
remote: true,
|
||||
class: "delete float-right"
|
||||
else
|
||||
link_to t('images.form.delete_button'),
|
||||
"#",
|
||||
class: "delete float-right remove-image"
|
||||
end
|
||||
end
|
||||
|
||||
def render_image_attachment(image)
|
||||
html = file_field_tag :attachment,
|
||||
accept: imageable_accepted_content_types_extensions,
|
||||
class: 'image_ajax_attachment',
|
||||
data: {
|
||||
url: image_direct_upload_url(image),
|
||||
cached_attachment_input_field: image_nested_field_id(image, :cached_attachment),
|
||||
multiple: false,
|
||||
nested_image: true
|
||||
},
|
||||
name: image_nested_field_name(image, :attachment),
|
||||
id: image_nested_field_id(image, :attachment)
|
||||
if image.attachment.blank? && image.cached_attachment.blank?
|
||||
klass = image.errors[:attachment].any? ? "error" : ""
|
||||
html += label_tag image_nested_field_id(image, :attachment),
|
||||
t("images.form.attachment_label"),
|
||||
class: "button hollow #{klass}"
|
||||
if image.errors[:attachment].any?
|
||||
html += content_tag :small, class: "error" do
|
||||
image_errors_on_attachment(image)
|
||||
end
|
||||
end
|
||||
end
|
||||
html
|
||||
end
|
||||
|
||||
def render_image(image, version, show_caption = true)
|
||||
version = image.persisted? ? version : :original
|
||||
render partial: "images/image", locals: { image: image,
|
||||
version: version,
|
||||
show_caption: show_caption }
|
||||
end
|
||||
|
||||
def image_direct_upload_url(image)
|
||||
upload_images_url(
|
||||
imageable_type: image.imageable_type,
|
||||
imageable_id: image.imageable_id,
|
||||
format: :js
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -5,7 +5,7 @@ module InvestmentsHelper
|
||||
end
|
||||
|
||||
def investment_image_advice_note(investment)
|
||||
if investment.image.exists?
|
||||
if investment.image.present?
|
||||
t("budgets.investments.edit_image.edit_note", title: investment.title)
|
||||
else
|
||||
t("budgets.investments.edit_image.add_note", title: investment.title)
|
||||
@@ -13,7 +13,7 @@ module InvestmentsHelper
|
||||
end
|
||||
|
||||
def investment_image_button_text(investment)
|
||||
investment.image.exists? ? t("budgets.investments.show.edit_image") : t("budgets.investments.show.add_image")
|
||||
investment.image.present? ? t("budgets.investments.show.edit_image") : t("budgets.investments.show.add_image")
|
||||
end
|
||||
|
||||
def errors_on_image(investment)
|
||||
|
||||
@@ -74,6 +74,7 @@ module Abilities
|
||||
cannot :comment_as_moderator, [::Legislation::Question, Legislation::Annotation]
|
||||
|
||||
can [:create, :destroy], Document
|
||||
can [:create, :destroy], Image
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,6 +40,9 @@ module Abilities
|
||||
can [:create, :destroy, :new], Document, documentable: { author_id: user.id }
|
||||
can [:new_nested, :upload, :destroy_upload], Document
|
||||
|
||||
can [:create, :destroy, :new], Image, imageable: { author_id: user.id }
|
||||
can [:new_nested, :upload, :destroy_upload], Image
|
||||
|
||||
unless user.organization?
|
||||
can :vote, Debate
|
||||
can :vote, Comment
|
||||
|
||||
@@ -7,8 +7,8 @@ class Budget
|
||||
include Reclassification
|
||||
include Followable
|
||||
include Communitable
|
||||
include Documentable
|
||||
include Imageable
|
||||
include Documentable
|
||||
documentable max_documents_allowed: 3,
|
||||
max_file_size: 3.megabytes,
|
||||
accepted_content_types: [ "application/pdf" ]
|
||||
|
||||
@@ -15,6 +15,7 @@ module Documentable
|
||||
@max_file_size = options[:max_file_size]
|
||||
@accepted_content_types = options[:accepted_content_types]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,7 +8,7 @@ class Document < ActiveRecord::Base
|
||||
belongs_to :documentable, polymorphic: true
|
||||
|
||||
# Disable paperclip security validation due to polymorphic configuration
|
||||
# Paperclip do not allow to user Procs on valiations definition
|
||||
# Paperclip do not allow to use Procs on valiations definition
|
||||
do_not_validate_attachment_file_type :attachment
|
||||
validate :attachment_presence
|
||||
validate :validate_attachment_content_type, if: -> { attachment.present? }
|
||||
@@ -64,7 +64,7 @@ class Document < ActiveRecord::Base
|
||||
!accepted_content_types(documentable).include?(attachment_content_type)
|
||||
errors[:attachment] = I18n.t("documents.errors.messages.wrong_content_type",
|
||||
content_type: attachment_content_type,
|
||||
accepted_content_types: humanized_accepted_content_types(documentable))
|
||||
accepted_content_types: documentable_humanized_accepted_content_types(documentable))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,35 +1,62 @@
|
||||
class Image < ActiveRecord::Base
|
||||
include ImagesHelper
|
||||
include ImageablesHelper
|
||||
|
||||
TITLE_LEGHT_RANGE = 4..80
|
||||
MIN_SIZE = 475
|
||||
MAX_IMAGE_SIZE = 1.megabyte
|
||||
ACCEPTED_CONTENT_TYPE = %w(image/jpeg image/jpg)
|
||||
|
||||
attr_accessor :content_type, :original_filename, :attachment_data, :attachment_urls
|
||||
has_attached_file :attachment, styles: { large: "x#{MIN_SIZE}", medium: "300x300#", thumb: "140x245#" },
|
||||
path: ":rails_root/public/system/:class/:prefix/:style/:filename",
|
||||
url: "/system/:class/:prefix/:style/:filename"
|
||||
attr_accessor :cached_attachment
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :imageable, polymorphic: true
|
||||
before_validation :set_styles
|
||||
has_attached_file :attachment, styles: { large: "x475", medium: "300x300#", thumb: "140x245#" },
|
||||
url: "/system/:class/:attachment/:imageable_name_path/:style/:hash.:extension",
|
||||
hash_secret: Rails.application.secrets.secret_key_base
|
||||
validates_attachment :attachment, presence: true, content_type: { content_type: %w(image/jpeg image/jpg) },
|
||||
size: { less_than: 1.megabytes }
|
||||
|
||||
# Disable paperclip security validation due to polymorphic configuration
|
||||
# Paperclip do not allow to use Procs on valiations definition
|
||||
do_not_validate_attachment_file_type :attachment
|
||||
validate :attachment_presence
|
||||
validate :validate_attachment_content_type, if: -> { attachment.present? }
|
||||
validate :validate_attachment_size, if: -> { attachment.present? }
|
||||
validates :title, presence: true, length: { in: TITLE_LEGHT_RANGE }
|
||||
validate :check_image_dimensions
|
||||
validates :user_id, presence: true
|
||||
validates :imageable_id, presence: true, if: -> { persisted? }
|
||||
validates :imageable_type, presence: true, if: -> { persisted? }
|
||||
|
||||
validate :validate_image_dimensions, if: -> { attachment.present? && attachment.dirty? }
|
||||
|
||||
after_create :redimension_using_origin_styles
|
||||
accepts_nested_attributes_for :imageable
|
||||
after_save :remove_cached_image, if: -> { valid? && persisted? && cached_attachment.present? }
|
||||
|
||||
# # overwrite default styles for Image class
|
||||
# def set_image_styles
|
||||
# { large: "x#{MIN_SIZE}", medium: "300x300#", thumb: "140x245#" }
|
||||
# end
|
||||
def set_styles
|
||||
if imageable
|
||||
imageable.set_styles if imageable.respond_to? :set_styles
|
||||
def set_cached_attachment_from_attachment(prefix)
|
||||
self.cached_attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem
|
||||
attachment.path
|
||||
else
|
||||
{ large: "x#{MIN_SIZE}", medium: "300x300#", thumb: "140x245#" }
|
||||
prefix + attachment.url
|
||||
end
|
||||
end
|
||||
|
||||
Paperclip.interpolates :imageable_name_path do |attachment, _style|
|
||||
attachment.instance.imageable.class.to_s.downcase.split('::').map(&:pluralize).join('/')
|
||||
def set_attachment_from_cached_attachment
|
||||
self.attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem
|
||||
File.open(cached_attachment)
|
||||
else
|
||||
URI.parse(cached_attachment)
|
||||
end
|
||||
end
|
||||
|
||||
Paperclip.interpolates :prefix do |attachment, style|
|
||||
attachment.instance.prefix(attachment, style)
|
||||
end
|
||||
|
||||
def prefix(attachment, style)
|
||||
if !attachment.instance.persisted?
|
||||
"cached_attachments/user/#{attachment.instance.user_id}"
|
||||
else
|
||||
":attachment/:id_partition"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
@@ -38,11 +65,37 @@ class Image < ActiveRecord::Base
|
||||
attachment.reprocess!
|
||||
end
|
||||
|
||||
def check_image_dimensions
|
||||
return unless attachment?
|
||||
|
||||
def validate_image_dimensions
|
||||
dimensions = Paperclip::Geometry.from_file(attachment.queued_for_write[:original].path)
|
||||
errors.add(:attachment, :min_image_width, required_min_width: MIN_SIZE) if dimensions.width < MIN_SIZE
|
||||
errors.add(:attachment, :min_image_height, required_min_height: MIN_SIZE) if dimensions.height < MIN_SIZE
|
||||
end
|
||||
|
||||
def validate_attachment_size
|
||||
if imageable.present? &&
|
||||
attachment_file_size > 1.megabytes
|
||||
errors[:attachment] = I18n.t("images.errors.messages.in_between",
|
||||
min: "0 Bytes",
|
||||
max: "#{imageable_max_file_size} MB")
|
||||
end
|
||||
end
|
||||
|
||||
def validate_attachment_content_type
|
||||
if imageable.present? &&
|
||||
!imageable_accepted_content_types.include?(attachment_content_type)
|
||||
errors[:attachment] = I18n.t("images.errors.messages.wrong_content_type",
|
||||
content_type: attachment_content_type,
|
||||
accepted_content_types: imageable_humanized_accepted_content_types)
|
||||
end
|
||||
end
|
||||
|
||||
def attachment_presence
|
||||
if attachment.blank? && cached_attachment.blank?
|
||||
errors[:attachment] = I18n.t("errors.messages.blank")
|
||||
end
|
||||
end
|
||||
|
||||
def remove_cached_image
|
||||
File.delete(cached_attachment) if File.exists?(cached_attachment)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ class Proposal < ActiveRecord::Base
|
||||
include Graphqlable
|
||||
include Followable
|
||||
include Communitable
|
||||
include Imageable
|
||||
include Documentable
|
||||
documentable max_documents_allowed: 3,
|
||||
max_file_size: 3.megabytes,
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<%= f.text_field :external_url %>
|
||||
</div>
|
||||
|
||||
<div class="images small-12 column" data-max-images="1">
|
||||
<%= render 'images/nested_images', imageable: @investment %>
|
||||
</div>
|
||||
|
||||
<div class="documents small-12 column" data-max-documents="<%= Budget::Investment.max_documents_allowed %>">
|
||||
<%= render 'documents/nested_documents', documentable: @investment %>
|
||||
</div>
|
||||
@@ -53,29 +57,6 @@
|
||||
data: {js_url: suggest_tags_path} %>
|
||||
</div>
|
||||
|
||||
<%= f.fields_for :image do |builder| %>
|
||||
|
||||
<div class="small-12 column">
|
||||
<div class="image-upload">
|
||||
<%= f.file_field :attachment, accept: 'image/jpg,image/jpeg', label: false, class:'show-for-sr' %>
|
||||
<br>
|
||||
<%= f.label :attachment, t("budgets.investments.form.image_label"), class:'button' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if @investment.errors.has_key?(:attachment) %>
|
||||
<div class="small-12 column">
|
||||
<div class="image-errors">
|
||||
<small class="error"><%= errors_on_image(@investment)%></small>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= f.text_field :title %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% unless current_user.manager? %>
|
||||
|
||||
<div class="small-12 column">
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
<%= form_for([@investment.budget, @investment], url: update_image_budget_investment_path(@investment.budget, @investment), multipart: true, method: :put) do |f| %>
|
||||
<%= render 'shared/errors', resource: @investment %>
|
||||
|
||||
<div class="row">
|
||||
<div class="small-12 column">
|
||||
<span class="note-marked">
|
||||
<p><%= investment_image_advice_note(@investment) %></p>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<% if @investment.image.exists? %>
|
||||
<div class="small-12 column">
|
||||
<%= image_tag @investment.image_url(:large) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="small-12 column">
|
||||
<div class="image-upload">
|
||||
<%= f.file_field :attachment, accept: 'image/jpg,image/jpeg', label: false, class:'show-for-sr' %>
|
||||
<br>
|
||||
<%= f.label :attachment, t("budgets.investments.edit_image.form.image_label"), class:'button' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if @investment.errors.has_key?(:image) %>
|
||||
<div class="small-12 column">
|
||||
<div class="image-errors">
|
||||
<small class="error"><%= errors_on_image(@investment)%></small>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= f.label :title, t("budgets.investments.edit_image.form.image_title") %>
|
||||
<%= f.text_field :title, placeholder: t("budgets.investments.edit_image.form.image_title"), label: false %>
|
||||
</div>
|
||||
|
||||
<div class="actions small-12 column">
|
||||
<%= f.submit(class: "button", value: t("budgets.investments.edit_image.form.submit_button")) %>
|
||||
|
||||
<% if @investment.image.exists? %>
|
||||
<div class="float-right">
|
||||
<%= link_to t("budgets.investments.edit_image.form.remove_button"),
|
||||
remove_image_budget_investment_path(@investment.budget, @investment),
|
||||
class: "button hollow alert",
|
||||
method: :delete,
|
||||
data: { confirm: t("budgets.investments.edit_image.form.remove_alert") } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -3,11 +3,8 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="small-12 medium-3 large-2 column">
|
||||
<% if investment.image.exists? %>
|
||||
<figure>
|
||||
<% if investment.image.present? %>
|
||||
<%= image_tag investment.image_url(:thumb), alt: investment.image.title %>
|
||||
<figcaption><%= investment.image.title %></figcaption>
|
||||
</figure>
|
||||
<% else %>
|
||||
<div class="no-image"></div>
|
||||
<% end %>
|
||||
|
||||
@@ -10,9 +10,16 @@
|
||||
class: 'button hollow float-right' %>
|
||||
<% end %>
|
||||
|
||||
<% if can?(:edit_image, @investment) %>
|
||||
<%= link_to investment_image_button_text(@investment),
|
||||
edit_image_budget_investment_path(investment.budget, investment),
|
||||
<% if can?(:create, @image) %>
|
||||
<%= link_to t("images.upload_image"),
|
||||
new_image_path(imageable_id:investment, imageable_type: investment.class.name, from: request.url),
|
||||
class: 'button hollow float-right' %>
|
||||
<% end %>
|
||||
|
||||
<% if @investment.image.present? && can?(:destroy, @investment.image) %>
|
||||
<%= link_to t("images.remove_image"),
|
||||
image_path(@investment.image, from: request.url),
|
||||
method: :delete,
|
||||
class: 'button hollow float-right' %>
|
||||
<% end %>
|
||||
|
||||
@@ -28,20 +35,7 @@
|
||||
|
||||
<br>
|
||||
|
||||
<% if investment.image.exists? %>
|
||||
<div class="row">
|
||||
<div class="small-12 column text-center">
|
||||
<figure>
|
||||
<%= image_tag investment.image_url(:large),
|
||||
alt: investment.image.title %>
|
||||
<figcaption class="text-right">
|
||||
<em><%= investment.image.title %></em>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= render_image(investment.image, :large, true) if investment.image.present? %>
|
||||
|
||||
<p id="investment_code">
|
||||
<%= t("budgets.investments.show.code_html", code: investment.id) %>
|
||||
@@ -138,7 +132,7 @@
|
||||
<%= render partial: 'shared/social_share', locals: {
|
||||
share_title: t("budgets.investments.show.share"),
|
||||
title: investment.title,
|
||||
image_url: investment.image.exists? ? investment_image_full_url(investment, :thumb) : '',
|
||||
image_url: investment.image.present? ? investment_image_full_url(investment, :thumb) : '',
|
||||
url: budget_investment_url(budget_id: investment.budget_id, id: investment.id)
|
||||
} %>
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<div class="budget-investment-image-form row">
|
||||
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= render "shared/back_link" %>
|
||||
|
||||
<h1><%= t("budgets.investments.edit_image.title") %></h1>
|
||||
|
||||
<%= render "image_form" %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 medium-3 column">
|
||||
<span class="icon-budget-investment-image float-right"></span>
|
||||
<h2><%= t("budgets.investments.edit_image.recommendation_title") %></h2>
|
||||
<ul class="recommendations">
|
||||
<li><%= t("budgets.investments.edit_image.recommendation_one") %></li>
|
||||
<li><%= t("budgets.investments.edit_image.recommendation_two") %></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,8 +3,9 @@
|
||||
<p class="help-text"><%= documentables_note(documentable) %></p>
|
||||
|
||||
<% documentable.documents.each_with_index do |document, index| %>
|
||||
<%= render 'documents/nested_fields', document: document, index: index, documentable: documentable %>
|
||||
<%= render 'documents/nested_fields', document: document, index: index %>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
<% unless max_documents_allowed?(documentable) %>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<% if document.errors.has_key?(:attachment) %>
|
||||
<div class="small-12 column source-option-file">
|
||||
<div class="attachment-errors">
|
||||
<small class="error"><%= errors_on_attachment(document) %></small>
|
||||
<small class="error"><%= document_errors_on_attachment(document) %></small>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<%= t "documents.recommendation_two_html",
|
||||
accepted_content_types: humanized_accepted_content_types(@document.documentable) %>
|
||||
accepted_content_types: documentable_humanized_accepted_content_types(@document.documentable) %>
|
||||
</li>
|
||||
<li>
|
||||
<%= t "documents.recommendation_three_html",
|
||||
|
||||
20
app/views/images/_form.html.erb
Normal file
20
app/views/images/_form.html.erb
Normal file
@@ -0,0 +1,20 @@
|
||||
<%= form_for @image,
|
||||
url: images_path(
|
||||
imageable_type: @image.imageable_type,
|
||||
imageable_id: @image.imageable_id,
|
||||
from: params[:from]
|
||||
),
|
||||
html: { multipart: true, class: "imageable"},
|
||||
data: { direct_upload_url: upload_images_url(imageable_type: @image.imageable_type, imageable_id: @image.imageable_id) } do |f| %>
|
||||
|
||||
<%= render 'shared/errors', resource: @image %>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<%= render 'plain_fields', image: @image %>
|
||||
|
||||
<div class="actions small-12 medium-6 large-4 end column">
|
||||
<%= f.submit(t("images.form.submit_button"), class: "button expanded") %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
19
app/views/images/_image.html.erb
Normal file
19
app/views/images/_image.html.erb
Normal file
@@ -0,0 +1,19 @@
|
||||
<div class="row">
|
||||
<div class="small-12 column text-center">
|
||||
<figure>
|
||||
<%= image_tag image.attachment.url(version),
|
||||
class: image_class(image),
|
||||
alt: image.title %>
|
||||
<% if show_caption %>
|
||||
<figcaption class="text-right">
|
||||
<em><%= image.title %></em>
|
||||
</figcaption>
|
||||
<% end %>
|
||||
</figure>
|
||||
|
||||
<% if show_caption %>
|
||||
<hr>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
32
app/views/images/_nested_fields.html.erb
Normal file
32
app/views/images/_nested_fields.html.erb
Normal file
@@ -0,0 +1,32 @@
|
||||
<div id="<%= image_nested_field_wrapper_id %>" class="image">
|
||||
<%= hidden_field_tag :id,
|
||||
image.id,
|
||||
name: image_nested_field_name(image, :id),
|
||||
id: image_nested_field_id(image, :id) if image.persisted? %>
|
||||
<%= hidden_field_tag :user_id,
|
||||
current_user.id,
|
||||
name: image_nested_field_name(image, :user_id),
|
||||
id: image_nested_field_id(image, :user_id) %>
|
||||
<%= hidden_field_tag :cached_attachment,
|
||||
image.cached_attachment,
|
||||
name: image_nested_field_name(image, :cached_attachment),
|
||||
id: image_nested_field_id(image, :cached_attachment) %>
|
||||
|
||||
<%= label_tag :title, t("activerecord.attributes.image.title") %>
|
||||
<%= text_field_tag :title,
|
||||
image.title,
|
||||
name: image_nested_field_name(image, :title),
|
||||
id: image_nested_field_id(image, :title),
|
||||
class: "image-title" %>
|
||||
<% if image.errors[:title].any? %>
|
||||
<small class="error"><%= image.errors[:title].join(", ") %></small>
|
||||
<% end %>
|
||||
|
||||
<%= render_image(image, :thumb, false) if image.attachment.exists? %>
|
||||
|
||||
<%= render_image_attachment(image) %>
|
||||
|
||||
<%= render_destroy_image_link(image) %>
|
||||
<p class="file-name"><%= image_attachment_file_name(image) %></p>
|
||||
<div class="progress-bar-placeholder"><div class="loading-bar"></div></div>
|
||||
</div>
|
||||
16
app/views/images/_nested_images.html.erb
Normal file
16
app/views/images/_nested_images.html.erb
Normal file
@@ -0,0 +1,16 @@
|
||||
<div class="images-list">
|
||||
<%= label_tag :image, t("images.form.title") %>
|
||||
<p class="help-text"><%= imageables_note(imageable) %></p>
|
||||
|
||||
<%= render 'images/nested_fields', image: imageable.image if imageable.image.present? %>
|
||||
</div>
|
||||
|
||||
<% if imageable.image.blank? %>
|
||||
<%= link_to t("images.form.add_new_image"),
|
||||
new_nested_images_path(imageable_type: imageable.class.name, index: 0),
|
||||
remote: true,
|
||||
id: "new_image_link",
|
||||
class: "button hollow" %>
|
||||
<% end %>
|
||||
|
||||
<hr>
|
||||
50
app/views/images/_plain_fields.html.erb
Normal file
50
app/views/images/_plain_fields.html.erb
Normal file
@@ -0,0 +1,50 @@
|
||||
<div id="plain_image_fields" class="image">
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= label_tag :image_title, t("activerecord.attributes.image.title") %>
|
||||
<%= text_field_tag :image_title, image.title, name: "image[title]", class: "image-title" %>
|
||||
<% if image.errors.has_key?(:title) %>
|
||||
<small class="error"><%= image.errors[:title].join(", ") %></small>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= hidden_field_tag :cached_attachment, image.cached_attachment, name: "image[cached_attachment]" %>
|
||||
<%= file_field_tag :attachment,
|
||||
accept: imageable_accepted_content_types_extensions,
|
||||
label: false,
|
||||
class: 'image_ajax_attachment',
|
||||
data: {
|
||||
url: upload_images_url(imageable_type: image.imageable_type, imageable_id: image.imageable_id),
|
||||
cached_attachment_input_field: "image_cached_attachment",
|
||||
multiple: false,
|
||||
nested_image: false
|
||||
},
|
||||
id: "image_attachment",
|
||||
name: "image[attachment]" %>
|
||||
|
||||
<% if image.cached_attachment.blank? %>
|
||||
<%= label_tag :image_attachment, t("images.form.attachment_label"), class: 'button hollow' %>
|
||||
<% else %>
|
||||
<%= link_to t('images.form.delete_button'),
|
||||
destroy_upload_images_path(path: image.cached_attachment,
|
||||
nested_image: false,
|
||||
imageable_type: image.imageable_type,
|
||||
imageable_id: image.imageable_id),
|
||||
method: :delete,
|
||||
remote: true,
|
||||
class: "delete float-right" %>
|
||||
<% end %>
|
||||
|
||||
<% if image.errors.has_key?(:attachment) %>
|
||||
<div class="small-12 column source-option-file">
|
||||
<div class="attachment-errors">
|
||||
<small class="error"><%= image_errors_on_attachment(image) %></small>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<p class="file-name"><%= image_attachment_file_name(image) %></p>
|
||||
<div class="progress-bar-placeholder"><div class="loading-bar"></div></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
17
app/views/images/destroy.js.erb
Normal file
17
app/views/images/destroy.js.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<% if params[:nested_image] == "true" %>
|
||||
|
||||
App.Imageable.destroyNestedImage("<%= image_nested_field_wrapper_id %>", "<%= j render('layouts/flash') %>")
|
||||
<% new_image_link = link_to t("images.form.add_new_image"),
|
||||
new_nested_images_path(imageable_type: @image.imageable_type),
|
||||
remote: true,
|
||||
id: "new_image_link",
|
||||
class: "button hollow" %>
|
||||
App.Imageable.updateNewImageButton("<%= j new_image_link %>")
|
||||
|
||||
<% else %>
|
||||
|
||||
App.Imageable.replacePlainImage("plain_image_fields",
|
||||
"<%= j render('layouts/flash') %>",
|
||||
"<%= j render('plain_fields', image: @image) %>")
|
||||
|
||||
<% end %>
|
||||
26
app/views/images/new.html.erb
Normal file
26
app/views/images/new.html.erb
Normal file
@@ -0,0 +1,26 @@
|
||||
<div class="image-form <%= imageable_class(@image.imageable) %> row">
|
||||
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= back_link_to params[:from] %>
|
||||
<h1><%= t("images.new.title") %></h1>
|
||||
<%= render "form", form_url: images_url %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 medium-3 column">
|
||||
<span class="icon-images float-right"></span>
|
||||
<h2><%= t("images.recommendations_title") %></h2>
|
||||
<ul class="recommendations">
|
||||
<li>
|
||||
<%= t "images.recommendation_one_html" %>
|
||||
</li>
|
||||
<li>
|
||||
<%= t "images.recommendation_two_html",
|
||||
accepted_content_types: imageable_humanized_accepted_content_types %>
|
||||
</li>
|
||||
<li>
|
||||
<%= t "images.recommendation_three_html",
|
||||
max_file_size: imageable_max_file_size %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
9
app/views/images/new_nested.js.erb
Normal file
9
app/views/images/new_nested.js.erb
Normal file
@@ -0,0 +1,9 @@
|
||||
<%
|
||||
new_image_link = link_to t("images.form.add_new_image"),
|
||||
new_nested_images_path(imageable_type: params[:imageable_type]),
|
||||
remote: true,
|
||||
id: "new_image_link",
|
||||
class: "button hollow"
|
||||
%>
|
||||
App.Imageable.new("<%= j render('images/nested_fields', image: @image) %>")
|
||||
App.Imageable.updateNewImageButton("<%= j new_image_link %>")
|
||||
12
app/views/images/upload.js.erb
Normal file
12
app/views/images/upload.js.erb
Normal file
@@ -0,0 +1,12 @@
|
||||
<% if params[:nested_image] == "true" %>
|
||||
|
||||
App.Imageable.uploadNestedImage("<%= image_nested_field_wrapper_id %>",
|
||||
"<%= j render('images/nested_fields', image: @image) %>",
|
||||
<%= @image.cached_attachment.present? %>)
|
||||
<% else %>
|
||||
|
||||
App.Imageable.uploadPlainImage("plain_image_fields",
|
||||
"<%= j render('images/plain_fields', image: @image) %>",
|
||||
<%= @image.cached_attachment.present? %>)
|
||||
|
||||
<% end %>
|
||||
@@ -46,6 +46,10 @@
|
||||
<%= f.text_field :external_url, placeholder: t("proposals.form.proposal_external_url"), label: false %>
|
||||
</div>
|
||||
|
||||
<div class="images small-12 column" data-max-images="1">
|
||||
<%= render 'images/nested_images', imageable: @proposal %>
|
||||
</div>
|
||||
|
||||
<div class="documents small-12 column" data-max-documents="<%= Proposal.max_documents_allowed %>">
|
||||
<%= render 'documents/nested_documents', documentable: @proposal %>
|
||||
</div>
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<%= render_image(@proposal.image, :large, true) if @proposal.image.present? %>
|
||||
|
||||
<br>
|
||||
<p>
|
||||
<%= t("proposals.show.code") %>
|
||||
@@ -106,7 +108,8 @@
|
||||
</div>
|
||||
|
||||
<aside class="small-12 medium-3 column">
|
||||
<% if can_create_document?(@document, @proposal) || author_of_proposal?(@proposal) || current_editable?(@proposal) %>
|
||||
<% if can_create_document?(@document, @proposal) || author_of_proposal?(@proposal) || current_editable?(@proposal) ||
|
||||
can?(:create, @image) || (@proposal.image.present? && can?(:destroy, @proposal.image)) %>
|
||||
<div class="sidebar-divider"></div>
|
||||
<h2><%= t("proposals.show.author") %></h2>
|
||||
<div class="show-actions-menu">
|
||||
@@ -132,6 +135,22 @@
|
||||
<%= t("proposals.show.edit_proposal_link") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if can?(:create, @image) %>
|
||||
<%= link_to new_image_path(imageable_id: @proposal, imageable_type: @proposal.class.name, from: request.url),
|
||||
class: 'button hollow float-right' do %>
|
||||
<%= t("images.upload_image") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @proposal.image.present? && can?(:destroy, @proposal.image) %>
|
||||
<%= link_to image_path(@proposal.image, from: request.url),
|
||||
method: :delete,
|
||||
class: 'button hollow float-right',
|
||||
data: { confirm: 'Are you sure?' } do %>
|
||||
<% t("images.remove_image") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ data:
|
||||
- config/locales/%{locale}/legislation.yml
|
||||
- config/locales/%{locale}/community.yml
|
||||
- config/locales/%{locale}/documents.yml
|
||||
- config/locales/%{locale}/images.yml
|
||||
|
||||
# Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
|
||||
# `i18n-tasks normalize -p` will force move the keys according to these rules
|
||||
|
||||
@@ -79,6 +79,9 @@ en:
|
||||
documents:
|
||||
one: "Document"
|
||||
other: "Documents"
|
||||
images:
|
||||
one: "Image"
|
||||
other: "Images"
|
||||
attributes:
|
||||
budget:
|
||||
name: "Name"
|
||||
@@ -205,6 +208,9 @@ en:
|
||||
document:
|
||||
title: Title
|
||||
attachment: Attachment
|
||||
image:
|
||||
title: Title
|
||||
attachment: Attachment
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
|
||||
@@ -181,6 +181,7 @@ en:
|
||||
signature_sheet: Signature sheet
|
||||
document: Document
|
||||
topic: Topic
|
||||
image: Image
|
||||
geozones:
|
||||
none: All city
|
||||
all: All scopes
|
||||
|
||||
33
config/locales/en/images.yml
Normal file
33
config/locales/en/images.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
en:
|
||||
images:
|
||||
upload_image: Upload image
|
||||
remove_image: Remove image
|
||||
form:
|
||||
title: Descriptive image
|
||||
attachment_label: Choose image
|
||||
submit_button: Upload image
|
||||
delete_button: Remove image
|
||||
note: "You can upload one image of following content types: %{accepted_content_types}, up to %{max_file_size} MB."
|
||||
add_new_image: Add image
|
||||
new:
|
||||
title: Upload image
|
||||
recommendations_title: File upload tips
|
||||
recommendation_one_html: You can upload only one image. Proposals with image attract more attention of the users.
|
||||
recommendation_two_html: You can upload <strong>%{accepted_content_types}</strong> image.
|
||||
recommendation_three_html: You can upload one image up to <strong>%{max_file_size} MB</strong>.
|
||||
|
||||
actions:
|
||||
create:
|
||||
notice: Image was created successfully.
|
||||
alert: Cannot create image. Check form errors and try again.
|
||||
destroy:
|
||||
notice: Image was deleted successfully.
|
||||
alert: Cannot destroy image.
|
||||
confirm: Are you sure you want to delete the image? This action cannot be undone!
|
||||
buttons:
|
||||
download_image: Dowload file
|
||||
destroy_image: Destroy
|
||||
errors:
|
||||
messages:
|
||||
in_between: must be in between %{min} and %{max}
|
||||
wrong_content_type: content type %{content_type} does not match any of accepted content types %{accepted_content_types}
|
||||
@@ -79,6 +79,9 @@ es:
|
||||
documents:
|
||||
one: "Documento"
|
||||
other: "Documentos"
|
||||
images:
|
||||
one: "Imagen"
|
||||
other: "Imágenes"
|
||||
attributes:
|
||||
budget:
|
||||
name: "Nombre"
|
||||
@@ -99,8 +102,7 @@ es:
|
||||
title: "Título"
|
||||
location: "Ubicación"
|
||||
organization_name: "Si estás proponiendo en nombre de una organización o colectivo, escribe su nombre"
|
||||
image: "Imagen descriptiva de la propuesta"
|
||||
image_title: "Título de la imagen"
|
||||
image: "Imagen descriptiva de la propuesta de inversión"
|
||||
comment:
|
||||
body: "Comentario"
|
||||
user: "Usuario"
|
||||
@@ -200,6 +202,9 @@ es:
|
||||
document:
|
||||
title: Título
|
||||
attachment: Archivo adjunto
|
||||
image:
|
||||
title: Título
|
||||
attachment: Archivo adjunto
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
|
||||
33
config/locales/es/images.yml
Normal file
33
config/locales/es/images.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
es:
|
||||
images:
|
||||
upload_image: Subir imagen
|
||||
remove_image: Eliminar imagen
|
||||
form:
|
||||
title: Imagen descriptiva
|
||||
attachment_label: Selecciona una imagen
|
||||
submit_button: Subir imagen
|
||||
delete_button: Eliminar imagen
|
||||
note: "Puedes subir una imagen en los formatos: %{accepted_content_types}, y de hasta %{max_file_size} MB por archivo."
|
||||
add_new_image: Añadir imagen
|
||||
new:
|
||||
title: Subir una imagen
|
||||
recommendations_title: Consejos para subir imágenes
|
||||
recommendation_one_html: Puedes subir una imagen descriptiva, esta imagen se mostrará en los listados y ayudará a que tu propuesta llame más la atención de los usuarios.
|
||||
recommendation_two_html: "Sólo puedes subir <strong>imágenes en los formatos: %{accepted_content_types}</strong>."
|
||||
recommendation_three_html: Puedes subir una imagen de hasta <strong>%{max_file_size} MB</strong>
|
||||
|
||||
actions:
|
||||
create:
|
||||
notice: "La imagen se ha creado correctamente."
|
||||
alert: "La imagen no se ha podido crear. Revise los errores del formulario."
|
||||
destroy:
|
||||
notice: "La imagen se ha eliminado correctamente."
|
||||
alert: "La imagen no se ha podido eliminar."
|
||||
confirm: "¿Está seguro de que desea eliminar la imagen? Esta acción no se puede deshacer!"
|
||||
buttons:
|
||||
download_image: Descargar imagen
|
||||
destroy_image: Eliminar
|
||||
errors:
|
||||
messages:
|
||||
in_between: debe estar entre %{min} y %{max}
|
||||
wrong_content_type: El tipo de contenido %{content_type} de la imagen no coincide con ninguno de los tipos de contenido aceptados %{accepted_content_types}
|
||||
@@ -39,7 +39,6 @@ Rails.application.routes.draw do
|
||||
concern :imageable do
|
||||
resources :images
|
||||
end
|
||||
|
||||
resources :debates do
|
||||
member do
|
||||
post :vote
|
||||
@@ -84,9 +83,6 @@ Rails.application.routes.draw do
|
||||
resources :investments, controller: "budgets/investments", only: [:index, :new, :create, :show, :destroy], concerns: :imageable do
|
||||
member do
|
||||
post :vote
|
||||
get :edit_image
|
||||
put :update_image
|
||||
delete :remove_image
|
||||
end
|
||||
collection { get :suggest }
|
||||
end
|
||||
@@ -112,6 +108,14 @@ Rails.application.routes.draw do
|
||||
end
|
||||
end
|
||||
|
||||
resources :images, only: [:new, :create, :destroy] do
|
||||
collection do
|
||||
get :new_nested
|
||||
delete :destroy_upload
|
||||
post :upload
|
||||
end
|
||||
end
|
||||
|
||||
resources :stats, only: [:index]
|
||||
|
||||
resources :legacy_legislations, only: [:show], path: 'legislations'
|
||||
|
||||
5
db/migrate/20170911110109_add_user_id_to_images.rb
Normal file
5
db/migrate/20170911110109_add_user_id_to_images.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddUserIdToImages < ActiveRecord::Migration
|
||||
def change
|
||||
add_reference :images, :user, index: true, foreign_key: true
|
||||
end
|
||||
end
|
||||
@@ -398,9 +398,11 @@ ActiveRecord::Schema.define(version: 20170918231410) do
|
||||
t.string "attachment_content_type"
|
||||
t.integer "attachment_file_size"
|
||||
t.datetime "attachment_updated_at"
|
||||
t.integer "user_id"
|
||||
end
|
||||
|
||||
add_index "images", ["imageable_type", "imageable_id"], name: "index_images_on_imageable_type_and_imageable_id", using: :btree
|
||||
add_index "images", ["user_id"], name: "index_images_on_user_id", using: :btree
|
||||
|
||||
create_table "legacy_legislations", force: :cascade do |t|
|
||||
t.string "title"
|
||||
@@ -1093,6 +1095,7 @@ ActiveRecord::Schema.define(version: 20170918231410) do
|
||||
add_foreign_key "geozones_polls", "geozones"
|
||||
add_foreign_key "geozones_polls", "polls"
|
||||
add_foreign_key "identities", "users"
|
||||
add_foreign_key "images", "users"
|
||||
add_foreign_key "legislation_draft_versions", "legislation_processes"
|
||||
add_foreign_key "locks", "users"
|
||||
add_foreign_key "managers", "users"
|
||||
|
||||
@@ -319,14 +319,20 @@ FactoryGirl.define do
|
||||
valuation_finished true
|
||||
end
|
||||
|
||||
trait :with_descriptive_image do
|
||||
association :image, factory: :image
|
||||
end
|
||||
end
|
||||
|
||||
factory :image, class: 'Image' do
|
||||
factory :image do
|
||||
attachment { File.new("spec/fixtures/files/clippy.jpg") }
|
||||
title "Lorem ipsum dolor sit amet"
|
||||
association :user, factory: :user
|
||||
|
||||
trait :proposal_image do
|
||||
association :imageable, factory: :proposal
|
||||
end
|
||||
|
||||
trait :budget_investment_image do
|
||||
association :imageable, factory: :budget_investment
|
||||
end
|
||||
end
|
||||
|
||||
factory :budget_ballot, class: 'Budget::Ballot' do
|
||||
|
||||
@@ -29,17 +29,17 @@ feature 'Budget Investments' do
|
||||
end
|
||||
|
||||
scenario 'Index should show investment descriptive image only when is defined' do
|
||||
investment = FactoryGirl.create(:budget_investment, heading: heading)
|
||||
investment_with_image = FactoryGirl.create(:budget_investment, :with_descriptive_image, heading: heading)
|
||||
investment = create(:budget_investment, heading: heading)
|
||||
investment_with_image = create(:budget_investment, heading: heading)
|
||||
image = create(:image, imageable: investment_with_image)
|
||||
|
||||
visit budget_investments_path(budget, heading_id: heading.id)
|
||||
|
||||
within("#budget_investment_#{investment.id}") do
|
||||
expect(page).not_to have_css("picture")
|
||||
expect(page).not_to have_css("img")
|
||||
end
|
||||
|
||||
within("#budget_investment_#{investment_with_image.id}") do
|
||||
expect(page).to have_css("picture img[alt='#{investment_with_image.image.title}']")
|
||||
expect(page).to have_css("img[alt='#{investment_with_image.image.title}']")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -370,16 +370,6 @@ feature 'Budget Investments' do
|
||||
expect(page).not_to have_selector ".js-follow"
|
||||
end
|
||||
|
||||
scenario "Show descriptive image when exists" do
|
||||
user = create(:user)
|
||||
login_as(user)
|
||||
investment_with_image = create(:budget_investment, :with_descriptive_image, heading: heading)
|
||||
|
||||
visit budget_investment_path(budget_id: budget.id, id: investment_with_image.id)
|
||||
|
||||
expect(page).to have_css("img[alt='#{investment_with_image.image.title}']")
|
||||
end
|
||||
|
||||
scenario "Show back link contains heading id" do
|
||||
investment = create(:budget_investment, heading: heading)
|
||||
visit budget_investment_path(budget, investment)
|
||||
@@ -387,241 +377,6 @@ feature 'Budget Investments' do
|
||||
expect(page).to have_link "Go back", href: budget_investments_path(budget, heading_id: investment.heading)
|
||||
end
|
||||
|
||||
context "Show investment image button" do
|
||||
|
||||
scenario "Should show add text when investment has not image" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_link "Add image"
|
||||
end
|
||||
|
||||
scenario "Should show edit text when investment has already an image" do
|
||||
investment = create(:budget_investment, :with_descriptive_image, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_link "Edit image"
|
||||
end
|
||||
|
||||
scenario "Should not be shown for anonymous users" do
|
||||
investment = create(:budget_investment, heading: heading)
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).not_to have_link "Edit image", href: edit_image_budget_investment_path(budget, investment)
|
||||
end
|
||||
|
||||
scenario "Should not be shown when current user is not investment author" do
|
||||
investment = create(:budget_investment, heading: heading)
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).not_to have_link "Edit image", href: edit_image_budget_investment_path(budget, investment)
|
||||
end
|
||||
|
||||
scenario "Should be shown when current user is investment author" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_link "Add image", href: edit_image_budget_investment_path(budget, investment)
|
||||
end
|
||||
|
||||
scenario "Should be shown when current user is administrator" do
|
||||
administrator = create(:administrator).user
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(administrator)
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_link "Add image", href: edit_image_budget_investment_path(budget, investment)
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Create new investment project should show image title validation errors when image selected and title blank" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit new_budget_investment_path(budget)
|
||||
select 'Health: More hospitals', from: 'budget_investment_heading_id'
|
||||
fill_in 'budget_investment_title', with: 'Build a skyscraper'
|
||||
fill_in 'budget_investment_description', with: 'I want to live in a high tower over the clouds'
|
||||
attach_file :budget_investment_image, "spec/fixtures/files/clippy.jpg"
|
||||
check 'budget_investment_terms_of_service'
|
||||
click_button "Create Investment"
|
||||
|
||||
expect(page).to have_content "can't be blank, is too short (minimum is 4 characters)"
|
||||
end
|
||||
|
||||
scenario "Create new investment project should show image validation error when image selected is too small" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit new_budget_investment_path(budget)
|
||||
select 'Health: More hospitals', from: 'budget_investment_heading_id'
|
||||
fill_in 'budget_investment_title', with: 'Build a skyscraper'
|
||||
fill_in 'budget_investment_description', with: 'I want to live in a high tower over the clouds'
|
||||
attach_file :budget_investment_image, "spec/fixtures/files/logo_header.jpg"
|
||||
check 'budget_investment_terms_of_service'
|
||||
click_button "Create Investment"
|
||||
|
||||
expect(page).to have_content "Image dimensions are too small. For a good quality please upload a larger image. Minimum width: 475px, minimum height: 475px."
|
||||
end
|
||||
|
||||
scenario "Edit image page should show image title validation errors when image selected and title blank" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
attach_file :budget_investment_image, "spec/fixtures/files/clippy.jpg"
|
||||
click_button "Save image"
|
||||
|
||||
expect(page).to have_content "can't be blank, is too short (minimum is 4 characters)"
|
||||
end
|
||||
|
||||
scenario "Edit image page should show image validation error when image selected is too small" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
attach_file :budget_investment_image, "spec/fixtures/files/logo_header.jpg"
|
||||
click_button "Save image"
|
||||
|
||||
expect(page).to have_content "Image dimensions are too small. For a good quality please upload a larger image. Minimum width: 475px, minimum height: 475px."
|
||||
end
|
||||
|
||||
scenario "Edit image page should not be accesible when there is no logged user" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_content "You must sign in or register to continue"
|
||||
end
|
||||
|
||||
scenario "Edit image page should redirect to investment show page if logged user is not the author" do
|
||||
other_author = create(:user, :level_two, username: 'Manuel')
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(other_author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_content "You do not have permission to carry out the action 'edit_image' on budget/investment."
|
||||
end
|
||||
|
||||
scenario "Edit image page should be accesible when author is currently logged" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_content "Change your project image"
|
||||
end
|
||||
|
||||
scenario "Edit image page should be accesible when there is logged any administrator" do
|
||||
administrator = create(:administrator).user
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(administrator)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_content "Change your project image"
|
||||
end
|
||||
|
||||
scenario "Remove image button should not be present when investment image does not exists" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).not_to have_link "Remove image"
|
||||
end
|
||||
|
||||
scenario "Remove image button should be present when investment has an image defined" do
|
||||
investment = create(:budget_investment, :with_descriptive_image, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_link "Remove image"
|
||||
end
|
||||
|
||||
scenario "Remove image button should be possible for administrators" do
|
||||
administrator = create(:administrator).user
|
||||
investment = create(:budget_investment, :with_descriptive_image, heading: heading, author: author)
|
||||
login_as(administrator)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
click_link "Remove image"
|
||||
|
||||
expect(page).to have_content "Investment project image removed succesfully."
|
||||
end
|
||||
|
||||
scenario "Remove image should be possible for investment author" do
|
||||
investment = create(:budget_investment, :with_descriptive_image, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
click_link "Remove image"
|
||||
|
||||
expect(page).to have_content "Investment project image removed succesfully."
|
||||
end
|
||||
|
||||
scenario "Remove image should not be possible for any other logged users (except administrators and author)" do
|
||||
investment = create(:budget_investment, :with_descriptive_image, heading: heading, author: author)
|
||||
login_as(create(:user))
|
||||
|
||||
visit edit_image_budget_investment_path(budget, investment)
|
||||
|
||||
expect(page).to have_content "You do not have permission to carry out the action 'edit_image' on budget/investment."
|
||||
end
|
||||
|
||||
scenario "Update image should not be possible if logged user is not the author" do
|
||||
other_author = create(:user, :level_two, username: 'Manuel')
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(other_author)
|
||||
|
||||
visit edit_image_budget_investment_path(investment.budget, investment)
|
||||
|
||||
expect(current_path).not_to eq(edit_image_budget_investment_path(investment.budget, investment))
|
||||
expect(page).to have_content 'You do not have permission'
|
||||
end
|
||||
|
||||
scenario "Update image should be possible for authors" do
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(author)
|
||||
|
||||
visit edit_image_budget_investment_path(investment.budget, investment)
|
||||
fill_in :budget_investment_image_title, with: "New image title"
|
||||
attach_file :budget_investment_image, "spec/fixtures/files/clippy.jpg"
|
||||
click_on "Save image"
|
||||
|
||||
within ".budget-investment-show" do
|
||||
expect(page).to have_css("img[src*='clippy.jpg']")
|
||||
end
|
||||
expect(page).to have_content 'Investment project image updated succesfully. '
|
||||
end
|
||||
|
||||
scenario "Update image should be possible for administrators" do
|
||||
administrator = create(:administrator).user
|
||||
investment = create(:budget_investment, heading: heading, author: author)
|
||||
login_as(administrator)
|
||||
|
||||
visit edit_image_budget_investment_path(investment.budget, investment)
|
||||
fill_in :budget_investment_image_title, with: "New image title"
|
||||
attach_file :budget_investment_image, "spec/fixtures/files/clippy.jpg"
|
||||
click_on "Save image"
|
||||
|
||||
within ".budget-investment-show" do
|
||||
expect(page).to have_css("img[src*='clippy.jpg']")
|
||||
end
|
||||
expect(page).to have_content 'Investment project image updated succesfully. '
|
||||
end
|
||||
|
||||
context "Show (feasible budget investment)" do
|
||||
let(:investment) do
|
||||
create(:budget_investment,
|
||||
@@ -707,6 +462,8 @@ feature 'Budget Investments' do
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like "imageable", "budget_investment", "budget_investment_path", { "budget_id": "budget_id", "id": "id" }
|
||||
|
||||
it_behaves_like "followable", "budget_investment", "budget_investment_path", { "budget_id": "budget_id", "id": "id" }
|
||||
|
||||
it_behaves_like "documentable", "budget_investment", "budget_investment_path", {"budget_id": "budget_id", "id": "id"}
|
||||
|
||||
@@ -1272,6 +1272,8 @@ feature 'Proposals' do
|
||||
expect(Flag.flagged?(user, proposal)).to_not be
|
||||
end
|
||||
|
||||
it_behaves_like "imageable", "proposal", "proposal_path", { "id": "id" }
|
||||
|
||||
it_behaves_like "followable", "proposal", "proposal_path", { "id": "id" }
|
||||
|
||||
it_behaves_like "documentable", "proposal", "proposal_path", { "id": "id" }
|
||||
|
||||
@@ -20,6 +20,9 @@ describe "Abilities::Administrator" do
|
||||
let(:budget_investment_document) { build(:document, documentable: budget_investment) }
|
||||
let(:poll_question_document) { build(:document, documentable: poll_question) }
|
||||
|
||||
let(:proposal_image) { build(:image, imageable: proposal) }
|
||||
let(:budget_investment_image) { build(:image, imageable: budget_investment) }
|
||||
|
||||
let(:hidden_debate) { create(:debate, :hidden) }
|
||||
let(:hidden_comment) { create(:comment, :hidden) }
|
||||
let(:hidden_proposal) { create(:proposal, :hidden) }
|
||||
@@ -82,6 +85,10 @@ describe "Abilities::Administrator" do
|
||||
it { should be_able_to(:create, proposal_document) }
|
||||
it { should be_able_to(:destroy, proposal_document) }
|
||||
|
||||
it { should be_able_to(:new, proposal_image) }
|
||||
it { should be_able_to(:create, proposal_image) }
|
||||
it { should be_able_to(:destroy, proposal_image) }
|
||||
|
||||
it { should be_able_to(:new, budget_investment_document) }
|
||||
it { should be_able_to(:create, budget_investment_document) }
|
||||
it { should be_able_to(:destroy, budget_investment_document) }
|
||||
@@ -89,4 +96,9 @@ describe "Abilities::Administrator" do
|
||||
it { should be_able_to(:new, poll_question_document) }
|
||||
it { should be_able_to(:create, poll_question_document) }
|
||||
it { should be_able_to(:destroy, poll_question_document) }
|
||||
|
||||
it { should be_able_to(:new, budget_investment_image) }
|
||||
it { should be_able_to(:create, budget_investment_image) }
|
||||
it { should be_able_to(:destroy, budget_investment_image) }
|
||||
|
||||
end
|
||||
|
||||
@@ -29,82 +29,7 @@ describe Budget::Investment do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#image and #image_title", :investment_image do
|
||||
let(:investment_with_image) { build(:budget_investment, :with_descriptive_image) }
|
||||
|
||||
it "should be valid" do
|
||||
expect(investment_with_image).to be_valid
|
||||
end
|
||||
|
||||
describe "file extension" do
|
||||
it "should not be valid with '.png' extension" do
|
||||
investment_with_image.image = File.new("spec/fixtures/files/clippy.png")
|
||||
|
||||
expect(investment_with_image).to_not be_valid
|
||||
expect(investment_with_image.errors[:image].size).to eq(1)
|
||||
end
|
||||
|
||||
it "should not be valid with '.gif' extension" do
|
||||
investment_with_image.image = File.new("spec/fixtures/files/clippy.gif")
|
||||
|
||||
expect(investment_with_image).to_not be_valid
|
||||
expect(investment_with_image.errors[:image].size).to eq(1)
|
||||
end
|
||||
|
||||
it "should be valid with '.jpg' extension" do
|
||||
investment_with_image.image = File.new("spec/fixtures/files/clippy.jpg")
|
||||
|
||||
expect(investment_with_image).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "image dimmessions" do
|
||||
it "should be valid when image dimmessions are 475X475 at least" do
|
||||
expect(investment_with_image).to be_valid
|
||||
end
|
||||
|
||||
it "should not be valid when image dimmensions are smaller than 475X475" do
|
||||
investment_with_image.image = File.new("spec/fixtures/files/logo_header.jpg")
|
||||
|
||||
expect(investment_with_image).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "title" do
|
||||
|
||||
it "should be valid when image and image_title are not defined" do
|
||||
investment_with_image.image = nil
|
||||
investment_with_image.image_title = nil
|
||||
|
||||
expect(investment_with_image).to be_valid
|
||||
end
|
||||
|
||||
it "should not be valid when correct image attached but no image title provided" do
|
||||
investment_with_image.image_title = ''
|
||||
|
||||
expect(investment_with_image).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid when image title is too short" do
|
||||
investment_with_image.image_title = 'a'*3
|
||||
|
||||
expect(investment_with_image).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid when image title is too long" do
|
||||
investment_with_image.image_title = 'a'*81
|
||||
|
||||
expect(investment_with_image).to_not be_valid
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "image destroy should remove image from file storage" do
|
||||
image_url = investment_with_image.image.url
|
||||
|
||||
expect{ investment_with_image.image.destroy }.to change{ investment_with_image.image.url }.from(image_url).to("/images/original/missing.png")
|
||||
end
|
||||
end
|
||||
it_behaves_like "acts as imageable", "budget_investment_image"
|
||||
|
||||
it "sanitizes description" do
|
||||
investment.description = "<script>alert('danger');</script>"
|
||||
|
||||
@@ -304,7 +304,7 @@ shared_examples "documentable" do |documentable_factory_name, documentable_path,
|
||||
from: send(documentable_path, arguments))
|
||||
|
||||
expect(page).to have_content "You can upload up to a maximum of #{max_file_size(documentable)} documents."
|
||||
expect(page).to have_content "You can upload #{humanized_accepted_content_types(documentable)} files."
|
||||
expect(page).to have_content "You can upload #{documentable_humanized_accepted_content_types(documentable)} files."
|
||||
expect(page).to have_content "You can upload files up to #{max_file_size(documentable)} MB."
|
||||
end
|
||||
|
||||
|
||||
331
spec/shared/features/imageable.rb
Normal file
331
spec/shared/features/imageable.rb
Normal file
@@ -0,0 +1,331 @@
|
||||
shared_examples "imageable" do |imageable_factory_name, imageable_path, imageable_path_arguments|
|
||||
include ActionView::Helpers
|
||||
include ImagesHelper
|
||||
include ImageablesHelper
|
||||
|
||||
let!(:administrator) { create(:user) }
|
||||
let!(:user) { create(:user) }
|
||||
let!(:arguments) { {} }
|
||||
let!(:imageable) { create(imageable_factory_name, author: user) }
|
||||
let!(:imageable_dom_name) { imageable_factory_name.parameterize }
|
||||
|
||||
before do
|
||||
create(:administrator, user: administrator)
|
||||
|
||||
imageable_path_arguments.each do |argument_name, path_to_value|
|
||||
arguments.merge!("#{argument_name}": imageable.send(path_to_value))
|
||||
end
|
||||
end
|
||||
|
||||
context "Show" do
|
||||
|
||||
scenario "Show descriptive image when exists", :js do
|
||||
image = create(:image, imageable: imageable)
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
|
||||
expect(page).to have_css("img[alt='#{image.title}']")
|
||||
end
|
||||
|
||||
scenario "Show image title when image exists" do
|
||||
image = create(:image, imageable: imageable)
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
|
||||
expect(page).to have_content image.title
|
||||
end
|
||||
|
||||
scenario "Should not display upload image button when there is no logged user" do
|
||||
visit send(imageable_path, arguments)
|
||||
|
||||
within "##{dom_id(imageable)}" do
|
||||
expect(page).not_to have_link("Upload image")
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Should not display upload image button when maximum number of images reached " do
|
||||
create_list(:image, 3, imageable: imageable)
|
||||
visit send(imageable_path, arguments)
|
||||
|
||||
within "##{dom_id(imageable)}" do
|
||||
expect(page).not_to have_link("Upload image")
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Should display upload image button when user is logged in and is imageable owner" do
|
||||
login_as(user)
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
|
||||
within "##{dom_id(imageable)}" do
|
||||
expect(page).to have_link("Upload image")
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Should display upload image button when admin is logged in" do
|
||||
login_as(administrator)
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
|
||||
within "##{dom_id(imageable)}" do
|
||||
expect(page).to have_link("Upload image")
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Should navigate to new image page when click un upload button" do
|
||||
login_as(user)
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
click_link "Upload image"
|
||||
|
||||
expect(page).to have_selector("h1", text: "Upload image")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "New" do
|
||||
|
||||
scenario "Should not be able for unathenticated users" do
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
expect(page).to have_content("You must sign in or register to continue.")
|
||||
end
|
||||
|
||||
scenario "Should not be able for other users" do
|
||||
login_as create(:user)
|
||||
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
expect(page).to have_content("You do not have permission to carry out the action 'new' on image. ")
|
||||
end
|
||||
|
||||
scenario "Should be able to imageable author" do
|
||||
login_as imageable.author
|
||||
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
expect(page).to have_selector("h1", text: "Upload image")
|
||||
end
|
||||
|
||||
scenario "Should display file name after file selection", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
attach_file :image_attachment, "spec/fixtures/files/empty.pdf", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(page).to have_content "empty.pdf"
|
||||
end
|
||||
|
||||
scenario "Should not display file name after file selection", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
attach_file :image_attachment, "spec/fixtures/files/logo_header.png", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(page).not_to have_content "logo_header.jpg"
|
||||
end
|
||||
|
||||
scenario "Should update loading bar style after valid file upload", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
attach_file :image_attachment, "spec/fixtures/files/clippy.jpg", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(page).to have_selector ".loading-bar.complete"
|
||||
end
|
||||
|
||||
scenario "Should update loading bar style after unvalid file upload", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
attach_file :image_attachment, "spec/fixtures/files/logo_header.png", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(page).to have_selector ".loading-bar.errors"
|
||||
end
|
||||
|
||||
scenario "Should update image title with attachment original file name after file selection if no title defined by user", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
attach_file :image_attachment, "spec/fixtures/files/clippy.jpg", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(find("input[name='image[title]']").value).to eq("clippy.jpg")
|
||||
end
|
||||
|
||||
scenario "Should not update image title with attachment original file name after file selection when title already defined by user", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
fill_in :image_title, with: "My custom title"
|
||||
attach_file :image_attachment, "spec/fixtures/files/clippy.jpg", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(find("input[name='image[title]']").value).to eq("My custom title")
|
||||
end
|
||||
|
||||
scenario "Should update image cached_attachment field after valid file upload", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
attach_file :image_attachment, "spec/fixtures/files/clippy.jpg", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(find("input[name='image[cached_attachment]']", visible: false).value).to include("clippy.jpg")
|
||||
end
|
||||
|
||||
scenario "Should not update image cached_attachment field after unvalid file upload", :js do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
attach_file :image_attachment, "spec/fixtures/files/logo_header.png", make_visible: true
|
||||
sleep 1
|
||||
|
||||
expect(find("input[name='image[cached_attachment]']", visible: false).value).to eq ""
|
||||
end
|
||||
|
||||
scenario "Should show imageable custom recomentations" do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id,
|
||||
from: send(imageable_path, arguments))
|
||||
|
||||
expect(page).to have_content "You can upload only one image. Proposals with image attract more attention of the users."
|
||||
expect(page).to have_content "You can upload #{imageable_humanized_accepted_content_types} image."
|
||||
expect(page).to have_content "You can upload one image up to #{imageable_max_file_size} MB."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "Create" do
|
||||
|
||||
scenario "Should show validation errors" do
|
||||
login_as imageable.author
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id)
|
||||
|
||||
click_on "Upload image"
|
||||
|
||||
expect(page).to have_content "3 errors prevented this Image from being saved: "
|
||||
expect(page).to have_selector "small.error", text: "can't be blank", count: 2
|
||||
end
|
||||
|
||||
scenario "Should show error notice after unsuccessfull image upload" do
|
||||
login_as imageable.author
|
||||
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id,
|
||||
from: send(imageable_path, arguments))
|
||||
attach_file :image_attachment, "spec/fixtures/files/empty.pdf"
|
||||
sleep 1
|
||||
click_on "Upload image"
|
||||
|
||||
expect(page).to have_content "Cannot create image. Check form errors and try again."
|
||||
end
|
||||
|
||||
scenario "Should show success notice after successfull image upload" do
|
||||
login_as imageable.author
|
||||
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id,
|
||||
from: send(imageable_path, arguments))
|
||||
fill_in :image_title, with: "Image title"
|
||||
attach_file :image_attachment, "spec/fixtures/files/clippy.jpg"
|
||||
sleep 1
|
||||
click_on "Upload image"
|
||||
|
||||
expect(page).to have_content "Image was created successfully."
|
||||
end
|
||||
|
||||
scenario "Should redirect to imageable path after successfull image upload" do
|
||||
login_as imageable.author
|
||||
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id,
|
||||
from: send(imageable_path, arguments))
|
||||
fill_in :image_title, with: "Image title"
|
||||
attach_file :image_attachment, "spec/fixtures/files/clippy.jpg"
|
||||
sleep 1
|
||||
click_on "Upload image"
|
||||
|
||||
within "##{dom_id(imageable)}" do
|
||||
expect(page).to have_selector "h1", text: imageable.title
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Should show new image on imageable images tab after successfull image upload" do
|
||||
login_as imageable.author
|
||||
|
||||
visit new_image_path(imageable_type: imageable.class.name,
|
||||
imageable_id: imageable.id,
|
||||
from: send(imageable_path, arguments))
|
||||
fill_in :image_title, with: "Image title"
|
||||
attach_file :image_attachment, "spec/fixtures/files/clippy.jpg"
|
||||
sleep 1
|
||||
click_on "Upload image"
|
||||
|
||||
expect(page).to have_content "Image title"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "Destroy" do
|
||||
|
||||
let!(:image) { create(:image, imageable: imageable, user: imageable.author) }
|
||||
|
||||
scenario "Should show success notice after successfull deletion by an admin" do
|
||||
login_as administrator
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
click_on "Remove image"
|
||||
|
||||
expect(page).to have_content "Image was deleted successfully."
|
||||
end
|
||||
|
||||
scenario "Should show success notice after successfull deletion" do
|
||||
login_as imageable.author
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
click_on "Remove image"
|
||||
|
||||
expect(page).to have_content "Image was deleted successfully."
|
||||
end
|
||||
|
||||
scenario "Should not show image after successful deletion" do
|
||||
login_as imageable.author
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
click_on "Remove image"
|
||||
|
||||
expect(page).not_to have_selector "figure img"
|
||||
end
|
||||
|
||||
scenario "Should redirect to imageable path after successful deletion" do
|
||||
login_as imageable.author
|
||||
|
||||
visit send(imageable_path, arguments)
|
||||
click_on "Remove image"
|
||||
|
||||
within "##{dom_id(imageable)}" do
|
||||
expect(page).to have_selector "h1", text: imageable.title
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
76
spec/shared/models/acts_as_imageable.rb
Normal file
76
spec/shared/models/acts_as_imageable.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
shared_examples "acts as imageable" do |imageable_factory|
|
||||
|
||||
let!(:image) { build(:image, imageable_factory.to_sym) }
|
||||
let!(:imageable) { image.imageable }
|
||||
|
||||
it "should be valid" do
|
||||
expect(image).to be_valid
|
||||
end
|
||||
|
||||
describe "file extension" do
|
||||
|
||||
it "should not be valid with '.png' extension" do
|
||||
image.attachment = File.new("spec/fixtures/files/clippy.png")
|
||||
|
||||
expect(image).to_not be_valid
|
||||
expect(image.errors[:attachment].size).to eq(1)
|
||||
end
|
||||
|
||||
it "should not be valid with '.gif' extension" do
|
||||
image.attachment = File.new("spec/fixtures/files/clippy.gif")
|
||||
|
||||
expect(image).to_not be_valid
|
||||
expect(image.errors[:attachment].size).to eq(1)
|
||||
end
|
||||
|
||||
it "should be valid with '.jpg' extension" do
|
||||
image.attachment = File.new("spec/fixtures/files/clippy.jpg")
|
||||
|
||||
expect(image).to be_valid
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "image dimmessions" do
|
||||
|
||||
it "should be valid when image dimmessions are 475X475 at least" do
|
||||
expect(image).to be_valid
|
||||
end
|
||||
|
||||
it "should not be valid when image dimmensions are smaller than 475X475" do
|
||||
image.attachment = File.new("spec/fixtures/files/logo_header.jpg")
|
||||
|
||||
expect(image).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "title" do
|
||||
|
||||
it "should not be valid when correct image attached but no image title provided" do
|
||||
image.title = ''
|
||||
|
||||
expect(image).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid when image title is too short" do
|
||||
image.title = 'a' * 3
|
||||
|
||||
expect(image).to_not be_valid
|
||||
end
|
||||
|
||||
it "should not be valid when image title is too long" do
|
||||
image.title = 'a' * 81
|
||||
|
||||
expect(image).to_not be_valid
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "image destroy should remove image from file storage" do
|
||||
image.save
|
||||
image_url = image.attachment.url
|
||||
|
||||
expect{ image.attachment.destroy }.to change{ image.attachment.url }.from(image_url).to("/attachments/original/missing.png")
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user