Feature #34703 » 0001-Add-link-to-copy-object-s-url-to-the-clipboard.patch
| app/helpers/application_helper.rb | ||
|---|---|---|
| 1818 | 1818 |
) |
| 1819 | 1819 |
end |
| 1820 | 1820 | |
| 1821 |
def copy_object_url_link(url) |
|
| 1822 |
link_to_function( |
|
| 1823 |
l(:button_copy_link), "copyTextToClipboard(this);", |
|
| 1824 |
class: 'icon icon-copy-link', |
|
| 1825 |
data: {'clipboard-text' => url}
|
|
| 1826 |
) |
|
| 1827 |
end |
|
| 1828 | ||
| 1821 | 1829 |
private |
| 1822 | 1830 | |
| 1823 | 1831 |
def wiki_helper |
| app/helpers/journals_helper.rb | ||
|---|---|---|
| 29 | 29 |
def render_journal_actions(issue, journal, options={})
|
| 30 | 30 |
links = [] |
| 31 | 31 |
dropbown_links = [] |
| 32 |
indice = journal.indice || @journal.issue.visible_journals_with_index.find{|j| j.id == @journal.id}.indice
|
|
| 33 | ||
| 34 |
dropbown_links << copy_object_url_link(issue_url(issue, anchor: "note-#{indice}", only_path: false))
|
|
| 32 | 35 |
if journal.notes.present? |
| 33 | 36 |
if options[:reply_links] |
| 34 |
indice = journal.indice || @journal.issue.visible_journals_with_index.find{|j| j.id == @journal.id}.indice
|
|
| 35 | 37 |
links << link_to(l(:button_quote), |
| 36 | 38 |
quoted_issue_path(issue, :journal_id => journal, :journal_indice => indice), |
| 37 | 39 |
:remote => true, |
| app/views/issues/_action_menu.html.erb | ||
|---|---|---|
| 6 | 6 |
:class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project) %> |
| 7 | 7 |
<%= watcher_link(@issue, User.current) %> |
| 8 | 8 |
<%= actions_dropdown do %> |
| 9 |
<%= copy_object_url_link(issue_url(@issue, only_path: false)) %> |
|
| 9 | 10 |
<%= link_to l(:button_copy), project_copy_issue_path(@project, @issue), |
| 10 | 11 |
:class => 'icon icon-copy' if User.current.allowed_to?(:copy_issues, @project) && Issue.allowed_target_projects.any? %> |
| 11 | 12 |
<%= link_to l(:button_delete), issue_path(@issue), |
| config/locales/en.yml | ||
|---|---|---|
| 1149 | 1149 |
button_change_password: Change password |
| 1150 | 1150 |
button_copy: Copy |
| 1151 | 1151 |
button_copy_and_follow: Copy and follow |
| 1152 |
button_copy_link: Copy link |
|
| 1152 | 1153 |
button_annotate: Annotate |
| 1153 | 1154 |
button_fetch_changesets: Fetch commits |
| 1154 | 1155 |
button_update: Update |
| public/javascripts/application.js | ||
|---|---|---|
| 563 | 563 |
return key; |
| 564 | 564 |
} |
| 565 | 565 | |
| 566 |
function copyTextToClipboard(target) {
|
|
| 567 |
if (target) {
|
|
| 568 |
var temp = document.createElement('textarea');
|
|
| 569 |
temp.value = target.getAttribute('data-clipboard-text');
|
|
| 570 |
document.body.appendChild(temp); |
|
| 571 |
temp.select(); |
|
| 572 |
document.execCommand('copy');
|
|
| 573 |
if (temp.parentNode) {
|
|
| 574 |
temp.parentNode.removeChild(temp); |
|
| 575 |
} |
|
| 576 |
if ($(target).closest('.drdn.expanded').length) {
|
|
| 577 |
$(target).closest('.drdn.expanded').removeClass("expanded");
|
|
| 578 |
} |
|
| 579 |
} |
|
| 580 |
return false; |
|
| 581 |
} |
|
| 582 | ||
| 566 | 583 |
function updateIssueFrom(url, el) {
|
| 567 | 584 |
$('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
|
| 568 | 585 |
$(this).data('valuebeforeupdate', $(this).val());
|
| public/stylesheets/application.css | ||
|---|---|---|
| 1612 | 1612 |
.icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
|
| 1613 | 1613 |
.icon-file.application-zip { background-image: url(../images/files/zip.png); }
|
| 1614 | 1614 |
.icon-file.application-gzip { background-image: url(../images/files/zip.png); }
|
| 1615 |
.icon-copy-link { background-image: url(../images/copy_link.png); }
|
|
| 1615 | 1616 | |
| 1616 | 1617 |
.sort-handle.ajax-loading { background-image: url(../images/loading.gif); }
|
| 1617 | 1618 |
tr.ui-sortable-helper { border:1px solid #e4e4e4; }
|
| test/functional/issues_controller_test.rb | ||
|---|---|---|
| 2072 | 2072 |
get(:show, :params => {:id => 1})
|
| 2073 | 2073 |
assert_response :success |
| 2074 | 2074 |
assert_select 'div.issue div.description', :text => /Unable to print recipes/ |
| 2075 |
assert_select '.contextual' do |
|
| 2076 |
assert_select 'a', {:count => 2, :text => /Edit/}
|
|
| 2077 |
assert_select 'a', {:count => 0, :text => /Log time/}
|
|
| 2078 |
assert_select 'a', {:count => 0, :text => /Watch/}
|
|
| 2079 |
assert_select 'div.drdn-items a', {:count => 0, :text => /Copy/}
|
|
| 2080 |
assert_select 'div.drdn-items a', {:count => 0, :text => /Delete/}
|
|
| 2075 |
assert_select '#content>.contextual:first-child' do |
|
| 2076 |
assert_select 'a', {:count => 1, :text => 'Edit'}
|
|
| 2077 |
assert_select 'a', {:count => 0, :text => 'Log time'}
|
|
| 2078 |
assert_select 'a', {:count => 0, :text => 'Watch'}
|
|
| 2079 |
assert_select 'div.drdn-items a', {:count => 1, :text => 'Copy link'}
|
|
| 2080 |
assert_select 'div.drdn-items a', {:count => 0, :text => 'Copy'}
|
|
| 2081 |
assert_select 'div.drdn-items a', {:count => 0, :text => 'Delete'}
|
|
| 2081 | 2082 |
end |
| 2082 | 2083 |
# anonymous role is allowed to add a note |
| 2083 | 2084 |
assert_select 'form#issue-form' do |
| ... | ... | |
| 2093 | 2094 |
@request.session[:user_id] = 2 |
| 2094 | 2095 |
get(:show, :params => {:id => 1})
|
| 2095 | 2096 |
assert_select 'a', :text => /Quote/ |
| 2096 |
assert_select '.contextual' do |
|
| 2097 |
assert_select 'a', {:count => 2, :text => /Edit/}
|
|
| 2098 |
assert_select 'a', :text => /Log time/ |
|
| 2099 |
assert_select 'a', :text => /Watch/ |
|
| 2100 |
assert_select 'div.drdn-items a', :text => /Copy/ |
|
| 2101 |
assert_select 'div.drdn-items a', :text => /Delete/ |
|
| 2097 |
assert_select '#content>.contextual:first-child' do |
|
| 2098 |
assert_select 'a', {:count => 1, :text => 'Edit'}
|
|
| 2099 |
assert_select 'a', {:count => 1, :text => 'Log time'}
|
|
| 2100 |
assert_select 'a', {:count => 1, :text => 'Watch'}
|
|
| 2101 |
assert_select 'div.drdn-items a', {:count => 1, :text => 'Copy link'}
|
|
| 2102 |
assert_select 'div.drdn-items a', {:count => 1, :text => 'Copy'}
|
|
| 2103 |
assert_select 'div.drdn-items a', {:count => 1, :text => 'Delete'}
|
|
| 2102 | 2104 |
end |
| 2103 | 2105 |
assert_select 'form#issue-form' do |
| 2104 | 2106 |
assert_select 'fieldset' do |
| test/helpers/journals_helper_test.rb | ||
|---|---|---|
| 59 | 59 |
assert_select_in journal_actions, 'a[title=?][class="icon-only icon-comment"]', 'Quote' |
| 60 | 60 |
assert_select_in journal_actions, 'a[title=?][class="icon-only icon-edit"]', 'Edit' |
| 61 | 61 |
assert_select_in journal_actions, 'div[class="drdn-items"] a[class="icon icon-del"]' |
| 62 |
assert_select_in journal_actions, 'div[class="drdn-items"] a[class="icon icon-copy-link"]' |
|
| 62 | 63 |
end |
| 63 | 64 | |
| 64 | 65 |
def test_journal_thumbnail_attachments_should_be_in_the_same_order_as_the_journal_details |
| test/system/copy_to_clipboard_test.rb | ||
|---|---|---|
| 1 |
# frozen_string_literal: true |
|
| 2 | ||
| 3 |
# Redmine - project management software |
|
| 4 |
# Copyright (C) 2006-2020 Jean-Philippe Lang |
|
| 5 |
# |
|
| 6 |
# This program is free software; you can redistribute it and/or |
|
| 7 |
# modify it under the terms of the GNU General Public License |
|
| 8 |
# as published by the Free Software Foundation; either version 2 |
|
| 9 |
# of the License, or (at your option) any later version. |
|
| 10 |
# |
|
| 11 |
# This program is distributed in the hope that it will be useful, |
|
| 12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 |
# GNU General Public License for more details. |
|
| 15 |
# |
|
| 16 |
# You should have received a copy of the GNU General Public License |
|
| 17 |
# along with this program; if not, write to the Free Software |
|
| 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 19 | ||
| 20 |
require File.expand_path('../../application_system_test_case', __FILE__)
|
|
| 21 | ||
| 22 |
class CopyToClipboardSystemTest < ApplicationSystemTestCase |
|
| 23 |
fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, |
|
| 24 |
:trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues, |
|
| 25 |
:enumerations, :custom_fields, :custom_values, :custom_fields_trackers, |
|
| 26 |
:watchers, :journals, :journal_details, :versions, |
|
| 27 |
:workflows, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions |
|
| 28 | ||
| 29 |
def test_copy_issue_url_to_clipboard |
|
| 30 |
log_user('jsmith', 'jsmith')
|
|
| 31 |
visit 'issues/1' |
|
| 32 | ||
| 33 |
# Copy issue url to Clipboard |
|
| 34 |
first('.contextual span.icon-actions').click
|
|
| 35 |
find('.contextual div.drdn-items a.icon-copy-link').click
|
|
| 36 | ||
| 37 |
# Paste the value copied to the clipboard into the textarea to get and test |
|
| 38 |
first('.icon-edit').click
|
|
| 39 |
find('textarea#issue_notes').send_keys([:control, 'v'])
|
|
| 40 |
assert find('textarea#issue_notes').value.end_with?('/issues/1')
|
|
| 41 |
end |
|
| 42 | ||
| 43 |
def test_copy_issue_journal_url_to_clipboard |
|
| 44 |
log_user('jsmith', 'jsmith')
|
|
| 45 |
visit 'issues/1' |
|
| 46 | ||
| 47 |
# Copy issue journal url to Clipboard |
|
| 48 |
first('#note-2 .icon-actions').click
|
|
| 49 |
first('#note-2 div.drdn-items a.icon-copy-link').click
|
|
| 50 | ||
| 51 |
# Paste the value copied to the clipboard into the textarea to get and test |
|
| 52 |
first('.icon-edit').click
|
|
| 53 |
find('textarea#issue_notes').send_keys([:control, 'v'])
|
|
| 54 |
assert find('textarea#issue_notes').value.end_with?('/issues/1#note-2')
|
|
| 55 |
end |
|
| 56 |
end |
|