Commit 6a85cdf1 authored by Earle Bunao & Neil Calabroso's avatar Earle Bunao & Neil Calabroso Committed by erbunao
Browse files

Implements drag and drop upload in creating issues

parent 1bd97e133496
......@@ -69,6 +69,9 @@ gem "haml-rails"
# Files attachments
gem "carrierwave"
# Drag and Drop UI
gem 'dropzonejs-rails'
# for aws storage
gem "fog", "~> 1.14", group: :aws
gem "unf", group: :aws
......
......@@ -103,6 +103,8 @@ GEM
diffy (3.0.3)
docile (1.1.1)
dotenv (0.9.0)
dropzonejs-rails (0.4.14)
rails (> 3.1)
email_spec (1.5.0)
launchy (~> 2.1)
mail (~> 2.2)
......@@ -579,6 +581,7 @@ DEPENDENCIES
devise (= 3.0.4)
devise-async (= 0.8.0)
diffy (~> 3.0.3)
dropzonejs-rails
email_spec
email_validator (~> 1.4.0)
enumerize
......
......@@ -29,6 +29,7 @@
#= require underscore
#= require nprogress
#= require nprogress-turbolinks
#= require dropzone
#= require_tree .
window.slugify = (text) ->
......
$ ->
$("body").on "click", ".js-toggler-target", ->
container = $(@).closest(".js-toggler-container")
container = $(".notes-container")
container.toggleClass("on")
# Toggle button. Show/hide content inside parent container.
......
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
$(document).ready ->
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"icon-picture div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"icon-spinner icon-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
$("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
$(".div-dropzone").parent().addClass "div-dropzone-wrapper"
$(".div-dropzone").append divHover
$(".div-dropzone-hover").append iconPicture
$(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner
dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
$(".div-dropzone > textarea").addClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
return
drop: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
$(".div-dropzone > textarea").focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
$(".div-dropzone-spinner").css "opacity", 0.7
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css "opacity", 0
return
)
$(".markdown-selector").click (e) ->
e.preventDefault()
$(".div-dropzone").click()
return
return
\ No newline at end of file
......@@ -10,6 +10,7 @@
*= require_self
*= require nprogress
*= require nprogress-bootstrap
*= require dropzone/basic
*/
@import "main/*";
......
......@@ -5,10 +5,19 @@
.js-details-container.open .content { display: block; }
.js-details-container.open .content.hide { display: none; }
// Toggler
//--------
.js-toggler-container .turn-on { display: inherit; }
.write-preview-btn .turn-on { display: inherit; }
.write-preview-btn .turn-off { display: none; }
.js-toggler-container .turn-off { display: none; }
.js-toggler-container.on .turn-on { display: none; }
.js-toggler-container.on .turn-off { display: inherit; }
.js-toggler-container.on ~ .note-form-actions {
.write-preview-btn .turn-on { display: none; }
}
.js-toggler-container.on ~ .note-form-actions {
.write-preview-btn .turn-off { display: inherit; }
}
.div-dropzone-wrapper {
.div-dropzone {
position: relative;
padding: 0;
border: 0;
margin-bottom: 5px;
.div-dropzone-focus {
border-color: #66afe9 !important;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6) !important;
outline: 0 !important;
}
.div-dropzone-hover {
position: absolute;
top: 50%;
left: 50%;
margin-top: -0.5em;
margin-left: -0.6em;
opacity: 0;
font-size: 50px;
transition: opacity 200ms ease-in-out;
}
.div-dropzone-spinner {
position: absolute;
top: 100%;
left: 100%;
margin-top: -1.1em;
margin-left: -1.1em;
opacity: 0;
font-size: 30px;
transition: opacity 200ms ease-in-out;
}
.div-dropzone-icon {
display: block;
text-align: center;
font-size: inherit;
}
.dz-preview {
display: none;
}
}
.hint {
float: left;
padding: 0;
margin: 0;
}
}
.div-dropzone-alert {
margin-top: 5px;
margin-bottom: 0;
transition: opacity 200ms ease-in-out;
}
......@@ -272,21 +272,16 @@ ul.notes {
margin-bottom: 0;
}
.note_text_and_preview {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
bottom: -60px;
}
.note_preview {
background: #f5f5f5;
border: 1px solid #ddd;
@include border-radius(4px);
min-height: 80px;
padding: 4px 6px;
> p {
overflow-x: auto;
}
}
.note_text {
border: 1px solid #DDD;
......@@ -310,7 +305,6 @@ ul.notes {
float: none;
}
.common-note-form {
margin: 0;
background: #F9F9F9;
......@@ -318,7 +312,6 @@ ul.notes {
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
height: 45px;
......@@ -333,6 +326,18 @@ ul.notes {
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
}
}
}
.note-edit-form {
......@@ -367,3 +372,8 @@ ul.notes {
.parallel-comment {
padding: 6px;
}
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
}
......@@ -14,4 +14,3 @@ def download
end
end
end
......@@ -69,7 +69,9 @@ def create
render :new
end
end
format.js
format.js do |format|
@link = @issue.attachment.url.to_js
end
end
end
......
......@@ -162,8 +162,28 @@ def unarchive
end
end
def upload_image
uploader = FileUploader.new('uploads', upload_path, accepted_images)
alt = params['markdown_img'].original_filename
uploader.store!(params['markdown_img'])
link = { 'alt' => File.basename(alt, '.*'),
'url' => File.join(root_url, uploader.url) }
respond_to do |format|
format.json { render json: { link: link } }
end
end
private
def upload_path
base_dir = FileUploader.generate_dir
File.join(repository.path_with_namespace, base_dir)
end
def accepted_images
%w(png jpg jpeg gif)
end
def set_title
@title = 'New Project'
end
......
......@@ -15,8 +15,12 @@
# milestone_id :integer
# state :string(255)
# iid :integer
# attachment :string(255)
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Issue < ActiveRecord::Base
include Issuable
include InternalId
......
# encoding: utf-8
class FileUploader < CarrierWave::Uploader::Base
storage :file
def initialize(base_dir, path = '', allowed_extensions = nil)
@base_dir = base_dir
@path = path
@allowed_extensions = allowed_extensions
end
def base_dir
@base_dir
end
def store_dir
File.join(@base_dir, @path)
end
def cache_dir
File.join(@base_dir, 'tmp', @path)
end
def extension_white_list
@allowed_extensions
end
def store!(file)
file.original_filename = self.class.generate_filename(file)
super
end
def self.generate_filename(file)
original_filename = File.basename(file.original_filename, '.*')
extension = File.extname(file.original_filename)
new_filename = Digest::MD5.hexdigest(original_filename) + extension
end
def self.generate_dir
SecureRandom.hex(5)
end
end
......@@ -10,3 +10,4 @@
.container
.content= yield
= yield :embedded_scripts
\ No newline at end of file
......@@ -14,3 +14,4 @@
.container
.content= yield
= yield :embedded_scripts
\ No newline at end of file
......@@ -5,6 +5,7 @@
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
= form_for [@project, @issue], html: { class: 'form-horizontal issue-form' } do |f|
-if @issue.errors.any?
.alert.alert-danger
......@@ -19,8 +20,12 @@
.form-group
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
= f.text_area :description, class: "form-control js-gfm-input", rows: 14
%p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
= f.text_area :description, class: 'form-control js-gfm-input markdown-area', rows: 14
.col-sm-12.hint
.pull-left Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.clearfix
.error-alert
%hr
.form-group
.issue-assignee
......@@ -57,9 +62,6 @@
- cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue)
= link_to "Cancel", cancel_path, class: 'btn btn-cancel'
:javascript
$("#issue_label_list")
.bind( "keydown", function( event ) {
......@@ -94,3 +96,5 @@
$('#issue_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
window.project_image_path_upload = "#{upload_image_project_path @project}";
......@@ -73,4 +73,4 @@
= label.name
&nbsp;
.voting_notes#notes= render "projects/notes/notes_with_form"
.voting_notes#notes= render "projects/notes/notes_with_form"
\ No newline at end of file
......@@ -22,8 +22,12 @@
.form-group
= f.label :description, "Description", class: 'control-label'
.col-sm-10
= f.text_area :description, class: "form-control js-gfm-input", rows: 14
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
= f.text_area :description, class: "form-control js-gfm-input markdown-area", rows: 14
.col-sm-12.hint
.pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.clearfix
.error-alert
%hr
.form-group
.issue-assignee
......@@ -98,3 +102,5 @@
return false;
}
});
window.project_image_path_upload = "#{upload_image_project_path @project}";
......@@ -23,8 +23,12 @@
.form-group
.light
= f.label :description, "Description"
= f.text_area :description, class: "form-control js-gfm-input", rows: 10
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
= f.text_area :description, class: "form-control js-gfm-input markdown-area", rows: 10
.col-sm-12.hint
.pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.clearfix
.error-alert
.form-group
.issue-assignee
= f.label :assignee_id do
......@@ -80,3 +84,5 @@
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
window.project_image_path_upload = "#{upload_image_project_path @project}";
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment