Commit 6a85cdf1627629ecaa762fa60a7abdbd092cc20a

Authored by Earle Bunao & Neil Calabroso
Committed by erbunao
1 parent 696b9903

Implements drag and drop upload in creating issues

Gemfile
... ... @@ -69,6 +69,9 @@ gem "haml-rails"
69 69 # Files attachments
70 70 gem "carrierwave"
71 71  
  72 +# Drag and Drop UI
  73 +gem 'dropzonejs-rails'
  74 +
72 75 # for aws storage
73 76 gem "fog", "~> 1.14", group: :aws
74 77 gem "unf", group: :aws
... ...
Gemfile.lock
... ... @@ -103,6 +103,8 @@ GEM
103 103 diffy (3.0.3)
104 104 docile (1.1.1)
105 105 dotenv (0.9.0)
  106 + dropzonejs-rails (0.4.14)
  107 + rails (> 3.1)
106 108 email_spec (1.5.0)
107 109 launchy (~> 2.1)
108 110 mail (~> 2.2)
... ... @@ -579,6 +581,7 @@ DEPENDENCIES
579 581 devise (= 3.0.4)
580 582 devise-async (= 0.8.0)
581 583 diffy (~> 3.0.3)
  584 + dropzonejs-rails
582 585 email_spec
583 586 email_validator (~> 1.4.0)
584 587 enumerize
... ...
app/assets/javascripts/application.js.coffee
... ... @@ -29,6 +29,7 @@
29 29 #= require underscore
30 30 #= require nprogress
31 31 #= require nprogress-turbolinks
  32 +#= require dropzone
32 33 #= require_tree .
33 34  
34 35 window.slugify = (text) ->
... ...
app/assets/javascripts/behaviors/toggler_behavior.coffee
1 1 $ ->
2 2 $("body").on "click", ".js-toggler-target", ->
3   - container = $(@).closest(".js-toggler-container")
  3 + container = $(".notes-container")
4 4 container.toggleClass("on")
5 5  
6 6 # Toggle button. Show/hide content inside parent container.
... ...
app/assets/javascripts/markdown_area.js.coffee 0 → 100644
... ... @@ -0,0 +1,85 @@
  1 +formatLink = (str) ->
  2 + "![" + str.alt + "](" + str.url + ")"
  3 +
  4 +$(document).ready ->
  5 + alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
  6 + alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
  7 + divHover = "<div class=\"div-dropzone-hover\"></div>"
  8 + divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
  9 + divAlert = "<div class=\"" + alertClass + "\"></div>"
  10 + iconPicture = "<i class=\"icon-picture div-dropzone-icon\"></i>"
  11 + iconSpinner = "<i class=\"icon-spinner icon-spin div-dropzone-icon\"></i>"
  12 + btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
  13 + project_image_path_upload = window.project_image_path_upload or null
  14 +
  15 + $("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
  16 +
  17 + $(".div-dropzone").parent().addClass "div-dropzone-wrapper"
  18 +
  19 + $(".div-dropzone").append divHover
  20 + $(".div-dropzone-hover").append iconPicture
  21 + $(".div-dropzone").append divSpinner
  22 + $(".div-dropzone-spinner").append iconSpinner
  23 +
  24 +
  25 + dropzone = $(".div-dropzone").dropzone(
  26 + url: project_image_path_upload
  27 + dictDefaultMessage: ""
  28 + clickable: true
  29 + paramName: "markdown_img"
  30 + maxFilesize: 10
  31 + uploadMultiple: false
  32 + acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
  33 + headers:
  34 + "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
  35 +
  36 + previewContainer: false
  37 +
  38 + processing: ->
  39 + $(".div-dropzone-alert").alert "close"
  40 +
  41 + dragover: ->
  42 + $(".div-dropzone > textarea").addClass "div-dropzone-focus"
  43 + $(".div-dropzone-hover").css "opacity", 0.7
  44 + return
  45 +
  46 + dragleave: ->
  47 + $(".div-dropzone > textarea").removeClass "div-dropzone-focus"
  48 + $(".div-dropzone-hover").css "opacity", 0
  49 + return
  50 +
  51 + drop: ->
  52 + $(".div-dropzone > textarea").removeClass "div-dropzone-focus"
  53 + $(".div-dropzone-hover").css "opacity", 0
  54 + $(".div-dropzone > textarea").focus()
  55 + return
  56 +
  57 + success: (header, response) ->
  58 + child = $(dropzone[0]).children("textarea")
  59 + $(child).val $(child).val() + formatLink(response.link) + "\n"
  60 + return
  61 +
  62 + error: (temp, errorMessage) ->
  63 + checkIfMsgExists = $(".error-alert").children().length
  64 + if checkIfMsgExists is 0
  65 + $(".error-alert").append divAlert
  66 + $(".div-dropzone-alert").append btnAlert + errorMessage
  67 + return
  68 +
  69 + sending: ->
  70 + $(".div-dropzone-spinner").css "opacity", 0.7
  71 + return
  72 +
  73 + complete: ->
  74 + $(".dz-preview").remove()
  75 + $(".markdown-area").trigger "input"
  76 + $(".div-dropzone-spinner").css "opacity", 0
  77 + return
  78 + )
  79 +
  80 + $(".markdown-selector").click (e) ->
  81 + e.preventDefault()
  82 + $(".div-dropzone").click()
  83 + return
  84 +
  85 + return
0 86 \ No newline at end of file
... ...
app/assets/stylesheets/application.scss
... ... @@ -10,6 +10,7 @@
10 10 *= require_self
11 11 *= require nprogress
12 12 *= require nprogress-bootstrap
  13 + *= require dropzone/basic
13 14 */
14 15  
15 16 @import "main/*";
... ...
app/assets/stylesheets/behaviors.scss
... ... @@ -5,10 +5,19 @@
5 5 .js-details-container.open .content { display: block; }
6 6 .js-details-container.open .content.hide { display: none; }
7 7  
8   -
9 8 // Toggler
10 9 //--------
11   -.js-toggler-container .turn-on { display: inherit; }
  10 +.write-preview-btn .turn-on { display: inherit; }
  11 +.write-preview-btn .turn-off { display: none; }
  12 +
12 13 .js-toggler-container .turn-off { display: none; }
13 14 .js-toggler-container.on .turn-on { display: none; }
14 15 .js-toggler-container.on .turn-off { display: inherit; }
  16 +
  17 +.js-toggler-container.on ~ .note-form-actions {
  18 + .write-preview-btn .turn-on { display: none; }
  19 +}
  20 +
  21 +.js-toggler-container.on ~ .note-form-actions {
  22 + .write-preview-btn .turn-off { display: inherit; }
  23 +}
... ...
app/assets/stylesheets/generic/markdown_area.scss 0 → 100644
... ... @@ -0,0 +1,58 @@
  1 +.div-dropzone-wrapper {
  2 + .div-dropzone {
  3 + position: relative;
  4 + padding: 0;
  5 + border: 0;
  6 + margin-bottom: 5px;
  7 +
  8 + .div-dropzone-focus {
  9 + border-color: #66afe9 !important;
  10 + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6) !important;
  11 + outline: 0 !important;
  12 + }
  13 +
  14 + .div-dropzone-hover {
  15 + position: absolute;
  16 + top: 50%;
  17 + left: 50%;
  18 + margin-top: -0.5em;
  19 + margin-left: -0.6em;
  20 + opacity: 0;
  21 + font-size: 50px;
  22 + transition: opacity 200ms ease-in-out;
  23 + }
  24 +
  25 + .div-dropzone-spinner {
  26 + position: absolute;
  27 + top: 100%;
  28 + left: 100%;
  29 + margin-top: -1.1em;
  30 + margin-left: -1.1em;
  31 + opacity: 0;
  32 + font-size: 30px;
  33 + transition: opacity 200ms ease-in-out;
  34 + }
  35 +
  36 + .div-dropzone-icon {
  37 + display: block;
  38 + text-align: center;
  39 + font-size: inherit;
  40 + }
  41 +
  42 + .dz-preview {
  43 + display: none;
  44 + }
  45 + }
  46 +
  47 + .hint {
  48 + float: left;
  49 + padding: 0;
  50 + margin: 0;
  51 + }
  52 +}
  53 +
  54 +.div-dropzone-alert {
  55 + margin-top: 5px;
  56 + margin-bottom: 0;
  57 + transition: opacity 200ms ease-in-out;
  58 +}
... ...
app/assets/stylesheets/sections/notes.scss
... ... @@ -272,21 +272,16 @@ ul.notes {
272 272 margin-bottom: 0;
273 273 }
274 274 .note_text_and_preview {
275   - // makes the "absolute" position for links relative to this
276   - position: relative;
277   -
278   - // preview/edit buttons
279   - > a {
280   - position: absolute;
281   - right: 5px;
282   - bottom: -60px;
283   - }
284 275 .note_preview {
285 276 background: #f5f5f5;
286 277 border: 1px solid #ddd;
287 278 @include border-radius(4px);
288 279 min-height: 80px;
289 280 padding: 4px 6px;
  281 +
  282 + > p {
  283 + overflow-x: auto;
  284 + }
290 285 }
291 286 .note_text {
292 287 border: 1px solid #DDD;
... ... @@ -310,7 +305,6 @@ ul.notes {
310 305 float: none;
311 306 }
312 307  
313   -
314 308 .common-note-form {
315 309 margin: 0;
316 310 background: #F9F9F9;
... ... @@ -318,7 +312,6 @@ ul.notes {
318 312 border: 1px solid #DDD;
319 313 }
320 314  
321   -
322 315 .note-form-actions {
323 316 background: #F9F9F9;
324 317 height: 45px;
... ... @@ -333,6 +326,18 @@ ul.notes {
333 326 .js-notify-commit-author {
334 327 float: left;
335 328 }
  329 +
  330 + .write-preview-btn {
  331 + // makes the "absolute" position for links relative to this
  332 + position: relative;
  333 +
  334 + // preview/edit buttons
  335 + > a {
  336 + position: absolute;
  337 + right: 5px;
  338 + top: 8px;
  339 + }
  340 + }
336 341 }
337 342  
338 343 .note-edit-form {
... ... @@ -367,3 +372,8 @@ ul.notes {
367 372 .parallel-comment {
368 373 padding: 6px;
369 374 }
  375 +
  376 +.error-alert > .alert {
  377 + margin-top: 5px;
  378 + margin-bottom: 5px;
  379 +}
... ...
app/controllers/files_controller.rb
... ... @@ -14,4 +14,3 @@ class FilesController &lt; ApplicationController
14 14 end
15 15 end
16 16 end
17   -
... ...
app/controllers/projects/issues_controller.rb
... ... @@ -69,7 +69,9 @@ class Projects::IssuesController &lt; Projects::ApplicationController
69 69 render :new
70 70 end
71 71 end
72   - format.js
  72 + format.js do |format|
  73 + @link = @issue.attachment.url.to_js
  74 + end
73 75 end
74 76 end
75 77  
... ...
app/controllers/projects_controller.rb
... ... @@ -162,8 +162,28 @@ class ProjectsController &lt; ApplicationController
162 162 end
163 163 end
164 164  
  165 + def upload_image
  166 + uploader = FileUploader.new('uploads', upload_path, accepted_images)
  167 + alt = params['markdown_img'].original_filename
  168 + uploader.store!(params['markdown_img'])
  169 + link = { 'alt' => File.basename(alt, '.*'),
  170 + 'url' => File.join(root_url, uploader.url) }
  171 + respond_to do |format|
  172 + format.json { render json: { link: link } }
  173 + end
  174 + end
  175 +
165 176 private
166 177  
  178 + def upload_path
  179 + base_dir = FileUploader.generate_dir
  180 + File.join(repository.path_with_namespace, base_dir)
  181 + end
  182 +
  183 + def accepted_images
  184 + %w(png jpg jpeg gif)
  185 + end
  186 +
167 187 def set_title
168 188 @title = 'New Project'
169 189 end
... ...
app/models/issue.rb
... ... @@ -15,8 +15,12 @@
15 15 # milestone_id :integer
16 16 # state :string(255)
17 17 # iid :integer
  18 +# attachment :string(255)
18 19 #
19 20  
  21 +require 'carrierwave/orm/activerecord'
  22 +require 'file_size_validator'
  23 +
20 24 class Issue < ActiveRecord::Base
21 25 include Issuable
22 26 include InternalId
... ...
app/uploaders/file_uploader.rb 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 +# encoding: utf-8
  2 +class FileUploader < CarrierWave::Uploader::Base
  3 + storage :file
  4 +
  5 + def initialize(base_dir, path = '', allowed_extensions = nil)
  6 + @base_dir = base_dir
  7 + @path = path
  8 + @allowed_extensions = allowed_extensions
  9 + end
  10 +
  11 + def base_dir
  12 + @base_dir
  13 + end
  14 +
  15 + def store_dir
  16 + File.join(@base_dir, @path)
  17 + end
  18 +
  19 + def cache_dir
  20 + File.join(@base_dir, 'tmp', @path)
  21 + end
  22 +
  23 + def extension_white_list
  24 + @allowed_extensions
  25 + end
  26 +
  27 + def store!(file)
  28 + file.original_filename = self.class.generate_filename(file)
  29 + super
  30 + end
  31 +
  32 + def self.generate_filename(file)
  33 + original_filename = File.basename(file.original_filename, '.*')
  34 + extension = File.extname(file.original_filename)
  35 + new_filename = Digest::MD5.hexdigest(original_filename) + extension
  36 + end
  37 +
  38 + def self.generate_dir
  39 + SecureRandom.hex(5)
  40 + end
  41 +end
... ...
app/views/layouts/admin.html.haml
... ... @@ -10,3 +10,4 @@
10 10  
11 11 .container
12 12 .content= yield
  13 + = yield :embedded_scripts
13 14 \ No newline at end of file
... ...
app/views/layouts/projects.html.haml
... ... @@ -14,3 +14,4 @@
14 14  
15 15 .container
16 16 .content= yield
  17 + = yield :embedded_scripts
17 18 \ No newline at end of file
... ...
app/views/projects/issues/_form.html.haml
... ... @@ -5,6 +5,7 @@
5 5 - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
6 6 .alert.alert-info.col-sm-10.col-sm-offset-2
7 7 ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
  8 +
8 9 = form_for [@project, @issue], html: { class: 'form-horizontal issue-form' } do |f|
9 10 -if @issue.errors.any?
10 11 .alert.alert-danger
... ... @@ -19,8 +20,12 @@
19 20 .form-group
20 21 = f.label :description, 'Description', class: 'control-label'
21 22 .col-sm-10
22   - = f.text_area :description, class: "form-control js-gfm-input", rows: 14
23   - %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  23 + = f.text_area :description, class: 'form-control js-gfm-input markdown-area', rows: 14
  24 + .col-sm-12.hint
  25 + .pull-left Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  26 + .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
  27 + .clearfix
  28 + .error-alert
24 29 %hr
25 30 .form-group
26 31 .issue-assignee
... ... @@ -57,9 +62,6 @@
57 62 - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue)
58 63 = link_to "Cancel", cancel_path, class: 'btn btn-cancel'
59 64  
60   -
61   -
62   -
63 65 :javascript
64 66 $("#issue_label_list")
65 67 .bind( "keydown", function( event ) {
... ... @@ -94,3 +96,5 @@
94 96 $('#issue_assignee_id').val("#{current_user.id}").trigger("change");
95 97 e.preventDefault();
96 98 });
  99 +
  100 + window.project_image_path_upload = "#{upload_image_project_path @project}";
... ...
app/views/projects/issues/show.html.haml
... ... @@ -73,4 +73,4 @@
73 73 = label.name
74 74 &nbsp;
75 75  
76 76 -.voting_notes#notes= render "projects/notes/notes_with_form"
  77 +.voting_notes#notes= render "projects/notes/notes_with_form"
77 78 \ No newline at end of file
... ...
app/views/projects/merge_requests/_form.html.haml
... ... @@ -22,8 +22,12 @@
22 22 .form-group
23 23 = f.label :description, "Description", class: 'control-label'
24 24 .col-sm-10
25   - = f.text_area :description, class: "form-control js-gfm-input", rows: 14
26   - %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  25 + = f.text_area :description, class: "form-control js-gfm-input markdown-area", rows: 14
  26 + .col-sm-12.hint
  27 + .pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  28 + .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
  29 + .clearfix
  30 + .error-alert
27 31 %hr
28 32 .form-group
29 33 .issue-assignee
... ... @@ -98,3 +102,5 @@
98 102 return false;
99 103 }
100 104 });
  105 +
  106 + window.project_image_path_upload = "#{upload_image_project_path @project}";
... ...
app/views/projects/merge_requests/_new_submit.html.haml
... ... @@ -23,8 +23,12 @@
23 23 .form-group
24 24 .light
25 25 = f.label :description, "Description"
26   - = f.text_area :description, class: "form-control js-gfm-input", rows: 10
27   - %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  26 + = f.text_area :description, class: "form-control js-gfm-input markdown-area", rows: 10
  27 + .col-sm-12.hint
  28 + .pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  29 + .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
  30 + .clearfix
  31 + .error-alert
28 32 .form-group
29 33 .issue-assignee
30 34 = f.label :assignee_id do
... ... @@ -80,3 +84,5 @@
80 84 $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
81 85 e.preventDefault();
82 86 });
  87 +
  88 + window.project_image_path_upload = "#{upload_image_project_path @project}";
... ...
app/views/projects/milestones/_form.html.haml
... ... @@ -21,8 +21,12 @@
21 21 .form-group
22 22 = f.label :description, "Description", class: "control-label"
23 23 .col-sm-10
24   - = f.text_area :description, maxlength: 2000, class: "form-control", rows: 10
25   - %p.hint Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  24 + = f.text_area :description, maxlength: 2000, class: "form-control markdown-area", rows: 10
  25 + .hint
  26 + .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  27 + .pull-left Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
  28 + .clearfix
  29 + .error-alert
26 30 .col-md-6
27 31 .form-group
28 32 = f.label :due_date, "Due Date", class: "control-label"
... ... @@ -45,3 +49,5 @@
45 49 dateFormat: "yy-mm-dd",
46 50 onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
47 51 }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
  52 +
  53 + window.project_image_path_upload = "#{upload_image_project_path @project}";
... ...
app/views/projects/notes/_form.html.haml
... ... @@ -5,20 +5,15 @@
5 5 = f.hidden_field :noteable_id
6 6 = f.hidden_field :noteable_type
7 7  
8   - .note_text_and_preview.js-toggler-container
9   - %a.btn.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", data: {url: preview_project_notes_path(@project)} }
10   - %i.icon-eye-open
11   - Preview
12   - %a.btn.btn-primary.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;" }
13   - %i.icon-edit
14   - Write
15   -
16   - = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on'
  8 + .note_text_and_preview.js-toggler-container.notes-container
  9 + = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on markdown-area'
17 10 .note_preview.js-note-preview.turn-off
18 11  
19 12 .hint
20   - .pull-right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  13 + .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  14 + .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
21 15 .clearfix
  16 + .error-alert
22 17  
23 18 .note-form-actions
24 19 .buttons
... ... @@ -35,4 +30,14 @@
35 30 %span.file_name.js-attachment-filename File name...
36 31 = f.file_field :attachment, class: "js-note-attachment-input hidden"
37 32  
  33 + .write-preview-btn
  34 + %a.btn.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", data: {url: preview_project_notes_path(@project)} }
  35 + %i.icon-eye-open
  36 + Preview
  37 + %a.btn.btn-primary.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;" }
  38 + %i.icon-edit
  39 + Write
38 40 .clearfix
  41 +
  42 +:javascript
  43 + window.project_image_path_upload = "#{upload_image_project_path @project}";
... ...
app/views/projects/wikis/_form.html.haml
... ... @@ -15,7 +15,6 @@
15 15 .col-sm-2
16 16 .col-sm-10
17 17 %p.cgray
18   - Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
19 18 To link to a (new) page you can just type
20 19 %code [Link Title](page-slug)
21 20 \.
... ... @@ -23,8 +22,12 @@
23 22 .form-group
24 23 = f.label :content, class: 'control-label'
25 24 .col-sm-10
26   - = f.text_area :content, class: 'form-control js-gfm-input', rows: 18
27   -
  25 + = f.text_area :content, class: 'form-control js-gfm-input markdown-area', rows: 18
  26 + .col-sm-12.hint
  27 + .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  28 + .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
  29 + .clearfix
  30 + .error-alert
28 31 .form-group
29 32 = f.label :commit_message, class: 'control-label'
30 33 .col-sm-10= f.text_field :message, class: 'form-control', rows: 18
... ... @@ -36,3 +39,7 @@
36 39 - else
37 40 = f.submit 'Create page', class: "btn-create btn"
38 41 = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel"
  42 +
  43 +:javascript
  44 + window.project_image_path_upload = "#{upload_image_project_path @project}";
  45 +
... ...
config/routes.rb
... ... @@ -136,8 +136,6 @@ Gitlab::Application.routes.draw do
136 136  
137 137 match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ }, via: :get
138 138  
139   -
140   -
141 139 #
142 140 # Dashboard Area
143 141 #
... ... @@ -178,6 +176,7 @@ Gitlab::Application.routes.draw do
178 176 post :fork
179 177 post :archive
180 178 post :unarchive
  179 + post :upload_image
181 180 get :autocomplete_sources
182 181 get :import
183 182 put :retry_import
... ...
spec/controllers/commits_controller_spec.rb
... ... @@ -6,8 +6,7 @@ describe Projects::CommitsController do
6 6  
7 7 before do
8 8 sign_in(user)
9   -
10   - project.team << [user, :master]
  9 + project.creator = user
11 10 end
12 11  
13 12 describe "GET show" do
... ...
spec/controllers/projects_controller_spec.rb 0 → 100644
... ... @@ -0,0 +1,44 @@
  1 +require('spec_helper')
  2 +
  3 +describe ProjectsController do
  4 + let(:project) { create(:project) }
  5 + let(:user) { create(:user) }
  6 + let(:png) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png') }
  7 + let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
  8 + let(:gif) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
  9 + let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
  10 +
  11 + describe "POST #upload_image" do
  12 + before do
  13 + sign_in(user)
  14 + end
  15 +
  16 + context "without params['markdown_img']" do
  17 + it "returns an error" do
  18 + post :upload_image, id: project.to_param
  19 + expect(response.status).to eq(404)
  20 + end
  21 + end
  22 +
  23 + context "with invalid file" do
  24 + before do
  25 + post :upload_image, id: project.to_param, markdown_img: @img
  26 + end
  27 +
  28 + it "returns an error" do
  29 + expect(response.status).to eq(404)
  30 + end
  31 + end
  32 +
  33 + context "with valid file" do
  34 + before do
  35 + post :upload_image, id: project.to_param, markdown_img: @img
  36 + end
  37 +
  38 + it "returns a content with original filename and new link." do
  39 + link = { alt: 'rails_sample', link: '' }.to_json
  40 + expect(response.body).to have_content link
  41 + end
  42 + end
  43 + end
  44 +end
0 45 \ No newline at end of file
... ...
spec/factories.rb
... ... @@ -201,7 +201,7 @@ FactoryGirl.define do
201 201 end
202 202  
203 203 trait :with_attachment do
204   - attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") }
  204 + attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") }
205 205 end
206 206 end
207 207  
... ...
spec/fixtures/banana_sample.gif 0 → 100644

70.1 KB

spec/fixtures/doc_sample.txt 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  2 +
  3 +Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
0 4 \ No newline at end of file
... ...
spec/fixtures/rails_sample.jpg 0 → 100644

34.4 KB