diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index b198a0a89..0b3ee0201 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -63,7 +63,7 @@ //= require followable //= require flaggable //= require documentable -// require imageable +//= require imageable //= require tree_navigator //= require custom //= require tag_autocomplete diff --git a/app/assets/javascripts/documentable.js.coffee b/app/assets/javascripts/documentable.js.coffee index 563c317bf..08f8f6dce 100644 --- a/app/assets/javascripts/documentable.js.coffee +++ b/app/assets/javascripts/documentable.js.coffee @@ -28,7 +28,7 @@ App.Documentable = change: (e, data) -> $.each data.files, (index, file) -> - App.Documentable.setFilename(data, file) + App.Documentable.setFilename(inputData, file) fail: (e, data) -> $(data.cachedAttachmentField).val("") @@ -47,9 +47,9 @@ App.Documentable = App.Documentable.clearInputErrors(data) $(data.addAttachmentLabel).hide() - $(data.destroyAttachmentLinkContainer).html(data.result.destroy_link) - data.destroyAttachmentLinkContainer = $(data.wrapper).find('.action-remove .remove-cached-attachment') - $(data.destroyAttachmentLinkContainer).on 'click', (e) -> + destroyAttachmentLink = $(data.result.destroy_link) + $(data.destroyAttachmentLinkContainer).html(destroyAttachmentLink) + $(destroyAttachmentLink).on 'click', (e) -> e.preventDefault() e.stopPropagation() App.Documentable.doDeleteCachedAttachmentRequest(this.href, data) diff --git a/app/assets/javascripts/imageable.js.coffee b/app/assets/javascripts/imageable.js.coffee index 004d8a40f..62a013bf0 100644 --- a/app/assets/javascripts/imageable.js.coffee +++ b/app/assets/javascripts/imageable.js.coffee @@ -1,45 +1,173 @@ App.Imageable = initialize: -> - console.log 'App.Imageable initialize' - input_files = $('input.direct_upload_attachment[type=file]') + inputFiles = $('input.direct_upload_image_attachment[type=file]') - $.each input_files, (index, file) -> - wrapper = $(file).closest(".direct-upload") - App.Imageable.watchRemoveImagebutton(wrapper) + $.each inputFiles, (index, input) -> + App.Imageable.initializeDirectUploadInput(input) - $("#new_image_link").on 'click', -> - $(this).hide() + $("#new_image_link").on 'click', -> $(this).hide() + + initializeDirectUploadInput: (input) -> + + inputData = @buildData([], input) + + @initializeRemoveImageLink(input) + + @initializeRemoveCachedImageLink(input, inputData) + + $(input).fileupload + + paramName: "attachment" + + formData: null + + add: (e, data) -> + data = App.Imageable.buildFileUploadData(e, data) + App.Imageable.clearProgressBar(data) + App.Imageable.setProgressBar(data, 'uploading') + data.submit() + + change: (e, data) -> + $.each data.files, (index, file) -> + App.Imageable.setFilename(inputData, file.name) + + fail: (e, data) -> + $(data.cachedAttachmentField).val("") + App.Imageable.clearFilename(data) + App.Imageable.setProgressBar(data, 'errors') + App.Imageable.clearInputErrors(data) + App.Imageable.setInputErrors(data) + App.Imageable.clearPreview(data) + $(data.destroyAttachmentLinkContainer).find("a.delete:not(.remove-nested)").remove() + $(data.addAttachmentLabel).show() + + done: (e, data) -> + $(data.cachedAttachmentField).val(data.result.cached_attachment) + App.Imageable.setTitleFromFile(data, data.result.filename) + App.Imageable.setProgressBar(data, 'complete') + App.Imageable.setFilename(data, data.result.filename) + App.Imageable.clearInputErrors(data) + $(data.addAttachmentLabel).hide() + + App.Imageable.setPreview(data) + destroyAttachmentLink = $(data.result.destroy_link) + $(data.destroyAttachmentLinkContainer).html(destroyAttachmentLink) + $(destroyAttachmentLink).on 'click', (e) -> + e.preventDefault() + e.stopPropagation() + App.Imageable.doDeleteCachedAttachmentRequest(this.href, data) + + progress: (e, data) -> + progress = parseInt(data.loaded / data.total * 100, 10) + $(data.progressBar).find('.loading-bar').css 'width', progress + '%' + return + + buildFileUploadData: (e, data) -> + data = @buildData(data, e.target) + return data + + buildData: (data, input) -> + wrapper = $(input).closest('.direct-upload') + data.input = input + data.wrapper = wrapper + data.progressBar = $(wrapper).find('.progress-bar-placeholder') + data.preview = $(wrapper).find('.image-preview') + data.errorContainer = $(wrapper).find('.attachment-errors') + data.fileNameContainer = $(wrapper).find('p.file-name') + data.destroyAttachmentLinkContainer = $(wrapper).find('.action-remove') + data.addAttachmentLabel = $(wrapper).find('.action-add label') + data.cachedAttachmentField = $(wrapper).find("#" + $(input).data('cached-attachment-input-field')) + data.titleField = $(wrapper).find("#" + $(input).data('title-input-field')) + $(wrapper).find('.progress-bar-placeholder').css('display', 'block') + return data + + clearFilename: (data) -> + $(data.fileNameContainer).text('') + $(data.fileNameContainer).hide() + + clearInputErrors: (data) -> + $(data.errorContainer).find('small.error').remove() + + clearProgressBar: (data) -> + $(data.progressBar).find('.loading-bar').removeClass('complete errors uploading').css('width', "0px") + + clearPreview: (data) -> + $(data.wrapper).find('.image-preview').remove() + + setFilename: (data, file_name) -> + $(data.fileNameContainer).text(file_name) + $(data.fileNameContainer).show() + + setProgressBar: (data, klass) -> + $(data.progressBar).find('.loading-bar').addClass(klass) + + setTitleFromFile: (data, title) -> + if $(data.titleField).val() == "" + $(data.titleField).val(title) + + setInputErrors: (data) -> + errors = '' + data.jqXHR.responseJSON.errors + '' + $(data.errorContainer).append(errors) + + setPreview: (data) -> + image_preview = '
' + if $(data.preview).length > 0 + $(data.preview).replaceWith(image_preview) + else + $(image_preview).insertBefore($(data.wrapper).find(".attachment-actions")) + data.preview = $(data.wrapper).find('.image-preview') watchRemoveImagebutton: (wrapper) -> - console.log 'App.Imageable watchRemoveDocumentbutton' remove_image_button = $(wrapper).find('a.delete[href="#"]') $(remove_image_button).on 'click', (e) -> e.preventDefault() $(wrapper).remove() $('#new_image_link').show() - uploadNestedImage: (id, nested_image, result) -> - $('#' + id).replaceWith(nested_image) - @updateLoadingBar(id, result) - @initialize() + doDeleteCachedAttachmentRequest: (url, data) -> + $.ajax + type: "POST" + url: url + dataType: "json" + data: { "_method": "delete" } + complete: -> + $(data.cachedAttachmentField).val("") + $(data.addAttachmentLabel).show() - uploadPlainImage: (id, nested_image, result) -> - $('#' + id).replaceWith(nested_image) - @updateLoadingBar(id, result) - @initialize() + App.Imageable.clearFilename(data) + App.Imageable.clearInputErrors(data) + App.Imageable.clearProgressBar(data) + App.Imageable.clearPreview(data) - 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') + if $(data.input).data('nested-image') == true + $(data.wrapper).remove() + $('#new_image_link').show() + else + $(data.destroyAttachmentLinkContainer).find('a.delete').remove() - new: (nested_fields) -> - $(".images-list").append(nested_fields) + initializeRemoveImageLink: (input) -> + wrapper = $(input).closest(".direct-upload") + remove_image_link = $(wrapper).find('a.remove-nested-field') + $(remove_image_link).on 'click', (e) -> + e.preventDefault() + $(wrapper).remove() + $('#new_image_link').show() + + initializeRemoveCachedImageLink: (input, data) -> + wrapper = $(input).closest(".direct-upload") + remove_image_link = $(wrapper).find('a.remove-cached-attachment') + $(remove_image_link).on 'click', (e) -> + e.preventDefault() + e.stopPropagation() + App.Imageable.doDeleteCachedAttachmentRequest(this.href, data) + + new: (nested_field) -> + nested_field = $(nested_field) + $(".images-list").append(nested_field) + input = nested_field.find("input[type='file']") + @initializeDirectUploadInput(input) $("#new_image_link").hide() - @initialize() destroyNestedImage: (id, notice) -> $('#' + id).remove() diff --git a/app/assets/stylesheets/documentable.scss b/app/assets/stylesheets/documentable.scss index e6b67d2e4..b2ef50135 100644 --- a/app/assets/stylesheets/documentable.scss +++ b/app/assets/stylesheets/documentable.scss @@ -28,8 +28,8 @@ background-color: $light-gray; } - .js-document-attachment, - input.direct_upload_image_attachment[type=file] { + input.direct_upload_image_attachment[type=file], + .js-document-attachment{ display: none; } diff --git a/app/assets/stylesheets/imageable.scss b/app/assets/stylesheets/imageable.scss index 2630158c5..179e262da 100644 --- a/app/assets/stylesheets/imageable.scss +++ b/app/assets/stylesheets/imageable.scss @@ -30,8 +30,7 @@ } input.direct_upload_image_attachment[type=file], - input.direct_upload_document_attachment[type=file], - input.direct_upload_attachment[type=file] { + input.direct_upload_document_attachment[type=file] { display: none; } @@ -55,7 +54,6 @@ &.errors { background-color: $alert-color; - width: 100%; margin-top: $line-height / 2; } } diff --git a/app/controllers/direct_uploads_controller.rb b/app/controllers/direct_uploads_controller.rb index 0411e411e..ccca2fb13 100644 --- a/app/controllers/direct_uploads_controller.rb +++ b/app/controllers/direct_uploads_controller.rb @@ -18,8 +18,7 @@ class DirectUploadsController < ApplicationController render json: { cached_attachment: @direct_upload.relation.cached_attachment, filename: @direct_upload.relation.attachment.original_filename, destroy_link: render_destroy_upload_link(@direct_upload).html_safe, - attachment_url: @direct_upload.relation.attachment.url, - is_image: Image::ACCEPTED_CONTENT_TYPE.include?(@direct_upload.relation.attachment_content_type) + attachment_url: @direct_upload.relation.attachment.url } else @direct_upload.destroy_attachment diff --git a/app/controllers/images_controller.rb b/app/controllers/images_controller.rb index 353ef9550..870570ed7 100644 --- a/app/controllers/images_controller.rb +++ b/app/controllers/images_controller.rb @@ -3,10 +3,8 @@ class ImagesController < ApplicationController 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 + load_and_authorize_resource def new end @@ -46,32 +44,6 @@ class ImagesController < ApplicationController 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 @@ -83,10 +55,6 @@ class ImagesController < ApplicationController @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 diff --git a/app/helpers/direct_uploads_helper.rb b/app/helpers/direct_uploads_helper.rb index c41856d88..87ed630d7 100644 --- a/app/helpers/direct_uploads_helper.rb +++ b/app/helpers/direct_uploads_helper.rb @@ -1,7 +1,8 @@ module DirectUploadsHelper def render_destroy_upload_link(direct_upload) - link_to t('documents.form.delete_button'), + label = direct_upload.resource_relation == "image" ? "images" : "documents" + link_to t("#{label}.form.delete_button"), direct_upload_destroy_url("direct_upload[resource_type]": direct_upload.resource_type, "direct_upload[resource_id]": direct_upload.resource_id, "direct_upload[resource_relation]": direct_upload.resource_relation, diff --git a/app/helpers/images_helper.rb b/app/helpers/images_helper.rb index bd3b7a535..6256f09ee 100644 --- a/app/helpers/images_helper.rb +++ b/app/helpers/images_helper.rb @@ -56,7 +56,7 @@ module ImagesHelper method: :delete, remote: true, data: { confirm: t('images.actions.destroy.confirm') }, - class: "delete float-right" + class: "delete remove-image" elsif !image.persisted? && image.cached_attachment.present? link_to t('images.form.delete_button'), destroy_upload_images_path(path: image.cached_attachment, @@ -65,18 +65,18 @@ module ImagesHelper imageable_id: image.imageable_id), method: :delete, remote: true, - class: "delete float-right" + class: "delete remove-cached-attachment" else link_to t('images.form.delete_button'), "#", - class: "delete float-right remove-nested" + class: "delete remove-nested" end end def render_image_attachment(image) html = file_field_tag :attachment, accept: imageable_accepted_content_types_extensions, - class: 'direct_upload_attachment', + class: 'direct_upload_image_attachment', data: { url: image_direct_upload_url(image), cached_attachment_input_field: image_nested_field_id(image, :cached_attachment), diff --git a/app/views/documents/_form.html.erb b/app/views/documents/_form.html.erb index 05e422d4f..a35a5ba04 100644 --- a/app/views/documents/_form.html.erb +++ b/app/views/documents/_form.html.erb @@ -13,10 +13,10 @@ <%= f.hidden_field :cached_attachment %>
- <%= f.text_field :title %> + <%= f.text_field :title, placeholder: t("documents.new.form.title_placeholder") %>
-
+
<%= f.label :attachment, t("documents.form.attachment_label"), class: 'button hollow' %> <%= f.file_field :attachment, diff --git a/app/views/documents/_nested_fields.html.erb b/app/views/documents/_nested_fields.html.erb index 5ab68915d..575182a8c 100644 --- a/app/views/documents/_nested_fields.html.erb +++ b/app/views/documents/_nested_fields.html.erb @@ -16,7 +16,8 @@ <%= label_tag :title, t("activerecord.attributes.document.title") %> <%= text_field_tag :title, - document.errors.has_key?(:attachment) ? "" : document.title, + document.title, + placeholder: t("documents.new.form.title_placeholder"), name: document_nested_field_name(document, index, :title), id: document_nested_field_id(document, index, :title), class: "document-title" %> diff --git a/app/views/images/_form.html.erb b/app/views/images/_form.html.erb index 1d13189ec..5f5689a7f 100644 --- a/app/views/images/_form.html.erb +++ b/app/views/images/_form.html.erb @@ -30,11 +30,10 @@ "direct_upload[resource_id]": @image.imageable_id, "direct_upload[resource_relation]": "image"), cached_attachment_input_field: "image_cached_attachment", - title_input_field: "image_title", - multiple: false + title_input_field: "image_title" } %>
-
+
<% if @image.cached_attachment.present? %> <%= link_to t('images.form.delete_button'), direct_upload_destroy_url("direct_upload[resource_type]": @image.imageable_type, @@ -43,7 +42,7 @@ "direct_upload[cached_attachment]": @image.cached_attachment), method: :delete, remote: true, - class: "delete float-right" %> + class: "delete remove-cached-attachment" %> <% end %>
diff --git a/app/views/images/_nested_fields.html.erb b/app/views/images/_nested_fields.html.erb index f53bed7b5..ff5df41e4 100644 --- a/app/views/images/_nested_fields.html.erb +++ b/app/views/images/_nested_fields.html.erb @@ -14,6 +14,7 @@
<%= label_tag :title, t("activerecord.attributes.image.title") %> + <%= text_field_tag :title, image.title, placeholder: t("images.new.form.title_placeholder"), @@ -31,7 +32,7 @@
<%= render_image_attachment(image) %>
-
+
<%= render_destroy_image_link(image) %>
diff --git a/app/views/images/destroy.js.erb b/app/views/images/destroy.js.erb index 03ae571aa..fddb55300 100644 --- a/app/views/images/destroy.js.erb +++ b/app/views/images/destroy.js.erb @@ -1,17 +1,7 @@ -<% 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 %> +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 %>") diff --git a/config/locales/en/documents.yml b/config/locales/en/documents.yml index 0b36dc79d..299852daa 100644 --- a/config/locales/en/documents.yml +++ b/config/locales/en/documents.yml @@ -19,6 +19,9 @@ en: note: 'Add new document to your investment project: %{title}' proposal: note: 'Add new document to your proposal: %{title}' + form: + title_placeholder: Add a descriptive title for the document + recommendations_title: File upload tips recommendation_one_html: You can upload up to a maximum of %{max_documents_allowed} documents. recommendation_two_html: You can upload %{accepted_content_types} files. diff --git a/config/locales/es/documents.yml b/config/locales/es/documents.yml index e418b2e3b..4b342f478 100644 --- a/config/locales/es/documents.yml +++ b/config/locales/es/documents.yml @@ -19,6 +19,9 @@ es: note: 'Añade un documento la propuesta de inversión: %{title}.' proposal: note: 'Añade un documento a la propuesta: %{title}.' + form: + title_placeholder: Añade un título descriptivo para el documento + recommendations_title: Consejos para subir archivos recommendation_one_html: Puedes subir hasta un máximo de %{max_documents_allowed} documentos recommendation_two_html: Sólo puedes subir archivos %{accepted_content_types}. diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index 2c8db068d..4aa923216 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -1290,42 +1290,44 @@ feature 'Proposals' do it_behaves_like "followable", "proposal", "proposal_path", { "id": "id" } - it_behaves_like "imageable", "proposal", "proposal_path", { "id": "id" } + describe "my specs group" do - it_behaves_like "nested imageable", - "proposal", - "new_proposal_path", - { }, - "imageable_fill_new_valid_proposal", - "Create proposal", - "Proposal created successfully" + it_behaves_like "imageable", "proposal", "proposal_path", { "id": "id" } - it_behaves_like "nested imageable", - "proposal", - "edit_proposal_path", - { "id": "id" }, - nil, - "Save changes", - "Proposal updated successfully" + it_behaves_like "nested imageable", + "proposal", + "new_proposal_path", + { }, + "imageable_fill_new_valid_proposal", + "Create proposal", + "Proposal created successfully" - it_behaves_like "documentable", "proposal", "proposal_path", { "id": "id" } + it_behaves_like "nested imageable", + "proposal", + "edit_proposal_path", + { "id": "id" }, + nil, + "Save changes", + "Proposal updated successfully" - it_behaves_like "nested documentable", - "proposal", - "new_proposal_path", - { }, - "documentable_fill_new_valid_proposal", - "Create proposal", - "Proposal created successfully" + it_behaves_like "documentable", "proposal", "proposal_path", { "id": "id" } - it_behaves_like "nested documentable", - "proposal", - "edit_proposal_path", - { "id": "id" }, - nil, - "Save changes", - "Proposal updated successfully" + it_behaves_like "nested documentable", + "proposal", + "new_proposal_path", + { }, + "documentable_fill_new_valid_proposal", + "Create proposal", + "Proposal created successfully" + it_behaves_like "nested documentable", + "proposal", + "edit_proposal_path", + { "id": "id" }, + nil, + "Save changes", + "Proposal updated successfully" + end scenario 'Erased author' do user = create(:user) proposal = create(:proposal, author: user) diff --git a/spec/shared/features/nested_documentable.rb b/spec/shared/features/nested_documentable.rb index fad7f94a3..3f22d4a62 100644 --- a/spec/shared/features/nested_documentable.rb +++ b/spec/shared/features/nested_documentable.rb @@ -243,8 +243,6 @@ def documentable_attach_new_file(documentable_factory_name, index, path, success expect(page).to have_css ".loading-bar.errors" end end - - end def documentable_fill_new_valid_proposal diff --git a/spec/shared/features/nested_imageable.rb b/spec/shared/features/nested_imageable.rb index 0636a686d..410b614af 100644 --- a/spec/shared/features/nested_imageable.rb +++ b/spec/shared/features/nested_imageable.rb @@ -45,7 +45,7 @@ shared_examples "nested imageable" do |imageable_factory_name, path, imageable_p imageable_attach_new_file(imageable_factory_name, "spec/fixtures/files/clippy.jpg") - expect(page).to have_css("##{imageable_factory_name}_image_attributes_title[value$='clippy.jpg']") + expect(find("##{imageable_factory_name}_image_attributes_title").value).to eq("clippy.jpg") end scenario "Should not update nested image file title with file name after choosing a file when title already defined", :js do @@ -98,7 +98,6 @@ shared_examples "nested imageable" do |imageable_factory_name, path, imageable_p end scenario "Should show nested image errors after unvalid form submit", :js do - page.driver.resize_window 1200, 2000 login_as user visit send(path, arguments) @@ -130,7 +129,7 @@ shared_examples "nested imageable" do |imageable_factory_name, path, imageable_p click_link "Remove image" end - expect(page).to have_content "Image was deleted successfully." + expect(page).not_to have_css "#nested_image" end scenario "Should show successful notice when resource filled correctly without any nested images", :js do