Add new protocol template dropdown

This commit is contained in:
aignatov-bio 2020-04-16 19:13:18 +02:00
parent 1cea6116b5
commit ad705a740c
8 changed files with 224 additions and 275 deletions

View file

@ -385,7 +385,7 @@ function refreshProtocolStatusBar() {
type: 'GET',
dataType: 'json',
success: function(data) {
$("[data-role='protocol-status-bar']").html(data.html);
$(".my-module-protocol-status").replaceWith(data.html);
initLinkUpdate();
}
});

View file

@ -33,12 +33,12 @@
display: inline-block;
h2 {
margin-bottom: 8px;
margin-top: 11px;
margin: 10px 0;
}
}
.task-section-header {
align-items: center;
display: flex;
flex-wrap: wrap;
@ -345,48 +345,119 @@
}
}
.my-module-recent-protocols {
flex-grow: 1;
height: 36px;
margin-bottom: 5px;
.my-module-protocol-status {
position: relative;
.btn-group {
align-items: center;
display: flex;
float: right;
height: 33px;
.status-label {
@include font-h3;
color: $color-alto;
float: left;
margin: 0 3px;
&.linked {
color: $brand-primary;
}
}
.title {
font-size: 14px;
.status-info {
@include font-h2;
color: inherit;
text-decoration: none;
&:hover,
&:active {
color: inherit;
text-decoration: none;
}
}
.dropdown-button {
cursor: pointer;
padding: 10px 5px;
}
.status-info-dropdown {
left: -125px;
max-width: 100vw;
width: 650px;
.dropdown-menu {
left: auto;
padding: 0;
right: 0;
width: 402px;
.dropdown-body {
border-bottom: $border-tertiary;
padding: 10px 32px;
.protocol {
cursor: pointer;
display: inline-block;
float: left;
padding: 5px 10px;
transition: $md-transaction;
width: 200px;
.info-line {
align-items: center;
display: flex;
flex-wrap: wrap;
margin: 9px 0;
&:hover {
background: $color-gainsboro;
.description {
@include font-button;
flex-grow: 1;
min-width: 120px;
}
.value {
@include font-h3;
flex-shrink: 0;
}
&.new-parent-version {
.value {
color: $brand-warning;
&::before {
@include font-awesome;
content: "\f2f1";
margin-right: 5px;
}
}
}
}
.fas {
margin-right: 5px;
.notification-line {
@include font-button;
color: $color-silver-chalice;
display: flex;
margin: 8px 0;
.fas {
line-height: 21px;
margin-right: 3px;
}
&.new-parent-version {
color: $brand-warning;
}
&.new-protocol-version {
color: $brand-focus;
}
}
}
.dropdown-footer {
display: flex;
flex-wrap: wrap;
padding: 16px;
.button-container {
flex-basis: calc(100% / 3);
flex-grow: 1;
}
}
}
}
@media (max-width: 700px) {
.my-module-protocol-status {
.status-info-dropdown {
left: -75px;
width: 300px;
.dropdown-footer {
flex-direction: column;
.btn {
float: left !important;
margin: 5px 0;
width: auto;
}
}
}
}

View file

@ -1,22 +1,5 @@
module ProtocolStatusHelper
def protocol_status_href(protocol)
parent = protocol.parent
res = ''
res << '<a href="#" data-toggle="popover" data-html="true" class="preview-protocol"'
res << 'data-trigger="focus" data-placement="bottom" title="'
res << protocol_status_popover_title(parent) +
'" data-content="' + protocol_status_popover_content(parent) +
'">' + protocol_name(parent).truncate(Constants::NAME_TRUNCATION_LENGTH) + '</a>'
res.html_safe
end
private
def protocol_private_for_current_user?(protocol)
protocol.in_repository_private? && protocol.added_by != current_user
end
def protocol_name(protocol)
if protocol_private_for_current_user?(protocol)
I18n.t('my_modules.protocols.protocol_status_bar.private_parent')
@ -25,43 +8,9 @@ module ProtocolStatusHelper
end
end
def protocol_status_popover_title(protocol)
res = ""
if protocol.in_repository_public?
res << "<span class='fas fa-eye' title='" + I18n.t("my_modules.protocols.protocol_status_bar.public_desc") + "'></span>"
elsif protocol.in_repository_private?
res << "<span class='fas fa-eye-slash' title='" + I18n.t("my_modules.protocols.protocol_status_bar.private_desc") + "'></span>"
end
res << "&nbsp;"
if can_read_protocol_in_repository?(protocol)
res << "<a href='" + edit_protocol_path(protocol) + "' target='_blank'>" + protocol_name(protocol) + "</a>"
else
res << "<span style='font-weight: bold;'>" + protocol_name(protocol) + "</span>"
end
res << "&nbsp;-&nbsp;"
res << "<span style='font-style: italic;'>" + I18n.t("my_modules.protocols.protocol_status_bar.added_by") + "&nbsp;"
res << "<a href='#' data-toggle='tooltip' data-placement='right' title='" +
I18n.t('my_modules.protocols.protocol_status_bar.added_by_tooltip',
ts: I18n.l(protocol.created_at, format: :full)) + "'>" +
escape_input(protocol.added_by.full_name) + '</a></span>'
res
end
private
def protocol_status_popover_content(protocol)
if protocol_private_for_current_user?(protocol)
res = '<p><em>' + I18n.t('my_modules.protocols.protocol_status_bar.private_protocol_desc') + '</em></p>'
else
res = '<p><b>' + I18n.t('my_modules.protocols.protocol_status_bar.keywords') + ':</b>&nbsp;'
if protocol.protocol_keywords.size.positive?
protocol.protocol_keywords.each do |kw|
res << kw.name + ', '
end
res = res[0..-3]
else
res << '<em>' + I18n.t('my_modules.protocols.protocol_status_bar.no_keywords') + '</em>'
end
res << '</p>'
end
escape_input(res)
def protocol_private_for_current_user?(protocol)
protocol.in_repository_private? && protocol.added_by != current_user
end
end

View file

@ -1,7 +1,4 @@
<div class="protocol-status-container">
<div class="protocol-status-bar" data-role="protocol-status-bar">
<%= render partial: "my_modules/protocols/protocol_status_bar.html.erb" %>
</div>
<% if can_manage_protocol_in_module?(@protocol) %>
<%= render partial: "my_modules/recent_protocol_dropdown.html.erb", locals: {protocol: @my_module.protocol}%>
<% end %>

View file

@ -73,6 +73,7 @@
<h2><%= t('my_modules.protocol.title') %></h2>
</div>
</a>
<%= render partial: "my_modules/protocols/protocol_status_bar.html.erb" %>
<div class="actions-block">
<%= render partial: "my_modules/protocols/protocol_options_dropdown" %>
</div>

View file

@ -1,14 +1,101 @@
<div class="panel panel-default panel-protocol-status help_tooltips"
data-tooltiplink="<%= I18n.t('tooltips.link.protocol.linking') %>"
data-tooltipcontent="<%= I18n.t('tooltips.text.protocol.linking') %>">
<div class="panel-body">
<% if @protocol.linked? %>
<%= protocol_status_href(@protocol) %>
<% else %>
<%= t("my_modules.protocols.protocol_status_bar.unlinked") %>
<% end %>
&nbsp;
<%= render partial: "my_modules/protocols/protocol_status_bar_buttons.html.erb" %>
<div class="dropdown my-module-protocol-status">
<% if @protocol.linked? %>
<span class="status-label linked">
[<%= protocol_name(@protocol.parent) %>]
</span>
<% else %>
<span class="status-label">
[<%= t("my_modules.protocols.protocol_status_bar.unlinked") %>]
</span>
<% end %>
<a href="#" id="my-module-protocol-info-button" class="status-info" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-info-circle"></i>
</a>
<div class="dropdown-menu status-info-dropdown" aria-labelledby="my-module-protocol-info-button">
<div class="dropdown-body">
<div class="info-line">
<div class="description"><%= t("my_modules.protocols.protocol_status_bar.protocol_created") %></div>
<div class="value"><%= I18n.l(@protocol.created_at, format: :full) %></div>
</div>
<% if @protocol.linked? && can_read_protocol_in_repository?(@protocol.parent) %>
<div class="info-line <%= "new-parent-version" if @protocol.parent_newer? || @protocol.parent_and_self_newer? %>">
<div class="description"><%= t("my_modules.protocols.protocol_status_bar.template_updated") %></div>
<% if @protocol.parent_newer? || @protocol.parent_and_self_newer? %>
<% end %>
<div class="value"><%= I18n.l(@protocol.parent.updated_at, format: :full) %></div>
</div>
<% end %>
<div class="info-line">
<div class="description"><%= t("my_modules.protocols.protocol_status_bar.protocol_updated") %></div>
<div class="value"><%= I18n.l(@protocol.updated_at, format: :full) %></div>
</div>
<% if @protocol.parent_newer? || @protocol.parent_and_self_newer? %>
<div class="notification-line new-parent-version">
<i class="fas fa-sync-alt"></i>
<div class="description"><%= t("my_modules.protocols.protocol_status_bar.messages.template_updated_html") %></div>
</div>
<% end %>
<% if @protocol.newer_than_parent? || @protocol.parent_and_self_newer? %>
<div class="notification-line new-protocol-version">
<i class="fas fa-info-circle"></i>
<div class="description"><%= t("my_modules.protocols.protocol_status_bar.messages.protocol_updated") %></div>
</div>
<% end %>
<% if @protocol.unlinked? %>
<div class="notification-line">
<i class="fas fa-unlink"></i>
<div class="description"><%= t("my_modules.protocols.protocol_status_bar.messages.unlinked") %></div>
</div>
<% end %>
</div>
<div class="dropdown-footer">
<div class="button-container no-padding">
<% if @protocol.linked? && can_manage_protocol_in_module?(@protocol) %>
<%= link_to unlink_modal_protocol_path(@protocol, format: :json), remote: true,
title: t("my_modules.protocols.protocol_status_bar.btns.unlink_title"),
data: { action: "unlink" },
class: "btn btn-light" do %>
<i class="fas fa-unlink"></i>
<%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %>
<% end %>
<% end %>
</div>
<div class="button-container no-padding">
<% if can_read_protocol_in_module?(@protocol) && can_create_protocols_in_repository?(@protocol.team) %>
<%= link_to copy_to_repository_modal_protocol_path(@protocol, format: :json), remote: true,
title: t("my_modules.protocols.protocol_status_bar.btns.save_to_repository_title"),
class: "btn btn-light btn-block",
data: {
action: "copy-to-repository",
hook: "protocol-copy-to-repository"
} do %>
<i class="fas fa-save"></i>
<%= t("my_modules.protocols.protocol_status_bar.btns.save_to_repository") %>
<% end %>
<% end %>
</div>
<div class="button-container no-padding">
<% if @protocol.linked? && can_manage_protocol_in_module?(@protocol) %>
<% if @protocol.parent_newer? || @protocol.parent_and_self_newer? %>
<%= link_to update_from_parent_modal_protocol_path(@protocol, format: :json), remote: true,
title: t("my_modules.protocols.protocol_status_bar.btns.update_self_title"),
class: "btn btn-light pull-right",
data: { action: "update-self" } do %>
<i class="fas fa-sync-alt"></i>
<%= t("my_modules.protocols.protocol_status_bar.btns.update_self") %>
<% end %>
<% elsif @protocol.newer_than_parent? %>
<%= link_to revert_modal_protocol_path(@protocol, format: :json), remote: true,
title: t("my_modules.protocols.protocol_status_bar.btns.revert_title"),
class: "btn btn-light pull-right",
data: { action: "revert" } do %>
<i class="fas fa-undo"></i>
<%= t("my_modules.protocols.protocol_status_bar.btns.revert") %>
<% end %>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
<%= javascript_include_tag("my_modules/protocols/protocol_status_bar") %>

View file

@ -1,149 +0,0 @@
<div class="btn-group">
<% if @protocol.unlinked? %>
<a type="button" class="btn btn-info link-button" tabindex="0" role="button" data-trigger="focus" data-container="body" data-html="true" data-toggle="popover" data-placement="bottom" title="<%= t("my_modules.protocols.protocol_status_bar.btns_unlinked.title") %>" data-content="<%= t("my_modules.protocols.protocol_status_bar.btns_unlinked.text") %>">
<span class="fas fa-book"></span>
&nbsp;
<span class="fas fa-times-sign"></span>
</a>
<a type="button" class="btn btn-info dropdown-toggle link-toggle" disabled="disabled">
<span class="caret"></span>
</a>
<% else %>
<% if @protocol.linked_no_diff? %>
<a type="button" class="btn btn-info link-button" tabindex="0" role="button" data-trigger="focus" data-container="body" data-html="true" data-toggle="popover" data-placement="bottom" title="<%= t("my_modules.protocols.protocol_status_bar.btns_linked_no_diff.title") %>" data-content="<%= t("my_modules.protocols.protocol_status_bar.btns_linked_no_diff.text") %>">
<span class="fas fa-book"></span>
&nbsp;
<span class="fas fa-check-circle"></span>
</a>
<a type="button" class="btn btn-info dropdown-toggle link-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<% if can_manage_protocol_in_module?(@protocol) %>
<li>
<%= link_to unlink_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.unlink_title"), data: { action: "unlink" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.unlink_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %></a>
</li>
<% end %>
</ul>
<% elsif @protocol.newer_than_parent? %>
<a type="button" class="btn btn-warning link-button" tabindex="0" role="button" data-trigger="focus" data-container="body" data-html="true" data-toggle="popover" data-placement="bottom" title="<%= t("my_modules.protocols.protocol_status_bar.btns_newer_than_parent.title") %>" data-content="<%= t("my_modules.protocols.protocol_status_bar.btns_newer_than_parent.text", self_ts: l(@protocol.updated_at, format: :full), parent_ts: l(@protocol.parent.updated_at, format: :full)).html_safe %>">
<span class="fas fa-book"></span>
&nbsp;
<span class="fas fa-arrow-circle-up"></span>
</a>
<a type="button" class="btn btn-warning dropdown-toggle link-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<% if can_read_protocol_in_module?(@protocol) && can_manage_protocol_in_repository?(@protocol.parent) %>
<li>
<%= link_to update_parent_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.update_parent_title"), data: { action: "update-parent" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.update_parent") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.update_parent_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.update_parent") %></a>
</li>
<% end %>
<% if can_manage_protocol_in_module?(@protocol) %>
<li>
<%= link_to revert_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.revert_title"), data: { action: "revert" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.revert") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.revert_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.revert") %></a>
</li>
<% end %>
<% if can_manage_protocol_in_module?(@protocol) %>
<li>
<%= link_to unlink_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.unlink_title"), data: { action: "unlink" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.unlink_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %></a>
</li>
<% end %>
</ul>
<% elsif @protocol.parent_newer? %>
<a type="button" class="btn btn-warning link-button" tabindex="0" role="button" data-trigger="focus" data-container="body" data-html="true" data-toggle="popover" data-placement="bottom" title="<%= t("my_modules.protocols.protocol_status_bar.btns_parent_newer.title") %>" data-content="<%= t("my_modules.protocols.protocol_status_bar.btns_parent_newer.text", self_ts: l(@protocol.updated_at, format: :full), parent_ts: l(@protocol.parent.updated_at, format: :full)).html_safe %>">
<span class="fas fa-book"></span>
&nbsp;
<span class="fas fa-arrow-circle-up"></span>
</a>
<a type="button" class="btn btn-warning dropdown-toggle link-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<%-# For updating module protocol from repository protocol we don't need to check read permission for the repository protocol -%>
<% if can_manage_protocol_in_module?(@protocol) %>
<li><%= link_to update_from_parent_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.update_self_title"), data: { action: "update-self" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.update_self") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.update_self_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.update_self") %></a>
</li>
<% end %>
<% if can_manage_protocol_in_module?(@protocol) %>
<li>
<%= link_to unlink_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.unlink_title"), data: { action: "unlink" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.unlink_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %></a>
</li>
<% end %>
</ul>
<% elsif @protocol.parent_and_self_newer? %>
<a type="button" class="btn btn-warning link-button" tabindex="0" role="button" data-trigger="focus" data-container="body" data-html="true" data-toggle="popover" data-placement="bottom" title="<%= t("my_modules.protocols.protocol_status_bar.btns_parent_and_self_newer.title") %>" data-content="<%= t("my_modules.protocols.protocol_status_bar.btns_parent_and_self_newer.text", self_ts: l(@protocol.updated_at, format: :full), parent_ts: l(@protocol.parent.updated_at, format: :full)).html_safe %>">
<span class="fas fa-book"></span>
&nbsp;
<span class="fas fa-exclamation-triangle"></span>
</a>
<a type="button" class="btn btn-warning dropdown-toggle link-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<% if can_read_protocol_in_module?(@protocol) && can_manage_protocol_in_repository?(@protocol.parent) %>
<li>
<%= link_to update_parent_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.update_parent_title"), data: { action: "update-parent" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.update_parent") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.update_parent_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.update_parent") %></a>
</li>
<% end %>
<%-# For updating module protocol from repository protocol we don't need to check read permission for the repository protocol -%>
<% if can_manage_protocol_in_module?(@protocol) %>
<li>
<%= link_to update_from_parent_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.update_self_title"), data: { action: "update-self" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.update_self") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.update_self_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.update_self") %></a>
</li>
<% end %>
<% if can_manage_protocol_in_module?(@protocol) %>
<li>
<%= link_to unlink_modal_protocol_path(@protocol, format: :json), remote: true, title: t("my_modules.protocols.protocol_status_bar.btns.unlink_title"), data: { action: "unlink" } do %>
<%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %>
<% end %>
</li>
<% else %>
<li class="disabled"><a href="#" title="<%= t("my_modules.protocols.protocol_status_bar.btns.unlink_title") %>"><%= t("my_modules.protocols.protocol_status_bar.btns.unlink") %></a>
</li>
<% end %>
</ul>
<% end %>
<% end %>
</div>

View file

@ -673,41 +673,34 @@ en:
protocols:
head_title: "%{project} | %{module} | Protocols"
protocol_status_bar:
private_parent: "(private)"
unlinked: "(unlinked)"
public_desc: "This protocol is public; everyone from team can see it."
private_desc: "This protocol is private; it is only visible to you."
added_by: "Added by"
added_by_tooltip: "Added at %{ts}"
private_protocol_desc: "The parent protocol is private. Only its author can manage it."
private_parent: "private"
unlinked: "unlinked"
no_description: "No protocol description"
empty_description_edit_label: "Click here to enter Protocol Description (optional)"
keywords: "Keywords"
no_keywords: "no keywords"
protocol_created: "Protocol created:"
template_updated: "Template updated on:"
protocol_updated: "Protocol updated on:"
messages:
template_updated_html: "There is a new version of this protocol avaliable in the protocol repository.<br>Would you like to update it?"
protocol_updated: "The version of this protocol in the task is modified, do you wish to save it as a new protocol template?"
unlinked: "This protocol is not linked to the repository."
btns:
unlink: "Unlink protocol"
unlink_title: "Unlink this protocol from the parent in the repository"
revert: "Revert protocol"
revert_title: "Revert the protocol to the repository version. This will discard any local changes."
update_parent: "Update repository version"
update_parent_title: "Update the repository protocol with this version"
save_to_repository: "Save to repository"
save_to_repository_title: "Copy this protocol to repository"
update_self: "Update from repository"
update_self_title: "Update this protocol with the version stored in the repository"
btns_unlinked:
title: "Unlinked"
text: "This protocol is not stored in the protocol repository."
btns_linked_no_diff:
title: "Linked"
text: "This protocol is linked with the version stored in the protocol repository."
btns_newer_than_parent:
title: "Newer version in task"
text: "This <a href='#' data-toggle='tooltip' data-placement='below' title='Last modified at %{self_ts}'>version</a> of protocol is newer than the <a href='#' data-toggle='tooltip' data-placement='below' title='Last modified at %{parent_ts}'>version</a> stored in protocol repository."
btns_parent_newer:
title: "Newer version in repository"
text: "There is a newer <a href='#' data-toggle='tooltip' data-placement='below' title='Last modified at %{parent_ts}'>version</a> of <a href='#' data-toggle='tooltip' data-placement='below' title='Last modified at %{self_ts}'>this protocol</a> in the repository."
btns_parent_and_self_newer:
title: "Newer version in repository and task"
text: "There is a newer <a href='#' data-toggle='tooltip' data-placement='below' title='Last modified at %{parent_ts}'>version</a> of this protocol in the repository. This <a href='#' data-toggle='tooltip' data-placement='below' title='Last modified at %{self_ts}'>version</a> of protocol is newer than the version stored in protocol repository."
buttons:
load_protocol: "Load Protocol"
load_protocol_from_repository: "from repository"
load_protocol_from_file: "from computer"
export: "Export Protocol"
copy_to_repository: "Copy To Protocol Repository"
confirm_link_update_modal:
unlink_title: "Unlink protocol"
unlink_message: "Are you sure you want to unlink the task protocol from the repository version? This will stop any tracking of changes."