Commit 69bf7bfa authored by Robert Speicher's avatar Robert Speicher

Merge branch 'dm-copy-diff-file-title-as-gfm' into 'master'

Copy diff file path as GFM

See merge request !9876
parents 00e00cac 12c18ee5
...@@ -38,9 +38,35 @@ showTooltip = function(target, title) { ...@@ -38,9 +38,35 @@ showTooltip = function(target, title) {
}; };
$(function() { $(function() {
var clipboard; const clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
clipboard.on('success', genericSuccess); clipboard.on('success', genericSuccess);
return clipboard.on('error', genericError); clipboard.on('error', genericError);
// This a workaround around ClipboardJS limitations to allow the context-specific copy/pasting of plain text or GFM.
// The Ruby `clipboard_button` helper sneaks a JSON hash with `text` and `gfm` keys into the `data-clipboard-text`
// attribute that ClipboardJS reads from.
// When ClipboardJS creates a new `textarea` (directly inside `body`, with a `readonly` attribute`), sets its value
// to the value of this data attribute, focusses on it, and finally programmatically issues the 'Copy' command,
// this code intercepts the copy command/event at the last minute to deconstruct this JSON hash and set the
// `text/plain` and `text/x-gfm` copy data types to the intended values.
$(document).on('copy', 'body > textarea[readonly]', function(e) {
const clipboardData = e.originalEvent.clipboardData;
if (!clipboardData) return;
const text = e.target.value;
let json;
try {
json = JSON.parse(text);
} catch (ex) {
return;
}
if (!json.text || !json.gfm) return;
e.preventDefault();
clipboardData.setData('text/plain', json.text);
clipboardData.setData('text/x-gfm', json.gfm);
});
}); });
...@@ -210,13 +210,13 @@ module BlobHelper ...@@ -210,13 +210,13 @@ module BlobHelper
end end
def copy_file_path_button(file_path) def copy_file_path_button(file_path)
clipboard_button(clipboard_text: file_path, class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy file path to clipboard') clipboard_button(text: file_path, gfm: "`#{file_path}`", class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy file path to clipboard')
end end
def copy_blob_content_button(blob) def copy_blob_content_button(blob)
return if markup?(blob.name) return if markup?(blob.name)
clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{blob.id}']", class: "btn btn-sm", title: "Copy content to clipboard") clipboard_button(target: ".blob-content[data-blob-id='#{blob.id}']", class: "btn btn-sm", title: "Copy content to clipboard")
end end
def open_raw_file_button(path) def open_raw_file_button(path)
......
module ButtonHelper module ButtonHelper
# Output a "Copy to Clipboard" button # Output a "Copy to Clipboard" button
# #
# data - Data attributes passed to `content_tag` # data - Data attributes passed to `content_tag` (default: {}):
# :text - Text to copy (optional)
# :gfm - GitLab Flavored Markdown to copy, if different from `text` (optional)
# :target - Selector for target element to copy from (optional)
# #
# Examples: # Examples:
# #
# # Define the clipboard's text # # Define the clipboard's text
# clipboard_button(clipboard_text: "Foo") # clipboard_button(text: "Foo")
# # => "<button class='...' data-clipboard-text='Foo'>...</button>" # # => "<button class='...' data-clipboard-text='Foo'>...</button>"
# #
# # Define the target element # # Define the target element
# clipboard_button(clipboard_target: "div#foo") # clipboard_button(target: "div#foo")
# # => "<button class='...' data-clipboard-target='div#foo'>...</button>" # # => "<button class='...' data-clipboard-target='div#foo'>...</button>"
# #
# See http://clipboardjs.com/#usage # See http://clipboardjs.com/#usage
def clipboard_button(data = {}) def clipboard_button(data = {})
css_class = data[:class] || 'btn-clipboard btn-transparent' css_class = data[:class] || 'btn-clipboard btn-transparent'
title = data[:title] || 'Copy to clipboard' title = data[:title] || 'Copy to clipboard'
# This supports code in app/assets/javascripts/copy_to_clipboard.js that
# works around ClipboardJS limitations to allow the context-specific copy/pasting of plain text or GFM.
if text = data.delete(:text)
data[:clipboard_text] =
if gfm = data.delete(:gfm)
{ text: text, gfm: gfm }
else
text
end
end
target = data.delete(:target)
data[:clipboard_target] = target if target
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data) data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
content_tag :button, content_tag :button,
icon('clipboard', 'aria-hidden': 'true'), icon('clipboard', 'aria-hidden': 'true'),
class: "btn #{css_class}", class: "btn #{css_class}",
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
Your New Personal Access Token Your New Personal Access Token
.form-group .form-group
= text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control", 'aria-describedby' => "created-personal-access-token-help-block" = text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control", 'aria-describedby' => "created-personal-access-token-help-block"
= clipboard_button(clipboard_text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left") = clipboard_button(text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left")
%span#created-personal-access-token-help-block.help-block.text-danger Make sure you save it - you won't be able to access it again. %span#created-personal-access-token-help-block.help-block.text-danger Make sure you save it - you won't be able to access it again.
%hr %hr
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
- if @project && event.project != @project - if @project && event.project != @project
%span at %span at
%strong= link_to_project event.project %strong= link_to_project event.project
= clipboard_button(clipboard_text: event.ref_name, class: 'btn-clipboard btn-transparent', title: 'Copy branch to clipboard') = clipboard_button(text: event.ref_name, class: 'btn-clipboard btn-transparent', title: 'Copy branch to clipboard')
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
.pull-right .pull-right
......
.page-content-header .page-content-header
.header-main-content .header-main-content
%strong Commit #{@commit.short_id} %strong Commit #{@commit.short_id}
= clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard") = clipboard_button(text: @commit.id, title: "Copy commit SHA to clipboard")
%span.hidden-xs authored %span.hidden-xs authored
#{time_ago_with_tooltip(@commit.authored_date)} #{time_ago_with_tooltip(@commit.authored_date)}
%span by %span by
......
...@@ -37,6 +37,6 @@ ...@@ -37,6 +37,6 @@
.commit-actions.flex-row.hidden-xs .commit-actions.flex-row.hidden-xs
- if commit.status(ref) - if commit.status(ref)
= render_commit_status(commit, ref: ref) = render_commit_status(commit, ref: ref)
= clipboard_button(clipboard_text: commit.id, title: "Copy commit SHA to clipboard") = clipboard_button(text: commit.id, title: "Copy commit SHA to clipboard")
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.email-modal-input-group.input-group .email-modal-input-group.input-group
= text_field_tag :issue_email, email, class: "monospace js-select-on-focus form-control", readonly: true = text_field_tag :issue_email, email, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#issue_email') = clipboard_button(target: '#issue_email')
%p %p
The subject will be used as the title of the new issue, and the message will be the description. The subject will be used as the title of the new issue, and the message will be the description.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
%p %p
%strong Step 1. %strong Step 1.
Fetch and check out the branch for this merge request Fetch and check out the branch for this merge request
= clipboard_button(clipboard_target: "pre#merge-info-1", title: "Copy commands to clipboard") = clipboard_button(target: "pre#merge-info-1", title: "Copy commands to clipboard")
%pre.dark#merge-info-1 %pre.dark#merge-info-1
- if @merge_request.for_fork? - if @merge_request.for_fork?
:preserve :preserve
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
%p %p
%strong Step 3. %strong Step 3.
Merge the branch and fix any conflicts that come up Merge the branch and fix any conflicts that come up
= clipboard_button(clipboard_target: "pre#merge-info-3", title: "Copy commands to clipboard") = clipboard_button(target: "pre#merge-info-3", title: "Copy commands to clipboard")
%pre.dark#merge-info-3 %pre.dark#merge-info-3
- if @merge_request.for_fork? - if @merge_request.for_fork?
:preserve :preserve
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
%p %p
%strong Step 4. %strong Step 4.
Push the result of the merge to GitLab Push the result of the merge to GitLab
= clipboard_button(clipboard_target: "pre#merge-info-4", title: "Copy commands to clipboard") = clipboard_button(target: "pre#merge-info-4", title: "Copy commands to clipboard")
%pre.dark#merge-info-4 %pre.dark#merge-info-4
:preserve :preserve
git push origin #{h @merge_request.target_branch} git push origin #{h @merge_request.target_branch}
......
...@@ -46,4 +46,4 @@ ...@@ -46,4 +46,4 @@
\... \...
%span.js-details-content.hide %span.js-details-content.hide
= link_to @pipeline.sha, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "monospace commit-hash-full" = link_to @pipeline.sha, namespace_project_commit_path(@project.namespace, @project, @pipeline.sha), class: "monospace commit-hash-full"
= clipboard_button(clipboard_text: @pipeline.sha, title: "Copy commit SHA to clipboard") = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
%tr.tag %tr.tag
%td %td
= escape_once(tag.name) = escape_once(tag.name)
= clipboard_button(clipboard_text: "docker pull #{tag.path}") = clipboard_button(text: "docker pull #{tag.path}")
%td %td
- if tag.revision - if tag.revision
%span.has-tooltip{ title: "#{tag.revision}" } %span.has-tooltip{ title: "#{tag.revision}" }
......
...@@ -22,14 +22,14 @@ ...@@ -22,14 +22,14 @@
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#display_name') = clipboard_button(target: '#display_name')
.form-group .form-group
= label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label' = label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#description') = clipboard_button(target: '#description')
.form-group .form-group
= label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label' = label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label'
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#request_url') = clipboard_button(target: '#request_url')
.form-group .form-group
= label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label' = label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label'
...@@ -57,14 +57,14 @@ ...@@ -57,14 +57,14 @@
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#response_username') = clipboard_button(target: '#response_username')
.form-group .form-group
= label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label' = label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#response_icon') = clipboard_button(target: '#response_icon')
.form-group .form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label' = label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
...@@ -75,14 +75,14 @@ ...@@ -75,14 +75,14 @@
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_hint') = clipboard_button(target: '#autocomplete_hint')
.form-group .form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label' = label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description') = clipboard_button(target: '#autocomplete_description')
%hr %hr
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#url') = clipboard_button(target: '#url')
.form-group .form-group
= label_tag nil, 'Method', class: 'col-sm-2 col-xs-12 control-label' = label_tag nil, 'Method', class: 'col-sm-2 col-xs-12 control-label'
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :customize_name, 'GitLab', class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :customize_name, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#customize_name') = clipboard_button(target: '#customize_name')
.form-group .form-group
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label' = label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label'
...@@ -68,21 +68,21 @@ ...@@ -68,21 +68,21 @@
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description') = clipboard_button(target: '#autocomplete_description')
.form-group .form-group
= label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-xs-12 control-label' = label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_usage_hint') = clipboard_button(target: '#autocomplete_usage_hint')
.form-group .form-group
= label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-xs-12 control-label' = label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group .col-sm-10.col-xs-12.input-group
= text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control input-sm', readonly: 'readonly' = text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#descriptive_label') = clipboard_button(target: '#descriptive_label')
%hr %hr
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
%i.fa.fa-angle-right %i.fa.fa-angle-right
%small.light %small.light
= link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
= clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard") = clipboard_button(text: @commit.id, title: "Copy commit SHA to clipboard")
= time_ago_with_tooltip(@commit.committed_date) = time_ago_with_tooltip(@commit.committed_date)
\- \-
= @commit.full_title = @commit.full_title
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%td %td
- if can?(current_user, :admin_trigger, trigger) - if can?(current_user, :admin_trigger, trigger)
%span= trigger.token %span= trigger.token
= clipboard_button(clipboard_text: trigger.token, title: "Copy trigger token to clipboard") = clipboard_button(text: trigger.token, title: "Copy trigger token to clipboard")
- else - else
%span= trigger.short_token %span= trigger.short_token
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#project_clone', title: "Copy URL to clipboard") = clipboard_button(target: '#project_clone', title: "Copy URL to clipboard")
:javascript :javascript
$('ul.clone-options-dropdown a').on('click',function(e){ $('ul.clone-options-dropdown a').on('click',function(e){
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
- if impersonation - if impersonation
%td.token-token-container %td.token-token-container
= text_field_tag 'impersonation-token-token', token.token, readonly: true, class: "form-control" = text_field_tag 'impersonation-token-token', token.token, readonly: true, class: "form-control"
= clipboard_button(clipboard_text: token.token) = clipboard_button(text: token.token)
- path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token) - path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token)
%td= link_to "Revoke", path, method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." } %td= link_to "Revoke", path, method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." }
- else - else
......
...@@ -160,13 +160,13 @@ ...@@ -160,13 +160,13 @@
- project_ref = cross_project_reference(@project, issuable) - project_ref = cross_project_reference(@project, issuable)
.block.project-reference .block.project-reference
.sidebar-collapsed-icon.dont-change-state .sidebar-collapsed-icon.dont-change-state
= clipboard_button(clipboard_text: project_ref, title: "Copy reference to clipboard", placement: "left") = clipboard_button(text: project_ref, title: "Copy reference to clipboard", placement: "left")
.cross-project-reference.hide-collapsed .cross-project-reference.hide-collapsed
%span %span
Reference: Reference:
%cite{ title: project_ref } %cite{ title: project_ref }
= project_ref = project_ref
= clipboard_button(clipboard_text: project_ref, title: "Copy reference to clipboard", placement: "left") = clipboard_button(text: project_ref, title: "Copy reference to clipboard", placement: "left")
:javascript :javascript
gl.IssuableResource = new gl.SubbableResource('#{issuable_json_path(issuable)}'); gl.IssuableResource = new gl.SubbableResource('#{issuable_json_path(issuable)}');
......
...@@ -122,10 +122,10 @@ ...@@ -122,10 +122,10 @@
- if milestone_ref.present? - if milestone_ref.present?
.block.reference .block.reference
.sidebar-collapsed-icon.dont-change-state .sidebar-collapsed-icon.dont-change-state
= clipboard_button(clipboard_text: milestone_ref, title: "Copy reference to clipboard", placement: "left") = clipboard_button(text: milestone_ref, title: "Copy reference to clipboard", placement: "left")
.cross-project-reference.hide-collapsed .cross-project-reference.hide-collapsed
%span %span
Reference: Reference:
%cite{ title: milestone_ref } %cite{ title: milestone_ref }
= milestone_ref = milestone_ref
= clipboard_button(clipboard_text: milestone_ref, title: "Copy reference to clipboard", placement: "left") = clipboard_button(text: milestone_ref, title: "Copy reference to clipboard", placement: "left")
---
title: After copying a diff file or blob path, pasting it into a comment field will format it as Markdown.
merge_request: 9876
author:
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment