From cc89907bff6b89b291222c57aff879aee9355c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Wed, 23 Aug 2017 18:26:05 +0200 Subject: [PATCH] Add nested documents to proposal form. --- app/assets/javascripts/documentable.js.coffee | 69 +++++- app/assets/javascripts/forms.js.coffee | 2 +- app/assets/stylesheets/documentable.scss | 2 + .../concerns/commentable_actions.rb | 29 +-- app/controllers/documents_controller.rb | 63 +++-- app/controllers/proposals_controller.rb | 5 +- app/helpers/documentables_helper.rb | 10 +- app/helpers/documents_helper.rb | 16 +- app/models/abilities/common.rb | 1 + app/models/document.rb | 2 +- app/views/documents/_document.html.erb | 2 +- app/views/documents/_form.html.erb | 2 +- app/views/documents/_nested_document.html.erb | 68 ++++++ .../documents/_nested_documents.html.erb | 24 ++ .../documents/_nested_form_fields.html.erb | 29 --- app/views/documents/destroy.js.erb | 6 + app/views/documents/new.js.erb | 9 + app/views/documents/upload.js.erb | 9 + app/views/proposals/_form.html.erb | 5 +- config/locales/en/documents.yml | 8 +- config/locales/es/documents.yml | 7 +- config/routes.rb | 7 +- db/schema.rb | 2 +- spec/features/proposals_spec.rb | 2 + spec/shared/features/nested_documentable.rb | 226 ++++++++++++++++++ 25 files changed, 501 insertions(+), 104 deletions(-) create mode 100644 app/views/documents/_nested_document.html.erb create mode 100644 app/views/documents/_nested_documents.html.erb delete mode 100644 app/views/documents/_nested_form_fields.html.erb create mode 100644 app/views/documents/destroy.js.erb create mode 100644 app/views/documents/new.js.erb create mode 100644 app/views/documents/upload.js.erb create mode 100644 spec/shared/features/nested_documentable.rb diff --git a/app/assets/javascripts/documentable.js.coffee b/app/assets/javascripts/documentable.js.coffee index ba4154266..b4c9139f9 100644 --- a/app/assets/javascripts/documentable.js.coffee +++ b/app/assets/javascripts/documentable.js.coffee @@ -1,17 +1,26 @@ App.Documentable = initialize: -> + @initializeDirectUploads() + @initializeInterface() + + initializeDirectUploads: -> $('input.document_ajax_attachment[type=file]').fileupload - paramName : "document[attachment]" + paramName: "document[attachment]" formData: null add: (e, data) -> wrapper = $(e.target).parent() + index = $(e.target).data('index') $(wrapper).find('.progress-bar-placeholder').empty() data.progressBar = $(wrapper).find('.progress-bar-placeholder').html('
') + data.formData = { + "document[title]": $(wrapper).find('input.document-title').val() || data.files[0].name + "index": index + } data.submit() change: (e, data) -> @@ -25,15 +34,51 @@ App.Documentable = $(data.progressBar).find('.loading-bar').css 'width', progress + '%' return - done: (e, data) -> - result = data.response().result - if result.status == 200 - $(data.progressBar).find('.loading-bar').removeClass 'uploading' - $(data.progressBar).find('.loading-bar').addClass 'complete' - inputId = '#' + $(e.target).data('cached-attachment-input-field') - $(inputId).val result.attachment - else - $(data.progressBar).find('.loading-bar').addClass 'errors' - $(data.progressBar).prepend("" + result.msg + "") - return + initializeInterface: -> + input_files = $('input.document_ajax_attachment[type=file]') + $.each input_files, (index, file) -> + wrapper = $(file).parent() + App.Documentable.watchRemoveDocumentbutton(wrapper) + + watchRemoveDocumentbutton: (wrapper) -> + remove_document_button = $(wrapper).find('.remove-document') + $(remove_document_button).on 'click', (e) -> + e.preventDefault() + $(wrapper).remove() + $('#new_document_link').show() + $('.max-documents-notice').hide() + + upload: (id, nested_document, result) -> + $('#' + id).replaceWith(nested_document) + if result + $('#' + id).find('.loading-bar').addClass 'complete' + else + $('#' + id).find('.loading-bar').addClass 'errors' + @initialize() + + new: (nested_fields) -> + $(".documents-list").append(nested_fields) + @initialize() + + destroy: (id, notice) -> + $('#' + id).remove() + @updateNotice(notice) + + updateNotice: (notice) -> + if $('[data-alert]').length > 0 + $('[data-alert]').replaceWith(notice) + else + $("body").append(notice) + + updateNewDocumentButton: (link) -> + if $('.document').length >= $('.documents').data('max-documents') + $('#new_document_link').hide() + $('.max-documents-notice').removeClass('hide') + $('.max-documents-notice').show() + else if $('#new_document_link').length > 0 + $('#new_document_link').replaceWith(link) + $('.max-documents-notice').hide() + else + $('.max-documents-notice').hide() + $(link).insertBefore('.documents hr:last') diff --git a/app/assets/javascripts/forms.js.coffee b/app/assets/javascripts/forms.js.coffee index 1c4cee919..4750c1172 100644 --- a/app/assets/javascripts/forms.js.coffee +++ b/app/assets/javascripts/forms.js.coffee @@ -28,7 +28,7 @@ App.Forms = i = 0 while i < element.length element[i].addEventListener 'change', -> - $(element).parent().find('.file-name').text(@files[0].name) + $(element).parent().find('.file-name').text(@files[i].name) return i++ diff --git a/app/assets/stylesheets/documentable.scss b/app/assets/stylesheets/documentable.scss index 9015409b0..5efcd1c24 100644 --- a/app/assets/stylesheets/documentable.scss +++ b/app/assets/stylesheets/documentable.scss @@ -18,9 +18,11 @@ } &.complete{ background-color: #0f0; + width: 100%; } &.errors{ background-color: #f00; + width: 100%; } } diff --git a/app/controllers/concerns/commentable_actions.rb b/app/controllers/concerns/commentable_actions.rb index cdd2da4ae..e1e153495 100644 --- a/app/controllers/concerns/commentable_actions.rb +++ b/app/controllers/concerns/commentable_actions.rb @@ -28,7 +28,6 @@ module CommentableActions def new @resource = resource_model.new - prepare_new_resource_documents(@resource) set_geozone set_resource_instance end @@ -55,12 +54,12 @@ module CommentableActions end def edit - prepare_edit_resource_documents(resource) end def update resource.assign_attributes(strong_params) - parse_documents(resource) + recover_documents_from_cache(resource) + if resource.save redirect_to resource, notice: t("flash.actions.update.#{resource_name.underscore}") else @@ -113,28 +112,12 @@ module CommentableActions nil end - def prepare_new_resource_documents(resource) + def recover_documents_from_cache(resource) return false unless resource.try(:documents) - (1..resource.class.max_documents_allowed).each do - resource.documents.build - end - end - - def prepare_edit_resource_documents(resource) - return false unless resource.try(:documents) - (resource.documents.size + 1 .. resource.class.max_documents_allowed).each do - resource.documents.build - end - resource - end - - def parse_documents(resource) - return false unless resource.try(:documents) - resource.documents.each do |document| - document.user = current_user - end resource.documents = resource.documents.each do |document| - document.attachment = File.open(document.cached_attachment) if document.cached_attachment.present? + if document.cached_attachment.present? && File.exists?(document.cached_attachment) + document.attachment = File.open(document.cached_attachment) + end end end diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index 2558b3310..bfafb2a34 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -1,6 +1,6 @@ class DocumentsController < ApplicationController before_action :authenticate_user! - before_filter :find_documentable, except: :destroy + before_filter :find_documentable, except: [:destroy] before_filter :prepare_new_document, only: :new before_filter :prepare_document_for_creation, only: :create @@ -22,37 +22,61 @@ class DocumentsController < ApplicationController end def destroy - if @document.destroy - flash[:notice] = t "documents.actions.destroy.notice" - else - flash[:alert] = t "documents.actions.destroy.alert" + respond_to do |format| + format.html do + if @document.destroy + flash[:notice] = t "documents.actions.destroy.notice" + else + flash[:alert] = t "documents.actions.destroy.alert" + end + redirect_to params[:from] + end + format.js do + if @document.destroy + flash.now[:notice] = t "documents.actions.destroy.notice" + else + flash.now[:alert] = t "documents.actions.destroy.alert" + end + end end - redirect_to params[:from] + end + + def destroy_upload + @document = Document.new(attachment: File.open(params[:path])) + @document.documentable = @documentable + + if @document.attachment.destroy + flash.now[:notice] = t "documents.actions.destroy.notice" + else + flash.now[:alert] = t "documents.actions.destroy.alert" + end + render:destroy end def upload - attachment = params[:document][:attachment] - document = Document.new(documentable: @documentable, attachment: attachment.tempfile, title: "faketitle", user: current_user ) - # We only are intested in attachment validators. We set some fake data to get attachment errors only - document.valid? - if document.errors[:attachment].empty? - # Move image from tmp to cache - msg = { status: 200, attachment: attachment.tempfile.path } + @document = Document.new(document_params.merge(user: current_user)) + @document.documentable = @documentable + if @document.valid? + @document.attachment.save + @document.cached_attachment = @document.attachment.path else - attachment.tempfile.delete - msg = { status: 422, msg: document.errors[:attachment].join(', ') } + @document.attachment.destroy end - render :json => msg end private + def document_params + params.require(:document).permit(:title, :documentable_type, :documentable_id, + :attachment, :cached_attachment, :user_id) + end + def find_documentable @documentable = params[:documentable_type].constantize.find_or_initialize_by(id: params[:documentable_id]) end def prepare_new_document - @document = Document.new(documentable: @documentable, user_id: @documentable.author_id) + @document = Document.new(documentable: @documentable, user_id: current_user.id) end def prepare_document_for_creation @@ -61,11 +85,6 @@ class DocumentsController < ApplicationController @document.user = current_user end - def document_params - params.require(:document).permit(:title, :documentable_type, :documentable_id, - :attachment, :cached_attachment) - end - def recover_attachment_from_cache if @document.attachment.blank? && @document.cached_attachment.present? @document.attachment = File.open(@document.cached_attachment) diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index 5a40d955b..cedc4e8ef 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -25,12 +25,11 @@ class ProposalsController < ApplicationController def create @proposal = Proposal.new(proposal_params.merge(author: current_user)) - parse_documents(@proposal) + recover_documents_from_cache(@proposal) if @proposal.save redirect_to share_proposal_path(@proposal), notice: I18n.t('flash.actions.create.proposal') else - @proposal = prepare_edit_resource_documents(@proposal) render :new end end @@ -79,7 +78,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, - documents_attributes: [:id, :title, :attachment, :cached_attachment ] ) + documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id] ) end def retired_params diff --git a/app/helpers/documentables_helper.rb b/app/helpers/documentables_helper.rb index c6804f20a..90b185152 100644 --- a/app/helpers/documentables_helper.rb +++ b/app/helpers/documentables_helper.rb @@ -16,8 +16,8 @@ module DocumentablesHelper documentable.class.accepted_content_types end - def accepted_content_types_extensions(documentable) - documentable.class.accepted_content_types + def accepted_content_types_extensions(documentable_class) + documentable_class.accepted_content_types .collect{ |content_type| ".#{content_type.split("/").last}" } .join(",") end @@ -28,4 +28,10 @@ module DocumentablesHelper .join(", ") end + def documentables_note(documentable) + t "documents.form.note", max_documents_allowed: max_documents_allowed(documentable), + accepted_content_types: humanized_accepted_content_types(documentable), + max_file_size: max_file_size(documentable) + end + end \ No newline at end of file diff --git a/app/helpers/documents_helper.rb b/app/helpers/documents_helper.rb index 60c486d52..ec2541aaa 100644 --- a/app/helpers/documents_helper.rb +++ b/app/helpers/documents_helper.rb @@ -1,7 +1,7 @@ module DocumentsHelper def document_attachment_file_name(document) - document.attachment_file_name if document.attachment.exists? + document.attachment_file_name end def errors_on_attachment(document) @@ -22,4 +22,18 @@ module DocumentsHelper bytes / Numeric::MEGABYTE end + def document_nested_field_name(document, index, field) + parent = document.documentable_type.constantize.name.downcase + "#{parent}[documents_attributes][#{index}][#{field}]" + end + + def document_nested_field_id(document, index, field) + parent = document.documentable_type.constantize.name.downcase + "#{parent}_documents_attributes_#{index}_#{field}" + end + + def document_nested_field_wrapper_id(index) + "document_#{index}" + end + end diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb index 4eb202332..34ee06f23 100644 --- a/app/models/abilities/common.rb +++ b/app/models/abilities/common.rb @@ -37,6 +37,7 @@ module Abilities can [:create, :destroy], Follow can [:create, :destroy], Document, documentable: { author_id: user.id } + can [:new, :destroy_upload], Document unless user.organization? can :vote, Debate diff --git a/app/models/document.rb b/app/models/document.rb index abe9c6643..0aafcfecc 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -16,7 +16,7 @@ class Document < ActiveRecord::Base validate :validate_attachment_content_type, if: -> { attachment.present? } validate :validate_attachment_size, if: -> { attachment.present? } validates :title, presence: true - validates :user, presence: true + validates :user_id, presence: true # validates :documentable_id, presence: true # validates :documentable_type, presence: true diff --git a/app/views/documents/_document.html.erb b/app/views/documents/_document.html.erb index f9f3b3d9e..c4ead4c65 100644 --- a/app/views/documents/_document.html.erb +++ b/app/views/documents/_document.html.erb @@ -13,7 +13,7 @@ <% if can? :destroy, Document %> <%= link_to t('documents.buttons.destroy_document'), document_path(document, from: request.url), method: :delete, - data: { confirm: t('documents.actions.destroy.alert') }, + data: { confirm: t('documents.actions.destroy.confirm') }, class: 'button hollow alert' %> <% end %> diff --git a/app/views/documents/_form.html.erb b/app/views/documents/_form.html.erb index 2e648a3da..f4dd49ded 100644 --- a/app/views/documents/_form.html.erb +++ b/app/views/documents/_form.html.erb @@ -18,7 +18,7 @@
<%= f.hidden_field :cached_attachment %> <%= f.file_field :attachment, - accept: accepted_content_types_extensions(@document.documentable), + accept: accepted_content_types_extensions(@document.documentable.class), label: false, class: 'document_ajax_attachment show-for-sr', data: { diff --git a/app/views/documents/_nested_document.html.erb b/app/views/documents/_nested_document.html.erb new file mode 100644 index 000000000..75f9c8e5f --- /dev/null +++ b/app/views/documents/_nested_document.html.erb @@ -0,0 +1,68 @@ +
+ <%= hidden_field_tag :id, + document.id, + name: document_nested_field_name(document, index, :id), + id: document_nested_field_id(document, index, :id) if document.persisted? %> + <%= hidden_field_tag :user_id, + current_user.id, + name: document_nested_field_name(document, index, :user_id), + id: document_nested_field_id(document, index, :user_id) %> + <%= hidden_field_tag :cached_attachment, + document.cached_attachment, + name: document_nested_field_name(document, index, :cached_attachment), + id: document_nested_field_id(document, index, :cached_attachment) %> + + <%= label_tag :title, t("activerecord.attributes.document.title") %> + <%= text_field_tag :title, + document.title, + name: document_nested_field_name(document, index, :title), + id: document_nested_field_id(document, index, :title), + class: "document-title" %> + <% if document.errors[:title].any? %> + <%= document.errors[:title].join(", ") %> +
+ <% end %> + + <%= file_field_tag :attachment, + accept: accepted_content_types_extensions(document.documentable_type.constantize), + class: 'document_ajax_attachment show-for-sr', + data: { + url: upload_documents_url( + documentable_type: document.documentable_type, + documentable_id: document.documentable_id, + format: :js + ), + cached_attachment_input_field: "#{document.documentable.class.name.downcase}_documents_attributes_#{index}_cached_attachment", + multiple: false, + index: index + }, + name: document_nested_field_name(document, index, :attachment), + id: document_nested_field_id(document, index, :attachment) %> + + <% if document.persisted? %> + <%= link_to t('documents.form.delete_button'), document_path(document, index: index), + method: :delete, + remote: true, + data: { confirm: t('documents.actions.destroy.confirm') }, + class: "button hollow alert" if document.persisted? %> + <% elsif !document.persisted? && document.cached_attachment.present? %> + <%= link_to t('documents.form.delete_button'), destroy_upload_documents_path(path: document.cached_attachment, index: index, documentable_type: document.documentable_type, documentable_id: document.documentable_id), + method: :delete, + remote: true, + class: "button hollow alert" %> + <% else %> + <%= label_tag document_nested_field_id(document, index, :attachment), + t("documents.form.attachment_label"), + class: "button hollow #{"error" if document.errors[:attachment].any?}" %> + <%= link_to t('documents.form.delete_button'), "#", class: "button hollow alert remove-document" %> + <% if document.errors[:attachment].any? %> +
+ <%= document.errors[:attachment].join(", ") %> +
+ <% end %> + <% end %> + +
+

<%= document_attachment_file_name(document) %>

+
+
\ No newline at end of file diff --git a/app/views/documents/_nested_documents.html.erb b/app/views/documents/_nested_documents.html.erb new file mode 100644 index 000000000..1520b8485 --- /dev/null +++ b/app/views/documents/_nested_documents.html.erb @@ -0,0 +1,24 @@ +
+ <%= label_tag :documents, t("documents.form.title") %> +

<%= documentables_note(resource) %>

+ + <% resource.documents.each_with_index do |document, index| %> + <%= render 'documents/nested_document', document: document, index: index, resource: resource %> + <% end %> +
+ +<% if resource.documents.count < resource.class.max_documents_allowed %> + <%= link_to t("documents.form.add_new_document"), + new_document_path(documentable_type: resource.class.name, index: resource.documents.size), + remote: true, + id: "new_document_link" %> +
+ <%= t "documents.max_documents_allowed_reached_html" %> +
+<% else %> +
+ <%= t "documents.max_documents_allowed_reached_html" %> +
+<% end %> + +
\ No newline at end of file diff --git a/app/views/documents/_nested_form_fields.html.erb b/app/views/documents/_nested_form_fields.html.erb deleted file mode 100644 index e5afde8a2..000000000 --- a/app/views/documents/_nested_form_fields.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -<%= form.label :documents %> -

Aquí puedes añadir hasta 3 doucmentos en formato PDF

-<% documents.each_with_index do |document, index| %> -
- <%= form.fields_for :documents, document do |document_fields| %> - <%= document_fields.text_field :title %> - <%= document_fields.hidden_field :cached_attachment, value: document.attachment.path %> - <%= document_fields.file_field :attachment, - accept: accepted_content_types_extensions(resource), - label: false, - class: 'document_ajax_attachment show-for-sr', - data: { - url: upload_documents_url( - documentable_type: document_fields.object.documentable_type, - documentable_id: document_fields.object.documentable_id - ), - cached_attachment_input_field: "#{resource.class.name.downcase}_documents_attributes_#{index}_cached_attachment", - multiple: false - } %> - <%= document_fields.label :attachment, t("documents.form.attachment_label"), class: 'button hollow' %> -
-

<%= document_attachment_file_name(document) %>

- <% if document.errors[:attachment].any? %> -
<%= document.errors[:attachment].join(", ") %>
- <% end %> - <% end %> -
-
-<% end %> \ No newline at end of file diff --git a/app/views/documents/destroy.js.erb b/app/views/documents/destroy.js.erb new file mode 100644 index 000000000..39de52fb0 --- /dev/null +++ b/app/views/documents/destroy.js.erb @@ -0,0 +1,6 @@ +App.Documentable.destroy("<%= document_nested_field_wrapper_id(params[:index]) %>", "<%= j render('layouts/flash') %>") +<% new_document_link = link_to t("documents.form.add_new_document"), + new_document_path(documentable_type: @document.documentable_type, index: params[:index]), + remote: true, + id: "new_document_link" %> +App.Documentable.updateNewDocumentButton("<%= j new_document_link %>") \ No newline at end of file diff --git a/app/views/documents/new.js.erb b/app/views/documents/new.js.erb new file mode 100644 index 000000000..a5ff28cac --- /dev/null +++ b/app/views/documents/new.js.erb @@ -0,0 +1,9 @@ +<% + nested_fields = render 'documents/nested_document', document: @document, index: params[:index] + new_document_link = link_to "Añadir nuevo documento", + new_document_path(documentable_type: params[:documentable_type], index: params[:index].to_i + 1), + remote: true, + id: "new_document_link" +%> +App.Documentable.new("<%= j nested_fields %>") +App.Documentable.updateNewDocumentButton("<%= j new_document_link %>") \ No newline at end of file diff --git a/app/views/documents/upload.js.erb b/app/views/documents/upload.js.erb new file mode 100644 index 000000000..106004e9a --- /dev/null +++ b/app/views/documents/upload.js.erb @@ -0,0 +1,9 @@ +<% + nested_fields = render 'documents/nested_document', document: @document, index: params[:index] +%> + +<% if @document.cached_attachment.present? %> + App.Documentable.upload("<%= document_nested_field_wrapper_id(params[:index]) %>", "<%= j nested_fields %>", true) +<% else %> + App.Documentable.upload("<%= document_nested_field_wrapper_id(params[:index]) %>", "<%= j nested_fields %>", false) +<% end %> diff --git a/app/views/proposals/_form.html.erb b/app/views/proposals/_form.html.erb index 6a2fba14d..d1ed7f3b2 100644 --- a/app/views/proposals/_form.html.erb +++ b/app/views/proposals/_form.html.erb @@ -34,7 +34,6 @@ <%= f.cktext_area :description, maxlength: Proposal.description_max_length, ckeditor: { language: I18n.locale }, label: false %>
-
<%= f.label :video_url, t("proposals.form.proposal_video_url") %>

<%= t("proposals.form.proposal_video_url_note") %>

@@ -47,8 +46,8 @@ <%= f.text_field :external_url, placeholder: t("proposals.form.proposal_external_url"), label: false %>
-
- <%= render 'documents/nested_form_fields', form: f, resource: @proposal, documents: @proposal.documents %> +
+ <%= render 'documents/nested_documents', resource: @proposal %>
diff --git a/config/locales/en/documents.yml b/config/locales/en/documents.yml index e65e288e2..d2485df4c 100644 --- a/config/locales/en/documents.yml +++ b/config/locales/en/documents.yml @@ -5,14 +5,19 @@ en: upload_document: Upload document max_documents_allowed_reached_html: You have reached the maximum number of documents allowed! You have to delete one before you can upload another. form: - attachment_label: Choose attachment file + title: Documents + attachment_label: Choose document submit_button: Upload document + delete_button: Remove document + note: "You can upload up to a maximum of %{max_documents_allowed} documents of following content types: %{accepted_content_types}, up to %{max_file_size} MB per file." + add_new_document: Add new document new: title: Upload 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. recommendation_three_html: You can upload files up to %{max_file_size} MB. + actions: create: notice: Document was created successfully. @@ -20,6 +25,7 @@ en: destroy: notice: Document was deleted successfully. alert: Cannot destroy document. + confirm: Are you sure you want to delete the document? This action cannot be undone! buttons: download_document: Dowload file destroy_document: Destroy diff --git a/config/locales/es/documents.yml b/config/locales/es/documents.yml index 9002613cb..a0b646ecc 100644 --- a/config/locales/es/documents.yml +++ b/config/locales/es/documents.yml @@ -5,8 +5,12 @@ es: upload_document: Subir documento max_documents_allowed_reached_html: ¡Has alcanzado el número máximo de documentos permitidos! Tienes que eliminar uno antes de poder subir otro. form: - attachment_label: Selecciona un archivo + title: Documentos + attachment_label: Selecciona un documento submit_button: Subir documento + delete_button: Eliminar documento + note: "Puedes subir hasta un máximo de %{max_documents_allowed} documentos en los formatos: %{accepted_content_types}, y de hasta %{max_file_size} MB por archivo." + add_new_document: Añadir nuevo documento new: title: Subir un documento recommendations_title: Consejos para subir archivos @@ -20,6 +24,7 @@ es: destroy: notice: "El documento se ha eliminado correctamente." alert: "El documento no se ha podido eliminar." + confirm: "¿Está seguro de que desea eliminar el documento? Esta acción no se puede deshacer!" buttons: download_document: Descargar archivo destroy_document: Eliminar diff --git a/config/routes.rb b/config/routes.rb index fe8a895ce..f8afd632c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -96,8 +96,11 @@ Rails.application.routes.draw do resources :follows, only: [:create, :destroy] resources :documents, only: [:new, :create, :destroy] do - collection { post :upload, format: :json} - collection { post :progress, format: :json} + collection do + delete :destroy_upload + post :upload + post :progress + end end resources :stats, only: [:index] diff --git a/db/schema.rb b/db/schema.rb index ac9c5cb67..52e868f9d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -936,7 +936,7 @@ ActiveRecord::Schema.define(version: 20170720092638) do t.boolean "email_digest", default: true t.boolean "email_on_direct_message", default: true t.boolean "official_position_badge", default: false - t.datetime "password_changed_at", default: '2017-07-31 21:36:00', null: false + t.datetime "password_changed_at", default: '2017-08-22 16:28:06', null: false t.boolean "created_from_signature", default: false t.integer "failed_email_digests_count", default: 0 t.text "former_users_data_log", default: "" diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index 9573a4639..9405e779e 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -1230,6 +1230,8 @@ feature 'Proposals' do it_behaves_like "documentable", "proposal", "proposal_path", { "id": "id" } + it_behaves_like "nested documentable", "proposal", "new_proposal_path" + 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 new file mode 100644 index 000000000..123dbc05b --- /dev/null +++ b/spec/shared/features/nested_documentable.rb @@ -0,0 +1,226 @@ +shared_examples "nested documentable" do |documentable_factory_name, new_documentable_path, documentable_path_arguments| + include ActionView::Helpers + include DocumentsHelper + include DocumentablesHelper + + let!(:administrator) { create(:user) } + let!(:user) { create(:user) } + let!(:arguments) { {} } + let!(:documentable) { create(documentable_factory_name, author: user) } + + before do + create(:administrator, user: administrator) + + if documentable_path_arguments + documentable_path_arguments.each do |argument_name, path_to_value| + arguments.merge!("#{argument_name}": documentable.send(path_to_value)) + end + end + end + + context "Nested documents" do + + context "On documentable new" do + + scenario "Should show new document link" do + login_as user + visit send(new_documentable_path, arguments) + + expect(page).to have_selector "#new_document_link", visible: true + end + + scenario "Should not show new document when documentable max documents allowed limit is reached", :js do + login_as user + visit send(new_documentable_path, arguments) + + find("#new_document_link").click + sleep 1 + find("#new_document_link").click + sleep 1 + find("#new_document_link").click + + expect(page).to have_selector "#new_document_link", visible: false + end + + scenario "Should not show max documents warning when no documents added", :js do + login_as user + visit send(new_documentable_path, arguments) + + expect(page).to have_selector ".max-documents-notice", visible: false + end + + scenario "Should show max documents warning when max documents allowed limit is reached", :js do + login_as user + visit send(new_documentable_path, arguments) + + find("#new_document_link").click + sleep 1 + find("#new_document_link").click + sleep 1 + find("#new_document_link").click + + expect(page).to have_selector ".max-documents-notice", visible: true + end + + scenario "Should hide max documents warning after any document link", :js do + login_as user + visit send(new_documentable_path, arguments) + + find("#new_document_link").click + sleep 1 + find("#new_document_link").click + sleep 1 + find("#new_document_link").click + sleep 1 + within "#document_0" do + find("a", text: "Remove document").click + end + sleep 1 + + expect(page).to have_selector ".max-documents-notice", visible: false + end + + scenario "Should update nested document file name after choosing a file", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + + expect(page).to have_selector ".file-name", text: "empty.pdf" + end + + scenario "Should update nested document file title with file name after choosing a file when no title defined", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + sleep 1 + + expect(find("##{documentable_factory_name}_documents_attributes_0_title").value).to eq "empty.pdf" + end + + scenario "Should not update nested document file title with file name after choosing a file when title already defined", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title" + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + sleep 1 + + expect(find("##{documentable_factory_name}_documents_attributes_0_title").value).to eq "Title" + end + + scenario "Should update loading bar style after valid file upload", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title" + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + 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 user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title" + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/logo_header.png" + sleep 1 + + expect(page).to have_selector ".loading-bar.errors" + end + + scenario "Should update document cached_attachment field after valid file upload", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title" + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + sleep 1 + + expect(find("input[name='#{documentable_factory_name}[documents_attributes][0][cached_attachment]']", visible: false).value).to include("empty.pdf") + end + + scenario "Should not update document cached_attachment field after unvalid file upload", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title" + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/logo_header.png" + sleep 1 + + expect(find("input[name='#{documentable_factory_name}[documents_attributes][0][cached_attachment]']", visible: false).value).to eq "" + end + + scenario "Should show document errors after unvalid file upload", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + sleep 1 + click_on "Create #{documentable_factory_name}" + + within "#document_0" do + expect(page).to have_content("can't be blank", count: 2) + end + end + + scenario "Should delete document after valid file upload and click on remove button", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + sleep 1 + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + sleep 1 + within "#document_0" do + click_link "Remove document" + end + + expect(page).not_to have_selector("#document_0") + end + + scenario "Should delete document after valid file upload and click on remove button", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + sleep 1 + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + sleep 1 + within "#document_0" do + click_link "Remove document" + end + + expect(page).to have_content "Document was deleted successfully." + end + + scenario "Should delete document after valid file upload and click on remove button", :js do + login_as user + visit send(new_documentable_path, arguments) + + click_link "Add new document" + sleep 1 + attach_file "#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf" + sleep 1 + within "#document_0" do + click_link "Remove document" + end + + expect(page).to have_content "Document was deleted successfully." + end + + end + + end + +end