From 4464c22d6d23d893494682d309aec3fb31c11ae3 Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Wed, 3 May 2017 17:26:49 +0200 Subject: Support descriptions for snippets --- app/assets/javascripts/dispatcher.js | 10 +++++ app/assets/javascripts/dropzone_input.js | 7 +++- app/controllers/projects/snippets_controller.rb | 2 +- app/controllers/snippets_controller.rb | 10 ++++- app/controllers/uploads_controller.rb | 4 ++ app/helpers/gitlab_routing_helper.rb | 2 +- app/models/snippet.rb | 1 + app/uploaders/file_mover.rb | 48 ++++++++++++++++++++++ app/uploaders/personal_file_uploader.rb | 6 ++- app/views/layouts/snippets.html.haml | 5 +-- .../shared/form_elements/_description.html.haml | 23 +++++++++++ app/views/shared/issuable/_form.html.haml | 2 +- .../shared/issuable/form/_description.html.haml | 22 ---------- app/views/shared/snippets/_form.html.haml | 7 +++- app/views/shared/snippets/_header.html.haml | 7 ++++ 15 files changed, 124 insertions(+), 32 deletions(-) create mode 100644 app/uploaders/file_mover.rb create mode 100644 app/views/shared/form_elements/_description.html.haml delete mode 100644 app/views/shared/issuable/form/_description.html.haml (limited to 'app') diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 6e2f06112dd..f75f2662cea 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -218,6 +218,16 @@ import ShortcutsBlob from './shortcuts_blob'; new gl.GLForm($('.tag-form')); new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs); break; + case 'projects:snippets:new': + case 'projects:snippets:edit': + case 'projects:snippets:create': + case 'projects:snippets:update': + case 'snippets:new': + case 'snippets:edit': + case 'snippets:create': + case 'snippets:update': + new gl.GLForm($('.snippet-form')); + break; case 'projects:releases:edit': new ZenMode(); new gl.GLForm($('.release-form')); diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 266cd3966c6..f886ce21493 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -5,7 +5,7 @@ import './preview_markdown'; window.DropzoneInput = (function() { function DropzoneInput(form) { - var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile; + var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile, addFileToForm; Dropzone.autoDiscover = false; divHover = '
'; iconPaperclip = ''; @@ -71,6 +71,7 @@ window.DropzoneInput = (function() { pasteText(response.link.markdown, shouldPad); // Show 'Attach a file' link only when all files have been uploaded. if (!processingFileCount) $attachButton.removeClass('hide'); + addFileToForm(response.link.url); }, error: function(file, errorMessage = 'Attaching the file failed.', xhr) { // If 'error' event is fired by dropzone, the second parameter is error message. @@ -197,6 +198,10 @@ window.DropzoneInput = (function() { return formTextarea.trigger('input'); }; + addFileToForm = function(path) { + $(form).append(''); + }; + getFilename = function(e) { var value; if (window.clipboardData && window.clipboardData.getData) { diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 3b2b0d9e502..08f339d98fc 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -107,6 +107,6 @@ class Projects::SnippetsController < Projects::ApplicationController end def snippet_params - params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level) + params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description) end end diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 7445f61195d..1334f7daa44 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -45,6 +45,8 @@ class SnippetsController < ApplicationController @snippet = CreateSnippetService.new(nil, current_user, create_params).execute + move_temporary_files if params[:files] + recaptcha_check_with_fallback { render :new } end @@ -124,6 +126,12 @@ class SnippetsController < ApplicationController end def snippet_params - params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level) + params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description) + end + + def move_temporary_files + params[:files].each do |file| + FileMover.new(file, @snippet).execute + end end end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index eef53730291..5cb3de3d4f5 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -9,6 +9,8 @@ class UploadsController < ApplicationController private def find_model + return nil unless params[:id] + return render_404 unless upload_model && upload_mount @model = upload_model.find(params[:id]) @@ -33,6 +35,8 @@ class UploadsController < ApplicationController end def authorize_create_access! + return unless model + # for now we support only personal snippets comments authorized = can?(current_user, :comment_personal_snippet, model) diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index fc308b3960e..7bf2600fd1a 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -128,7 +128,7 @@ module GitlabRoutingHelper def preview_markdown_path(project, *args) if @snippet.is_a?(PersonalSnippet) - preview_markdown_snippet_path(@snippet) + preview_markdown_snippets_path else preview_markdown_namespace_project_path(project.namespace, project, *args) end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 882e2fa0594..ace684b820b 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -10,6 +10,7 @@ class Snippet < ActiveRecord::Base include Spammable cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :description cache_markdown_field :content # Aliases to make application_helper#edited_time_ago_with_tooltip helper work properly with snippets. diff --git a/app/uploaders/file_mover.rb b/app/uploaders/file_mover.rb new file mode 100644 index 00000000000..21e37a08a82 --- /dev/null +++ b/app/uploaders/file_mover.rb @@ -0,0 +1,48 @@ +class FileMover + attr_reader :secret, :file_name, :model + + def initialize(file_path, model, update_field = :description) + @secret = File.split(File.dirname(file_path)).last + @file_name = File.basename(file_path) + @model = model + end + + def execute + move + update_markdown + end + + private + + def move + FileUtils.mkdir_p(file_path) + FileUtils.move(temp_file_path, file_path) + end + + def update_markdown(field = :description) + updated_text = model.send(field).sub(temp_file_uploader.to_markdown, uploader.to_markdown) + model.update_attribute(field, updated_text) + end + + def temp_file_path + temp_file_uploader.retrieve_from_store!(file_name) + + temp_file_uploader.file.path + end + + def file_path + return @file_path if @file_path + + uploader.retrieve_from_store!(file_name) + + @file_path = uploader.file.path + end + + def uploader + @uploader ||= PersonalFileUploader.new(model, secret) + end + + def temp_file_uploader + @temp_file_uploader ||= PersonalFileUploader.new(nil, secret) + end +end diff --git a/app/uploaders/personal_file_uploader.rb b/app/uploaders/personal_file_uploader.rb index 969b0a20d38..7f857765fbf 100644 --- a/app/uploaders/personal_file_uploader.rb +++ b/app/uploaders/personal_file_uploader.rb @@ -10,6 +10,10 @@ class PersonalFileUploader < FileUploader end def self.model_path(model) - File.join("/#{base_dir}", model.class.to_s.underscore, model.id.to_s) + if model + File.join("/#{base_dir}", model.class.to_s.underscore, model.id.to_s) + else + File.join("/#{base_dir}", 'temp') + end end end diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index 98b75cea03f..57971205e0e 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -1,9 +1,8 @@ - header_title "Snippets", snippets_path - content_for :page_specific_javascripts do - - if @snippet&.persisted? && current_user + - if @snippet && current_user :javascript - window.uploads_path = "#{upload_path('personal_snippet', @snippet)}"; - window.preview_markdown_path = "#{preview_markdown_snippet_path(@snippet)}"; + window.uploads_path = "#{upload_path('personal_snippet', id: @snippet.id)}"; = render template: "layouts/application" diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml new file mode 100644 index 00000000000..91224e232ca --- /dev/null +++ b/app/views/shared/form_elements/_description.html.haml @@ -0,0 +1,23 @@ +- project = local_assigns.fetch(:project) +- model = local_assigns.fetch(:model) + +- form = local_assigns.fetch(:form) +- supports_slash_commands = !model.persisted? + +- if supports_slash_commands + - preview_url = preview_markdown_path(project, slash_commands_target_type: model.class.name) +- else + - preview_url = preview_markdown_path(project) + +.form-group.detail-page-description + = form.label :description, 'Description', class: 'control-label' + .col-sm-10 + + = render layout: 'projects/md_preview', locals: { url: preview_url, referenced_users: true } do + = render 'projects/zen', f: form, attr: :description, + classes: 'note-textarea', + placeholder: "Write a comment or drag your files here...", + supports_slash_commands: supports_slash_commands + = render 'shared/notes/hints', supports_slash_commands: supports_slash_commands + .clearfix + .error-alert diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 7748351b333..c016aa2abcd 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -17,7 +17,7 @@ = render 'shared/issuable/form/template_selector', issuable: issuable = render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:work_in_progress?) -= render 'shared/issuable/form/description', issuable: issuable, form: form, project: project += render 'shared/form_elements/description', model: issuable, form: form, project: project - if issuable.respond_to?(:confidential) .form-group diff --git a/app/views/shared/issuable/form/_description.html.haml b/app/views/shared/issuable/form/_description.html.haml deleted file mode 100644 index 7ef0ae96be2..00000000000 --- a/app/views/shared/issuable/form/_description.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -- project = local_assigns.fetch(:project) -- issuable = local_assigns.fetch(:issuable) -- form = local_assigns.fetch(:form) -- supports_slash_commands = issuable.new_record? - -- if supports_slash_commands - - preview_url = preview_markdown_path(project, slash_commands_target_type: issuable.class.name) -- else - - preview_url = preview_markdown_path(project) - -.form-group.detail-page-description - = form.label :description, 'Description', class: 'control-label' - .col-sm-10 - - = render layout: 'projects/md_preview', locals: { url: preview_url, referenced_users: true } do - = render 'projects/zen', f: form, attr: :description, - classes: 'note-textarea', - placeholder: "Write a comment or drag your files here...", - supports_slash_commands: supports_slash_commands - = render 'shared/notes/hints', supports_slash_commands: supports_slash_commands - .clearfix - .error-alert diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 0296597b294..8549cb91b03 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -3,7 +3,7 @@ = page_specific_javascript_bundle_tag('snippet') .snippet-form-holder - = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit" } do |f| + = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit common-note-form" } do |f| = form_errors(@snippet) .form-group @@ -11,6 +11,8 @@ .col-sm-10 = f.text_field :title, class: 'form-control', required: true, autofocus: true + = render 'shared/form_elements/description', model: @snippet, project: @project, form: f + = render 'shared/visibility_level', f: f, visibility_level: @snippet.visibility_level, can_change_visibility_level: true, form_model: @snippet .file-editor @@ -23,6 +25,9 @@ .file-content.code %pre#editor= @snippet.content = f.hidden_field :content, class: 'snippet-file-content' + - if params[:files] + - params[:files].each_with_index do |file, index| + = hidden_field_tag "files[]", file, id: "files_#{index}" .form-actions - if @snippet.new_record? diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 501c09d71d5..d2b94ed4c0b 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -22,3 +22,10 @@ - if @snippet.updated_at != @snippet.created_at = edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true) + %div + - if @snippet.description.present? + .description + .wiki + = markdown_field(@snippet, :description) + %textarea.hidden.js-task-list-field + = @snippet.description -- cgit v1.2.1 From 2e311d9d1aac58bbd9c7d6c97c7cbcccf2715347 Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Mon, 29 May 2017 09:54:35 +0200 Subject: Support uploads for newly created personal snippets --- app/assets/javascripts/dropzone_input.js | 2 +- app/controllers/snippets_controller.rb | 2 +- app/controllers/uploads_controller.rb | 11 ++++++-- app/uploaders/file_mover.rb | 29 ++++++++++++++++------ app/uploaders/records_uploads.rb | 7 +++--- .../shared/form_elements/_description.html.haml | 2 +- app/views/shared/snippets/_header.html.haml | 13 +++++----- 7 files changed, 44 insertions(+), 22 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index f886ce21493..8837341153b 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -199,7 +199,7 @@ window.DropzoneInput = (function() { }; addFileToForm = function(path) { - $(form).append(''); + $(form).append(''); }; getFilename = function(e) { diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 1334f7daa44..6c25f59ccbb 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -45,7 +45,7 @@ class SnippetsController < ApplicationController @snippet = CreateSnippetService.new(nil, current_user, create_params).execute - move_temporary_files if params[:files] + move_temporary_files if @snippet.valid? && params[:files] recaptcha_check_with_fallback { render :new } end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 5cb3de3d4f5..dc882b17143 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -17,6 +17,8 @@ class UploadsController < ApplicationController end def authorize_access! + return nil unless model + authorized = case model when Note @@ -35,7 +37,7 @@ class UploadsController < ApplicationController end def authorize_create_access! - return unless model + return nil unless model # for now we support only personal snippets comments authorized = can?(current_user, :comment_personal_snippet, model) @@ -77,7 +79,12 @@ class UploadsController < ApplicationController def uploader return @uploader if defined?(@uploader) - if model.is_a?(PersonalSnippet) + case model + when nil + @uploader = PersonalFileUploader.new(nil, params[:secret]) + + @uploader.retrieve_from_store!(params[:filename]) + when PersonalSnippet @uploader = PersonalFileUploader.new(model, params[:secret]) @uploader.retrieve_from_store!(params[:filename]) diff --git a/app/uploaders/file_mover.rb b/app/uploaders/file_mover.rb index 21e37a08a82..00c2888d224 100644 --- a/app/uploaders/file_mover.rb +++ b/app/uploaders/file_mover.rb @@ -1,33 +1,42 @@ class FileMover - attr_reader :secret, :file_name, :model + attr_reader :secret, :file_name, :model, :update_field def initialize(file_path, model, update_field = :description) @secret = File.split(File.dirname(file_path)).last @file_name = File.basename(file_path) @model = model + @update_field = update_field end def execute move - update_markdown + uploader.record_upload if update_markdown end private def move - FileUtils.mkdir_p(file_path) + FileUtils.mkdir_p(File.dirname(file_path)) FileUtils.move(temp_file_path, file_path) end - def update_markdown(field = :description) - updated_text = model.send(field).sub(temp_file_uploader.to_markdown, uploader.to_markdown) - model.update_attribute(field, updated_text) + def update_markdown + updated_text = model.read_attribute(update_field).gsub(temp_file_uploader.to_markdown, uploader.to_markdown) + model.update_attribute(update_field, updated_text) + + true + rescue + revert + + false end def temp_file_path + return @temp_file_path if @temp_file_path + temp_file_uploader.retrieve_from_store!(file_name) - temp_file_uploader.file.path + @temp_file_path = temp_file_uploader.file.path end def file_path @@ -45,4 +54,10 @@ class FileMover def temp_file_uploader @temp_file_uploader ||= PersonalFileUploader.new(nil, secret) end + + def revert + Rails.logger.warn("Markdown not updated, file move reverted for #{model}") + + FileUtils.move(file_path, temp_file_path) + end end diff --git a/app/uploaders/records_uploads.rb b/app/uploaders/records_uploads.rb index 4c127f29250..feb4f04d7b7 100644 --- a/app/uploaders/records_uploads.rb +++ b/app/uploaders/records_uploads.rb @@ -6,8 +6,6 @@ module RecordsUploads before :remove, :destroy_upload end - private - # After storing an attachment, create a corresponding Upload record # # NOTE: We're ignoring the argument passed to this callback because we want @@ -15,13 +13,16 @@ module RecordsUploads # `Tempfile` object the callback gets. # # Called `after :store` - def record_upload(_tempfile) + def record_upload(_tempfile = nil) + return unless model return unless file_storage? return unless file.exists? Upload.record(self) end + private + # Before removing an attachment, destroy any Upload records at the same path # # Called `before :remove` diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml index 91224e232ca..307d4919224 100644 --- a/app/views/shared/form_elements/_description.html.haml +++ b/app/views/shared/form_elements/_description.html.haml @@ -2,7 +2,7 @@ - model = local_assigns.fetch(:model) - form = local_assigns.fetch(:form) -- supports_slash_commands = !model.persisted? +- supports_slash_commands = model.new_record? - if supports_slash_commands - preview_url = preview_markdown_path(project, slash_commands_target_type: model.class.name) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index d2b94ed4c0b..813d8d69d8d 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -22,10 +22,9 @@ - if @snippet.updated_at != @snippet.created_at = edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true) - %div - - if @snippet.description.present? - .description - .wiki - = markdown_field(@snippet, :description) - %textarea.hidden.js-task-list-field - = @snippet.description + - if @snippet.description.present? + .description + .wiki + = markdown_field(@snippet, :description) + %textarea.hidden.js-task-list-field + = @snippet.description -- cgit v1.2.1