|
|
@@ -44,7 +44,9 @@ module ApplicationHelper |
|
|
|
# @param [optional, Hash] html_options Options passed to link_to |
|
|
|
# @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to |
|
|
|
def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) |
|
|
|
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action]) |
|
|
|
if authorize_for(options[:controller] || params[:controller], options[:action]) |
|
|
|
link_to(name, options, html_options, *parameters_for_method_reference) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
# Displays a link to user's account page if active |
|
|
@@ -194,16 +196,22 @@ module ApplicationHelper |
|
|
|
end |
|
|
|
|
|
|
|
RECORD_LINK = { |
|
|
|
'CustomValue' => ->(custom_value) {link_to_record(custom_value.customized)}, |
|
|
|
'Document' => ->(document) {link_to(document.title, document_path(document))}, |
|
|
|
'Group' => ->(group) {link_to(group.name, group_path(group))}, |
|
|
|
'Issue' => ->(issue) {link_to_issue(issue, :subject => false)}, |
|
|
|
'Message' => ->(message) {link_to_message(message)}, |
|
|
|
'News' => ->(news) {link_to(news.title, news_path(news))}, |
|
|
|
'Project' => ->(project) {link_to_project(project)}, |
|
|
|
'User' => ->(user) {link_to_user(user)}, |
|
|
|
'Version' => ->(version) {link_to_version(version)}, |
|
|
|
'WikiPage' => ->(wiki_page) {link_to(wiki_page.pretty_title, project_wiki_page_path(wiki_page.project, wiki_page.title))} |
|
|
|
'CustomValue' => lambda {|custom_value| link_to_record(custom_value.customized)}, |
|
|
|
'Document' => lambda {|document| link_to(document.title, document_path(document))}, |
|
|
|
'Group' => lambda {|group| link_to(group.name, group_path(group))}, |
|
|
|
'Issue' => lambda {|issue| link_to_issue(issue, :subject => false)}, |
|
|
|
'Message' => lambda {|message| link_to_message(message)}, |
|
|
|
'News' => lambda {|news| link_to(news.title, news_path(news))}, |
|
|
|
'Project' => lambda {|project| link_to_project(project)}, |
|
|
|
'User' => lambda {|user| link_to_user(user)}, |
|
|
|
'Version' => lambda {|version| link_to_version(version)}, |
|
|
|
'WikiPage' => |
|
|
|
lambda do |wiki_page| |
|
|
|
link_to( |
|
|
|
wiki_page.pretty_title, |
|
|
|
project_wiki_page_path(wiki_page.project, wiki_page.title) |
|
|
|
) |
|
|
|
end |
|
|
|
} |
|
|
|
|
|
|
|
def link_to_record(record) |
|
|
@@ -215,8 +223,10 @@ module ApplicationHelper |
|
|
|
ATTACHMENT_CONTAINER_LINK = { |
|
|
|
# Custom list, since project/version attachments are listed in the files |
|
|
|
# view and not in the project/milestone view |
|
|
|
'Project' => ->(project) {link_to(l(:project_module_files), project_files_path(project))}, |
|
|
|
'Version' => ->(version) {link_to(l(:project_module_files), project_files_path(version.project))}, |
|
|
|
'Project' => |
|
|
|
lambda {|project| link_to(l(:project_module_files), project_files_path(project))}, |
|
|
|
'Version' => |
|
|
|
lambda {|version| link_to(l(:project_module_files), project_files_path(version.project))}, |
|
|
|
} |
|
|
|
|
|
|
|
def link_to_attachment_container(attachment_container) |
|
|
@@ -287,7 +297,8 @@ module ApplicationHelper |
|
|
|
end |
|
|
|
|
|
|
|
def wiki_page_path(page, options={}) |
|
|
|
url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options)) |
|
|
|
url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, |
|
|
|
:id => page.title}.merge(options)) |
|
|
|
end |
|
|
|
|
|
|
|
def thumbnail_tag(attachment) |
|
|
@@ -399,7 +410,7 @@ module ApplicationHelper |
|
|
|
# set the project environment to please macros. |
|
|
|
@project = project |
|
|
|
if ancestors.empty? || project.is_descendant_of?(ancestors.last) |
|
|
|
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n" |
|
|
|
s << "<ul class='projects #{ancestors.empty? ? 'root' : nil}'>\n" |
|
|
|
else |
|
|
|
ancestors.pop |
|
|
|
s << "</li>" |
|
|
@@ -573,7 +584,8 @@ module ApplicationHelper |
|
|
|
project_tree(projects) do |project, level| |
|
|
|
name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe |
|
|
|
tag_options = {:value => project.id} |
|
|
|
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project)) |
|
|
|
if project == options[:selected] || (options[:selected].respond_to?(:include?) && |
|
|
|
options[:selected].include?(project)) |
|
|
|
tag_options[:selected] = 'selected' |
|
|
|
else |
|
|
|
tag_options[:selected] = nil |
|
|
@@ -594,10 +606,17 @@ module ApplicationHelper |
|
|
|
def principals_check_box_tags(name, principals) |
|
|
|
s = +'' |
|
|
|
principals.each do |principal| |
|
|
|
s << content_tag('label', |
|
|
|
check_box_tag(name, principal.id, false, :id => nil) + |
|
|
|
(avatar(principal, :size => 16).presence || content_tag('span', nil, :class => "name icon icon-#{principal.class.name.downcase}")) + |
|
|
|
principal) |
|
|
|
s << |
|
|
|
content_tag( |
|
|
|
'label', |
|
|
|
check_box_tag(name, principal.id, false, :id => nil) + |
|
|
|
(avatar(principal, :size => 16).presence || |
|
|
|
content_tag( |
|
|
|
'span', nil, |
|
|
|
:class => "name icon icon-#{principal.class.name.downcase}" |
|
|
|
) |
|
|
|
) + principal |
|
|
|
) |
|
|
|
end |
|
|
|
s.html_safe |
|
|
|
end |
|
|
@@ -610,8 +629,11 @@ module ApplicationHelper |
|
|
|
end |
|
|
|
groups = +'' |
|
|
|
collection.sort.each do |element| |
|
|
|
selected_attribute = ' selected="selected"' if option_value_selected?(element, selected) || element.id.to_s == selected |
|
|
|
(element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>) |
|
|
|
if option_value_selected?(element, selected) || element.id.to_s == selected |
|
|
|
selected_attribute = ' selected="selected"' |
|
|
|
end |
|
|
|
(element.is_a?(Group) ? groups : s) << |
|
|
|
%(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>) |
|
|
|
end |
|
|
|
unless groups.empty? |
|
|
|
s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>) |
|
|
@@ -642,7 +664,10 @@ module ApplicationHelper |
|
|
|
end |
|
|
|
|
|
|
|
def html_hours(text) |
|
|
|
text.gsub(%r{(\d+)([\.:])(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">\2\3</span>').html_safe |
|
|
|
text.gsub( |
|
|
|
%r{(\d+)([\.:])(\d+)}, |
|
|
|
'<span class="hours hours-int">\1</span><span class="hours hours-dec">\2\3</span>' |
|
|
|
).html_safe |
|
|
|
end |
|
|
|
|
|
|
|
def authoring(created, author, options={}) |
|
|
@@ -652,7 +677,9 @@ module ApplicationHelper |
|
|
|
def time_tag(time) |
|
|
|
text = distance_of_time_in_words(Time.now, time) |
|
|
|
if @project |
|
|
|
link_to(text, project_activity_path(@project, :from => User.current.time_to_date(time)), :title => format_time(time)) |
|
|
|
link_to(text, |
|
|
|
project_activity_path(@project, :from => User.current.time_to_date(time)), |
|
|
|
:title => format_time(time)) |
|
|
|
else |
|
|
|
content_tag('abbr', text, :title => format_time(time)) |
|
|
|
end |
|
|
@@ -755,7 +782,9 @@ module ApplicationHelper |
|
|
|
def actions_dropdown(&block) |
|
|
|
content = capture(&block) |
|
|
|
if content.present? |
|
|
|
trigger = content_tag('span', l(:button_actions), :class => 'icon-only icon-actions', :title => l(:button_actions)) |
|
|
|
trigger = |
|
|
|
content_tag('span', l(:button_actions), :class => 'icon-only icon-actions', |
|
|
|
:title => l(:button_actions)) |
|
|
|
trigger = content_tag('span', trigger, :class => 'drdn-trigger') |
|
|
|
content = content_tag('div', content, :class => 'drdn-items') |
|
|
|
content = content_tag('div', content, :class => 'drdn-content') |
|
|
@@ -958,14 +987,21 @@ module ApplicationHelper |
|
|
|
"##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') |
|
|
|
else |
|
|
|
wiki_page_id = page.present? ? Wiki.titleize(page) : nil |
|
|
|
parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil |
|
|
|
parent = |
|
|
|
if wiki_page.nil? && obj.is_a?(WikiContent) && |
|
|
|
obj.page && project == link_project |
|
|
|
obj.page.title |
|
|
|
else |
|
|
|
nil |
|
|
|
end |
|
|
|
url_for(:only_path => only_path, :controller => 'wiki', |
|
|
|
:action => 'show', :project_id => link_project, |
|
|
|
:id => wiki_page_id, :version => nil, :anchor => anchor, |
|
|
|
:parent => parent) |
|
|
|
end |
|
|
|
end |
|
|
|
link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new'))) |
|
|
|
link_to(title.present? ? title.html_safe : h(page), |
|
|
|
url, :class => ('wiki-page' + (wiki_page ? '' : ' new'))) |
|
|
|
else |
|
|
|
# project or wiki doesn't exist |
|
|
|
all |
|
|
@@ -1094,7 +1130,9 @@ module ApplicationHelper |
|
|
|
end |
|
|
|
when 'document' |
|
|
|
if document = Document.visible.find_by_id(oid) |
|
|
|
link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document') |
|
|
|
link = link_to(document.title, |
|
|
|
document_url(document, :only_path => only_path), |
|
|
|
:class => 'document') |
|
|
|
end |
|
|
|
when 'version' |
|
|
|
if version = Version.visible.find_by_id(oid) |
|
|
@@ -1106,7 +1144,9 @@ module ApplicationHelper |
|
|
|
end |
|
|
|
when 'forum' |
|
|
|
if board = Board.visible.find_by_id(oid) |
|
|
|
link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board') |
|
|
|
link = link_to(board.name, |
|
|
|
project_board_url(board.project, board, :only_path => only_path), |
|
|
|
:class => 'board') |
|
|
|
end |
|
|
|
when 'news' |
|
|
|
if news = News.visible.find_by_id(oid) |
|
|
@@ -1125,7 +1165,9 @@ module ApplicationHelper |
|
|
|
case prefix |
|
|
|
when 'document' |
|
|
|
if project && document = project.documents.visible.find_by_title(name) |
|
|
|
link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document') |
|
|
|
link = link_to(document.title, |
|
|
|
document_url(document, :only_path => only_path), |
|
|
|
:class => 'document') |
|
|
|
end |
|
|
|
when 'version' |
|
|
|
if project && version = project.versions.visible.find_by_name(name) |
|
|
@@ -1133,7 +1175,9 @@ module ApplicationHelper |
|
|
|
end |
|
|
|
when 'forum' |
|
|
|
if project && board = project.boards.visible.find_by_name(name) |
|
|
|
link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board') |
|
|
|
link = link_to(board.name, |
|
|
|
project_board_url(board.project, board, :only_path => only_path), |
|
|
|
:class => 'board') |
|
|
|
end |
|
|
|
when 'news' |
|
|
|
if project && news = project.news.visible.find_by_title(name) |
|
|
@@ -1284,33 +1328,40 @@ module ApplicationHelper |
|
|
|
item = strip_tags(content).strip |
|
|
|
anchor = sanitize_anchor_name(item) |
|
|
|
# used for single-file wiki export |
|
|
|
anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) |
|
|
|
if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || |
|
|
|
obj.is_a?(WikiContent::Version)) |
|
|
|
anchor = "#{obj.page.title}_#{anchor}" |
|
|
|
end |
|
|
|
@heading_anchors[anchor] ||= 0 |
|
|
|
idx = (@heading_anchors[anchor] += 1) |
|
|
|
if idx > 1 |
|
|
|
anchor = "#{anchor}-#{idx}" |
|
|
|
end |
|
|
|
@parsed_headings << [level, anchor, item] |
|
|
|
"<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">¶</a></h#{level}>" |
|
|
|
"<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}" \ |
|
|
|
"<a href=\"##{anchor}\" class=\"wiki-anchor\">¶</a></h#{level}>" |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
MACROS_RE = /( |
|
|
|
(!)? # escaping |
|
|
|
( |
|
|
|
\{\{ # opening tag |
|
|
|
([\w]+) # macro name |
|
|
|
(\(([^\n\r]*?)\))? # optional arguments |
|
|
|
([\n\r].*?[\n\r])? # optional block of text |
|
|
|
\}\} # closing tag |
|
|
|
) |
|
|
|
)/mx unless const_defined?(:MACROS_RE) |
|
|
|
|
|
|
|
MACRO_SUB_RE = /( |
|
|
|
\{\{ |
|
|
|
macro\((\d+)\) |
|
|
|
\}\} |
|
|
|
)/x unless const_defined?(:MACRO_SUB_RE) |
|
|
|
unless const_defined?(:MACROS_RE) |
|
|
|
MACROS_RE = /( |
|
|
|
(!)? # escaping |
|
|
|
( |
|
|
|
\{\{ # opening tag |
|
|
|
([\w]+) # macro name |
|
|
|
(\(([^\n\r]*?)\))? # optional arguments |
|
|
|
([\n\r].*?[\n\r])? # optional block of text |
|
|
|
\}\} # closing tag |
|
|
|
) |
|
|
|
)/mx |
|
|
|
end |
|
|
|
unless const_defined?(:MACRO_SUB_RE) |
|
|
|
MACRO_SUB_RE = /( |
|
|
|
\{\{ |
|
|
|
macro\((\d+)\) |
|
|
|
\}\} |
|
|
|
)/x |
|
|
|
end |
|
|
|
|
|
|
|
# Extracts macros from text |
|
|
|
def catch_macros(text) |
|
|
@@ -1545,7 +1596,9 @@ module ApplicationHelper |
|
|
|
|
|
|
|
def calendar_for(field_id) |
|
|
|
include_calendar_headers_tags |
|
|
|
javascript_tag("$(function() { $('##{field_id}').addClass('date').datepickerFallback(datepickerOptions); });") |
|
|
|
javascript_tag( |
|
|
|
"$(function() { $('##{field_id}').addClass('date').datepickerFallback(datepickerOptions); });" |
|
|
|
) |
|
|
|
end |
|
|
|
|
|
|
|
def include_calendar_headers_tags |
|
|
@@ -1560,12 +1613,13 @@ module ApplicationHelper |
|
|
|
start_of_week = start_of_week.to_i % 7 |
|
|
|
tags << |
|
|
|
javascript_tag( |
|
|
|
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " + |
|
|
|
"showOn: 'button', buttonImageOnly: true, buttonImage: '" + |
|
|
|
path_to_image('/images/calendar.png') + |
|
|
|
"', showButtonPanel: true, showWeek: true, showOtherMonths: true, " + |
|
|
|
"selectOtherMonths: true, changeMonth: true, changeYear: true, " + |
|
|
|
"beforeShow: beforeShowDatePicker};") |
|
|
|
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " \ |
|
|
|
"showOn: 'button', buttonImageOnly: true, buttonImage: '" + |
|
|
|
path_to_image('/images/calendar.png') + |
|
|
|
"', showButtonPanel: true, showWeek: true, showOtherMonths: true, " \ |
|
|
|
"selectOtherMonths: true, changeMonth: true, changeYear: true, " \ |
|
|
|
"beforeShow: beforeShowDatePicker};" |
|
|
|
) |
|
|
|
jquery_locale = l('jquery.locale', :default => current_language.to_s) |
|
|
|
unless jquery_locale == 'en' |
|
|
|
tags << javascript_include_tag("i18n/datepicker-#{jquery_locale}.js") |
|
|
@@ -1655,7 +1709,12 @@ module ApplicationHelper |
|
|
|
'application', |
|
|
|
'responsive') |
|
|
|
unless User.current.pref.warn_on_leaving_unsaved == '0' |
|
|
|
tags << "\n".html_safe + javascript_tag("$(window).on('load', function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });") |
|
|
|
warn_text = escape_javascript(l(:text_warn_on_leaving_unsaved)) |
|
|
|
tags << |
|
|
|
"\n".html_safe + |
|
|
|
javascript_tag( |
|
|
|
"$(window).on('load', function(){ warnLeavingUnsaved('#{warn_text}'); });" |
|
|
|
) |
|
|
|
end |
|
|
|
tags |
|
|
|
end |
|
|
@@ -1750,10 +1809,9 @@ module ApplicationHelper |
|
|
|
|
|
|
|
def heads_for_auto_complete(project) |
|
|
|
data_sources = autocomplete_data_sources(project) |
|
|
|
|
|
|
|
javascript_tag( |
|
|
|
"rm = window.rm || {};" + |
|
|
|
"rm.AutoComplete = rm.AutoComplete || {};" + |
|
|
|
"rm = window.rm || {};" \ |
|
|
|
"rm.AutoComplete = rm.AutoComplete || {};" \ |
|
|
|
"rm.AutoComplete.dataSources = '#{data_sources.to_json}';" |
|
|
|
) |
|
|
|
end |