From c18e2778b2f1a4a2c3fb8e0f79c400456645e036 Mon Sep 17 00:00:00 2001 From: Olaf Mandel Date: Tue, 20 May 2014 15:22:23 +0200 Subject: [PATCH 01/49] Fix avatar URLs in JS-lists for relative_url_root In user lists created when entering a (partial) user name into a field, the URL to the user avatar was invalid if running with relative_url_root. This patch is the result of: sed -i 's/\(= *\)\(user\.avatar_url\)/\1gon.relative_url_root + \2/' \ app/assets/javascripts/*.coffee --- app/assets/javascripts/project_users_select.js.coffee | 2 +- app/assets/javascripts/users_select.js.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee index b0e39610feb31..a7fec96040681 100644 --- a/app/assets/javascripts/project_users_select.js.coffee +++ b/app/assets/javascripts/project_users_select.js.coffee @@ -37,7 +37,7 @@ projectUserFormatResult: (user) -> if user.avatar_url - avatar = user.avatar_url + avatar = gon.relative_url_root + user.avatar_url else if gon.gravatar_enabled avatar = gon.gravatar_url avatar = avatar.replace('%{hash}', md5(user.email)) diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index ce9a505b1e3a6..cab9f6271eba8 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -1,7 +1,7 @@ $ -> userFormatResult = (user) -> if user.avatar_url - avatar = user.avatar_url + avatar = gon.relative_url_root + user.avatar_url else if gon.gravatar_enabled avatar = gon.gravatar_url avatar = avatar.replace('%{hash}', md5(user.email)) From ac46130748c31f3052f7ac325f897637e7b326f5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 20 May 2014 16:36:51 +0300 Subject: [PATCH 02/49] Allow set group avatar when create group. Not only edit page Signed-off-by: Dmitriy Zaporozhets --- app/views/groups/new.html.haml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 955a107e54270..ebf5e8571aa0b 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -13,6 +13,17 @@ .col-sm-10 = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4 + .form-group.group-description-holder + = f.label :avatar, "Group avatar", class: 'control-label' + .col-sm-10 + %a.choose-btn.btn.btn-small.js-choose-group-avatar-button + %i.icon-paper-clip + %span Choose File ... +   + %span.file_name.js-avatar-filename File name... + = f.file_field :avatar, class: "js-group-avatar-input hidden" + .light The maximum file size allowed is 100KB. + .form-group .col-sm-2 .col-sm-10 From b08fe47fe8977905252c73653cece86359922e29 Mon Sep 17 00:00:00 2001 From: Alan Kehoe Date: Tue, 20 May 2014 22:03:34 +0100 Subject: [PATCH 03/49] Update message on the group dashboard page --- app/views/groups/show.html.haml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 0c62772a5c946..17475288a874c 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -5,7 +5,10 @@ = link_to dashboard_path, class: 'btn btn-tiny' do ← To dashboard   - %span.cgray You will only see events from projects in this group + %span.cgray + Currently you are only seeing events from the + = @group.name + group %hr = render 'shared/event_filter' - if @events.any? From d1980adf93e417850fcd3bac8caed1f2c9da7fb4 Mon Sep 17 00:00:00 2001 From: Marc Radulescu Date: Thu, 22 May 2014 10:35:01 +0200 Subject: [PATCH 04/49] typos fixed --- app/views/groups/_projects.html.haml | 2 +- app/views/groups/edit.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index bd4e3156af09b..41f8cb9da40bd 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -8,7 +8,7 @@ New project %ul.well-list - if projects.blank? - .nothing-here-block This groups has no projects yet + .nothing-here-block This group has no projects yet - projects.each do |project| %li.project-row = link_to project_path(project), class: dom_class(project) do diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 500c37ab71d41..2a5614cff6c39 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -45,7 +45,7 @@ - if @group.avatar? You can change your group avatar here - else - You can upload an group avatar here + You can upload a group avatar here %a.choose-btn.btn.btn-small.js-choose-group-avatar-button %i.icon-paper-clip %span Choose File ... From b019c06195cf3ca3e097f57c53cbd863165fa077 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 22 May 2014 11:01:01 +0200 Subject: [PATCH 05/49] Required shell version is now 1.9.4. --- lib/tasks/gitlab/check.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index bf015a1fe1661..0387795fa4846 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -779,7 +779,7 @@ namespace :gitlab do end def check_gitlab_shell - required_version = Gitlab::VersionInfo.new(1, 9, 3) + required_version = Gitlab::VersionInfo.new(1, 9, 4) current_version = Gitlab::VersionInfo.parse(gitlab_shell_version) print "GitLab Shell version >= #{required_version} ? ... " From 38d0e001eac2026b8526fc7098bd147df4fc522d Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 22 May 2014 11:09:00 +0200 Subject: [PATCH 06/49] Version to 6.9.0 --- CHANGELOG | 1 + VERSION | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 2f7874ba724f9..aaf1879dcceef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ v 6.9.0 - Accept merge request via API (sponsored by O'Reilly Media) - Add more access checks during API calls - Block SSH access for 'disabled' Active Directory users + - Labels for merge requests (Drew Blessing) v 6.8.0 - Ability to at mention users that are participating in issue and merge req. discussion diff --git a/VERSION b/VERSION index fed1b9285ff85..97f5781528147 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.9.0.rc1 +6.9.0 From 1ad174fbcdb7c14eb8fbe9820e1152383f41a89e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 May 2014 12:25:44 +0300 Subject: [PATCH 07/49] Improve edit page UI/UX Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/sections/diff.scss | 5 ++++ .../projects/edit_tree/preview.html.haml | 2 +- app/views/projects/edit_tree/show.html.haml | 23 +++++++++++-------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss index af44654d5da96..24d997c9fcafc 100644 --- a/app/assets/stylesheets/sections/diff.scss +++ b/app/assets/stylesheets/sections/diff.scss @@ -330,3 +330,8 @@ } } } + +.file-content .diff-file { + margin: 0; + border: none; +} diff --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/edit_tree/preview.html.haml index fc6d3bfbc2413..340f68cc05c4f 100644 --- a/app/views/projects/edit_tree/preview.html.haml +++ b/app/views/projects/edit_tree/preview.html.haml @@ -23,4 +23,4 @@ %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) - else - %p.nothing_here_message No changes. + .nothing-here-block No changes. diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml index 93037ef958560..32a6d4ef36ed9 100644 --- a/app/views/projects/edit_tree/show.html.haml +++ b/app/views/projects/edit_tree/show.html.haml @@ -1,23 +1,26 @@ -%h3.page-title Edit mode .file-editor + %ul.nav.nav-tabs.js-edit-mode + %li.active + = link_to 'Edit', '#editor' + %li + = link_to editing_preview_title(@blob.name), '#preview', 'data-preview-url' => preview_project_edit_tree_path(@project, @id) + = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do .file-holder.file .file-title - .btn-group.js-edit-mode.left-options - = link_to 'Edit', '#editor', class: 'active hover btn btn-tiny' - = link_to editing_preview_title(@blob.name), '#preview', class: 'btn btn-tiny', 'data-preview-url' => preview_project_edit_tree_path(@project, @id) %i.icon-file %span.file_name + %span.monospace.light #{@ref}: = @path - %small - on - %strong= @ref %span.options .btn-group.tree-btn-group = link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message } .file-content.code %pre.js-edit-mode-pane#editor= @blob.data .js-edit-mode-pane#preview.hide + %center + %h2 + %i.icon-spinner.icon-spin .form-group.commit_message-group = label_tag 'commit_message', class: "control-label" do @@ -61,14 +64,14 @@ paneId = currentLink.attr('href'), currentPane = editModePanes.filter(paneId); - editModeLinks.removeClass('active hover'); - currentLink.addClass('active hover'); + editModeLinks.parent().removeClass('active hover'); + currentLink.parent().addClass('active hover'); editModePanes.hide(); if (paneId == '#preview') { + currentPane.fadeIn(200); $.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) { currentPane.empty().append(response); - currentPane.fadeIn(200); }) } else { currentPane.fadeIn(200); From 3296fb9f3a30fab41bac61b2afbe689cfcbbd15f Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 22 May 2014 16:27:32 +0200 Subject: [PATCH 08/49] Add missing lines in update guide. --- doc/update/6.8-to-6.9.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/update/6.8-to-6.9.md b/doc/update/6.8-to-6.9.md index a5e644b8a0789..4a3e45ae5eeb6 100644 --- a/doc/update/6.8-to-6.9.md +++ b/doc/update/6.8-to-6.9.md @@ -52,6 +52,12 @@ sudo -u git -H bundle install --without development test postgres --deployment # PostgreSQL installations (note: the line below states '--without ... mysql') sudo -u git -H bundle install --without development test mysql --deployment + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` ### 5. Update config files From 74079fe2369c20767cb6c639eec1886c9822fefc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 May 2014 18:19:50 +0300 Subject: [PATCH 09/49] Fix scroll to highlighted line. Replace scrollTO js file with gem Signed-off-by: Dmitriy Zaporozhets --- Gemfile | 1 + Gemfile.lock | 3 + app/assets/javascripts/application.js.coffee | 2 +- app/assets/javascripts/blob.js.coffee | 2 +- vendor/assets/javascripts/jquery.scrollto.js | 225 ------------------- 5 files changed, 6 insertions(+), 227 deletions(-) delete mode 100644 vendor/assets/javascripts/jquery.scrollto.js diff --git a/Gemfile b/Gemfile index 7ff7515143c7c..f7e3fe7b6dd14 100644 --- a/Gemfile +++ b/Gemfile @@ -163,6 +163,7 @@ gem 'select2-rails' gem 'jquery-atwho-rails', "~> 0.3.3" gem "jquery-rails" gem "jquery-ui-rails" +gem "jquery-scrollto-rails" gem "raphael-rails", "~> 2.1.2" gem 'bootstrap-sass', '~> 3.0' gem "font-awesome-rails", '~> 3.2' diff --git a/Gemfile.lock b/Gemfile.lock index f5f31105e184e..86c752505bdb6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -250,6 +250,8 @@ GEM jquery-rails (3.1.0) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) + jquery-scrollto-rails (1.4.3) + railties (> 3.1, < 5.0) jquery-turbolinks (2.0.1) railties (>= 3.1.0) turbolinks @@ -607,6 +609,7 @@ DEPENDENCIES jasmine (= 2.0.0.rc5) jquery-atwho-rails (~> 0.3.3) jquery-rails + jquery-scrollto-rails jquery-turbolinks jquery-ui-rails kaminari (~> 0.15.1) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index a22ff6dec3197..587e51a7a8388 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -13,7 +13,7 @@ #= require jquery.history #= require jquery.waitforimages #= require jquery.atwho -#= require jquery.scrollto +#= require jquery.scrollTo #= require jquery.blockUI #= require turbolinks #= require jquery.turbolinks diff --git a/app/assets/javascripts/blob.js.coffee b/app/assets/javascripts/blob.js.coffee index 584f6faea161c..9db919e5a62c9 100644 --- a/app/assets/javascripts/blob.js.coffee +++ b/app/assets/javascripts/blob.js.coffee @@ -26,7 +26,7 @@ class BlobView unless isNaN first_line $("#tree-content-holder .highlight .line").removeClass("hll") $("#LC#{line}").addClass("hll") for line in [first_line..last_line] - $("#L#{first_line}").ScrollTo() unless e? + $.scrollTo("#L#{first_line}") unless e? # parse selected lines from hash # always return first and last line (initialized to NaN) diff --git a/vendor/assets/javascripts/jquery.scrollto.js b/vendor/assets/javascripts/jquery.scrollto.js deleted file mode 100644 index 7f10b7f108254..0000000000000 --- a/vendor/assets/javascripts/jquery.scrollto.js +++ /dev/null @@ -1,225 +0,0 @@ -/** - * @depends jquery - * @name jquery.scrollto - * @package jquery-scrollto {@link http://balupton.com/projects/jquery-scrollto} - */ - -/** - * jQuery Aliaser - */ -(function(window,undefined){ - // Prepare - var jQuery, $, ScrollTo; - jQuery = $ = window.jQuery; - - /** - * jQuery ScrollTo (balupton edition) - * @version 1.2.0 - * @date July 9, 2012 - * @since 0.1.0, August 27, 2010 - * @package jquery-scrollto {@link http://balupton.com/projects/jquery-scrollto} - * @author Benjamin "balupton" Lupton {@link http://balupton.com} - * @copyright (c) 2010 Benjamin Arthur Lupton {@link http://balupton.com} - * @license MIT License {@link http://creativecommons.org/licenses/MIT/} - */ - ScrollTo = $.ScrollTo = $.ScrollTo || { - /** - * The Default Configuration - */ - config: { - duration: 400, - easing: 'swing', - callback: undefined, - durationMode: 'each', - offsetTop: 0, - offsetLeft: 0 - }, - - /** - * Configure ScrollTo - */ - configure: function(options){ - // Apply Options to Config - $.extend(ScrollTo.config, options||{}); - - // Chain - return this; - }, - - /** - * Perform the Scroll Animation for the Collections - * We use $inline here, so we can determine the actual offset start for each overflow:scroll item - * Each collection is for each overflow:scroll item - */ - scroll: function(collections, config){ - // Prepare - var collection, $container, container, $target, $inline, position, - containerScrollTop, containerScrollLeft, - containerScrollTopEnd, containerScrollLeftEnd, - startOffsetTop, targetOffsetTop, targetOffsetTopAdjusted, - startOffsetLeft, targetOffsetLeft, targetOffsetLeftAdjusted, - scrollOptions, - callback; - - // Determine the Scroll - collection = collections.pop(); - $container = collection.$container; - container = $container.get(0); - $target = collection.$target; - - // Prepare the Inline Element of the Container - $inline = $('').css({ - 'position': 'absolute', - 'top': '0px', - 'left': '0px' - }); - position = $container.css('position'); - - // Insert the Inline Element of the Container - $container.css('position','relative'); - $inline.appendTo($container); - - // Determine the top offset - startOffsetTop = $inline.offset().top; - targetOffsetTop = $target.offset().top; - targetOffsetTopAdjusted = targetOffsetTop - startOffsetTop - parseInt(config.offsetTop,10); - - // Determine the left offset - startOffsetLeft = $inline.offset().left; - targetOffsetLeft = $target.offset().left; - targetOffsetLeftAdjusted = targetOffsetLeft - startOffsetLeft - parseInt(config.offsetLeft,10); - - // Determine current scroll positions - containerScrollTop = container.scrollTop; - containerScrollLeft = container.scrollLeft; - - // Reset the Inline Element of the Container - $inline.remove(); - $container.css('position',position); - - // Prepare the scroll options - scrollOptions = {}; - - // Prepare the callback - callback = function(event){ - // Check - if ( collections.length === 0 ) { - // Callback - if ( typeof config.callback === 'function' ) { - config.callback.apply(this,[event]); - } - } - else { - // Recurse - ScrollTo.scroll(collections,config); - } - // Return true - return true; - }; - - // Handle if we only want to scroll if we are outside the viewport - if ( config.onlyIfOutside ) { - // Determine current scroll positions - containerScrollTopEnd = containerScrollTop + $container.height(); - containerScrollLeftEnd = containerScrollLeft + $container.width(); - - // Check if we are in the range of the visible area of the container - if ( containerScrollTop < targetOffsetTopAdjusted && targetOffsetTopAdjusted < containerScrollTopEnd ) { - targetOffsetTopAdjusted = containerScrollTop; - } - if ( containerScrollLeft < targetOffsetLeftAdjusted && targetOffsetLeftAdjusted < containerScrollLeftEnd ) { - targetOffsetLeftAdjusted = containerScrollLeft; - } - } - - // Determine the scroll options - if ( targetOffsetTopAdjusted !== containerScrollTop ) { - scrollOptions.scrollTop = targetOffsetTopAdjusted; - } - if ( targetOffsetLeftAdjusted !== containerScrollLeft ) { - scrollOptions.scrollLeft = targetOffsetLeftAdjusted; - } - - // Perform the scroll - if ( $.browser.safari && container === document.body ) { - window.scrollTo(scrollOptions.scrollLeft, scrollOptions.scrollTop); - callback(); - } - else if ( scrollOptions.scrollTop || scrollOptions.scrollLeft ) { - $container.animate(scrollOptions, config.duration, config.easing, callback); - } - else { - callback(); - } - - // Return true - return true; - }, - - /** - * ScrollTo the Element using the Options - */ - fn: function(options){ - // Prepare - var collections, config, $container, container; - collections = []; - - // Prepare - var $target = $(this); - if ( $target.length === 0 ) { - // Chain - return this; - } - - // Handle Options - config = $.extend({},ScrollTo.config,options); - - // Fetch - $container = $target.parent(); - container = $container.get(0); - - // Cycle through the containers - while ( ($container.length === 1) && (container !== document.body) && (container !== document) ) { - // Check Container for scroll differences - var scrollTop, scrollLeft; - scrollTop = $container.css('overflow-y') !== 'visible' && container.scrollHeight !== container.clientHeight; - scrollLeft = $container.css('overflow-x') !== 'visible' && container.scrollWidth !== container.clientWidth; - if ( scrollTop || scrollLeft ) { - // Push the Collection - collections.push({ - '$container': $container, - '$target': $target - }); - // Update the Target - $target = $container; - } - // Update the Container - $container = $container.parent(); - container = $container.get(0); - } - - // Add the final collection - collections.push({ - '$container': $( - ($.browser.msie || $.browser.mozilla) ? 'html' : 'body' - ), - '$target': $target - }); - - // Adjust the Config - if ( config.durationMode === 'all' ) { - config.duration /= collections.length; - } - - // Handle - ScrollTo.scroll(collections,config); - - // Chain - return this; - } - }; - - // Apply our jQuery Prototype Function - $.fn.ScrollTo = $.ScrollTo.fn; - -})(window); From babfc068ae2747ce578a90c7cb22fd88f03349bd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 May 2014 20:34:03 +0300 Subject: [PATCH 10/49] Lets start 7.0 development Signed-off-by: Dmitriy Zaporozhets --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 97f5781528147..2e45d00c58279 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.9.0 +7.0.0.pre From 8bf7b62e8169fc1e2b9cf66edd9a66232f285270 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 May 2014 20:40:25 +0300 Subject: [PATCH 11/49] First entries for 7.0 CHANGELOG Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index aaf1879dcceef..ca0b96b840223 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +v 7.0.0 + - The CPU no longer overheats when you hold down the spacebar + - Improve edit file UI + - Add ability to upload group avatar when create + v 6.9.0 - Store Rails cache data in the Redis `cache:gitlab` namespace - Adjust MySQL limits for existing installations From b192aa5694b8df233124c4be58802a9100b0c321 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 May 2014 22:34:34 +0300 Subject: [PATCH 12/49] Fix tests Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index ebb8a90c63003..e356d09a270e6 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -190,6 +190,6 @@ def participants_in(type, id) end def sorted(users) - users.uniq.sort_by(&:username).map { |user| { username: user.username, name: user.name } } + users.uniq.compact.sort_by(&:username).map { |user| { username: user.username, name: user.name } } end end From ebe2a34a6b81f468187a8049ddae8242b00d1ebe Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 May 2014 22:44:32 +0300 Subject: [PATCH 13/49] Add missing feature flag to mr notes spec Signed-off-by: Dmitriy Zaporozhets --- spec/features/notes_on_merge_requests_spec.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index cfb6deb1834eb..45aebf128c299 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -132,7 +132,7 @@ end end -describe "On a merge request diff", js: true do +describe "On a merge request diff", js: true, feature: true do let(:merge_request) { create(:merge_request, :with_diffs, :simple) } let(:project) { merge_request.source_project } @@ -210,9 +210,3 @@ end end end - -describe "On merge request discussion", js: true do - describe "with merge request diff note" - describe "with commit note" - describe "with commit diff note" -end From 696b9903f08011e37811dc8b8ff4f7da77201d13 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 May 2014 09:47:58 +0300 Subject: [PATCH 14/49] Fix spinach tests Signed-off-by: Dmitriy Zaporozhets --- features/steps/group/group.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/group/group.rb b/features/steps/group/group.rb index 820d0ef2a1fa4..5e0c71581f1cc 100644 --- a/features/steps/group/group.rb +++ b/features/steps/group/group.rb @@ -89,7 +89,7 @@ class Groups < Spinach::FeatureSteps Then 'I should see newly created group "Samurai"' do page.should have_content "Samurai" page.should have_content "Tokugawa Shogunate" - page.should have_content "You will only see events from projects in this group" + page.should have_content "Currently you are only seeing events from the" end And 'I change group "Owned" name to "new-name"' do From c417e2536ea2f2033fc1ac6fc250b24397d09039 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Wed, 14 May 2014 15:51:43 +0800 Subject: [PATCH 15/49] Implements drag and drop upload in creating issues --- Gemfile | 3 + Gemfile.lock | 3 + app/assets/javascripts/application.js.coffee | 1 + app/assets/stylesheets/application.scss | 1 + app/assets/stylesheets/sections/issues.scss | 55 +++++++++++++++ app/controllers/files_controller.rb | 1 - app/controllers/projects/issues_controller.rb | 24 +++++-- app/models/issue.rb | 7 +- app/services/issues/create_service.rb | 2 - app/uploaders/attachment_uploader.rb | 2 +- app/uploaders/file_uploader.rb | 27 +++++++ app/views/projects/issues/_form.html.haml | 66 ++++++++++++++++-- app/views/projects/issues/new.html.haml | 2 +- app/views/projects/issues/show.html.haml | 3 +- config/routes.rb | 4 +- db/schema.rb | 1 + features/support/env.rb | 55 --------------- public/favicon.ico | Bin 32988 -> 0 bytes 18 files changed, 184 insertions(+), 73 deletions(-) create mode 100644 app/uploaders/file_uploader.rb delete mode 100644 features/support/env.rb delete mode 100644 public/favicon.ico diff --git a/Gemfile b/Gemfile index f7e3fe7b6dd14..9775334a7c1ea 100644 --- a/Gemfile +++ b/Gemfile @@ -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 diff --git a/Gemfile.lock b/Gemfile.lock index 86c752505bdb6..3d4a673af1ca5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 587e51a7a8388..35e435546611b 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -29,6 +29,7 @@ #= require underscore #= require nprogress #= require nprogress-turbolinks +#= require dropzone #= require_tree . window.slugify = (text) -> diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index c53873f95a2af..0b372a87a111c 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,7 @@ *= require_self *= require nprogress *= require nprogress-bootstrap + *= require dropzone/basic */ @import "main/*"; diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index 02c9123178f1d..57d1618291d2f 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -168,3 +168,58 @@ form.edit-issue { } } } + +#div-dropzone { + position: relative; + + #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-error { + position: absolute; + top: 50%; + width: 100%; + background-color: #e74c3c; + margin-top: -1em; + opacity: 0; + font-size: 20px; + text-align: center; + 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 { + padding: 0; + border: 0; +} + +.div-dropzone-icon { + display: block; + text-align: center; + font-size: inherit; +} + +.div-dropzone-icon-error { + display: inline; + text-align: center; + font-size: inherit; +} diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index bf30de565ed09..7937454810d99 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -14,4 +14,3 @@ def download end end end - diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 4e7a716bfe411..a7eca4aab596c 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -60,7 +60,6 @@ def show def create @issue = Issues::CreateService.new(project, current_user, params[:issue]).execute - respond_to do |format| format.html do if @issue.valid? @@ -69,7 +68,11 @@ def create render :new end end - format.js + format.js do |format| + # issue = Issues.create() + + @link = @issue.attachment.url.to_js + end end end @@ -77,7 +80,7 @@ def update @issue = Issues::UpdateService.new(project, current_user, params[:issue]).execute(issue) respond_to do |format| - format.js + format.js format.html do if @issue.valid? redirect_to [@project, @issue] @@ -93,6 +96,20 @@ def bulk_update redirect_to :back, notice: "#{result[:count]} issues updated" end + def upload_image + @upload_path = File.join(project.namespace.path, @project.path) + @accepted_types = %w(png jpg jpeg gif) + uploader = FileUploader.new('uploads/' + @upload_path, @accepted_types) + links = [] + params['issue-imgs'].each do |img| + uploader.store!(img) + links << File.join(root_url, uploader.url) + end + respond_to do |format| + format.json { render json: { links: links } } + end + end + protected def issue @@ -128,7 +145,6 @@ def issues_filtered # def redirect_old issue = @project.issues.find_by(id: params[:id]) - if issue redirect_to project_issue_path(@project, issue) return diff --git a/app/models/issue.rb b/app/models/issue.rb index 16d51345e5a16..b4666edb19df6 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -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 @@ -30,7 +34,8 @@ class Issue < ActiveRecord::Base scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } attr_accessible :title, :assignee_id, :position, :description, - :milestone_id, :label_list, :state_event + :milestone_id, :label_list, :author_id_of_changes, + :state_event acts_as_taggable_on :labels diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index d6137833bb9d2..cbb9e4dfa09e4 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -3,14 +3,12 @@ class CreateService < Issues::BaseService def execute issue = project.issues.new(params) issue.author = current_user - if issue.save notification_service.new_issue(issue, current_user) event_service.open_issue(issue, current_user) issue.create_cross_references!(issue.project, current_user) execute_hooks(issue) end - issue end end diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb index b122b6c8658c6..9ed9754f03f8b 100644 --- a/app/uploaders/attachment_uploader.rb +++ b/app/uploaders/attachment_uploader.rb @@ -33,4 +33,4 @@ def file_storage? def reset_events_cache(file) model.reset_events_cache if model.is_a?(User) end -end +end \ No newline at end of file diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb new file mode 100644 index 0000000000000..98c0f94170069 --- /dev/null +++ b/app/uploaders/file_uploader.rb @@ -0,0 +1,27 @@ +# encoding: utf-8 +class FileUploader < CarrierWave::Uploader::Base + storage :file + + def initialize(path, allowed_extensions = nil) + @path = path + @allowed_extensions = allowed_extensions + end + + def store_dir + @path + end + + def extension_white_list + @allowed_extensions + end + + def store!(file) + generate_filename(file) + super + end + + def generate_filename(file) + new_filename = Digest::MD5.hexdigest(file.original_filename) + file.original_filename = new_filename + File.extname(file.original_filename) + end +end diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 84703229fe6be..3daad55f110d6 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -19,7 +19,16 @@ .form-group = f.label :description, 'Description', class: 'control-label' .col-sm-10 - = f.text_area :description, class: "form-control js-gfm-input", rows: 14 + .demo#div-dropzone + = f.text_area :description, class: 'form-control js-gfm-input', rows: 14 + #div-dropzone-hover + %i.icon-picture.div-dropzone-icon + #div-dropzone-error + %i.icon-remove.div-dropzone-icon-error + %div{style:'display:inline'} + Unsupported File Type + #div-dropzone-spinner + %i.icon-spinner.icon-spin.div-dropzone-icon %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. %hr .form-group @@ -57,9 +66,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 +100,55 @@ $('#issue_assignee_id').val("#{current_user.id}").trigger("change"); e.preventDefault(); }); + + function formatLinks(links) { + var md_links = []; + links.forEach(function (str) { + md_links.push('![Alt text]('+ str + ')'); + }) + return md_links; + } + + $('#div-dropzone').dropzone({ + url: "#{upload_image_project_issues_path}", + dictDefaultMessage: "", + clickable: false, + paramName: "issue-imgs", + maxFilesize: 2, + uploadMultiple: true, + acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png", + headers: { "X-CSRF-Token": $('meta[name="csrf-token"]').attr('content')}, + + success: function(header,response){ + $("#issue_description").val($("#issue_description").val() + formatLinks(response.links).join("\n") + "\n"); + }, + + error: function(temp, errorMessage) { + $('#div-dropzone-error').css('opacity', 0.7); + $('#div-dropzone-error').delay(500).fadeTo("slow", 0); + }, + + sending: function() { + $('#div-dropzone-spinner').css('opacity', 0.7); + }, + + complete: function() { + $('#div-dropzone-spinner').css('opacity',0); + } + }); + + $('#div-dropzone').on('dragenter',function(){ + console.log($(this).children('textarea').attr('id')); + $(this).children('textarea').focus(); + $('#div-dropzone-hover').css('opacity',0.7); + }); + + $('body').on('dragover',function(){ + $('#div-dropzone').children('textarea').blur(); + $('#div-dropzone-hover').css('opacity',0); + }); + + $('#div-dropzone').on('drop',function(){ + $('.dz-preview').remove(); + $('#div-dropzone-hover').css('opacity',0); + }); diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index b1bc3ba0eba68..de8a34167ef1e 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1 +1 @@ -= render "form" += render "form" \ No newline at end of file diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index b6d3a8edf4da9..fee5c165118e9 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -49,6 +49,7 @@ .wiki = preserve do = markdown @issue.description + .context %cite.cgray = render partial: 'issue_context', locals: { issue: @issue } @@ -73,4 +74,4 @@ = label.name   -.voting_notes#notes= render "projects/notes/notes_with_form" +.voting_notes#notes= render "projects/notes/notes_with_form" \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 7641fe430889b..40d695176c1d9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,7 +67,6 @@ # Attachments serving # get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ } - # # Admin Area # @@ -136,8 +135,6 @@ match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ }, via: :get - - # # Dashboard Area # @@ -303,6 +300,7 @@ resources :issues, constraints: {id: /\d+/}, except: [:destroy] do collection do post :bulk_update + post :upload_image end end diff --git a/db/schema.rb b/db/schema.rb index 93837337afc31..4d3a6b8219478 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -87,6 +87,7 @@ t.integer "milestone_id" t.string "state" t.integer "iid" + t.string "attachment" end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree diff --git a/features/support/env.rb b/features/support/env.rb deleted file mode 100644 index a5b297775db5b..0000000000000 --- a/features/support/env.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'simplecov' unless ENV['CI'] - -if ENV['TRAVIS'] - require 'coveralls' - Coveralls.wear! -end - -ENV['RAILS_ENV'] = 'test' -require './config/environment' - -require 'rspec' -require 'rspec/expectations' -require 'database_cleaner' -require 'spinach/capybara' -require 'sidekiq/testing/inline' - - -%w(valid_commit valid_commit_with_alt_email big_commits select2_helper test_env).each do |f| - require Rails.root.join('spec', 'support', f) -end - -Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file} - -WebMock.allow_net_connect! -# -# JS driver -# -require 'capybara/poltergeist' -Capybara.javascript_driver = :poltergeist -Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new(app, :js_errors => false, :timeout => 60) -end -Spinach.hooks.on_tag("javascript") do - ::Capybara.current_driver = ::Capybara.javascript_driver -end -Capybara.default_wait_time = 60 -Capybara.ignore_hidden_elements = false - -DatabaseCleaner.strategy = :truncation - -Spinach.hooks.before_scenario do - TestEnv.setup_stubs - DatabaseCleaner.start -end - -Spinach.hooks.after_scenario do - DatabaseCleaner.clean -end - -Spinach.hooks.before_run do - TestEnv.init(mailer: false, init_repos: true, repos: false) - RSpec::Mocks::setup self - - include FactoryGirl::Syntax::Methods -end diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index bfb74960c480e6cb14f1d38437303af6b375ccaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32988 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 From 50ec7c15668350b015021fad74aa77950ce30a79 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Thu, 15 May 2014 13:33:12 +0800 Subject: [PATCH 16/49] Reformats alternative. --- app/controllers/projects/issues_controller.rb | 5 +++-- app/uploaders/file_uploader.rb | 2 ++ app/views/projects/issues/_form.html.haml | 3 +-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index a7eca4aab596c..bebd728a0b6c4 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -102,9 +102,10 @@ def upload_image uploader = FileUploader.new('uploads/' + @upload_path, @accepted_types) links = [] params['issue-imgs'].each do |img| - uploader.store!(img) - links << File.join(root_url, uploader.url) + alt = uploader.store!(img) + links << {'alt' => File.basename(alt,'.*'), 'url' => File.join(root_url, uploader.url)} end + respond_to do |format| format.json { render json: { links: links } } end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 98c0f94170069..dabbe521e3c3a 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -16,8 +16,10 @@ def extension_white_list end def store!(file) + original_filename = file.original_filename generate_filename(file) super + original_filename end def generate_filename(file) diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 3daad55f110d6..2af3b67d9e0a0 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -104,7 +104,7 @@ function formatLinks(links) { var md_links = []; links.forEach(function (str) { - md_links.push('![Alt text]('+ str + ')'); + md_links.push('![' + str.alt + '](' + str.url + ')'); }) return md_links; } @@ -138,7 +138,6 @@ }); $('#div-dropzone').on('dragenter',function(){ - console.log($(this).children('textarea').attr('id')); $(this).children('textarea').focus(); $('#div-dropzone-hover').css('opacity',0.7); }); From 69de5303e2512b07ead376a3eeb8d18334494d49 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Thu, 15 May 2014 15:58:54 +0800 Subject: [PATCH 17/49] Generalizes markdown area. --- app/assets/stylesheets/sections/issues.scss | 10 ++-- app/views/projects/issues/_form.html.haml | 64 +------------------- app/views/projects/notes/_form.html.haml | 4 +- app/views/projects/wikis/_form.html.haml | 4 +- app/views/shared/_markdown_area.html.haml | 65 +++++++++++++++++++++ 5 files changed, 79 insertions(+), 68 deletions(-) create mode 100644 app/views/shared/_markdown_area.html.haml diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index 57d1618291d2f..ed1abbc16dabe 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -169,10 +169,10 @@ form.edit-issue { } } -#div-dropzone { +.div-dropzone { position: relative; - #div-dropzone-hover { + .div-dropzone-hover { position: absolute; top: 50%; left: 50%; @@ -183,7 +183,7 @@ form.edit-issue { transition: opacity 200ms ease-in-out; } - #div-dropzone-error { + .div-dropzone-error { position: absolute; top: 50%; width: 100%; @@ -195,7 +195,7 @@ form.edit-issue { transition: opacity 200ms ease-in-out; } - #div-dropzone-spinner { + .div-dropzone-spinner { position: absolute; top: 100%; left: 100%; @@ -207,7 +207,7 @@ form.edit-issue { } } -#div-dropzone { +.div-dropzone { padding: 0; border: 0; } diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 2af3b67d9e0a0..176c2c987f3ce 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -19,16 +19,7 @@ .form-group = f.label :description, 'Description', class: 'control-label' .col-sm-10 - .demo#div-dropzone - = f.text_area :description, class: 'form-control js-gfm-input', rows: 14 - #div-dropzone-hover - %i.icon-picture.div-dropzone-icon - #div-dropzone-error - %i.icon-remove.div-dropzone-icon-error - %div{style:'display:inline'} - Unsupported File Type - #div-dropzone-spinner - %i.icon-spinner.icon-spin.div-dropzone-icon + = f.text_area :description, class: 'form-control js-gfm-input markdown-area', rows: 14 %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. %hr .form-group @@ -66,6 +57,8 @@ - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue) = link_to "Cancel", cancel_path, class: 'btn btn-cancel' += render 'shared/markdown_area' + :javascript $("#issue_label_list") .bind( "keydown", function( event ) { @@ -100,54 +93,3 @@ $('#issue_assignee_id').val("#{current_user.id}").trigger("change"); e.preventDefault(); }); - - function formatLinks(links) { - var md_links = []; - links.forEach(function (str) { - md_links.push('![' + str.alt + '](' + str.url + ')'); - }) - return md_links; - } - - $('#div-dropzone').dropzone({ - url: "#{upload_image_project_issues_path}", - dictDefaultMessage: "", - clickable: false, - paramName: "issue-imgs", - maxFilesize: 2, - uploadMultiple: true, - acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png", - headers: { "X-CSRF-Token": $('meta[name="csrf-token"]').attr('content')}, - - success: function(header,response){ - $("#issue_description").val($("#issue_description").val() + formatLinks(response.links).join("\n") + "\n"); - }, - - error: function(temp, errorMessage) { - $('#div-dropzone-error').css('opacity', 0.7); - $('#div-dropzone-error').delay(500).fadeTo("slow", 0); - }, - - sending: function() { - $('#div-dropzone-spinner').css('opacity', 0.7); - }, - - complete: function() { - $('#div-dropzone-spinner').css('opacity',0); - } - }); - - $('#div-dropzone').on('dragenter',function(){ - $(this).children('textarea').focus(); - $('#div-dropzone-hover').css('opacity',0.7); - }); - - $('body').on('dragover',function(){ - $('#div-dropzone').children('textarea').blur(); - $('#div-dropzone-hover').css('opacity',0); - }); - - $('#div-dropzone').on('drop',function(){ - $('.dz-preview').remove(); - $('#div-dropzone-hover').css('opacity',0); - }); diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 3db551e993b07..80be7199a528b 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -13,7 +13,7 @@ %i.icon-edit Write - = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on' + = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on markdown-area' .note_preview.js-note-preview.turn-off .hint @@ -36,3 +36,5 @@ = f.file_field :attachment, class: "js-note-attachment-input hidden" .clearfix + += render 'shared/markdown_area' \ No newline at end of file diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 0c2e33f22820c..97cecdcb9acb3 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -23,7 +23,7 @@ .form-group = f.label :content, class: 'control-label' .col-sm-10 - = f.text_area :content, class: 'form-control js-gfm-input', rows: 18 + = f.text_area :content, class: 'form-control js-gfm-input markdown-area', rows: 18 .form-group = f.label :commit_message, class: 'control-label' @@ -36,3 +36,5 @@ - else = f.submit 'Create page', class: "btn-create btn" = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel" + += render('shared/markdown_area') diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml new file mode 100644 index 0000000000000..f2f2fb56f51f0 --- /dev/null +++ b/app/views/shared/_markdown_area.html.haml @@ -0,0 +1,65 @@ +:javascript + function formatLinks(links) { + var md_links = []; + links.forEach(function (str) { + md_links.push('![' + str.alt + '](' + str.url + ')'); + }) + return md_links; + } + $(document).ready(function() { + $('textarea.markdown-area').wrap('
'); + + $('.div-dropzone').append('
'); + + $('.div-dropzone').append('
Unsupported File Type
'); + + $('.div-dropzone').append('
'); + + dz = $('.div-dropzone').dropzone({ + url: "#{upload_image_project_issues_path}", + dictDefaultMessage: "", + clickable: false, + paramName: "issue-imgs", + maxFilesize: 2, + uploadMultiple: true, + acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png", + headers: { "X-CSRF-Token": $('meta[name="csrf-token"]').attr('content')}, + + success: function(header,response){ + var child = $(dz[0]).children('textarea'); + $(child).val($(child).val() + formatLinks(response.links).join("\n") + "\n"); + }, + + error: function(temp, errorMessage) { + $('.div-dropzone-error').css('opacity', 0.7); + $('.div-dropzone-error').delay(500).fadeTo("slow", 0); + }, + + sending: function() { + $('.div-dropzone-spinner').css('opacity', 0.7); + }, + + complete: function() { + $('.div-dropzone-spinner').css('opacity',0); + } + }); + + $('.div-dropzone').on('dragenter',function(){ + $(this).children('textarea').focus(); + $('.div-dropzone-hover').css('opacity',0.7); + }); + + $('body').on('dragover',function(){ + $('.div-dropzone').children('textarea').blur(); + $('.div-dropzone-hover').css('opacity',0); + }); + + $('.div-dropzone').on('drop',function(){ + $('.dz-preview').remove(); + $('.div-dropzone-hover').css('opacity',0); + }); + + }); + + + \ No newline at end of file From 451101cb762fbd18acb51b5150e59afa4492b858 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Mon, 19 May 2014 08:18:07 +0800 Subject: [PATCH 18/49] upload files using native uploader --- app/assets/stylesheets/sections/issues.scss | 5 +++ app/controllers/projects/issues_controller.rb | 15 +++++---- app/uploaders/attachment_uploader.rb | 2 +- app/uploaders/file_uploader.rb | 28 +++++++++++----- app/views/projects/issues/_form.html.haml | 1 + app/views/shared/_markdown_area.html.haml | 33 ++++++++++++------- 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index ed1abbc16dabe..f13ea2d095b27 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -223,3 +223,8 @@ form.edit-issue { text-align: center; font-size: inherit; } + +.hint { + display: inline; +} + diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index bebd728a0b6c4..e7e917a121faa 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -80,7 +80,7 @@ def update @issue = Issues::UpdateService.new(project, current_user, params[:issue]).execute(issue) respond_to do |format| - format.js + format.js format.html do if @issue.valid? redirect_to [@project, @issue] @@ -97,13 +97,16 @@ def bulk_update end def upload_image - @upload_path = File.join(project.namespace.path, @project.path) - @accepted_types = %w(png jpg jpeg gif) - uploader = FileUploader.new('uploads/' + @upload_path, @accepted_types) + @base_dir = FileUploader.generate_dir + upload_path = File.join(repository.path_with_namespace, 'issues', @base_dir) + accepted_types = %w(png jpg jpeg gif) + uploader = FileUploader.new('uploads', upload_path, accepted_types) links = [] params['issue-imgs'].each do |img| - alt = uploader.store!(img) - links << {'alt' => File.basename(alt,'.*'), 'url' => File.join(root_url, uploader.url)} + alt = img.original_filename + uploader.store!(img) + links << { 'alt' => File.basename(alt, '.*'), + 'url' => File.join(root_url, uploader.url) } end respond_to do |format| diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb index 9ed9754f03f8b..b122b6c8658c6 100644 --- a/app/uploaders/attachment_uploader.rb +++ b/app/uploaders/attachment_uploader.rb @@ -33,4 +33,4 @@ def file_storage? def reset_events_cache(file) model.reset_events_cache if model.is_a?(User) end -end \ No newline at end of file +end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index dabbe521e3c3a..9a907d45e5d6a 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -2,13 +2,22 @@ class FileUploader < CarrierWave::Uploader::Base storage :file - def initialize(path, allowed_extensions = nil) + 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 - @path + File.join(@base_dir, @path) + end + + def cache_dir + File.join(@base_dir, 'tmp', @path) end def extension_white_list @@ -16,14 +25,17 @@ def extension_white_list end def store!(file) - original_filename = file.original_filename - generate_filename(file) + file.original_filename = self.class.generate_filename(file) super - original_filename end - def generate_filename(file) - new_filename = Digest::MD5.hexdigest(file.original_filename) - file.original_filename = new_filename + File.extname(file.original_filename) + 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 diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 176c2c987f3ce..088d93c146c51 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -21,6 +21,7 @@ .col-sm-10 = f.text_area :description, class: 'form-control js-gfm-input markdown-area', rows: 14 %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + %p.hint.pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. %hr .form-group .issue-assignee diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index f2f2fb56f51f0..3975a615f3841 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -6,24 +6,32 @@ }) return md_links; } + + function deletePreviewLink() { + $('.dz-preview').ready(function() { + alert(1); + $(this).remove(); + }); + } + $(document).ready(function() { $('textarea.markdown-area').wrap('
'); $('.div-dropzone').append('
'); - $('.div-dropzone').append('
Unsupported File Type
'); + $('.div-dropzone').append('
Unsupported File Type
'); $('.div-dropzone').append('
'); dz = $('.div-dropzone').dropzone({ url: "#{upload_image_project_issues_path}", - dictDefaultMessage: "", - clickable: false, - paramName: "issue-imgs", - maxFilesize: 2, + dictDefaultMessage: '', + clickable: true, + paramName: 'issue-imgs', + maxFilesize: 10, uploadMultiple: true, - acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png", - headers: { "X-CSRF-Token": $('meta[name="csrf-token"]').attr('content')}, + acceptedFiles: 'image/jpg,image/jpeg,image/gif,image/png', + headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')}, success: function(header,response){ var child = $(dz[0]).children('textarea'); @@ -31,6 +39,7 @@ }, error: function(temp, errorMessage) { + $('.dropzone-error-message').text(errorMessage); $('.div-dropzone-error').css('opacity', 0.7); $('.div-dropzone-error').delay(500).fadeTo("slow", 0); }, @@ -55,11 +64,13 @@ }); $('.div-dropzone').on('drop',function(){ - $('.dz-preview').remove(); + deletePreviewLink(); $('.div-dropzone-hover').css('opacity',0); }); + $('.markdown-selector').click(function(e) { + e.preventDefault; + $('.div-dropzone').click(); + deletePreviewLink(); + }); }); - - - \ No newline at end of file From 58411e13663c7bbab11ba4e380dac1b6358310db Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Mon, 19 May 2014 09:45:01 +0800 Subject: [PATCH 19/49] Removes preview when uploading. --- app/views/shared/_markdown_area.html.haml | 24 ++++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 3975a615f3841..5803dbd15c144 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -7,13 +7,6 @@ return md_links; } - function deletePreviewLink() { - $('.dz-preview').ready(function() { - alert(1); - $(this).remove(); - }); - } - $(document).ready(function() { $('textarea.markdown-area').wrap('
'); @@ -45,7 +38,7 @@ }, sending: function() { - $('.div-dropzone-spinner').css('opacity', 0.7); + $('.div-dropzone-spinner').css('opacity', 0.7); }, complete: function() { @@ -53,24 +46,27 @@ } }); - $('.div-dropzone').on('dragenter',function(){ + $('.div-dropzone').on('dragenter',function() { $(this).children('textarea').focus(); $('.div-dropzone-hover').css('opacity',0.7); }); - $('body').on('dragover',function(){ + $('body').on('dragover',function() { $('.div-dropzone').children('textarea').blur(); $('.div-dropzone-hover').css('opacity',0); }); - $('.div-dropzone').on('drop',function(){ - deletePreviewLink(); + $('.div-dropzone').on('drop',function() { + $('.dz-preview').remove(); $('.div-dropzone-hover').css('opacity',0); }); $('.markdown-selector').click(function(e) { - e.preventDefault; + e.preventDefault(); $('.div-dropzone').click(); - deletePreviewLink(); + }); + + $('.div-dropzone').bind('DOMNodeInserted', function() { + $('.dz-preview').remove(); }); }); From 82d77fed7eb61a1f68ada6f628e6eb5deb371cc2 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Mon, 19 May 2014 09:47:02 +0800 Subject: [PATCH 20/49] Readds favicon.ico --- public/favicon.ico | Bin 0 -> 32988 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/favicon.ico diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bfb74960c480e6cb14f1d38437303af6b375ccaf GIT binary patch literal 32988 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 literal 0 HcmV?d00001 From 2705df05ac3c1b71bbc6ec482d831746ad4ebd94 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Mon, 19 May 2014 16:27:52 +0800 Subject: [PATCH 21/49] Implements clipboard feature. --- app/controllers/projects/issues_controller.rb | 13 ++-- app/views/projects/issues/_form.html.haml | 1 + app/views/shared/_markdown_area.html.haml | 77 ++++++++++++++++--- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index e7e917a121faa..0c984a124c995 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -101,16 +101,13 @@ def upload_image upload_path = File.join(repository.path_with_namespace, 'issues', @base_dir) accepted_types = %w(png jpg jpeg gif) uploader = FileUploader.new('uploads', upload_path, accepted_types) - links = [] - params['issue-imgs'].each do |img| - alt = img.original_filename - uploader.store!(img) - links << { 'alt' => File.basename(alt, '.*'), - 'url' => File.join(root_url, uploader.url) } - end + alt = params['issue-img'].original_filename + uploader.store!(params['issue-img']) + link = { 'alt' => File.basename(alt, '.*'), + 'url' => File.join(root_url, uploader.url) } respond_to do |format| - format.json { render json: { links: links } } + format.json { render json: { link: link } } end end diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 088d93c146c51..969301b540dff 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -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 #{link_to "guidelines for contribution", contribution_guide_url} to this repository.".html_safe + = form_for [@project, @issue], html: { class: 'form-horizontal issue-form' } do |f| -if @issue.errors.any? .alert.alert-danger diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 5803dbd15c144..39a1eb94c325b 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -1,34 +1,84 @@ :javascript - function formatLinks(links) { - var md_links = []; - links.forEach(function (str) { - md_links.push('![' + str.alt + '](' + str.url + ')'); - }) - return md_links; + function formatLink(str) { + return '![' + str.alt + '](' + str.url + ')'; + } + + function handlePaste(e) { + filename = getdata(e); + e = e.originalEvent; + for (var i = 0; i < e.clipboardData.items.length; i++) { + var item = e.clipboardData.items[i]; + if (item.type.indexOf("image") != -1) { + uploadFile(item.getAsFile(), filename); + } + } + } + + function uploadFile(item, filename){ + var formData = new FormData(); + formData.append('issue-img', item, filename); + + var xhr = new XMLHttpRequest(); + + xhr.upload.onprogress = function(e) { + var percentComplete = (e.loaded / e.total) * 100; + }; + + xhr.onload = function() { + if (xhr.status == 200) { + alert('Success!'); + } + else { + alert('Failed!'); + } + }; + + xhr.open('POST', "#{upload_image_project_issues_path}", true); + xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')); + xhr.send(formData); + } + + function getdata(e) { + var value; + if(window.clipboardData && window.clipboardData.getData) { + value = window.clipboardData.getData('Text'); + } + else if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) { + value = e.originalEvent.clipboardData.getData('text/plain'); + } + return value; } $(document).ready(function() { $('textarea.markdown-area').wrap('
'); - $('.div-dropzone').append('
'); + $('.div-dropzone').append('
' + + '' + + '
'); - $('.div-dropzone').append('
Unsupported File Type
'); + $('.div-dropzone').append('
' + + '' + + '
' + + 'Unsupported File Type' + + '
'); - $('.div-dropzone').append('
'); + $('.div-dropzone').append('
' + + '' + + '
'); dz = $('.div-dropzone').dropzone({ url: "#{upload_image_project_issues_path}", dictDefaultMessage: '', clickable: true, - paramName: 'issue-imgs', + paramName: 'issue-img', maxFilesize: 10, - uploadMultiple: true, + uploadMultiple: false, acceptedFiles: 'image/jpg,image/jpeg,image/gif,image/png', headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')}, success: function(header,response){ var child = $(dz[0]).children('textarea'); - $(child).val($(child).val() + formatLinks(response.links).join("\n") + "\n"); + $(child).val($(child).val() + formatLink(response.link) + "\n"); }, error: function(temp, errorMessage) { @@ -69,4 +119,7 @@ $('.div-dropzone').bind('DOMNodeInserted', function() { $('.dz-preview').remove(); }); + + $('.div-dropzone').on('paste', handlePaste); + }); From 46405f1b6e404be7729ae012adc1fb0ebb17bfec Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Tue, 20 May 2014 09:34:42 +0800 Subject: [PATCH 22/49] work in progress. --- app/controllers/projects/issues_controller.rb | 1 + app/views/shared/_markdown_area.html.haml | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 0c984a124c995..a28c3f38f538b 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -99,6 +99,7 @@ def bulk_update def upload_image @base_dir = FileUploader.generate_dir upload_path = File.join(repository.path_with_namespace, 'issues', @base_dir) + #binding.pry accepted_types = %w(png jpg jpeg gif) uploader = FileUploader.new('uploads', upload_path, accepted_types) alt = params['issue-img'].original_filename diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 39a1eb94c325b..472aee630c446 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -3,7 +3,13 @@ return '![' + str.alt + '](' + str.url + ')'; } + function cleanArea(e){ + var child = $(dz[0]).children('textarea'); + init_text = $(child).val(); + } + function handlePaste(e) { + alert(1); filename = getdata(e); e = e.originalEvent; for (var i = 0; i < e.clipboardData.items.length; i++) { @@ -14,7 +20,16 @@ } } + function fillerContent(e){ + console.log(e); + alert(1); + } + function uploadFile(item, filename){ + reader = new FileReader(); + reader.readAsText(item); + console.log(reader.result); + var formData = new FormData(); formData.append('issue-img', item, filename); @@ -26,7 +41,8 @@ xhr.onload = function() { if (xhr.status == 200) { - alert('Success!'); + var child = $(dz[0]).children('textarea'); + $(child).val($(child).val() + formatLink(this.response.link) + "\n"); } else { alert('Failed!'); @@ -34,6 +50,7 @@ }; xhr.open('POST', "#{upload_image_project_issues_path}", true); + xhr.responseType = "json"; xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')); xhr.send(formData); } @@ -76,7 +93,7 @@ acceptedFiles: 'image/jpg,image/jpeg,image/gif,image/png', headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')}, - success: function(header,response){ + success: function(header, response){ var child = $(dz[0]).children('textarea'); $(child).val($(child).val() + formatLink(response.link) + "\n"); }, From 6440f159bc6fdcf89f5716af1e5d8c657e408051 Mon Sep 17 00:00:00 2001 From: erbunao Date: Tue, 20 May 2014 10:57:23 +0800 Subject: [PATCH 23/49] fix for focusing textarea --- app/assets/stylesheets/sections/issues.scss | 33 +++++++++++--------- app/views/shared/_markdown_area.html.haml | 34 +++++++++++---------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index f13ea2d095b27..4c183c6fa6099 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -171,6 +171,14 @@ form.edit-issue { .div-dropzone { position: relative; + padding: 0; + border: 0; + + .div-dropzone-focus { + border-color: #66afe9; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + outline: 0; + } .div-dropzone-hover { position: absolute; @@ -205,23 +213,18 @@ form.edit-issue { font-size: 30px; transition: opacity 200ms ease-in-out; } -} -.div-dropzone { - padding: 0; - border: 0; -} - -.div-dropzone-icon { - display: block; - text-align: center; - font-size: inherit; -} + .div-dropzone-icon { + display: block; + text-align: center; + font-size: inherit; + } -.div-dropzone-icon-error { - display: inline; - text-align: center; - font-size: inherit; + .div-dropzone-icon-error { + display: inline; + text-align: center; + font-size: inherit; + } } .hint { diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 472aee630c446..0d766c1ddd8ad 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -93,6 +93,23 @@ acceptedFiles: 'image/jpg,image/jpeg,image/gif,image/png', headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')}, + dragover: function() { + $('.div-dropzone > textarea').addClass('div-dropzone-focus'); + $('.div-dropzone-hover').css('opacity',0.7); + }, + + dragleave: function() { + $('.div-dropzone > textarea').removeClass('div-dropzone-focus'); + $('.div-dropzone-hover').css('opacity',0); + }, + + drop: function() { + $('.dz-preview').remove(); + $('.div-dropzone > textarea').removeClass('div-dropzone-focus'); + $('.div-dropzone-hover').css('opacity',0); + $('.div-dropzone > textarea').focus(); + }, + success: function(header, response){ var child = $(dz[0]).children('textarea'); $(child).val($(child).val() + formatLink(response.link) + "\n"); @@ -113,21 +130,6 @@ } }); - $('.div-dropzone').on('dragenter',function() { - $(this).children('textarea').focus(); - $('.div-dropzone-hover').css('opacity',0.7); - }); - - $('body').on('dragover',function() { - $('.div-dropzone').children('textarea').blur(); - $('.div-dropzone-hover').css('opacity',0); - }); - - $('.div-dropzone').on('drop',function() { - $('.dz-preview').remove(); - $('.div-dropzone-hover').css('opacity',0); - }); - $('.markdown-selector').click(function(e) { e.preventDefault(); $('.div-dropzone').click(); @@ -135,8 +137,8 @@ $('.div-dropzone').bind('DOMNodeInserted', function() { $('.dz-preview').remove(); + $('.div-dropzone > textarea').focus(); }); $('.div-dropzone').on('paste', handlePaste); - }); From 6ef5587b9f27965d57a9875113e13055647fadfa Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Tue, 20 May 2014 11:20:11 +0800 Subject: [PATCH 24/49] clean version for clipboard. --- app/controllers/projects/issues_controller.rb | 1 - app/views/shared/_markdown_area.html.haml | 11 +---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index a28c3f38f538b..0c984a124c995 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -99,7 +99,6 @@ def bulk_update def upload_image @base_dir = FileUploader.generate_dir upload_path = File.join(repository.path_with_namespace, 'issues', @base_dir) - #binding.pry accepted_types = %w(png jpg jpeg gif) uploader = FileUploader.new('uploads', upload_path, accepted_types) alt = params['issue-img'].original_filename diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 0d766c1ddd8ad..08310bf779bad 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -9,7 +9,6 @@ } function handlePaste(e) { - alert(1); filename = getdata(e); e = e.originalEvent; for (var i = 0; i < e.clipboardData.items.length; i++) { @@ -20,16 +19,8 @@ } } - function fillerContent(e){ - console.log(e); - alert(1); - } - function uploadFile(item, filename){ - reader = new FileReader(); - reader.readAsText(item); - console.log(reader.result); - + var image = document.createElement('img'); var formData = new FormData(); formData.append('issue-img', item, filename); From 1202211c8510e21754a9446aa823a3844856d926 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Tue, 20 May 2014 12:49:19 +0800 Subject: [PATCH 25/49] Removes clipboard feature huhu. --- app/views/shared/_markdown_area.html.haml | 56 ----------------------- 1 file changed, 56 deletions(-) diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 08310bf779bad..718afd7243890 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -3,60 +3,6 @@ return '![' + str.alt + '](' + str.url + ')'; } - function cleanArea(e){ - var child = $(dz[0]).children('textarea'); - init_text = $(child).val(); - } - - function handlePaste(e) { - filename = getdata(e); - e = e.originalEvent; - for (var i = 0; i < e.clipboardData.items.length; i++) { - var item = e.clipboardData.items[i]; - if (item.type.indexOf("image") != -1) { - uploadFile(item.getAsFile(), filename); - } - } - } - - function uploadFile(item, filename){ - var image = document.createElement('img'); - var formData = new FormData(); - formData.append('issue-img', item, filename); - - var xhr = new XMLHttpRequest(); - - xhr.upload.onprogress = function(e) { - var percentComplete = (e.loaded / e.total) * 100; - }; - - xhr.onload = function() { - if (xhr.status == 200) { - var child = $(dz[0]).children('textarea'); - $(child).val($(child).val() + formatLink(this.response.link) + "\n"); - } - else { - alert('Failed!'); - } - }; - - xhr.open('POST', "#{upload_image_project_issues_path}", true); - xhr.responseType = "json"; - xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')); - xhr.send(formData); - } - - function getdata(e) { - var value; - if(window.clipboardData && window.clipboardData.getData) { - value = window.clipboardData.getData('Text'); - } - else if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) { - value = e.originalEvent.clipboardData.getData('text/plain'); - } - return value; - } - $(document).ready(function() { $('textarea.markdown-area').wrap('
'); @@ -130,6 +76,4 @@ $('.dz-preview').remove(); $('.div-dropzone > textarea').focus(); }); - - $('.div-dropzone').on('paste', handlePaste); }); From 6668ce97860e6846465afe1424e0deca46f3043a Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Tue, 20 May 2014 13:55:29 +0800 Subject: [PATCH 26/49] Initializes markdown_area in notes. --- app/controllers/projects/issues_controller.rb | 14 +++++++++---- app/controllers/projects/notes_controller.rb | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 0c984a124c995..b5574af15378b 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -97,10 +97,7 @@ def bulk_update end def upload_image - @base_dir = FileUploader.generate_dir - upload_path = File.join(repository.path_with_namespace, 'issues', @base_dir) - accepted_types = %w(png jpg jpeg gif) - uploader = FileUploader.new('uploads', upload_path, accepted_types) + uploader = FileUploader.new('uploads', upload_path, accepted_images) alt = params['issue-img'].original_filename uploader.store!(params['issue-img']) link = { 'alt' => File.basename(alt, '.*'), @@ -139,6 +136,15 @@ def issues_filtered @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id)) end + def upload_path + base_dir = FileUploader.generate_dir + File.join(repository.path_with_namespace, 'issues', base_dir) + end + + def accepted_images + %w(png jpg jpeg gif) + end + # Since iids are implemented only in 6.1 # user may navigate to issue page using old global ids. # diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index b5b0446b43fa1..326ddd774b953 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -61,6 +61,17 @@ def preview render text: view_context.markdown(params[:note]) end + def upload_image + uploader = FileUploader.new('uploads', upload_path, accepted_images) + alt = params['note-img'].original_filename + uploader.store!(params['note-img']) + link = { 'alt' => File.basename(alt, '.*'), + 'url' => File.join(root_url, uploader.url) } + + respond_to do |format| + format.json { render json: { link: link } } + end + private def note @@ -97,4 +108,13 @@ def render_note_json(note) def authorize_admin_note! return access_denied! unless can?(current_user, :admin_note, note) end + + def upload_path + base_dir = FileUploader.generate_dir + File.join(repository.path_with_namespace, 'notes', base_dir) + end + + def accepted_images + %w(png jpg jpeg gif) + end end From 81ec392a01602a8bbb7e0091820f3cbd4c8d9ab6 Mon Sep 17 00:00:00 2001 From: erbunao Date: Tue, 20 May 2014 14:14:25 +0800 Subject: [PATCH 27/49] Reposition of error message --- app/assets/stylesheets/sections/issues.scss | 96 ++++++++++----------- app/views/projects/issues/_form.html.haml | 2 +- app/views/shared/_markdown_area.html.haml | 20 ++--- 3 files changed, 54 insertions(+), 64 deletions(-) diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index 4c183c6fa6099..eac0233b4ab17 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -169,65 +169,57 @@ form.edit-issue { } } -.div-dropzone { - position: relative; - padding: 0; - border: 0; - - .div-dropzone-focus { - border-color: #66afe9; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); - outline: 0; - } +.div-dropzone-wrapper { + .div-dropzone { + position: relative; + padding: 0; + border: 0; + margin-bottom: 5px; + + .div-dropzone-focus { + border-color: #66afe9; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + outline: 0; + } - .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-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-error { - position: absolute; - top: 50%; - width: 100%; - background-color: #e74c3c; - margin-top: -1em; - opacity: 0; - font-size: 20px; - text-align: center; - 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-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; + } } - .div-dropzone-icon { - display: block; - text-align: center; - font-size: inherit; + .hint { + float: left; + margin: 0; } - .div-dropzone-icon-error { - display: inline; - text-align: center; - font-size: inherit; + .div-dropzone-alert { + margin-top: 25px; + margin-bottom: 0; + transition: opacity 200ms ease-in-out; } } -.hint { - display: inline; -} - diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 969301b540dff..469ec5461563a 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -5,7 +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 #{link_to "guidelines for contribution", contribution_guide_url} to this repository.".html_safe - + = form_for [@project, @issue], html: { class: 'form-horizontal issue-form' } do |f| -if @issue.errors.any? .alert.alert-danger diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 718afd7243890..89a9127db5b24 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -5,17 +5,12 @@ $(document).ready(function() { $('textarea.markdown-area').wrap('
'); + $('.div-dropzone').parent().addClass('div-dropzone-wrapper'); $('.div-dropzone').append('
' + '' + '
'); - $('.div-dropzone').append('
' - + '' - + '
' - + 'Unsupported File Type' - + '
'); - $('.div-dropzone').append('
' + '' + '
'); @@ -30,7 +25,7 @@ acceptedFiles: 'image/jpg,image/jpeg,image/gif,image/png', headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')}, - dragover: function() { + dragover: function() { $('.div-dropzone > textarea').addClass('div-dropzone-focus'); $('.div-dropzone-hover').css('opacity',0.7); }, @@ -47,15 +42,18 @@ $('.div-dropzone > textarea').focus(); }, - success: function(header, response){ + success: function(header, response) { + $('.div-dropzone-alert').alert('close'); var child = $(dz[0]).children('textarea'); $(child).val($(child).val() + formatLink(response.link) + "\n"); }, error: function(temp, errorMessage) { - $('.dropzone-error-message').text(errorMessage); - $('.div-dropzone-error').css('opacity', 0.7); - $('.div-dropzone-error').delay(500).fadeTo("slow", 0); + $('p.hint.pull-right').after( + '
' + + '' + + errorMessage + '
'); }, sending: function() { From 03336b1ad0a95f4eb39e7b1ae8546b06f2f36407 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Tue, 20 May 2014 14:36:37 +0800 Subject: [PATCH 28/49] Fix syntax error. --- app/controllers/projects/notes_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 326ddd774b953..db8ade4b1f7ad 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -70,6 +70,7 @@ def upload_image respond_to do |format| format.json { render json: { link: link } } + end end private From 3e6baa5dc615a6d8b724f8fae0d762153470e720 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Tue, 20 May 2014 16:17:51 +0800 Subject: [PATCH 29/49] Transfers upload_image to project level. --- app/controllers/projects/issues_controller.rb | 21 ------------------- app/controllers/projects/notes_controller.rb | 21 ------------------- app/controllers/projects_controller.rb | 21 +++++++++++++++++++ app/views/shared/_markdown_area.html.haml | 4 ++-- config/routes.rb | 4 ++-- 5 files changed, 25 insertions(+), 46 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index b5574af15378b..308da203295a7 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -96,18 +96,6 @@ def bulk_update redirect_to :back, notice: "#{result[:count]} issues updated" end - def upload_image - uploader = FileUploader.new('uploads', upload_path, accepted_images) - alt = params['issue-img'].original_filename - uploader.store!(params['issue-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 - protected def issue @@ -136,15 +124,6 @@ def issues_filtered @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id)) end - def upload_path - base_dir = FileUploader.generate_dir - File.join(repository.path_with_namespace, 'issues', base_dir) - end - - def accepted_images - %w(png jpg jpeg gif) - end - # Since iids are implemented only in 6.1 # user may navigate to issue page using old global ids. # diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index db8ade4b1f7ad..b5b0446b43fa1 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -61,18 +61,6 @@ def preview render text: view_context.markdown(params[:note]) end - def upload_image - uploader = FileUploader.new('uploads', upload_path, accepted_images) - alt = params['note-img'].original_filename - uploader.store!(params['note-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 note @@ -109,13 +97,4 @@ def render_note_json(note) def authorize_admin_note! return access_denied! unless can?(current_user, :admin_note, note) end - - def upload_path - base_dir = FileUploader.generate_dir - File.join(repository.path_with_namespace, 'notes', base_dir) - end - - def accepted_images - %w(png jpg jpeg gif) - end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index e356d09a270e6..d3d6a635b8536 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -162,8 +162,29 @@ 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 diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 89a9127db5b24..2bf95a517ee88 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -16,10 +16,10 @@ + ''); dz = $('.div-dropzone').dropzone({ - url: "#{upload_image_project_issues_path}", + url: "#{upload_image_project_path @project}", dictDefaultMessage: '', clickable: true, - paramName: 'issue-img', + paramName: 'markdown-img', maxFilesize: 10, uploadMultiple: false, acceptedFiles: 'image/jpg,image/jpeg,image/gif,image/png', diff --git a/config/routes.rb b/config/routes.rb index 40d695176c1d9..499dc03222fc2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -169,12 +169,13 @@ # # Project Area # - resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do + resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index, :upload_image], path: "/" do member do put :transfer post :fork post :archive post :unarchive + post :upload_image get :autocomplete_sources get :import put :retry_import @@ -300,7 +301,6 @@ resources :issues, constraints: {id: /\d+/}, except: [:destroy] do collection do post :bulk_update - post :upload_image end end From cdc360a22cc7929e4fac5a9ac492f5f58538c813 Mon Sep 17 00:00:00 2001 From: erbunao Date: Tue, 20 May 2014 16:28:25 +0800 Subject: [PATCH 30/49] Comments section buttons --- app/views/projects/notes/_form.html.haml | 2 +- app/views/shared/_markdown_area.html.haml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 80be7199a528b..de4c158f7a170 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -37,4 +37,4 @@ .clearfix -= render 'shared/markdown_area' \ No newline at end of file += render 'shared/markdown_area' diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml index 2bf95a517ee88..420de75bd047b 100644 --- a/app/views/shared/_markdown_area.html.haml +++ b/app/views/shared/_markdown_area.html.haml @@ -3,7 +3,7 @@ return '![' + str.alt + '](' + str.url + ')'; } - $(document).ready(function() { + $(document).ready(function() { $('textarea.markdown-area').wrap('
'); $('.div-dropzone').parent().addClass('div-dropzone-wrapper'); @@ -61,10 +61,12 @@ }, complete: function() { + $('.markdown-area').trigger('input'); $('.div-dropzone-spinner').css('opacity',0); } }); + $('.markdown-selector').click(function(e) { e.preventDefault(); $('.div-dropzone').click(); From d7552155974b02be1edfa1f2d4e27b36c59133f7 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Wed, 21 May 2014 08:20:37 +0800 Subject: [PATCH 31/49] fix notes ui. --- app/assets/stylesheets/sections/issues.scss | 6 +++--- app/views/projects/notes/_form.html.haml | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index eac0233b4ab17..a24dd5077f609 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -177,9 +177,9 @@ form.edit-issue { margin-bottom: 5px; .div-dropzone-focus { - border-color: #66afe9; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); - outline: 0; + 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 { diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index de4c158f7a170..54aae33e687e2 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -14,10 +14,12 @@ Write = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on markdown-area' + %p.hint Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + %p.hint.pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. .note_preview.js-note-preview.turn-off .hint - .pull-right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + .pull-right .clearfix .note-form-actions From 2fe1a052b7f1e6b1b8b664c46fceeefb028d59b6 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Thu, 22 May 2014 10:56:13 +0800 Subject: [PATCH 32/49] Reorganizes the branch according to updated master. --- app/controllers/projects/issues_controller.rb | 2 -- app/controllers/projects_controller.rb | 3 +-- app/models/issue.rb | 3 +-- app/services/issues/create_service.rb | 2 ++ app/views/projects/issues/new.html.haml | 2 +- app/views/projects/issues/show.html.haml | 1 - config/routes.rb | 1 + 7 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 308da203295a7..098a17b8ddf8c 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -69,8 +69,6 @@ def create end end format.js do |format| - # issue = Issues.create() - @link = @issue.attachment.url.to_js end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index d3d6a635b8536..6b9e032ff7b63 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -168,7 +168,6 @@ def upload_image 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 @@ -181,7 +180,7 @@ def upload_path File.join(repository.path_with_namespace, base_dir) end - def accepted_images + def accepted_images %w(png jpg jpeg gif) end diff --git a/app/models/issue.rb b/app/models/issue.rb index b4666edb19df6..f0c2e5522738b 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -34,8 +34,7 @@ class Issue < ActiveRecord::Base scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } attr_accessible :title, :assignee_id, :position, :description, - :milestone_id, :label_list, :author_id_of_changes, - :state_event + :milestone_id, :label_list, :state_event acts_as_taggable_on :labels diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index cbb9e4dfa09e4..a5defb3838936 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -3,12 +3,14 @@ class CreateService < Issues::BaseService def execute issue = project.issues.new(params) issue.author = current_user + if issue.save notification_service.new_issue(issue, current_user) event_service.open_issue(issue, current_user) issue.create_cross_references!(issue.project, current_user) execute_hooks(issue) end + issue end end diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index de8a34167ef1e..b1bc3ba0eba68 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1 +1 @@ -= render "form" \ No newline at end of file += render "form" diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index fee5c165118e9..2c816e788decc 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -49,7 +49,6 @@ .wiki = preserve do = markdown @issue.description - .context %cite.cgray = render partial: 'issue_context', locals: { issue: @issue } diff --git a/config/routes.rb b/config/routes.rb index 499dc03222fc2..e3f8dfcde1a08 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,6 +67,7 @@ # Attachments serving # get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ } + # # Admin Area # From 1ac553f63161edbc480efbc0a4ca4c10f6747922 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Thu, 22 May 2014 11:03:03 +0800 Subject: [PATCH 33/49] Readds env.rb. --- features/support/env.rb | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 features/support/env.rb diff --git a/features/support/env.rb b/features/support/env.rb new file mode 100644 index 0000000000000..1b9430c68ee0c --- /dev/null +++ b/features/support/env.rb @@ -0,0 +1,54 @@ +require 'simplecov' unless ENV['CI'] + +if ENV['TRAVIS'] + require 'coveralls' + Coveralls.wear! +end + +ENV['RAILS_ENV'] = 'test' +require './config/environment' + +require 'rspec' +require 'rspec/expectations' +require 'database_cleaner' +require 'spinach/capybara' +require 'sidekiq/testing/inline' + +%w(valid_commit valid_commit_with_alt_email big_commits select2_helper test_env).each do |f| + require Rails.root.join('spec', 'support', f) +end + +Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file} + +WebMock.allow_net_connect! +# +# JS driver +# +require 'capybara/poltergeist' +Capybara.javascript_driver = :poltergeist +Capybara.register_driver :poltergeist do |app| + Capybara::Poltergeist::Driver.new(app, :js_errors => false, :timeout => 60) +end +Spinach.hooks.on_tag("javascript") do + ::Capybara.current_driver = ::Capybara.javascript_driver +end +Capybara.default_wait_time = 60 +Capybara.ignore_hidden_elements = false + +DatabaseCleaner.strategy = :truncation + +Spinach.hooks.before_scenario do + TestEnv.setup_stubs + DatabaseCleaner.start +end + +Spinach.hooks.after_scenario do + DatabaseCleaner.clean +end + +Spinach.hooks.before_run do + TestEnv.init(mailer: false, init_repos: true, repos: false) + RSpec::Mocks::setup self + + include FactoryGirl::Syntax::Methods +end From 629905d2ede0c54bda57c854b4b9387b5fa65e4c Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Thu, 22 May 2014 11:10:32 +0800 Subject: [PATCH 34/49] Syncing with master. --- app/controllers/projects/issues_controller.rb | 2 ++ app/services/issues/create_service.rb | 2 +- config/routes.rb | 2 +- db/schema.rb | 1 - features/support/env.rb | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 098a17b8ddf8c..f9ea0e57b1d02 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -60,6 +60,7 @@ def show def create @issue = Issues::CreateService.new(project, current_user, params[:issue]).execute + respond_to do |format| format.html do if @issue.valid? @@ -129,6 +130,7 @@ def issues_filtered # def redirect_old issue = @project.issues.find_by(id: params[:id]) + if issue redirect_to project_issue_path(@project, issue) return diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index a5defb3838936..d6137833bb9d2 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -10,7 +10,7 @@ def execute issue.create_cross_references!(issue.project, current_user) execute_hooks(issue) end - + issue end end diff --git a/config/routes.rb b/config/routes.rb index e3f8dfcde1a08..bdc9f868e092a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,7 +67,7 @@ # Attachments serving # get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ } - + # # Admin Area # diff --git a/db/schema.rb b/db/schema.rb index 4d3a6b8219478..93837337afc31 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -87,7 +87,6 @@ t.integer "milestone_id" t.string "state" t.integer "iid" - t.string "attachment" end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree diff --git a/features/support/env.rb b/features/support/env.rb index 1b9430c68ee0c..a5b297775db5b 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -14,6 +14,7 @@ require 'spinach/capybara' require 'sidekiq/testing/inline' + %w(valid_commit valid_commit_with_alt_email big_commits select2_helper test_env).each do |f| require Rails.root.join('spec', 'support', f) end From 13028b65b19b779ed7b52a0d9905910f5b287e16 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Thu, 22 May 2014 11:12:50 +0800 Subject: [PATCH 35/49] For hound ci. --- app/controllers/projects/issues_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index f9ea0e57b1d02..6eec2094f86bb 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -60,7 +60,7 @@ def show def create @issue = Issues::CreateService.new(project, current_user, params[:issue]).execute - + respond_to do |format| format.html do if @issue.valid? From 22707c44847771bc5d41d6fcd04fdb3d5cdb586a Mon Sep 17 00:00:00 2001 From: erbunao Date: Wed, 21 May 2014 12:25:17 +0800 Subject: [PATCH 36/49] refactors markdown_area and translate it to coffescript --- .../javascripts/markdown_area.js.coffee | 71 +++++++++++++++++ app/views/layouts/_head.html.haml | 1 + app/views/layouts/admin.html.haml | 1 + app/views/layouts/projects.html.haml | 1 + app/views/projects/issues/_form.html.haml | 5 +- app/views/projects/notes/_form.html.haml | 6 +- app/views/shared/_markdown_area.html.haml | 79 ------------------- 7 files changed, 83 insertions(+), 81 deletions(-) create mode 100644 app/assets/javascripts/markdown_area.js.coffee delete mode 100644 app/views/shared/_markdown_area.html.haml diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee new file mode 100644 index 0000000000000..d267b78c68d01 --- /dev/null +++ b/app/assets/javascripts/markdown_area.js.coffee @@ -0,0 +1,71 @@ +formatLink = (str) -> + "![" + str.alt + "](" + str.url + ")" + +$(document).ready -> + $("textarea.markdown-area").wrap "
" + + $(".div-dropzone").parent().addClass "div-dropzone-wrapper" + + $(".div-dropzone").append "
" + "" + "
" + $(".div-dropzone").append "
" + "" + "
" + + + 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") + + 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: -> + $(".dz-preview").remove() + $(".div-dropzone > textarea").removeClass "div-dropzone-focus" + $(".div-dropzone-hover").css "opacity", 0 + $(".div-dropzone > textarea").focus() + return + + success: (header, response) -> + $(".div-dropzone-alert").alert "close" + child = $(dropzone[0]).children("textarea") + $(child).val $(child).val() + formatLink(response.link) + "\n" + return + + error: (temp, errorMessage) -> + $("p.hint.pull-right").after "
" + "" + errorMessage + "
" + return + + sending: -> + $(".div-dropzone-spinner").css "opacity", 0.7 + return + + complete: -> + $(".markdown-area").trigger "input" + $(".div-dropzone-spinner").css "opacity", 0 + return + ) + + $(".markdown-selector").click (e) -> + e.preventDefault() + $(".div-dropzone").click() + return + + $(".div-dropzone").bind "DOMNodeInserted", -> + $(".dz-preview").remove() + $(".div-dropzone > textarea").focus() + return + + return \ No newline at end of file diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index d9a7a2d31cfa0..88c21386421d8 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -7,6 +7,7 @@ = stylesheet_link_tag "application", :media => "all" = stylesheet_link_tag "print", :media => "print" = javascript_include_tag "application" + = yield :js_file = csrf_meta_tags = include_gon :erb diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 53e0dbaef9ba4..c7a827555a7a4 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -10,3 +10,4 @@ .container .content= yield + = yield :embedded_scripts \ No newline at end of file diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml index 3ae4961b13752..11c815c52a764 100644 --- a/app/views/layouts/projects.html.haml +++ b/app/views/layouts/projects.html.haml @@ -14,3 +14,4 @@ .container .content= yield + = yield :embedded_scripts \ No newline at end of file diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 469ec5461563a..27ab1cb62cf84 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -59,7 +59,8 @@ - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue) = link_to "Cancel", cancel_path, class: 'btn btn-cancel' -= render 'shared/markdown_area' +- content_for :js_file do + = javascript_include_tag "markdown-area.js.coffee" :javascript $("#issue_label_list") @@ -95,3 +96,5 @@ $('#issue_assignee_id').val("#{current_user.id}").trigger("change"); e.preventDefault(); }); + + var project_image_path_upload = "#{upload_image_project_path @project}" diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 54aae33e687e2..ff230d21ce907 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -39,4 +39,8 @@ .clearfix -= render 'shared/markdown_area' +- content_for :js_file do + = javascript_include_tag "markdown-area.js.coffee" + +:javascript + var project_image_path_upload = "#{upload_image_project_path @project}" diff --git a/app/views/shared/_markdown_area.html.haml b/app/views/shared/_markdown_area.html.haml deleted file mode 100644 index 420de75bd047b..0000000000000 --- a/app/views/shared/_markdown_area.html.haml +++ /dev/null @@ -1,79 +0,0 @@ -:javascript - function formatLink(str) { - return '![' + str.alt + '](' + str.url + ')'; - } - - $(document).ready(function() { - $('textarea.markdown-area').wrap('
'); - $('.div-dropzone').parent().addClass('div-dropzone-wrapper'); - - $('.div-dropzone').append('
' - + '' - + '
'); - - $('.div-dropzone').append('
' - + '' - + '
'); - - dz = $('.div-dropzone').dropzone({ - url: "#{upload_image_project_path @project}", - 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')}, - - dragover: function() { - $('.div-dropzone > textarea').addClass('div-dropzone-focus'); - $('.div-dropzone-hover').css('opacity',0.7); - }, - - dragleave: function() { - $('.div-dropzone > textarea').removeClass('div-dropzone-focus'); - $('.div-dropzone-hover').css('opacity',0); - }, - - drop: function() { - $('.dz-preview').remove(); - $('.div-dropzone > textarea').removeClass('div-dropzone-focus'); - $('.div-dropzone-hover').css('opacity',0); - $('.div-dropzone > textarea').focus(); - }, - - success: function(header, response) { - $('.div-dropzone-alert').alert('close'); - var child = $(dz[0]).children('textarea'); - $(child).val($(child).val() + formatLink(response.link) + "\n"); - }, - - error: function(temp, errorMessage) { - $('p.hint.pull-right').after( - '
' - + '' - + errorMessage + '
'); - }, - - sending: function() { - $('.div-dropzone-spinner').css('opacity', 0.7); - }, - - complete: function() { - $('.markdown-area').trigger('input'); - $('.div-dropzone-spinner').css('opacity',0); - } - }); - - - $('.markdown-selector').click(function(e) { - e.preventDefault(); - $('.div-dropzone').click(); - }); - - $('.div-dropzone').bind('DOMNodeInserted', function() { - $('.dz-preview').remove(); - $('.div-dropzone > textarea').focus(); - }); - }); From 4131f509718b0e3f950954027d1953609105c0bf Mon Sep 17 00:00:00 2001 From: erbunao Date: Wed, 21 May 2014 14:40:36 +0800 Subject: [PATCH 37/49] Refactor for removing preview --- app/assets/javascripts/markdown_area.js.coffee | 9 ++------- app/assets/stylesheets/sections/issues.scss | 4 ++++ app/views/projects/issues/_form.html.haml | 3 --- app/views/projects/notes/_form.html.haml | 3 --- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee index d267b78c68d01..566d3ee356639 100644 --- a/app/assets/javascripts/markdown_area.js.coffee +++ b/app/assets/javascripts/markdown_area.js.coffee @@ -20,7 +20,7 @@ $(document).ready -> acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png" headers: "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") - + dragover: -> $(".div-dropzone > textarea").addClass "div-dropzone-focus" $(".div-dropzone-hover").css "opacity", 0.7 @@ -32,7 +32,6 @@ $(document).ready -> return drop: -> - $(".dz-preview").remove() $(".div-dropzone > textarea").removeClass "div-dropzone-focus" $(".div-dropzone-hover").css "opacity", 0 $(".div-dropzone > textarea").focus() @@ -53,6 +52,7 @@ $(document).ready -> return complete: -> + $(".dz-preview").remove() $(".markdown-area").trigger "input" $(".div-dropzone-spinner").css "opacity", 0 return @@ -63,9 +63,4 @@ $(document).ready -> $(".div-dropzone").click() return - $(".div-dropzone").bind "DOMNodeInserted", -> - $(".dz-preview").remove() - $(".div-dropzone > textarea").focus() - return - return \ No newline at end of file diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index a24dd5077f609..b5a3b063c9acb 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -209,6 +209,10 @@ form.edit-issue { text-align: center; font-size: inherit; } + + .dz-preview { + display: none; + } } .hint { diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 27ab1cb62cf84..13c1031882b36 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -59,9 +59,6 @@ - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue) = link_to "Cancel", cancel_path, class: 'btn btn-cancel' -- content_for :js_file do - = javascript_include_tag "markdown-area.js.coffee" - :javascript $("#issue_label_list") .bind( "keydown", function( event ) { diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index ff230d21ce907..6f400b1c2e0db 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -39,8 +39,5 @@ .clearfix -- content_for :js_file do - = javascript_include_tag "markdown-area.js.coffee" - :javascript var project_image_path_upload = "#{upload_image_project_path @project}" From 5be253ca016bf02a7d428d35f355d25a6e98b60a Mon Sep 17 00:00:00 2001 From: erbunao Date: Wed, 21 May 2014 15:06:44 +0800 Subject: [PATCH 38/49] Refactor css for preview --- app/views/projects/notes/_form.html.haml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 6f400b1c2e0db..37734c2ccca52 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -14,12 +14,11 @@ Write = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on markdown-area' - %p.hint Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. - %p.hint.pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. .note_preview.js-note-preview.turn-off .hint - .pull-right + .pull-left Comments 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 .note-form-actions From 78764fa90eef51c07ffc3c310e46493997061bce Mon Sep 17 00:00:00 2001 From: erbunao Date: Wed, 21 May 2014 17:07:50 +0800 Subject: [PATCH 39/49] WIP preview for comments --- .../javascripts/markdown_area.js.coffee | 20 +++++++++++++++---- app/assets/stylesheets/sections/issues.scss | 3 ++- app/views/projects/issues/_form.html.haml | 7 +++++-- app/views/projects/notes/_form.html.haml | 1 + 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee index 566d3ee356639..3f727a67a0f5a 100644 --- a/app/assets/javascripts/markdown_area.js.coffee +++ b/app/assets/javascripts/markdown_area.js.coffee @@ -2,12 +2,23 @@ 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 = "
" + divSpinner = "
" + divAlert = "
" + iconPicture = "" + iconSpinner = "" + btnAlert = "" + $("textarea.markdown-area").wrap "
" $(".div-dropzone").parent().addClass "div-dropzone-wrapper" - $(".div-dropzone").append "
" + "" + "
" - $(".div-dropzone").append "
" + "" + "
" + $(".div-dropzone").append divHover + $(".div-dropzone-hover").append iconPicture + $(".div-dropzone").append divSpinner + $(".div-dropzone-spinner").append iconSpinner dropzone = $(".div-dropzone").dropzone( @@ -32,19 +43,20 @@ $(document).ready -> return drop: -> + $(".div-dropzone-alert").alert "close" $(".div-dropzone > textarea").removeClass "div-dropzone-focus" $(".div-dropzone-hover").css "opacity", 0 $(".div-dropzone > textarea").focus() return success: (header, response) -> - $(".div-dropzone-alert").alert "close" child = $(dropzone[0]).children("textarea") $(child).val $(child).val() + formatLink(response.link) + "\n" return error: (temp, errorMessage) -> - $("p.hint.pull-right").after "
" + "" + errorMessage + "
" + $(".error-alert").append divAlert + $(".div-dropzone-alert").append btnAlert + errorMessage return sending: -> diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index b5a3b063c9acb..b1aa21f1bca97 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -217,11 +217,12 @@ form.edit-issue { .hint { float: left; + padding: 0; margin: 0; } .div-dropzone-alert { - margin-top: 25px; + margin-top: 5px; margin-bottom: 0; transition: opacity 200ms ease-in-out; } diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 13c1031882b36..5d3957f02125f 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -21,8 +21,11 @@ = f.label :description, 'Description', class: 'control-label' .col-sm-10 = f.text_area :description, class: 'form-control js-gfm-input markdown-area', rows: 14 - %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. - %p.hint.pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. + .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 diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 37734c2ccca52..2a78f76262a95 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -20,6 +20,7 @@ .pull-left Comments 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 .note-form-actions .buttons From 3da0c905d3cbf38fa906e4bb5910d176b8d39160 Mon Sep 17 00:00:00 2001 From: erbunao Date: Thu, 22 May 2014 12:48:34 +0800 Subject: [PATCH 40/49] Reposition of preview button in comment area and refactor css --- .../behaviors/toggler_behavior.coffee | 2 +- app/assets/stylesheets/behaviors.scss | 13 +++- .../stylesheets/generic/markdown_area.scss | 58 ++++++++++++++++++ app/assets/stylesheets/sections/issues.scss | 60 ------------------- app/assets/stylesheets/sections/notes.scss | 28 +++++---- app/views/projects/notes/_form.html.haml | 16 ++--- 6 files changed, 95 insertions(+), 82 deletions(-) create mode 100644 app/assets/stylesheets/generic/markdown_area.scss diff --git a/app/assets/javascripts/behaviors/toggler_behavior.coffee b/app/assets/javascripts/behaviors/toggler_behavior.coffee index d06cb116dfed8..8ac5bfe95d874 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.coffee +++ b/app/assets/javascripts/behaviors/toggler_behavior.coffee @@ -1,6 +1,6 @@ $ -> $("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. diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss index 3fdc20485f278..64e3c8d9ace2b 100644 --- a/app/assets/stylesheets/behaviors.scss +++ b/app/assets/stylesheets/behaviors.scss @@ -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; } +} diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss new file mode 100644 index 0000000000000..a1fe18b02fa38 --- /dev/null +++ b/app/assets/stylesheets/generic/markdown_area.scss @@ -0,0 +1,58 @@ +.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; +} diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index b1aa21f1bca97..02c9123178f1d 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -168,63 +168,3 @@ form.edit-issue { } } } - -.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; - } -} - diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index 7e56781f56a87..27c0386c78db7 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -272,15 +272,6 @@ 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; @@ -310,7 +301,6 @@ ul.notes { float: none; } - .common-note-form { margin: 0; background: #F9F9F9; @@ -318,7 +308,6 @@ ul.notes { border: 1px solid #DDD; } - .note-form-actions { background: #F9F9F9; height: 45px; @@ -333,6 +322,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 +368,8 @@ ul.notes { .parallel-comment { padding: 6px; } + +.error-alert > .alert { + margin-top: 5px; + margin-bottom: 5px; +} diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 2a78f76262a95..8b8bc1bc2dee5 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -5,14 +5,7 @@ = f.hidden_field :noteable_id = f.hidden_field :noteable_type - .note_text_and_preview.js-toggler-container - %a.btn.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", data: {url: preview_project_notes_path(@project)} } - %i.icon-eye-open - Preview - %a.btn.btn-primary.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;" } - %i.icon-edit - Write - + .note_text_and_preview.js-toggler-container.notes-container = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on markdown-area' .note_preview.js-note-preview.turn-off @@ -37,6 +30,13 @@ %span.file_name.js-attachment-filename File name... = f.file_field :attachment, class: "js-note-attachment-input hidden" + .write-preview-btn + %a.btn.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", data: {url: preview_project_notes_path(@project)} } + %i.icon-eye-open + Preview + %a.btn.btn-primary.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;" } + %i.icon-edit + Write .clearfix :javascript From d19d346053dc4cf0526c3fd0481ee56374202b55 Mon Sep 17 00:00:00 2001 From: erbunao Date: Thu, 22 May 2014 13:56:46 +0800 Subject: [PATCH 41/49] Minor fix for comments and additional option in js --- app/assets/javascripts/markdown_area.js.coffee | 2 ++ app/assets/stylesheets/sections/notes.scss | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee index 3f727a67a0f5a..6bbbcd94abaf3 100644 --- a/app/assets/javascripts/markdown_area.js.coffee +++ b/app/assets/javascripts/markdown_area.js.coffee @@ -32,6 +32,8 @@ $(document).ready -> headers: "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") + previewContainer: false + dragover: -> $(".div-dropzone > textarea").addClass "div-dropzone-focus" $(".div-dropzone-hover").css "opacity", 0.7 diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index 27c0386c78db7..755295be1f403 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -278,6 +278,10 @@ ul.notes { @include border-radius(4px); min-height: 80px; padding: 4px 6px; + + > p { + overflow-x: auto; + } } .note_text { border: 1px solid #DDD; From 7d93d4971a6f38b3915fcb68ca69c2474f5c6884 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Thu, 22 May 2014 16:03:47 +0800 Subject: [PATCH 42/49] Starts testing the project controller. --- .../javascripts/markdown_area.js.coffee | 2 +- app/controllers/projects_controller.rb | 4 +- app/uploaders/file_uploader.rb | 2 +- config/routes.rb | 2 +- spec/controllers/commits_controller_spec.rb | 3 +- spec/controllers/projects_controller_spec.rb | 40 ++++++++++++++++++ spec/factories.rb | 2 +- spec/fixtures/banana_sample.gif | Bin 0 -> 71759 bytes spec/fixtures/doc_sample.txt | 3 ++ spec/fixtures/rails_sample.jpg | Bin 0 -> 35255 bytes 10 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 spec/controllers/projects_controller_spec.rb create mode 100644 spec/fixtures/banana_sample.gif create mode 100644 spec/fixtures/doc_sample.txt create mode 100644 spec/fixtures/rails_sample.jpg diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee index 6bbbcd94abaf3..d29fab3d07460 100644 --- a/app/assets/javascripts/markdown_area.js.coffee +++ b/app/assets/javascripts/markdown_area.js.coffee @@ -25,7 +25,7 @@ $(document).ready -> url: project_image_path_upload dictDefaultMessage: "" clickable: true - paramName: "markdown-img" + paramName: "markdown_img" maxFilesize: 10 uploadMultiple: false acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png" diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 6b9e032ff7b63..c15205fb68fb6 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -164,8 +164,8 @@ def unarchive def upload_image uploader = FileUploader.new('uploads', upload_path, accepted_images) - alt = params['markdown-img'].original_filename - uploader.store!(params['markdown-img']) + 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| diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 9a907d45e5d6a..cbc9271ac14c8 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -2,7 +2,7 @@ class FileUploader < CarrierWave::Uploader::Base storage :file - def initialize(base_dir, path, allowed_extensions = nil) + def initialize(base_dir, path = '', allowed_extensions = nil) @base_dir = base_dir @path = path @allowed_extensions = allowed_extensions diff --git a/config/routes.rb b/config/routes.rb index bdc9f868e092a..da5a1ba7a871c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -170,7 +170,7 @@ # # Project Area # - resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index, :upload_image], path: "/" do + resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do member do put :transfer post :fork diff --git a/spec/controllers/commits_controller_spec.rb b/spec/controllers/commits_controller_spec.rb index fbf4f29acfd69..308cfa692192e 100644 --- a/spec/controllers/commits_controller_spec.rb +++ b/spec/controllers/commits_controller_spec.rb @@ -6,8 +6,7 @@ before do sign_in(user) - - project.team << [user, :master] + project.creator = user end describe "GET show" do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb new file mode 100644 index 0000000000000..1f3222a6cb399 --- /dev/null +++ b/spec/controllers/projects_controller_spec.rb @@ -0,0 +1,40 @@ +require('spec_helper') + +describe ProjectsController do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:png) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png') } + let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg','image/jpg') } + let(:gif) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif','image/gif') } + + describe "POST #upload_image" do + before do + sign_in(user) + project.creator = user + end + + context "without params['markdown_img']" do + it "returns an error" do + binding.pry + post :upload_image, project_id: project.to_param + expect(response.status).to eq(400) #bad request + end + end + + context "with invalid file" do + it "returns an error" do + + end + end + + context "with valid file" do + it "returns a JSON object" do + + end + + it "returns a content with original filename and link." do + + end + end + end +end \ No newline at end of file diff --git a/spec/factories.rb b/spec/factories.rb index 148477d638901..41cc99cbcb91e 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -201,7 +201,7 @@ end trait :with_attachment do - attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") } + attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") } end end diff --git a/spec/fixtures/banana_sample.gif b/spec/fixtures/banana_sample.gif new file mode 100644 index 0000000000000000000000000000000000000000..1322ac92d141f2240f3b68b1c4a10b5e18b49e2b GIT binary patch literal 71759 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 B?fU=# literal 0 HcmV?d00001 From 10c5caa99a5cce444534ef3bf19930564d5b3940 Mon Sep 17 00:00:00 2001 From: Earle Bunao & Neil Calabroso Date: Fri, 23 May 2014 08:47:34 +0800 Subject: [PATCH 43/49] WIP --- spec/controllers/projects_controller_spec.rb | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 1f3222a6cb399..66858659b216c 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -4,18 +4,17 @@ let(:project) { create(:project) } let(:user) { create(:user) } let(:png) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png') } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg','image/jpg') } - let(:gif) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif','image/gif') } + let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:gif) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + let(:gif) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } describe "POST #upload_image" do before do sign_in(user) - project.creator = user end context "without params['markdown_img']" do it "returns an error" do - binding.pry post :upload_image, project_id: project.to_param expect(response.status).to eq(400) #bad request end @@ -23,17 +22,26 @@ context "with invalid file" do it "returns an error" do - + expect(response.status).to eq(400) #bad request end end context "with valid file" do - it "returns a JSON object" do + before do + post :upload_image, project_id: project.to_param, markdown_img: jpg + end + it "returns a JSON object" do + expect(response.headers['Content-Type']).to have_content 'application/json' end it "returns a content with original filename and link." do + link = {alt: 'rails_sample', link: } + expect(response.body).to + end + it "returns a correct" do + end end end From 0422e947f880f5dbd99730b0d243f443cdb5d496 Mon Sep 17 00:00:00 2001 From: erbunao Date: Fri, 23 May 2014 10:25:25 +0800 Subject: [PATCH 44/49] merge request integration of drag and drop --- app/assets/javascripts/markdown_area.js.coffee | 1 + app/views/layouts/_head.html.haml | 1 - app/views/projects/issues/_form.html.haml | 2 +- app/views/projects/merge_requests/_form.html.haml | 10 ++++++++-- .../projects/merge_requests/_new_submit.html.haml | 10 ++++++++-- app/views/projects/notes/_form.html.haml | 2 +- app/views/projects/wikis/_form.html.haml | 2 -- 7 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee index d29fab3d07460..ccdd94877f6eb 100644 --- a/app/assets/javascripts/markdown_area.js.coffee +++ b/app/assets/javascripts/markdown_area.js.coffee @@ -10,6 +10,7 @@ $(document).ready -> iconPicture = "" iconSpinner = "" btnAlert = "" + project_image_path_upload = window.project_image_path_upload or null $("textarea.markdown-area").wrap "
" diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 88c21386421d8..d9a7a2d31cfa0 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -7,7 +7,6 @@ = stylesheet_link_tag "application", :media => "all" = stylesheet_link_tag "print", :media => "print" = javascript_include_tag "application" - = yield :js_file = csrf_meta_tags = include_gon :erb diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 5d3957f02125f..49d1a87743f13 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -97,4 +97,4 @@ e.preventDefault(); }); - var project_image_path_upload = "#{upload_image_project_path @project}" + window.project_image_path_upload = "#{upload_image_project_path @project}"; diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 290a15e2664d5..3088f0cf864a3 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -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}"; diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index b5479be708b4d..dc41ef8135e08 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -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}"; diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 8b8bc1bc2dee5..cadae9da8050e 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -40,4 +40,4 @@ .clearfix :javascript - var project_image_path_upload = "#{upload_image_project_path @project}" + window.project_image_path_upload = "#{upload_image_project_path @project}"; diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 97cecdcb9acb3..c3455eb629155 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -36,5 +36,3 @@ - else = f.submit 'Create page', class: "btn-create btn" = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel" - -= render('shared/markdown_area') From aeaf7d7004106b89eb488c6b7747744a79c32a6e Mon Sep 17 00:00:00 2001 From: erbunao Date: Fri, 23 May 2014 10:40:04 +0800 Subject: [PATCH 45/49] Milestone integration --- app/views/projects/milestones/_form.html.haml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index d770bb5b3710b..7e0fd19d0abfd 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -21,8 +21,12 @@ .form-group = f.label :description, "Description", class: "control-label" .col-sm-10 - = f.text_area :description, maxlength: 2000, class: "form-control", rows: 10 - %p.hint Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + = f.text_area :description, maxlength: 2000, class: "form-control markdown-area", rows: 10 + .hint + .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + .pull-left Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. + .clearfix + .error-alert .col-md-6 .form-group = f.label :due_date, "Due Date", class: "control-label" @@ -45,3 +49,5 @@ dateFormat: "yy-mm-dd", onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val())); + + window.project_image_path_upload = "#{upload_image_project_path @project}"; From 46fefee27da5ef2b93c8a6509b5046cb90273609 Mon Sep 17 00:00:00 2001 From: erbunao Date: Fri, 23 May 2014 10:48:34 +0800 Subject: [PATCH 46/49] Wiki integration --- app/views/projects/wikis/_form.html.haml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index c3455eb629155..a43e6f073e155 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -15,7 +15,6 @@ .col-sm-2 .col-sm-10 %p.cgray - Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. To link to a (new) page you can just type %code [Link Title](page-slug) \. @@ -24,7 +23,11 @@ = f.label :content, class: 'control-label' .col-sm-10 = f.text_area :content, class: 'form-control js-gfm-input markdown-area', rows: 18 - + .col-sm-12.hint + .pull-left Wiki content 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 = f.label :commit_message, class: 'control-label' .col-sm-10= f.text_field :message, class: 'form-control', rows: 18 @@ -36,3 +39,7 @@ - else = f.submit 'Create page', class: "btn-create btn" = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel" + +:javascript + window.project_image_path_upload = "#{upload_image_project_path @project}"; + From 614a2b7571466560257f5d67160454f16c8974cc Mon Sep 17 00:00:00 2001 From: erbunao Date: Fri, 23 May 2014 15:34:13 +0800 Subject: [PATCH 47/49] Fix for error msg --- app/assets/javascripts/markdown_area.js.coffee | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee index ccdd94877f6eb..def5d12a820f9 100644 --- a/app/assets/javascripts/markdown_area.js.coffee +++ b/app/assets/javascripts/markdown_area.js.coffee @@ -35,6 +35,9 @@ $(document).ready -> previewContainer: false + processing: -> + $(".div-dropzone-alert").alert "close" + dragover: -> $(".div-dropzone > textarea").addClass "div-dropzone-focus" $(".div-dropzone-hover").css "opacity", 0.7 @@ -46,7 +49,6 @@ $(document).ready -> return drop: -> - $(".div-dropzone-alert").alert "close" $(".div-dropzone > textarea").removeClass "div-dropzone-focus" $(".div-dropzone-hover").css "opacity", 0 $(".div-dropzone > textarea").focus() @@ -58,8 +60,10 @@ $(document).ready -> return error: (temp, errorMessage) -> - $(".error-alert").append divAlert - $(".div-dropzone-alert").append btnAlert + errorMessage + checkIfMsgExists = $(".error-alert").children().length + if checkIfMsgExists is 0 + $(".error-alert").append divAlert + $(".div-dropzone-alert").append btnAlert + errorMessage return sending: -> From e77a080915b39cc08f14064eef72e5f9c1d4ad88 Mon Sep 17 00:00:00 2001 From: Administrator Date: Fri, 23 May 2014 15:39:42 +0800 Subject: [PATCH 48/49] Edits test. --- spec/controllers/projects_controller_spec.rb | 30 +++++++++----------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 66858659b216c..f473ed1bd7931 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -6,42 +6,40 @@ let(:png) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png') } let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } let(:gif) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } - let(:gif) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } describe "POST #upload_image" do before do sign_in(user) + binding.pry end context "without params['markdown_img']" do it "returns an error" do - post :upload_image, project_id: project.to_param - expect(response.status).to eq(400) #bad request + post :upload_image, id: project.to_param + expect(response.status).to eq(404) end end context "with invalid file" do + before do + post :upload_image, id: project.to_param, markdown_img: @img + binding.pry + end + it "returns an error" do - expect(response.status).to eq(400) #bad request + expect(response.status).to eq(404) end end context "with valid file" do before do - post :upload_image, project_id: project.to_param, markdown_img: jpg - end - - it "returns a JSON object" do - expect(response.headers['Content-Type']).to have_content 'application/json' - end - - it "returns a content with original filename and link." do - link = {alt: 'rails_sample', link: } - expect(response.body).to + post :upload_image, id: project.to_param, markdown_img: @img end - it "returns a correct" do - + it "returns a content with original filename and new link." do + link = { alt: 'rails_sample', link: '' }.to_json + expect(response.body).to have_content link end end end From 1f492fc564a875016121a3a99be439b7fc90e32e Mon Sep 17 00:00:00 2001 From: Administrator Date: Fri, 23 May 2014 15:49:37 +0800 Subject: [PATCH 49/49] Clean spec. --- spec/controllers/projects_controller_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index f473ed1bd7931..07ca8d2502665 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -11,7 +11,6 @@ describe "POST #upload_image" do before do sign_in(user) - binding.pry end context "without params['markdown_img']" do @@ -24,7 +23,6 @@ context "with invalid file" do before do post :upload_image, id: project.to_param, markdown_img: @img - binding.pry end it "returns an error" do