diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2015-06-05 13:37:08 +0000 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2015-06-05 13:37:08 +0000 |
commit | 5a026152b745d5a716c522bc58a76682ee534e25 (patch) | |
tree | 0579a0d6522650f0ae929b6d200d2ddcc746db6c | |
parent | 6f7bf4e4f0ea40c4f69354b8ce75c77af4fab5cb (diff) | |
parent | 5256b5cc060338fb597e3519a18bf6a3faa4cf8c (diff) | |
download | gitlab-ce-5a026152b745d5a716c522bc58a76682ee534e25.tar.gz |
Merge branch 'warn-about-referenced-users' into 'master'
Show warning when a comment will add 10 or more people to the discussion.
Addresses internal issue https://dev.gitlab.org/gitlab/gitlabhq/issues/2054
New issue:
![Screen_Shot_2015-06-02_at_14.48.46](https://gitlab.com/gitlab-org/gitlab-ce/uploads/6771ff8748b9b548a9f0f32a5a8b9bb1/Screen_Shot_2015-06-02_at_14.48.46.png)
New comment:
![Screen_Shot_2015-06-02_at_14.48.51](https://gitlab.com/gitlab-org/gitlab-ce/uploads/25ec5f06490e1e6b36e453e055a7b403/Screen_Shot_2015-06-02_at_14.48.51.png)
See merge request !754
21 files changed, 133 insertions, 60 deletions
diff --git a/CHANGELOG b/CHANGELOG index 4d7d406b43f..624dd3a5c48 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 7.12.0 (unreleased) - Fix timeout when rendering file with thousands of lines. - Don't notify users mentioned in code blocks or blockquotes. - Omit link to generate labels if user does not have access to create them (Stan Hu) + - Show warning when a comment will add 10 or more people to the discussion. - Disable changing of the source branch in merge request update API (Stan Hu) - Shorten merge request WIP text. - Add option to disallow users from registering any application to use GitLab as an OAuth provider diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index fca2a290e2d..a7476146010 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -10,12 +10,17 @@ class @DropzoneInput iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>" btnAlert = "<button type=\"button\"" + alertAttr + ">×</button>" project_uploads_path = window.project_uploads_path or null + markdown_preview_path = window.markdown_preview_path or null max_file_size = gon.max_file_size or 10 form_textarea = $(form).find("textarea.markdown-area") form_textarea.wrap "<div class=\"div-dropzone\"></div>" - form_textarea.bind 'paste', (event) => + form_textarea.on 'paste', (event) => handlePaste(event) + form_textarea.on "input", -> + hideReferencedUsers() + form_textarea.on "blur", -> + renderMarkdown() form_dropzone = $(form).find('.div-dropzone') form_dropzone.parent().addClass "div-dropzone-wrapper" @@ -45,16 +50,7 @@ class @DropzoneInput form.find(".md-write-holder").hide() form.find(".md-preview-holder").show() - preview = form.find(".js-md-preview") - mdText = form.find(".markdown-area").val() - if mdText.trim().length is 0 - preview.text "Nothing to preview." - else - preview.text "Loading..." - $.post($(this).data("url"), - md_text: mdText - ).success (previewData) -> - preview.html previewData + renderMarkdown() # Write button $(document).off "click", ".js-md-write-button" @@ -133,6 +129,40 @@ class @DropzoneInput child = $(dropzone[0]).children("textarea") + hideReferencedUsers = -> + referencedUsers = form.find(".referenced-users") + referencedUsers.hide() + + renderReferencedUsers = (users) -> + referencedUsers = form.find(".referenced-users") + + if referencedUsers.length + if users.length >= 10 + referencedUsers.show() + referencedUsers.find(".js-referenced-users-count").text users.length + else + referencedUsers.hide() + + renderMarkdown = -> + preview = form.find(".js-md-preview") + mdText = form.find(".markdown-area").val() + if mdText.trim().length is 0 + preview.text "Nothing to preview." + hideReferencedUsers() + else + preview.text "Loading..." + $.ajax( + type: "POST", + url: markdown_preview_path, + data: { + text: mdText + }, + dataType: "json" + ).success (data) -> + preview.html data.body + + renderReferencedUsers data.references.users + formatLink = (link) -> text = "[#{link.alt}](#{link.url})" text = "!#{text}" if link.is_image diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index 4eb3f3c03f3..7967892f856 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.coffee +++ b/app/assets/javascripts/gfm_auto_complete.js.coffee @@ -10,7 +10,7 @@ GitLab.GfmAutoComplete = # Team Members Members: - template: '<li>${username} <small>${name}</small></li>' + template: '<li>${username} <small>${title}</small></li>' # Issues and MergeRequests Issues: @@ -34,7 +34,13 @@ GitLab.GfmAutoComplete = searchKey: 'search' callbacks: beforeSave: (members) -> - $.map members, (m) -> name: m.name, username: m.username, search: "#{m.username} #{m.name}" + $.map members, (m) -> + title = m.name + title += " (#{m.count})" if m.count + + username: m.username + title: sanitize(title) + search: sanitize("#{m.username} #{m.name}") input.atwho at: '#' @@ -44,7 +50,10 @@ GitLab.GfmAutoComplete = insertTpl: '${atwho-at}${id}' callbacks: beforeSave: (issues) -> - $.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}" + $.map issues, (i) -> + id: i.iid + title: sanitize(i.title) + search: "#{i.iid} #{i.title}" input.atwho at: '!' @@ -54,7 +63,10 @@ GitLab.GfmAutoComplete = insertTpl: '${atwho-at}${id}' callbacks: beforeSave: (merges) -> - $.map merges, (m) -> id: m.iid, title: sanitize(m.title), search: "#{m.iid} #{m.title}" + $.map merges, (m) -> + id: m.iid + title: sanitize(m.title) + search: "#{m.iid} #{m.title}" input.one 'focus', => $.getJSON(@dataSource).done (data) -> diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss index eb39b6bb7e9..f94677d1925 100644 --- a/app/assets/stylesheets/generic/markdown_area.scss +++ b/app/assets/stylesheets/generic/markdown_area.scss @@ -52,6 +52,22 @@ transition: opacity 200ms ease-in-out; } +.md-area { + position: relative; +} + +.md-header ul { + float: left; +} + +.referenced-users { + padding: 10px 0; + color: #999; + margin-left: 10px; + margin-top: 1px; + margin-right: 130px; +} + .md-preview-holder { background: #FFF; border: 1px solid #ddd; diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss index 74108c1f086..e61b39f1ac3 100644 --- a/app/assets/stylesheets/generic/mobile.scss +++ b/app/assets/stylesheets/generic/mobile.scss @@ -19,6 +19,10 @@ } } + .referenced-users { + margin-right: 0; + } + .issues-filters, .dash-projects-filters, .check-all-holder { diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/generic/zen.scss index bcb8bbe3134..7ab01187a02 100644 --- a/app/assets/stylesheets/generic/zen.scss +++ b/app/assets/stylesheets/generic/zen.scss @@ -1,6 +1,4 @@ .zennable { - position: relative; - .zen-toggle-comment { display: none; } @@ -8,8 +6,9 @@ .zen-enter-link { color: #888; position: absolute; - top: -26px; + top: 0px; right: 4px; + line-height: 40px; } .zen-leave-link { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index a0522030785..203f9374cee 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -39,11 +39,8 @@ .new_note, .edit_note { .buttons { - float: left; margin-top: 8px; - } - .clearfix { - margin-bottom: 0; + margin-bottom: 3px; } .note-preview-holder { @@ -82,7 +79,6 @@ .note-form-actions { background: #F9F9F9; - height: 45px; .note-form-option { margin-top: 8px; diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 4ca5fc65459..be5968cd7b0 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -151,7 +151,17 @@ class ProjectsController < ApplicationController end def markdown_preview - render text: view_context.markdown(params[:md_text]) + text = params[:text] + + ext = Gitlab::ReferenceExtractor.new(@project, current_user) + ext.analyze(text) + + render json: { + body: view_context.markdown(text), + references: { + users: ext.users.map(&:username) + } + } end private diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index b91590a1a90..0004a399f47 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -38,13 +38,13 @@ module Projects def groups current_user.authorized_groups.sort_by(&:path).map do |group| count = group.users.count - { username: group.path, name: "#{group.name} (#{count})" } + { username: group.path, name: group.name, count: count } end end def all_members count = project.team.members.flatten.count - [{ username: "all", name: "All Project and Group Members (#{count})" }] + [{ username: "all", name: "All Project and Group Members", count: count }] end end end diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index b1a57d9824e..4d8c5656a25 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -15,8 +15,10 @@ %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'} %meta{name: 'theme-color', content: '#474D57'} - = yield(:meta_tags) + = yield :meta_tags = render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id') = render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id') = render 'layouts/bootlint' if Rails.env.development? + + = yield :scripts_head diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index c1283734d25..54fcbca6bc6 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -22,5 +22,3 @@ = render "layouts/flash" .clearfix = yield - -= yield :embedded_scripts diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 155825cc4c2..ff23913d7d6 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -8,3 +8,5 @@ = render "layouts/header/public", title: header_title = render 'layouts/page', sidebar: sidebar + + = yield :scripts_body diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 4aeb9d397d2..03c7ba8c73f 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -2,7 +2,13 @@ - header_title project_title(@project) - sidebar "project" unless sidebar -- content_for :embedded_scripts do +- content_for :scripts_head do + -if current_user + :javascript + window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; + window.markdown_preview_path = "#{markdown_preview_namespace_project_path(@project.namespace, @project)}"; + +- content_for :scripts_body do = render "layouts/init_auto_complete" if current_user = render template: "layouts/application" diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml index 2c5b24b8130..491e2107da4 100644 --- a/app/views/projects/_issuable_form.html.haml +++ b/app/views/projects/_issuable_form.html.haml @@ -24,7 +24,7 @@ = f.label :description, 'Description', class: 'control-label' .col-sm-10 - = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do + = render layout: 'projects/md_preview', locals: { preview_class: "wiki", referenced_users: true } do = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' .col-sm-12.hint diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index b869fd6e12a..a831481cf80 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -1,13 +1,24 @@ -%ul.nav.nav-tabs - %li.active - = link_to '#md-write-holder', class: 'js-md-write-button' do - Write - %li - = link_to '#md-preview-holder', class: 'js-md-preview-button', - data: { url: markdown_preview_namespace_project_path(@project.namespace, @project) } do - Preview -%div - .md-write-holder - = yield - .md.md-preview-holder.hide - .js-md-preview{class: (preview_class if defined?(preview_class))} +.md-area + .md-header.clearfix + %ul.nav.nav-tabs + %li.active + = link_to '#md-write-holder', class: 'js-md-write-button' do + Write + %li + = link_to '#md-preview-holder', class: 'js-md-preview-button' do + Preview + + - if defined?(referenced_users) && referenced_users + %span.referenced-users.pull-left.hide + = icon('exclamation-triangle') + You are about to add + %strong + %span.js-referenced-users-count 0 + people + to the discussion. Proceed with caution. + + %div + .md-write-holder + = yield + .md.md-preview-holder.hide + .js-md-preview{class: (preview_class if defined?(preview_class))} diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 7d7217eb2a8..8d2564be55e 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -10,5 +10,3 @@ $('#issue_assignee_id').val("#{current_user.id}").trigger("change"); e.preventDefault(); }); - - window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 1c7160bce5f..be73f087449 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -8,5 +8,3 @@ $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change"); e.preventDefault(); }); - - window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 9a2edbf0a8c..6792104569b 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -51,8 +51,6 @@ e.preventDefault(); }); - window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; - :javascript var merge_request merge_request = new MergeRequest({ diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 95b7070ce5c..5650607f31f 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -50,5 +50,3 @@ 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_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 2ada6cb6700..f28b3e9b508 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -5,7 +5,7 @@ = f.hidden_field :noteable_id = f.hidden_field :noteable_type - = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do + = render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text' @@ -15,10 +15,7 @@ .error-alert .note-form-actions - .buttons + .buttons.clearfix = f.submit 'Add Comment', class: "btn comment-btn btn-grouped js-comment-button" = yield(:note_actions) %a.btn.grouped.js-close-discussion-note-form Cancel - -:javascript - window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 9fbfa0b1aeb..2a8ceaa2844 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -41,6 +41,3 @@ - else = f.submit 'Create page', class: "btn-create btn" = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel" - -:javascript - window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; |