User groups branch added.
git-svn-id: http://redmine.rubyforge.org/svn/branches/work@1372 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module AccountHelper
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module AdminHelper
|
||||
def project_status_options_for_select(selected)
|
||||
options_for_select([[l(:label_all), "*"],
|
||||
[l(:status_active), 1]], selected)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,510 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module ApplicationHelper
|
||||
include Redmine::WikiFormatting::Macros::Definitions
|
||||
|
||||
def current_role
|
||||
@current_role ||= User.current.role_for_project(@project)
|
||||
end
|
||||
|
||||
# Return true if user is authorized for controller/action, otherwise false
|
||||
def authorize_for(controller, action)
|
||||
User.current.allowed_to?({:controller => controller, :action => action}, @project)
|
||||
end
|
||||
|
||||
# Display a link if user is authorized
|
||||
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])
|
||||
end
|
||||
|
||||
# Display a link to user's account page
|
||||
def link_to_user(user)
|
||||
user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
|
||||
end
|
||||
|
||||
def link_to_issue(issue, options={})
|
||||
link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
|
||||
end
|
||||
|
||||
def toggle_link(name, id, options={})
|
||||
onclick = "Element.toggle('#{id}'); "
|
||||
onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
|
||||
onclick << "return false;"
|
||||
link_to(name, "#", :onclick => onclick)
|
||||
end
|
||||
|
||||
def show_and_goto_link(name, id, options={})
|
||||
onclick = "Element.show('#{id}'); "
|
||||
onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
|
||||
onclick << "Element.scrollTo('#{id}'); "
|
||||
onclick << "return false;"
|
||||
link_to(name, "#", options.merge(:onclick => onclick))
|
||||
end
|
||||
|
||||
def image_to_function(name, function, html_options = {})
|
||||
html_options.symbolize_keys!
|
||||
tag(:input, html_options.merge({
|
||||
:type => "image", :src => image_path(name),
|
||||
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
|
||||
}))
|
||||
end
|
||||
|
||||
def prompt_to_remote(name, text, param, url, html_options = {})
|
||||
html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
|
||||
link_to name, {}, html_options
|
||||
end
|
||||
|
||||
def format_date(date)
|
||||
return nil unless date
|
||||
# "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
|
||||
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
|
||||
date.strftime(@date_format)
|
||||
end
|
||||
|
||||
def format_time(time, include_date = true)
|
||||
return nil unless time
|
||||
time = time.to_time if time.is_a?(String)
|
||||
zone = User.current.time_zone
|
||||
if time.utc?
|
||||
local = zone ? zone.adjust(time) : time.getlocal
|
||||
else
|
||||
local = zone ? zone.adjust(time.getutc) : time
|
||||
end
|
||||
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
|
||||
@time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
|
||||
include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
|
||||
end
|
||||
|
||||
def html_hours(text)
|
||||
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
|
||||
end
|
||||
|
||||
def authoring(created, author)
|
||||
time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
|
||||
l(:label_added_time_by, author || 'Anonymous', time_tag)
|
||||
end
|
||||
|
||||
def l_or_humanize(s)
|
||||
l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
|
||||
end
|
||||
|
||||
def day_name(day)
|
||||
l(:general_day_names).split(',')[day-1]
|
||||
end
|
||||
|
||||
def month_name(month)
|
||||
l(:actionview_datehelper_select_month_names).split(',')[month-1]
|
||||
end
|
||||
|
||||
def pagination_links_full(paginator, count=nil, options={})
|
||||
page_param = options.delete(:page_param) || :page
|
||||
url_param = params.dup
|
||||
# don't reuse params if filters are present
|
||||
url_param.clear if url_param.has_key?(:set_filter)
|
||||
|
||||
html = ''
|
||||
html << link_to_remote(('« ' + l(:label_previous)),
|
||||
{:update => 'content',
|
||||
:url => url_param.merge(page_param => paginator.current.previous),
|
||||
:complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
|
||||
|
||||
html << (pagination_links_each(paginator, options) do |n|
|
||||
link_to_remote(n.to_s,
|
||||
{:url => {:params => url_param.merge(page_param => n)},
|
||||
:update => 'content',
|
||||
:complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(:params => url_param.merge(page_param => n))})
|
||||
end || '')
|
||||
|
||||
html << ' ' + link_to_remote((l(:label_next) + ' »'),
|
||||
{:update => 'content',
|
||||
:url => url_param.merge(page_param => paginator.current.next),
|
||||
:complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
|
||||
|
||||
unless count.nil?
|
||||
html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
|
||||
end
|
||||
|
||||
html
|
||||
end
|
||||
|
||||
def per_page_links(selected=nil)
|
||||
url_param = params.dup
|
||||
url_param.clear if url_param.has_key?(:set_filter)
|
||||
|
||||
links = Setting.per_page_options_array.collect do |n|
|
||||
n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
|
||||
{:href => url_for(url_param.merge(:per_page => n))})
|
||||
end
|
||||
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
|
||||
end
|
||||
|
||||
def breadcrumb(*args)
|
||||
content_tag('p', args.join(' » ') + ' » ', :class => 'breadcrumb')
|
||||
end
|
||||
|
||||
def html_title(*args)
|
||||
if args.empty?
|
||||
title = []
|
||||
title << @project.name if @project
|
||||
title += @html_title if @html_title
|
||||
title << Setting.app_title
|
||||
title.compact.join(' - ')
|
||||
else
|
||||
@html_title ||= []
|
||||
@html_title += args
|
||||
end
|
||||
end
|
||||
|
||||
def accesskey(s)
|
||||
Redmine::AccessKeys.key_for s
|
||||
end
|
||||
|
||||
# Formats text according to system settings.
|
||||
# 2 ways to call this method:
|
||||
# * with a String: textilizable(text, options)
|
||||
# * with an object and one of its attribute: textilizable(issue, :description, options)
|
||||
def textilizable(*args)
|
||||
options = args.last.is_a?(Hash) ? args.pop : {}
|
||||
case args.size
|
||||
when 1
|
||||
obj = nil
|
||||
text = args.shift
|
||||
when 2
|
||||
obj = args.shift
|
||||
text = obj.send(args.shift).to_s
|
||||
else
|
||||
raise ArgumentError, 'invalid arguments to textilizable'
|
||||
end
|
||||
return '' if text.blank?
|
||||
|
||||
only_path = options.delete(:only_path) == false ? false : true
|
||||
|
||||
# when using an image link, try to use an attachment, if possible
|
||||
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
|
||||
|
||||
if attachments
|
||||
text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
|
||||
style = $1
|
||||
filename = $6
|
||||
rf = Regexp.new(filename, Regexp::IGNORECASE)
|
||||
# search for the picture in attachments
|
||||
if found = attachments.detect { |att| att.filename =~ rf }
|
||||
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
|
||||
desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
|
||||
alt = desc.blank? ? nil : "(#{desc})"
|
||||
"!#{style}#{image_url}#{alt}!"
|
||||
else
|
||||
"!#{style}#{filename}!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
text = (Setting.text_formatting == 'textile') ?
|
||||
Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
|
||||
simple_format(auto_link(h(text)))
|
||||
|
||||
# different methods for formatting wiki links
|
||||
case options[:wiki_links]
|
||||
when :local
|
||||
# used for local links to html files
|
||||
format_wiki_link = Proc.new {|project, title| "#{title}.html" }
|
||||
when :anchor
|
||||
# used for single-file wiki export
|
||||
format_wiki_link = Proc.new {|project, title| "##{title}" }
|
||||
else
|
||||
format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
|
||||
end
|
||||
|
||||
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
|
||||
|
||||
# Wiki links
|
||||
#
|
||||
# Examples:
|
||||
# [[mypage]]
|
||||
# [[mypage|mytext]]
|
||||
# wiki links can refer other project wikis, using project name or identifier:
|
||||
# [[project:]] -> wiki starting page
|
||||
# [[project:|mytext]]
|
||||
# [[project:mypage]]
|
||||
# [[project:mypage|mytext]]
|
||||
text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
|
||||
link_project = project
|
||||
esc, all, page, title = $1, $2, $3, $5
|
||||
if esc.nil?
|
||||
if page =~ /^([^\:]+)\:(.*)$/
|
||||
link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
|
||||
page = $2
|
||||
title ||= $1 if page.blank?
|
||||
end
|
||||
|
||||
if link_project && link_project.wiki
|
||||
# check if page exists
|
||||
wiki_page = link_project.wiki.find_page(page)
|
||||
link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
|
||||
:class => ('wiki-page' + (wiki_page ? '' : ' new')))
|
||||
else
|
||||
# project or wiki doesn't exist
|
||||
title || page
|
||||
end
|
||||
else
|
||||
all
|
||||
end
|
||||
end
|
||||
|
||||
# Redmine links
|
||||
#
|
||||
# Examples:
|
||||
# Issues:
|
||||
# #52 -> Link to issue #52
|
||||
# Changesets:
|
||||
# r52 -> Link to revision 52
|
||||
# commit:a85130f -> Link to scmid starting with a85130f
|
||||
# Documents:
|
||||
# document#17 -> Link to document with id 17
|
||||
# document:Greetings -> Link to the document with title "Greetings"
|
||||
# document:"Some document" -> Link to the document with title "Some document"
|
||||
# Versions:
|
||||
# version#3 -> Link to version with id 3
|
||||
# version:1.0.0 -> Link to version named "1.0.0"
|
||||
# version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
|
||||
# Attachments:
|
||||
# attachment:file.zip -> Link to the attachment of the current object named file.zip
|
||||
# Source files:
|
||||
# source:some/file -> Link to the file located at /some/file in the project's repository
|
||||
# source:some/file@52 -> Link to the file's revision 52
|
||||
# source:some/file#L120 -> Link to line 120 of the file
|
||||
# source:some/file@52#L120 -> Link to line 120 of the file's revision 52
|
||||
# export:some/file -> Force the download of the file
|
||||
text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version|commit|source|export)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
|
||||
leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
|
||||
link = nil
|
||||
if esc.nil?
|
||||
if prefix.nil? && sep == 'r'
|
||||
if project && (changeset = project.changesets.find_by_revision(oid))
|
||||
link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
|
||||
:class => 'changeset',
|
||||
:title => truncate(changeset.comments, 100))
|
||||
end
|
||||
elsif sep == '#'
|
||||
oid = oid.to_i
|
||||
case prefix
|
||||
when nil
|
||||
if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
|
||||
link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
|
||||
:class => (issue.closed? ? 'issue closed' : 'issue'),
|
||||
:title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
|
||||
link = content_tag('del', link) if issue.closed?
|
||||
end
|
||||
when 'document'
|
||||
if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
|
||||
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
|
||||
:class => 'document'
|
||||
end
|
||||
when 'version'
|
||||
if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
|
||||
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
|
||||
:class => 'version'
|
||||
end
|
||||
end
|
||||
elsif sep == ':'
|
||||
# removes the double quotes if any
|
||||
name = oid.gsub(%r{^"(.*)"$}, "\\1")
|
||||
case prefix
|
||||
when 'document'
|
||||
if project && document = project.documents.find_by_title(name)
|
||||
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
|
||||
:class => 'document'
|
||||
end
|
||||
when 'version'
|
||||
if project && version = project.versions.find_by_name(name)
|
||||
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
|
||||
:class => 'version'
|
||||
end
|
||||
when 'commit'
|
||||
if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
|
||||
link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision}, :class => 'changeset', :title => truncate(changeset.comments, 100)
|
||||
end
|
||||
when 'source', 'export'
|
||||
if project && project.repository
|
||||
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
|
||||
path, rev, anchor = $1, $3, $5
|
||||
link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :path => path,
|
||||
:rev => rev,
|
||||
:anchor => anchor,
|
||||
:format => (prefix == 'export' ? 'raw' : nil)},
|
||||
:class => (prefix == 'export' ? 'source download' : 'source')
|
||||
end
|
||||
when 'attachment'
|
||||
if attachments && attachment = attachments.detect {|a| a.filename == name }
|
||||
link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
|
||||
:class => 'attachment'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
leading + (link || "#{prefix}#{sep}#{oid}")
|
||||
end
|
||||
|
||||
text
|
||||
end
|
||||
|
||||
# Same as Rails' simple_format helper without using paragraphs
|
||||
def simple_format_without_paragraph(text)
|
||||
text.to_s.
|
||||
gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
|
||||
gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
|
||||
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
|
||||
end
|
||||
|
||||
def error_messages_for(object_name, options = {})
|
||||
options = options.symbolize_keys
|
||||
object = instance_variable_get("@#{object_name}")
|
||||
if object && !object.errors.empty?
|
||||
# build full_messages here with controller current language
|
||||
full_messages = []
|
||||
object.errors.each do |attr, msg|
|
||||
next if msg.nil?
|
||||
msg = msg.first if msg.is_a? Array
|
||||
if attr == "base"
|
||||
full_messages << l(msg)
|
||||
else
|
||||
full_messages << "« " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " » " + l(msg) unless attr == "custom_values"
|
||||
end
|
||||
end
|
||||
# retrieve custom values error messages
|
||||
if object.errors[:custom_values]
|
||||
object.custom_values.each do |v|
|
||||
v.errors.each do |attr, msg|
|
||||
next if msg.nil?
|
||||
msg = msg.first if msg.is_a? Array
|
||||
full_messages << "« " + v.custom_field.name + " » " + l(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
content_tag("div",
|
||||
content_tag(
|
||||
options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
|
||||
) +
|
||||
content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
|
||||
"id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
|
||||
)
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def lang_options_for_select(blank=true)
|
||||
(blank ? [["(auto)", ""]] : []) +
|
||||
GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
|
||||
end
|
||||
|
||||
def label_tag_for(name, option_tags = nil, options = {})
|
||||
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
|
||||
content_tag("label", label_text)
|
||||
end
|
||||
|
||||
def labelled_tabular_form_for(name, object, options, &proc)
|
||||
options[:html] ||= {}
|
||||
options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
|
||||
form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
|
||||
end
|
||||
|
||||
def back_url_hidden_field_tag
|
||||
hidden_field_tag 'back_url', (params[:back_url] || request.env['HTTP_REFERER'])
|
||||
end
|
||||
|
||||
def check_all_links(form_name)
|
||||
link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
|
||||
" | " +
|
||||
link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
|
||||
end
|
||||
|
||||
def progress_bar(pcts, options={})
|
||||
pcts = [pcts, pcts] unless pcts.is_a?(Array)
|
||||
pcts[1] = pcts[1] - pcts[0]
|
||||
pcts << (100 - pcts[1] - pcts[0])
|
||||
width = options[:width] || '100px;'
|
||||
legend = options[:legend] || ''
|
||||
content_tag('table',
|
||||
content_tag('tr',
|
||||
(pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
|
||||
(pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
|
||||
(pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
|
||||
), :class => 'progress', :style => "width: #{width};") +
|
||||
content_tag('p', legend, :class => 'pourcent')
|
||||
end
|
||||
|
||||
def context_menu_link(name, url, options={})
|
||||
options[:class] ||= ''
|
||||
if options.delete(:selected)
|
||||
options[:class] << ' icon-checked disabled'
|
||||
options[:disabled] = true
|
||||
end
|
||||
if options.delete(:disabled)
|
||||
options.delete(:method)
|
||||
options.delete(:confirm)
|
||||
options.delete(:onclick)
|
||||
options[:class] << ' disabled'
|
||||
url = '#'
|
||||
end
|
||||
link_to name, url, options
|
||||
end
|
||||
|
||||
def calendar_for(field_id)
|
||||
include_calendar_headers_tags
|
||||
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
|
||||
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
|
||||
end
|
||||
|
||||
def include_calendar_headers_tags
|
||||
unless @calendar_headers_tags_included
|
||||
@calendar_headers_tags_included = true
|
||||
content_for :header_tags do
|
||||
javascript_include_tag('calendar/calendar') +
|
||||
javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
|
||||
javascript_include_tag('calendar/calendar-setup') +
|
||||
stylesheet_link_tag('calendar')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def wikitoolbar_for(field_id)
|
||||
return '' unless Setting.text_formatting == 'textile'
|
||||
|
||||
help_link = l(:setting_text_formatting) + ': ' +
|
||||
link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
|
||||
:onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
|
||||
|
||||
javascript_include_tag('jstoolbar/jstoolbar') +
|
||||
javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
|
||||
javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
|
||||
end
|
||||
|
||||
def content_for(name, content = nil, &block)
|
||||
@has_content ||= {}
|
||||
@has_content[name] = true
|
||||
super(name, content, &block)
|
||||
end
|
||||
|
||||
def has_content?(name)
|
||||
(@has_content && @has_content[name]) || false
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module AttachmentsHelper
|
||||
# displays the links to a collection of attachments
|
||||
def link_to_attachments(attachments, options = {})
|
||||
if attachments.any?
|
||||
render :partial => 'attachments/links', :locals => {:attachments => attachments, :options => options}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module AuthSourcesHelper
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module BoardsHelper
|
||||
end
|
||||
@@ -0,0 +1,84 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module CustomFieldsHelper
|
||||
|
||||
def custom_fields_tabs
|
||||
tabs = [{:name => 'IssueCustomField', :label => :label_issue_plural},
|
||||
{:name => 'ProjectCustomField', :label => :label_project_plural},
|
||||
{:name => 'UserCustomField', :label => :label_user_plural}
|
||||
]
|
||||
end
|
||||
|
||||
# Return custom field html tag corresponding to its format
|
||||
def custom_field_tag(custom_value)
|
||||
custom_field = custom_value.custom_field
|
||||
field_name = "custom_fields[#{custom_field.id}]"
|
||||
field_id = "custom_fields_#{custom_field.id}"
|
||||
|
||||
case custom_field.field_format
|
||||
when "date"
|
||||
text_field('custom_value', 'value', :name => field_name, :id => field_id, :size => 10) +
|
||||
calendar_for(field_id)
|
||||
when "text"
|
||||
text_area 'custom_value', 'value', :name => field_name, :id => field_id, :rows => 3, :style => 'width:99%'
|
||||
when "bool"
|
||||
check_box 'custom_value', 'value', :name => field_name, :id => field_id
|
||||
when "list"
|
||||
select 'custom_value', 'value', custom_field.possible_values, { :include_blank => true }, :name => field_name, :id => field_id
|
||||
else
|
||||
text_field 'custom_value', 'value', :name => field_name, :id => field_id
|
||||
end
|
||||
end
|
||||
|
||||
# Return custom field label tag
|
||||
def custom_field_label_tag(custom_value)
|
||||
content_tag "label", custom_value.custom_field.name +
|
||||
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
|
||||
:for => "custom_fields_#{custom_value.custom_field.id}",
|
||||
:class => (custom_value.errors.empty? ? nil : "error" )
|
||||
end
|
||||
|
||||
# Return custom field tag with its label tag
|
||||
def custom_field_tag_with_label(custom_value)
|
||||
custom_field_label_tag(custom_value) + custom_field_tag(custom_value)
|
||||
end
|
||||
|
||||
# Return a string used to display a custom value
|
||||
def show_value(custom_value)
|
||||
return "" unless custom_value
|
||||
format_value(custom_value.value, custom_value.custom_field.field_format)
|
||||
end
|
||||
|
||||
# Return a string used to display a custom value
|
||||
def format_value(value, field_format)
|
||||
return "" unless value && !value.empty?
|
||||
case field_format
|
||||
when "date"
|
||||
begin; format_date(value.to_date); rescue; value end
|
||||
when "bool"
|
||||
l_YesNo(value == "1")
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
# Return an array of custom field formats which can be used in select_tag
|
||||
def custom_field_formats_for_select
|
||||
CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module DocumentsHelper
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module EnumerationsHelper
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require 'iconv'
|
||||
require 'rfpdf/chinese'
|
||||
|
||||
module IfpdfHelper
|
||||
|
||||
class IFPDF < FPDF
|
||||
include GLoc
|
||||
attr_accessor :footer_date
|
||||
|
||||
def initialize(lang)
|
||||
super()
|
||||
set_language_if_valid lang
|
||||
case current_language.to_s
|
||||
when 'ja'
|
||||
extend(PDF_Japanese)
|
||||
AddSJISFont()
|
||||
@font_for_content = 'SJIS'
|
||||
@font_for_footer = 'SJIS'
|
||||
when 'zh'
|
||||
extend(PDF_Chinese)
|
||||
AddGBFont()
|
||||
@font_for_content = 'GB'
|
||||
@font_for_footer = 'GB'
|
||||
when 'zh-tw'
|
||||
extend(PDF_Chinese)
|
||||
AddBig5Font()
|
||||
@font_for_content = 'Big5'
|
||||
@font_for_footer = 'Big5'
|
||||
else
|
||||
@font_for_content = 'Arial'
|
||||
@font_for_footer = 'Helvetica'
|
||||
end
|
||||
SetCreator("redMine #{Redmine::VERSION}")
|
||||
SetFont(@font_for_content)
|
||||
end
|
||||
|
||||
def SetFontStyle(style, size)
|
||||
SetFont(@font_for_content, style, size)
|
||||
end
|
||||
|
||||
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
|
||||
@ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
|
||||
# these quotation marks are not correctly rendered in the pdf
|
||||
txt = txt.gsub(/[“”]/, '"') if txt
|
||||
txt = begin
|
||||
# 0x5c char handling
|
||||
txtar = txt.split('\\')
|
||||
txtar << '' if txt[-1] == ?\\
|
||||
txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
|
||||
rescue
|
||||
txt
|
||||
end || ''
|
||||
super w,h,txt,border,ln,align,fill,link
|
||||
end
|
||||
|
||||
def Footer
|
||||
SetFont(@font_for_footer, 'I', 8)
|
||||
SetY(-15)
|
||||
SetX(15)
|
||||
Cell(0, 5, @footer_date, 0, 0, 'L')
|
||||
SetY(-15)
|
||||
SetX(-30)
|
||||
Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module IssueCategoriesHelper
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module IssueRelationsHelper
|
||||
def collection_for_relation_type_select
|
||||
values = IssueRelation::TYPES
|
||||
values.keys.sort{|x,y| values[x][:order] <=> values[y][:order]}.collect{|k| [l(values[k][:name]), k]}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module IssueStatusesHelper
|
||||
end
|
||||
@@ -0,0 +1,177 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require 'csv'
|
||||
|
||||
module IssuesHelper
|
||||
include ApplicationHelper
|
||||
|
||||
def render_issue_tooltip(issue)
|
||||
@cached_label_start_date ||= l(:field_start_date)
|
||||
@cached_label_due_date ||= l(:field_due_date)
|
||||
@cached_label_assigned_to ||= l(:field_assigned_to)
|
||||
@cached_label_priority ||= l(:field_priority)
|
||||
|
||||
link_to_issue(issue) + ": #{h(issue.subject)}<br /><br />" +
|
||||
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
|
||||
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
|
||||
"<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
|
||||
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
|
||||
end
|
||||
|
||||
def sidebar_queries
|
||||
unless @sidebar_queries
|
||||
# User can see public queries and his own queries
|
||||
visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
|
||||
# Project specific queries and global queries
|
||||
visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
|
||||
@sidebar_queries = Query.find(:all,
|
||||
:order => "name ASC",
|
||||
:conditions => visible.conditions)
|
||||
end
|
||||
@sidebar_queries
|
||||
end
|
||||
|
||||
def show_detail(detail, no_html=false)
|
||||
case detail.property
|
||||
when 'attr'
|
||||
label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
|
||||
case detail.prop_key
|
||||
when 'due_date', 'start_date'
|
||||
value = format_date(detail.value.to_date) if detail.value
|
||||
old_value = format_date(detail.old_value.to_date) if detail.old_value
|
||||
when 'status_id'
|
||||
s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
|
||||
s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
|
||||
when 'assigned_to_id'
|
||||
u = User.find_by_id(detail.value) and value = u.name if detail.value
|
||||
u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
|
||||
when 'priority_id'
|
||||
e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
|
||||
e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
|
||||
when 'category_id'
|
||||
c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
|
||||
c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
|
||||
when 'fixed_version_id'
|
||||
v = Version.find_by_id(detail.value) and value = v.name if detail.value
|
||||
v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
|
||||
end
|
||||
when 'cf'
|
||||
custom_field = CustomField.find_by_id(detail.prop_key)
|
||||
if custom_field
|
||||
label = custom_field.name
|
||||
value = format_value(detail.value, custom_field.field_format) if detail.value
|
||||
old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
|
||||
end
|
||||
when 'attachment'
|
||||
label = l(:label_attachment)
|
||||
end
|
||||
|
||||
label ||= detail.prop_key
|
||||
value ||= detail.value
|
||||
old_value ||= detail.old_value
|
||||
|
||||
unless no_html
|
||||
label = content_tag('strong', label)
|
||||
old_value = content_tag("i", h(old_value)) if detail.old_value
|
||||
old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
|
||||
if detail.property == 'attachment' && !value.blank? && Attachment.find_by_id(detail.prop_key)
|
||||
# Link to the attachment if it has not been removed
|
||||
value = link_to(value, :controller => 'attachments', :action => 'download', :id => detail.prop_key)
|
||||
else
|
||||
value = content_tag("i", h(value)) if value
|
||||
end
|
||||
end
|
||||
|
||||
if !detail.value.blank?
|
||||
case detail.property
|
||||
when 'attr', 'cf'
|
||||
if !detail.old_value.blank?
|
||||
label + " " + l(:text_journal_changed, old_value, value)
|
||||
else
|
||||
label + " " + l(:text_journal_set_to, value)
|
||||
end
|
||||
when 'attachment'
|
||||
"#{label} #{value} #{l(:label_added)}"
|
||||
end
|
||||
else
|
||||
case detail.property
|
||||
when 'attr', 'cf'
|
||||
label + " " + l(:text_journal_deleted) + " (#{old_value})"
|
||||
when 'attachment'
|
||||
"#{label} #{old_value} #{l(:label_deleted)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def issues_to_csv(issues, project = nil)
|
||||
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
|
||||
export = StringIO.new
|
||||
CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
|
||||
# csv header fields
|
||||
headers = [ "#",
|
||||
l(:field_status),
|
||||
l(:field_project),
|
||||
l(:field_tracker),
|
||||
l(:field_priority),
|
||||
l(:field_subject),
|
||||
l(:field_assigned_to),
|
||||
l(:field_category),
|
||||
l(:field_fixed_version),
|
||||
l(:field_author),
|
||||
l(:field_start_date),
|
||||
l(:field_due_date),
|
||||
l(:field_done_ratio),
|
||||
l(:field_estimated_hours),
|
||||
l(:field_created_on),
|
||||
l(:field_updated_on)
|
||||
]
|
||||
# Export project custom fields if project is given
|
||||
# otherwise export custom fields marked as "For all projects"
|
||||
custom_fields = project.nil? ? IssueCustomField.for_all : project.all_custom_fields
|
||||
custom_fields.each {|f| headers << f.name}
|
||||
# Description in the last column
|
||||
headers << l(:field_description)
|
||||
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
|
||||
# csv lines
|
||||
issues.each do |issue|
|
||||
fields = [issue.id,
|
||||
issue.status.name,
|
||||
issue.project.name,
|
||||
issue.tracker.name,
|
||||
issue.priority.name,
|
||||
issue.subject,
|
||||
issue.assigned_to,
|
||||
issue.category,
|
||||
issue.fixed_version,
|
||||
issue.author.name,
|
||||
format_date(issue.start_date),
|
||||
format_date(issue.due_date),
|
||||
issue.done_ratio,
|
||||
issue.estimated_hours,
|
||||
format_time(issue.created_on),
|
||||
format_time(issue.updated_on)
|
||||
]
|
||||
custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
|
||||
fields << issue.description
|
||||
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
|
||||
end
|
||||
end
|
||||
export.rewind
|
||||
export
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module JournalsHelper
|
||||
def render_notes(journal, options={})
|
||||
content = ''
|
||||
editable = journal.editable_by?(User.current)
|
||||
if editable && !journal.notes.blank?
|
||||
links = []
|
||||
links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
|
||||
{ :controller => 'journals', :action => 'edit', :id => journal },
|
||||
:title => l(:button_edit))
|
||||
content << content_tag('div', links.join(' '), :class => 'contextual')
|
||||
end
|
||||
content << textilizable(journal, :notes)
|
||||
content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => (editable ? 'wiki editable' : 'wiki'))
|
||||
end
|
||||
|
||||
def link_to_in_place_notes_editor(text, field_id, url, options={})
|
||||
onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
|
||||
link_to text, '#', options.merge(:onclick => onclick)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module MembersHelper
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module MessagesHelper
|
||||
|
||||
def link_to_message(message)
|
||||
return '' unless message
|
||||
link_to h(truncate(message.subject, 60)), :controller => 'messages',
|
||||
:action => 'show',
|
||||
:board_id => message.board_id,
|
||||
:id => message.root,
|
||||
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module MyHelper
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module NewsHelper
|
||||
end
|
||||
@@ -0,0 +1,194 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module ProjectsHelper
|
||||
def link_to_version(version, options = {})
|
||||
return '' unless version && version.is_a?(Version)
|
||||
link_to h(version.name), { :controller => 'versions', :action => 'show', :id => version }, options
|
||||
end
|
||||
|
||||
def format_activity_day(date)
|
||||
date == Date.today ? l(:label_today).titleize : format_date(date)
|
||||
end
|
||||
|
||||
def format_activity_description(text)
|
||||
h(truncate(text, 250))
|
||||
end
|
||||
|
||||
def project_settings_tabs
|
||||
tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
|
||||
{:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
|
||||
{:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
|
||||
{:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
|
||||
{:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
|
||||
{:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
|
||||
{:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
|
||||
{:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural}
|
||||
]
|
||||
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
|
||||
end
|
||||
|
||||
# Generates a gantt image
|
||||
# Only defined if RMagick is avalaible
|
||||
def gantt_image(events, date_from, months, zoom)
|
||||
date_to = (date_from >> months)-1
|
||||
show_weeks = zoom > 1
|
||||
show_days = zoom > 2
|
||||
|
||||
subject_width = 320
|
||||
header_heigth = 18
|
||||
# width of one day in pixels
|
||||
zoom = zoom*2
|
||||
g_width = (date_to - date_from + 1)*zoom
|
||||
g_height = 20 * events.length + 20
|
||||
headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
|
||||
height = g_height + headers_heigth
|
||||
|
||||
imgl = Magick::ImageList.new
|
||||
imgl.new_image(subject_width+g_width+1, height)
|
||||
gc = Magick::Draw.new
|
||||
|
||||
# Subjects
|
||||
top = headers_heigth + 20
|
||||
gc.fill('black')
|
||||
gc.stroke('transparent')
|
||||
gc.stroke_width(1)
|
||||
events.each do |i|
|
||||
gc.text(4, top + 2, (i.is_a?(Issue) ? i.subject : i.name))
|
||||
top = top + 20
|
||||
end
|
||||
|
||||
# Months headers
|
||||
month_f = date_from
|
||||
left = subject_width
|
||||
months.times do
|
||||
width = ((month_f >> 1) - month_f) * zoom
|
||||
gc.fill('white')
|
||||
gc.stroke('grey')
|
||||
gc.stroke_width(1)
|
||||
gc.rectangle(left, 0, left + width, height)
|
||||
gc.fill('black')
|
||||
gc.stroke('transparent')
|
||||
gc.stroke_width(1)
|
||||
gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
|
||||
left = left + width
|
||||
month_f = month_f >> 1
|
||||
end
|
||||
|
||||
# Weeks headers
|
||||
if show_weeks
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
if date_from.cwday == 1
|
||||
# date_from is monday
|
||||
week_f = date_from
|
||||
else
|
||||
# find next monday after date_from
|
||||
week_f = date_from + (7 - date_from.cwday + 1)
|
||||
width = (7 - date_from.cwday + 1) * zoom
|
||||
gc.fill('white')
|
||||
gc.stroke('grey')
|
||||
gc.stroke_width(1)
|
||||
gc.rectangle(left, header_heigth, left + width, 2*header_heigth + g_height-1)
|
||||
left = left + width
|
||||
end
|
||||
while week_f <= date_to
|
||||
width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom
|
||||
gc.fill('white')
|
||||
gc.stroke('grey')
|
||||
gc.stroke_width(1)
|
||||
gc.rectangle(left.round, header_heigth, left.round + width, 2*header_heigth + g_height-1)
|
||||
gc.fill('black')
|
||||
gc.stroke('transparent')
|
||||
gc.stroke_width(1)
|
||||
gc.text(left.round + 2, header_heigth + 14, week_f.cweek.to_s)
|
||||
left = left + width
|
||||
week_f = week_f+7
|
||||
end
|
||||
end
|
||||
|
||||
# Days details (week-end in grey)
|
||||
if show_days
|
||||
left = subject_width
|
||||
height = g_height + header_heigth - 1
|
||||
wday = date_from.cwday
|
||||
(date_to - date_from + 1).to_i.times do
|
||||
width = zoom
|
||||
gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
|
||||
gc.stroke('grey')
|
||||
gc.stroke_width(1)
|
||||
gc.rectangle(left, 2*header_heigth, left + width, 2*header_heigth + g_height-1)
|
||||
left = left + width
|
||||
wday = wday + 1
|
||||
wday = 1 if wday > 7
|
||||
end
|
||||
end
|
||||
|
||||
# border
|
||||
gc.fill('transparent')
|
||||
gc.stroke('grey')
|
||||
gc.stroke_width(1)
|
||||
gc.rectangle(0, 0, subject_width+g_width, headers_heigth)
|
||||
gc.stroke('black')
|
||||
gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
|
||||
|
||||
# content
|
||||
top = headers_heigth + 20
|
||||
gc.stroke('transparent')
|
||||
events.each do |i|
|
||||
if i.is_a?(Issue)
|
||||
i_start_date = (i.start_date >= date_from ? i.start_date : date_from )
|
||||
i_end_date = (i.due_date <= date_to ? i.due_date : date_to )
|
||||
i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
|
||||
i_done_date = (i_done_date <= date_from ? date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= date_to ? date_to : i_done_date )
|
||||
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
|
||||
|
||||
i_left = subject_width + ((i_start_date - date_from)*zoom).floor
|
||||
i_width = ((i_end_date - i_start_date + 1)*zoom).floor # total width of the issue
|
||||
d_width = ((i_done_date - i_start_date)*zoom).floor # done width
|
||||
l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor : 0 # delay width
|
||||
|
||||
gc.fill('grey')
|
||||
gc.rectangle(i_left, top, i_left + i_width, top - 6)
|
||||
gc.fill('red')
|
||||
gc.rectangle(i_left, top, i_left + l_width, top - 6) if l_width > 0
|
||||
gc.fill('blue')
|
||||
gc.rectangle(i_left, top, i_left + d_width, top - 6) if d_width > 0
|
||||
gc.fill('black')
|
||||
gc.text(i_left + i_width + 5,top + 1, "#{i.status.name} #{i.done_ratio}%")
|
||||
else
|
||||
i_left = subject_width + ((i.start_date - date_from)*zoom).floor
|
||||
gc.fill('green')
|
||||
gc.rectangle(i_left, top, i_left + 6, top - 6)
|
||||
gc.fill('black')
|
||||
gc.text(i_left + 11, top + 1, i.name)
|
||||
end
|
||||
top = top + 20
|
||||
end
|
||||
|
||||
# today red line
|
||||
if Date.today >= date_from and Date.today <= date_to
|
||||
gc.stroke('red')
|
||||
x = (Date.today-date_from+1)*zoom + subject_width
|
||||
gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
|
||||
end
|
||||
|
||||
gc.draw(imgl)
|
||||
imgl
|
||||
end if Object.const_defined?(:Magick)
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module QueriesHelper
|
||||
|
||||
def operators_for_select(filter_type)
|
||||
Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
|
||||
end
|
||||
|
||||
def column_header(column)
|
||||
column.sortable ? sort_header_tag(column.sortable, :caption => column.caption,
|
||||
:default_order => column.default_order) :
|
||||
content_tag('th', column.caption)
|
||||
end
|
||||
|
||||
def column_content(column, issue)
|
||||
if column.is_a?(QueryCustomFieldColumn)
|
||||
cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
|
||||
show_value(cv)
|
||||
else
|
||||
value = issue.send(column.name)
|
||||
if value.is_a?(Date)
|
||||
format_date(value)
|
||||
elsif value.is_a?(Time)
|
||||
format_time(value)
|
||||
else
|
||||
case column.name
|
||||
when :subject
|
||||
h((@project.nil? || @project != issue.project) ? "#{issue.project.name} - " : '') +
|
||||
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
|
||||
when :done_ratio
|
||||
progress_bar(value, :width => '80px')
|
||||
else
|
||||
h(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module ReportsHelper
|
||||
|
||||
def aggregate(data, criteria)
|
||||
a = 0
|
||||
data.each { |row|
|
||||
match = 1
|
||||
criteria.each { |k, v|
|
||||
match = 0 unless (row[k].to_s == v.to_s) || (k == 'closed' && row[k] == (v == 0 ? "f" : "t"))
|
||||
} unless criteria.nil?
|
||||
a = a + row["total"].to_i if match == 1
|
||||
} unless data.nil?
|
||||
a
|
||||
end
|
||||
|
||||
def aggregate_link(data, criteria, *args)
|
||||
a = aggregate data, criteria
|
||||
a > 0 ? link_to(a, *args) : '-'
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,98 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require 'coderay'
|
||||
require 'coderay/helpers/file_type'
|
||||
require 'iconv'
|
||||
|
||||
module RepositoriesHelper
|
||||
def syntax_highlight(name, content)
|
||||
type = CodeRay::FileType[name]
|
||||
type ? CodeRay.scan(content, type).html : h(content)
|
||||
end
|
||||
|
||||
def format_revision(txt)
|
||||
txt.to_s[0,8]
|
||||
end
|
||||
|
||||
def to_utf8(str)
|
||||
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
|
||||
@encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
|
||||
@encodings.each do |encoding|
|
||||
begin
|
||||
return Iconv.conv('UTF-8', encoding, str)
|
||||
rescue Iconv::Failure
|
||||
# do nothing here and try the next encoding
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def repository_field_tags(form, repository)
|
||||
method = repository.class.name.demodulize.underscore + "_field_tags"
|
||||
send(method, form, repository) if repository.is_a?(Repository) && respond_to?(method)
|
||||
end
|
||||
|
||||
def scm_select_tag(repository)
|
||||
container = [[]]
|
||||
REDMINE_SUPPORTED_SCM.each {|scm| container << ["Repository::#{scm}".constantize.scm_name, scm]}
|
||||
select_tag('repository_scm',
|
||||
options_for_select(container, repository.class.name.demodulize),
|
||||
:disabled => (repository && !repository.new_record?),
|
||||
:onchange => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)")
|
||||
)
|
||||
end
|
||||
|
||||
def with_leading_slash(path)
|
||||
path.to_s.starts_with?('/') ? path : "/#{path}"
|
||||
end
|
||||
|
||||
def without_leading_slash(path)
|
||||
path.gsub(%r{^/+}, '')
|
||||
end
|
||||
|
||||
def subversion_field_tags(form, repository)
|
||||
content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
|
||||
'<br />(http://, https://, svn://, file:///)') +
|
||||
content_tag('p', form.text_field(:login, :size => 30)) +
|
||||
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
|
||||
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
|
||||
:onfocus => "this.value=''; this.name='repository[password]';",
|
||||
:onchange => "this.name='repository[password]';"))
|
||||
end
|
||||
|
||||
def darcs_field_tags(form, repository)
|
||||
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
|
||||
end
|
||||
|
||||
def mercurial_field_tags(form, repository)
|
||||
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
|
||||
end
|
||||
|
||||
def git_field_tags(form, repository)
|
||||
content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
|
||||
end
|
||||
|
||||
def cvs_field_tags(form, repository)
|
||||
content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
|
||||
content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
|
||||
end
|
||||
|
||||
def bazaar_field_tags(form, repository)
|
||||
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module RolesHelper
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module SearchHelper
|
||||
def highlight_tokens(text, tokens)
|
||||
return text unless text && tokens && !tokens.empty?
|
||||
regexp = Regexp.new "(#{tokens.join('|')})", Regexp::IGNORECASE
|
||||
result = ''
|
||||
text.split(regexp).each_with_index do |words, i|
|
||||
if result.length > 1200
|
||||
# maximum length of the preview reached
|
||||
result << '...'
|
||||
break
|
||||
end
|
||||
if i.even?
|
||||
result << h(words.length > 100 ? "#{words[0..44]} ... #{words[-45..-1]}" : words)
|
||||
else
|
||||
t = (tokens.index(words.downcase) || 0) % 4
|
||||
result << content_tag('span', h(words), :class => "highlight token-#{t}")
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module SettingsHelper
|
||||
def administration_settings_tabs
|
||||
tabs = [{:name => 'general', :partial => 'settings/general', :label => :label_general},
|
||||
{:name => 'authentication', :partial => 'settings/authentication', :label => :label_authentication},
|
||||
{:name => 'issues', :partial => 'settings/issues', :label => :label_issue_tracking},
|
||||
{:name => 'notifications', :partial => 'settings/notifications', :label => l(:field_mail_notification)},
|
||||
{:name => 'repositories', :partial => 'settings/repositories', :label => :label_repository_plural}
|
||||
]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,160 @@
|
||||
# Helpers to sort tables using clickable column headers.
|
||||
#
|
||||
# Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
|
||||
# License: This source code is released under the MIT license.
|
||||
#
|
||||
# - Consecutive clicks toggle the column's sort order.
|
||||
# - Sort state is maintained by a session hash entry.
|
||||
# - Icon image identifies sort column and state.
|
||||
# - Typically used in conjunction with the Pagination module.
|
||||
#
|
||||
# Example code snippets:
|
||||
#
|
||||
# Controller:
|
||||
#
|
||||
# helper :sort
|
||||
# include SortHelper
|
||||
#
|
||||
# def list
|
||||
# sort_init 'last_name'
|
||||
# sort_update
|
||||
# @items = Contact.find_all nil, sort_clause
|
||||
# end
|
||||
#
|
||||
# Controller (using Pagination module):
|
||||
#
|
||||
# helper :sort
|
||||
# include SortHelper
|
||||
#
|
||||
# def list
|
||||
# sort_init 'last_name'
|
||||
# sort_update
|
||||
# @contact_pages, @items = paginate :contacts,
|
||||
# :order_by => sort_clause,
|
||||
# :per_page => 10
|
||||
# end
|
||||
#
|
||||
# View (table header in list.rhtml):
|
||||
#
|
||||
# <thead>
|
||||
# <tr>
|
||||
# <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
|
||||
# <%= sort_header_tag('last_name', :caption => 'Name') %>
|
||||
# <%= sort_header_tag('phone') %>
|
||||
# <%= sort_header_tag('address', :width => 200) %>
|
||||
# </tr>
|
||||
# </thead>
|
||||
#
|
||||
# - The ascending and descending sort icon images are sort_asc.png and
|
||||
# sort_desc.png and reside in the application's images directory.
|
||||
# - Introduces instance variables: @sort_name, @sort_default.
|
||||
# - Introduces params :sort_key and :sort_order.
|
||||
#
|
||||
module SortHelper
|
||||
|
||||
# Initializes the default sort column (default_key) and sort order
|
||||
# (default_order).
|
||||
#
|
||||
# - default_key is a column attribute name.
|
||||
# - default_order is 'asc' or 'desc'.
|
||||
# - name is the name of the session hash entry that stores the sort state,
|
||||
# defaults to '<controller_name>_sort'.
|
||||
#
|
||||
def sort_init(default_key, default_order='asc', name=nil)
|
||||
@sort_name = name || params[:controller] + params[:action] + '_sort'
|
||||
@sort_default = {:key => default_key, :order => default_order}
|
||||
end
|
||||
|
||||
# Updates the sort state. Call this in the controller prior to calling
|
||||
# sort_clause.
|
||||
#
|
||||
def sort_update()
|
||||
if params[:sort_key]
|
||||
sort = {:key => params[:sort_key], :order => params[:sort_order]}
|
||||
elsif session[@sort_name]
|
||||
sort = session[@sort_name] # Previous sort.
|
||||
else
|
||||
sort = @sort_default
|
||||
end
|
||||
session[@sort_name] = sort
|
||||
end
|
||||
|
||||
# Returns an SQL sort clause corresponding to the current sort state.
|
||||
# Use this to sort the controller's table items collection.
|
||||
#
|
||||
def sort_clause()
|
||||
session[@sort_name][:key] + ' ' + session[@sort_name][:order]
|
||||
end
|
||||
|
||||
# Returns a link which sorts by the named column.
|
||||
#
|
||||
# - column is the name of an attribute in the sorted record collection.
|
||||
# - The optional caption explicitly specifies the displayed link text.
|
||||
# - A sort icon image is positioned to the right of the sort link.
|
||||
#
|
||||
def sort_link(column, caption, default_order)
|
||||
key, order = session[@sort_name][:key], session[@sort_name][:order]
|
||||
if key == column
|
||||
if order.downcase == 'asc'
|
||||
icon = 'sort_asc.png'
|
||||
order = 'desc'
|
||||
else
|
||||
icon = 'sort_desc.png'
|
||||
order = 'asc'
|
||||
end
|
||||
else
|
||||
icon = nil
|
||||
order = default_order
|
||||
end
|
||||
caption = titleize(Inflector::humanize(column)) unless caption
|
||||
|
||||
sort_options = { :sort_key => column, :sort_order => order }
|
||||
# don't reuse params if filters are present
|
||||
url_options = params.has_key?(:set_filter) ? sort_options : params.merge(sort_options)
|
||||
|
||||
link_to_remote(caption,
|
||||
{:update => "content", :url => url_options},
|
||||
{:href => url_for(url_options)}) +
|
||||
(icon ? nbsp(2) + image_tag(icon) : '')
|
||||
end
|
||||
|
||||
# Returns a table header <th> tag with a sort link for the named column
|
||||
# attribute.
|
||||
#
|
||||
# Options:
|
||||
# :caption The displayed link name (defaults to titleized column name).
|
||||
# :title The tag's 'title' attribute (defaults to 'Sort by :caption').
|
||||
#
|
||||
# Other options hash entries generate additional table header tag attributes.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
|
||||
#
|
||||
# Renders:
|
||||
#
|
||||
# <th title="Sort by contact ID" width="40">
|
||||
# <a href="/contact/list?sort_order=desc&sort_key=id">Id</a>
|
||||
# <img alt="Sort_asc" src="/images/sort_asc.png" />
|
||||
# </th>
|
||||
#
|
||||
def sort_header_tag(column, options = {})
|
||||
caption = options.delete(:caption) || titleize(Inflector::humanize(column))
|
||||
default_order = options.delete(:default_order) || 'asc'
|
||||
options[:title]= l(:label_sort_by, "\"#{caption}\"") unless options[:title]
|
||||
content_tag('th', sort_link(column, caption, default_order), options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Return n non-breaking spaces.
|
||||
def nbsp(n)
|
||||
' ' * n
|
||||
end
|
||||
|
||||
# Return capitalized title.
|
||||
def titleize(title)
|
||||
title.split.map {|w| w.capitalize }.join(' ')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,135 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module TimelogHelper
|
||||
def select_hours(data, criteria, value)
|
||||
data.select {|row| row[criteria] == value}
|
||||
end
|
||||
|
||||
def sum_hours(data)
|
||||
sum = 0
|
||||
data.each do |row|
|
||||
sum += row['hours'].to_f
|
||||
end
|
||||
sum
|
||||
end
|
||||
|
||||
def options_for_period_select(value)
|
||||
options_for_select([[l(:label_all_time), 'all'],
|
||||
[l(:label_today), 'today'],
|
||||
[l(:label_yesterday), 'yesterday'],
|
||||
[l(:label_this_week), 'current_week'],
|
||||
[l(:label_last_week), 'last_week'],
|
||||
[l(:label_last_n_days, 7), '7_days'],
|
||||
[l(:label_this_month), 'current_month'],
|
||||
[l(:label_last_month), 'last_month'],
|
||||
[l(:label_last_n_days, 30), '30_days'],
|
||||
[l(:label_this_year), 'current_year']],
|
||||
value)
|
||||
end
|
||||
|
||||
def entries_to_csv(entries)
|
||||
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
|
||||
export = StringIO.new
|
||||
CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
|
||||
# csv header fields
|
||||
headers = [l(:field_spent_on),
|
||||
l(:field_user),
|
||||
l(:field_activity),
|
||||
l(:field_project),
|
||||
l(:field_issue),
|
||||
l(:field_tracker),
|
||||
l(:field_subject),
|
||||
l(:field_hours),
|
||||
l(:field_comments)
|
||||
]
|
||||
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
|
||||
# csv lines
|
||||
entries.each do |entry|
|
||||
fields = [l_date(entry.spent_on),
|
||||
entry.user,
|
||||
entry.activity,
|
||||
entry.project,
|
||||
(entry.issue ? entry.issue.id : nil),
|
||||
(entry.issue ? entry.issue.tracker : nil),
|
||||
(entry.issue ? entry.issue.subject : nil),
|
||||
entry.hours,
|
||||
entry.comments
|
||||
]
|
||||
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
|
||||
end
|
||||
end
|
||||
export.rewind
|
||||
export
|
||||
end
|
||||
|
||||
def format_criteria_value(criteria, value)
|
||||
value.blank? ? l(:label_none) : ((k = @available_criterias[criteria][:klass]) ? k.find_by_id(value.to_i) : format_value(value, @available_criterias[criteria][:format]))
|
||||
end
|
||||
|
||||
def report_to_csv(criterias, periods, hours)
|
||||
export = StringIO.new
|
||||
CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
|
||||
# Column headers
|
||||
headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
|
||||
headers += periods
|
||||
headers << l(:label_total)
|
||||
csv << headers.collect {|c| to_utf8(c) }
|
||||
# Content
|
||||
report_criteria_to_csv(csv, criterias, periods, hours)
|
||||
# Total row
|
||||
row = [ l(:label_total) ] + [''] * (criterias.size - 1)
|
||||
total = 0
|
||||
periods.each do |period|
|
||||
sum = sum_hours(select_hours(hours, @columns, period.to_s))
|
||||
total += sum
|
||||
row << (sum > 0 ? "%.2f" % sum : '')
|
||||
end
|
||||
row << "%.2f" %total
|
||||
csv << row
|
||||
end
|
||||
export.rewind
|
||||
export
|
||||
end
|
||||
|
||||
def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
|
||||
hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
|
||||
hours_for_value = select_hours(hours, criterias[level], value)
|
||||
next if hours_for_value.empty?
|
||||
row = [''] * level
|
||||
row << to_utf8(format_criteria_value(criterias[level], value))
|
||||
row += [''] * (criterias.length - level - 1)
|
||||
total = 0
|
||||
periods.each do |period|
|
||||
sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
|
||||
total += sum
|
||||
row << (sum > 0 ? "%.2f" % sum : '')
|
||||
end
|
||||
row << "%.2f" %total
|
||||
csv << row
|
||||
|
||||
if criterias.length > level + 1
|
||||
report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_utf8(s)
|
||||
@ic ||= Iconv.new(l(:general_csv_encoding), 'UTF-8')
|
||||
begin; @ic.iconv(s.to_s); rescue; s.to_s; end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module TrackersHelper
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module UsersHelper
|
||||
def status_options_for_select(selected)
|
||||
options_for_select([[l(:label_all), ''],
|
||||
[l(:status_active), 1],
|
||||
[l(:status_registered), 2],
|
||||
[l(:status_locked), 3]], selected)
|
||||
end
|
||||
|
||||
def change_status_link(user)
|
||||
url = {:action => 'edit', :id => user, :page => params[:page], :status => params[:status]}
|
||||
|
||||
if user.locked?
|
||||
link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock'
|
||||
elsif user.registered?
|
||||
link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock'
|
||||
else
|
||||
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :post, :class => 'icon icon-lock'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,47 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module VersionsHelper
|
||||
|
||||
STATUS_BY_CRITERIAS = %w(category tracker priority author assigned_to)
|
||||
|
||||
def render_issue_status_by(version, criteria)
|
||||
criteria ||= 'category'
|
||||
raise 'Unknown criteria' unless STATUS_BY_CRITERIAS.include?(criteria)
|
||||
|
||||
h = Hash.new {|k,v| k[v] = [0, 0]}
|
||||
begin
|
||||
# Total issue count
|
||||
Issue.count(:group => criteria,
|
||||
:conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s}
|
||||
# Open issues count
|
||||
Issue.count(:group => criteria,
|
||||
:include => :status,
|
||||
:conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s}
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
# When grouping by an association, Rails throws this exception if there's no result (bug)
|
||||
end
|
||||
counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
|
||||
max = counts.collect {|c| c[:total]}.max
|
||||
|
||||
render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
|
||||
end
|
||||
|
||||
def status_by_options_for_select(value)
|
||||
options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module WatchersHelper
|
||||
def watcher_tag(object, user)
|
||||
content_tag("span", watcher_link(object, user), :id => 'watcher')
|
||||
end
|
||||
|
||||
def watcher_link(object, user)
|
||||
return '' unless user && user.logged? && object.respond_to?('watched_by?')
|
||||
watched = object.watched_by?(user)
|
||||
url = {:controller => 'watchers',
|
||||
:action => (watched ? 'remove' : 'add'),
|
||||
:object_type => object.class.to_s.underscore,
|
||||
:object_id => object.id}
|
||||
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
|
||||
{:url => url},
|
||||
:href => url_for(url),
|
||||
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module WelcomeHelper
|
||||
end
|
||||
@@ -0,0 +1,56 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module WikiHelper
|
||||
|
||||
def html_diff(wdiff)
|
||||
words = wdiff.words.collect{|word| h(word)}
|
||||
words_add = 0
|
||||
words_del = 0
|
||||
dels = 0
|
||||
del_off = 0
|
||||
wdiff.diff.diffs.each do |diff|
|
||||
add_at = nil
|
||||
add_to = nil
|
||||
del_at = nil
|
||||
deleted = ""
|
||||
diff.each do |change|
|
||||
pos = change[1]
|
||||
if change[0] == "+"
|
||||
add_at = pos + dels unless add_at
|
||||
add_to = pos + dels
|
||||
words_add += 1
|
||||
else
|
||||
del_at = pos unless del_at
|
||||
deleted << ' ' + change[2]
|
||||
words_del += 1
|
||||
end
|
||||
end
|
||||
if add_at
|
||||
words[add_at] = '<span class="diff_in">' + words[add_at]
|
||||
words[add_to] = words[add_to] + '</span>'
|
||||
end
|
||||
if del_at
|
||||
words.insert del_at - del_off + dels + words_add, '<span class="diff_out">' + deleted + '</span>'
|
||||
dels += 1
|
||||
del_off += words_del
|
||||
words_del = 0
|
||||
end
|
||||
end
|
||||
simple_format_without_paragraph(words.join(' '))
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user