Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
071bc7bc46 | ||
|
|
2c44829509 | ||
|
|
d259dd2dd5 | ||
|
|
ddc016d81d | ||
|
|
e7c82e3934 | ||
|
|
e503f41f6f | ||
|
|
cd67243de5 | ||
|
|
b0c3b7a574 | ||
|
|
9a67caf248 | ||
|
|
67ee8653bd | ||
|
|
36978279c3 | ||
|
|
d155392b3c | ||
|
|
dc98cec17f | ||
|
|
e1e006f09e | ||
|
|
e8757fec2b | ||
|
|
591922c365 | ||
|
|
5eeca35317 | ||
|
|
2de51892ee | ||
|
|
3fe5e3bb6f | ||
|
|
bbf3ffe0aa | ||
|
|
b724eb4fec | ||
|
|
493119e795 | ||
|
|
f1b6b4ef33 | ||
|
|
ce002ee3df | ||
|
|
eb7862445c | ||
|
|
b31b5328e4 | ||
|
|
8efcf60319 | ||
|
|
e07bc81c5e | ||
|
|
5c6349a7ca | ||
|
|
37fbdb1457 | ||
|
|
a13acb7851 | ||
|
|
01887f1e67 | ||
|
|
3d9f274140 | ||
|
|
7375dff6d6 | ||
|
|
9874a61ec3 | ||
|
|
b5998b59ab | ||
|
|
c590cded4b | ||
|
|
5b8af329e2 | ||
|
|
0badc79162 | ||
|
|
5e57cbfdc4 | ||
|
|
489527ada7 | ||
|
|
6ce685a63c | ||
|
|
8045385990 | ||
|
|
3a4c46089f | ||
|
|
53d45273a9 | ||
|
|
cc23ab9652 | ||
|
|
ea33a66c6b | ||
|
|
a8c27df9fe | ||
|
|
af632568e3 | ||
|
|
2c408ca6a5 | ||
|
|
2a6dadf787 | ||
|
|
958f1accec | ||
|
|
77a2cbfa6a | ||
|
|
73b4b614ee |
5
Gemfile
5
Gemfile
@@ -3,7 +3,7 @@ source 'https://rubygems.org'
|
||||
gem "rails", "3.2.13"
|
||||
gem "jquery-rails", "~> 2.0.2"
|
||||
gem "i18n", "~> 0.6.0"
|
||||
gem "coderay", "~> 1.0.6"
|
||||
gem "coderay", "~> 1.0.9"
|
||||
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
|
||||
gem "builder", "3.0.0"
|
||||
|
||||
@@ -14,7 +14,7 @@ end
|
||||
|
||||
# Optional gem for OpenID authentication
|
||||
group :openid do
|
||||
gem "ruby-openid", "~> 2.1.4", :require => "openid"
|
||||
gem "ruby-openid", "~> 2.2.3", :require => "openid"
|
||||
gem "rack-openid"
|
||||
end
|
||||
|
||||
@@ -80,6 +80,7 @@ group :test do
|
||||
gem "shoulda", "~> 3.3.2"
|
||||
gem "mocha", "~> 0.13.3"
|
||||
gem 'capybara', '~> 2.0.0'
|
||||
gem 'nokogiri', '< 1.6.0'
|
||||
end
|
||||
|
||||
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
|
||||
|
||||
@@ -70,14 +70,12 @@ class EnumerationsController < ApplicationController
|
||||
@enumeration.destroy
|
||||
redirect_to enumerations_path
|
||||
return
|
||||
elsif params[:reassign_to_id]
|
||||
if reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id])
|
||||
@enumeration.destroy(reassign_to)
|
||||
redirect_to enumerations_path
|
||||
return
|
||||
end
|
||||
elsif params[:reassign_to_id].present? && (reassign_to = @enumeration.class.find_by_id(params[:reassign_to_id].to_i))
|
||||
@enumeration.destroy(reassign_to)
|
||||
redirect_to enumerations_path
|
||||
return
|
||||
end
|
||||
@enumerations = @enumeration.class.all - [@enumeration]
|
||||
@enumerations = @enumeration.class.system.all - [@enumeration]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -53,7 +53,6 @@ class MyController < ApplicationController
|
||||
if request.post?
|
||||
@user.safe_attributes = params[:user]
|
||||
@user.pref.attributes = params[:pref]
|
||||
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
|
||||
if @user.save
|
||||
@user.pref.save
|
||||
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
||||
|
||||
@@ -43,10 +43,10 @@ class TimelogController < ApplicationController
|
||||
|
||||
def index
|
||||
@query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
|
||||
scope = time_entry_scope
|
||||
|
||||
sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
|
||||
sort_update(@query.sortable_columns)
|
||||
scope = time_entry_scope(:order => sort_clause)
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
@@ -55,7 +55,6 @@ class TimelogController < ApplicationController
|
||||
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
|
||||
@entries = scope.all(
|
||||
:include => [:project, :activity, :user, {:issue => :tracker}],
|
||||
:order => sort_clause,
|
||||
:limit => @entry_pages.per_page,
|
||||
:offset => @entry_pages.offset
|
||||
)
|
||||
@@ -68,15 +67,13 @@ class TimelogController < ApplicationController
|
||||
@offset, @limit = api_offset_and_limit
|
||||
@entries = scope.all(
|
||||
:include => [:project, :activity, :user, {:issue => :tracker}],
|
||||
:order => sort_clause,
|
||||
:limit => @limit,
|
||||
:offset => @offset
|
||||
)
|
||||
}
|
||||
format.atom {
|
||||
entries = scope.all(
|
||||
entries = scope.reorder("#{TimeEntry.table_name}.created_on DESC").all(
|
||||
:include => [:project, :activity, :user, {:issue => :tracker}],
|
||||
:order => "#{TimeEntry.table_name}.created_on DESC",
|
||||
:limit => Setting.feeds_limit.to_i
|
||||
)
|
||||
render_feed(entries, :title => l(:label_spent_time))
|
||||
@@ -84,8 +81,7 @@ class TimelogController < ApplicationController
|
||||
format.csv {
|
||||
# Export all entries
|
||||
@entries = scope.all(
|
||||
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
|
||||
:order => sort_clause
|
||||
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}]
|
||||
)
|
||||
send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
|
||||
}
|
||||
@@ -295,12 +291,10 @@ private
|
||||
end
|
||||
|
||||
# Returns the TimeEntry scope for index and report actions
|
||||
def time_entry_scope
|
||||
scope = TimeEntry.visible.where(@query.statement)
|
||||
def time_entry_scope(options={})
|
||||
scope = @query.results_scope(options)
|
||||
if @issue
|
||||
scope = scope.on_issue(@issue)
|
||||
elsif @project
|
||||
scope = scope.on_project(@project, Setting.display_subprojects_issues?)
|
||||
end
|
||||
scope
|
||||
end
|
||||
|
||||
@@ -92,7 +92,6 @@ class UsersController < ApplicationController
|
||||
|
||||
if @user.save
|
||||
@user.pref.attributes = params[:pref]
|
||||
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
|
||||
@user.pref.save
|
||||
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
||||
|
||||
@@ -137,7 +136,6 @@ class UsersController < ApplicationController
|
||||
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
|
||||
# TODO: Similar to My#account
|
||||
@user.pref.attributes = params[:pref]
|
||||
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
|
||||
|
||||
if @user.save
|
||||
@user.pref.save
|
||||
|
||||
@@ -214,6 +214,28 @@ module IssuesHelper
|
||||
out
|
||||
end
|
||||
|
||||
def email_issue_attributes(issue)
|
||||
items = []
|
||||
%w(author status priority assigned_to category fixed_version).each do |attribute|
|
||||
unless issue.disabled_core_fields.include?(attribute+"_id")
|
||||
items << "#{l("field_#{attribute}")}: #{issue.send attribute}"
|
||||
end
|
||||
end
|
||||
issue.custom_field_values.each do |value|
|
||||
items << "#{value.custom_field.name}: #{show_value(value)}"
|
||||
end
|
||||
items
|
||||
end
|
||||
|
||||
def render_email_issue_attributes(issue, html=false)
|
||||
items = email_issue_attributes(issue)
|
||||
if html
|
||||
content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe)
|
||||
else
|
||||
items.map{|s| "* #{s}"}.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the textual representation of a journal details
|
||||
# as an array of strings
|
||||
def details_to_strings(details, no_html=false, options={})
|
||||
|
||||
@@ -24,7 +24,7 @@ module ReportsHelper
|
||||
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"))
|
||||
match = 0 unless (row[k].to_s == v.to_s) || (k == 'closed' && (v == 0 ? ['f', false] : ['t', true]).include?(row[k]))
|
||||
} unless criteria.nil?
|
||||
a = a + row["total"].to_i if match == 1
|
||||
} unless data.nil?
|
||||
|
||||
@@ -38,6 +38,7 @@ class Enumeration < ActiveRecord::Base
|
||||
scope :shared, lambda { where(:project_id => nil) }
|
||||
scope :sorted, lambda { order("#{table_name}.position ASC") }
|
||||
scope :active, lambda { where(:active => true) }
|
||||
scope :system, lambda { where(:project_id => nil) }
|
||||
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
|
||||
|
||||
def self.default
|
||||
|
||||
@@ -744,12 +744,16 @@ class Issue < ActiveRecord::Base
|
||||
initial_status = IssueStatus.find_by_id(status_id_was)
|
||||
end
|
||||
initial_status ||= status
|
||||
|
||||
|
||||
initial_assigned_to_id = assigned_to_id_changed? ? assigned_to_id_was : assigned_to_id
|
||||
assignee_transitions_allowed = initial_assigned_to_id.present? &&
|
||||
(user.id == initial_assigned_to_id || user.group_ids.include?(initial_assigned_to_id))
|
||||
|
||||
statuses = initial_status.find_new_statuses_allowed_to(
|
||||
user.admin ? Role.all : user.roles_for_project(project),
|
||||
tracker,
|
||||
author == user,
|
||||
assigned_to_id_changed? ? assigned_to_id_was == user.id : assigned_to_id == user.id
|
||||
assignee_transitions_allowed
|
||||
)
|
||||
statuses << initial_status unless statuses.empty?
|
||||
statuses << IssueStatus.default if include_default
|
||||
@@ -854,18 +858,99 @@ class Issue < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# Returns all the other issues that depend on the issue
|
||||
# The algorithm is a modified breadth first search (bfs)
|
||||
def all_dependent_issues(except=[])
|
||||
except << self
|
||||
# The found dependencies
|
||||
dependencies = []
|
||||
dependencies += relations_from.map(&:issue_to)
|
||||
dependencies += children unless leaf?
|
||||
dependencies.compact!
|
||||
|
||||
# The visited flag for every node (issue) used by the breadth first search
|
||||
eNOT_DISCOVERED = 0 # The issue is "new" to the algorithm, it has not seen it before.
|
||||
|
||||
ePROCESS_ALL = 1 # The issue is added to the queue. Process both children and relations of
|
||||
# the issue when it is processed.
|
||||
|
||||
ePROCESS_RELATIONS_ONLY = 2 # The issue was added to the queue and will be output as dependent issue,
|
||||
# but its children will not be added to the queue when it is processed.
|
||||
|
||||
eRELATIONS_PROCESSED = 3 # The related issues, the parent issue and the issue itself have been added to
|
||||
# the queue, but its children have not been added.
|
||||
|
||||
ePROCESS_CHILDREN_ONLY = 4 # The relations and the parent of the issue have been added to the queue, but
|
||||
# the children still need to be processed.
|
||||
|
||||
eALL_PROCESSED = 5 # The issue and all its children, its parent and its related issues have been
|
||||
# added as dependent issues. It needs no further processing.
|
||||
|
||||
issue_status = Hash.new(eNOT_DISCOVERED)
|
||||
|
||||
# The queue
|
||||
queue = []
|
||||
|
||||
# Initialize the bfs, add start node (self) to the queue
|
||||
queue << self
|
||||
issue_status[self] = ePROCESS_ALL
|
||||
|
||||
while (!queue.empty?) do
|
||||
current_issue = queue.shift
|
||||
current_issue_status = issue_status[current_issue]
|
||||
dependencies << current_issue
|
||||
|
||||
# Add parent to queue, if not already in it.
|
||||
parent = current_issue.parent
|
||||
parent_status = issue_status[parent]
|
||||
|
||||
if parent && (parent_status == eNOT_DISCOVERED) && !except.include?(parent)
|
||||
queue << parent
|
||||
issue_status[parent] = ePROCESS_RELATIONS_ONLY
|
||||
end
|
||||
|
||||
# Add children to queue, but only if they are not already in it and
|
||||
# the children of the current node need to be processed.
|
||||
if (current_issue_status == ePROCESS_CHILDREN_ONLY || current_issue_status == ePROCESS_ALL)
|
||||
current_issue.children.each do |child|
|
||||
next if except.include?(child)
|
||||
|
||||
if (issue_status[child] == eNOT_DISCOVERED)
|
||||
queue << child
|
||||
issue_status[child] = ePROCESS_ALL
|
||||
elsif (issue_status[child] == eRELATIONS_PROCESSED)
|
||||
queue << child
|
||||
issue_status[child] = ePROCESS_CHILDREN_ONLY
|
||||
elsif (issue_status[child] == ePROCESS_RELATIONS_ONLY)
|
||||
queue << child
|
||||
issue_status[child] = ePROCESS_ALL
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add related issues to the queue, if they are not already in it.
|
||||
current_issue.relations_from.map(&:issue_to).each do |related_issue|
|
||||
next if except.include?(related_issue)
|
||||
|
||||
if (issue_status[related_issue] == eNOT_DISCOVERED)
|
||||
queue << related_issue
|
||||
issue_status[related_issue] = ePROCESS_ALL
|
||||
elsif (issue_status[related_issue] == eRELATIONS_PROCESSED)
|
||||
queue << related_issue
|
||||
issue_status[related_issue] = ePROCESS_CHILDREN_ONLY
|
||||
elsif (issue_status[related_issue] == ePROCESS_RELATIONS_ONLY)
|
||||
queue << related_issue
|
||||
issue_status[related_issue] = ePROCESS_ALL
|
||||
end
|
||||
end
|
||||
|
||||
# Set new status for current issue
|
||||
if (current_issue_status == ePROCESS_ALL) || (current_issue_status == ePROCESS_CHILDREN_ONLY)
|
||||
issue_status[current_issue] = eALL_PROCESSED
|
||||
elsif (current_issue_status == ePROCESS_RELATIONS_ONLY)
|
||||
issue_status[current_issue] = eRELATIONS_PROCESSED
|
||||
end
|
||||
end # while
|
||||
|
||||
# Remove the issues from the "except" parameter from the result array
|
||||
dependencies -= except
|
||||
dependencies += dependencies.map {|issue| issue.all_dependent_issues(except)}.flatten
|
||||
if parent
|
||||
dependencies << parent
|
||||
dependencies += parent.all_dependent_issues(except + parent.descendants)
|
||||
end
|
||||
dependencies.delete(self)
|
||||
|
||||
dependencies
|
||||
end
|
||||
|
||||
@@ -1252,7 +1337,8 @@ class Issue < ActiveRecord::Base
|
||||
if average == 0
|
||||
average = 1
|
||||
end
|
||||
done = p.leaves.sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)", :joins => :status).to_f
|
||||
done = p.leaves.sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " +
|
||||
"* (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)", :joins => :status).to_f
|
||||
progress = done / (average * leaves_count)
|
||||
p.done_ratio = progress.round
|
||||
end
|
||||
|
||||
@@ -393,10 +393,9 @@ class IssueQuery < Query
|
||||
|
||||
if relation_options[:sym] == field && !options[:reverse]
|
||||
sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
|
||||
sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
|
||||
else
|
||||
sql
|
||||
sql = sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
|
||||
end
|
||||
"(#{sql})"
|
||||
end
|
||||
|
||||
IssueRelation::TYPES.keys.each do |relation_type|
|
||||
|
||||
@@ -390,7 +390,7 @@ class Mailer < ActionMailer::Base
|
||||
|
||||
# Removes the author from the recipients and cc
|
||||
# if he doesn't want to receive notifications about what he does
|
||||
if @author && @author.logged? && @author.pref[:no_self_notified]
|
||||
if @author && @author.logged? && @author.pref.no_self_notified
|
||||
headers[:to].delete(@author.mail) if headers[:to].is_a?(Array)
|
||||
headers[:cc].delete(@author.mail) if headers[:cc].is_a?(Array)
|
||||
end
|
||||
|
||||
@@ -673,7 +673,7 @@ class Project < ActiveRecord::Base
|
||||
|
||||
# Returns an auto-generated project identifier based on the last identifier used
|
||||
def self.next_identifier
|
||||
p = Project.order('created_on DESC').first
|
||||
p = Project.order('id DESC').first
|
||||
p.nil? ? nil : p.identifier.to_s.succ
|
||||
end
|
||||
|
||||
@@ -840,6 +840,9 @@ class Project < ActiveRecord::Base
|
||||
new_issue = Issue.new
|
||||
new_issue.copy_from(issue, :subtasks => false, :link => false)
|
||||
new_issue.project = self
|
||||
# Changing project resets the custom field values
|
||||
# TODO: handle this in Issue#project=
|
||||
new_issue.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
|
||||
# Reassign fixed_versions by name, since names are unique per project
|
||||
if issue.fixed_version && issue.fixed_version.project == project
|
||||
new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name}
|
||||
|
||||
@@ -577,8 +577,11 @@ class Query < ActiveRecord::Base
|
||||
customized_class = queried_class.reflect_on_association(assoc.to_sym).klass.base_class rescue nil
|
||||
raise "Unknown #{queried_class.name} association #{assoc}" unless customized_class
|
||||
end
|
||||
"#{queried_table_name}.#{customized_key} #{not_in} IN (SELECT #{customized_class.table_name}.id FROM #{customized_class.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='#{customized_class}' AND #{db_table}.customized_id=#{customized_class.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE " +
|
||||
sql_for_field(field, operator, value, db_table, db_field, true) + ')'
|
||||
where = sql_for_field(field, operator, value, db_table, db_field, true)
|
||||
if operator =~ /[<>]/
|
||||
where = "(#{where}) AND #{db_table}.#{db_field} <> ''"
|
||||
end
|
||||
"#{queried_table_name}.#{customized_key} #{not_in} IN (SELECT #{customized_class.table_name}.id FROM #{customized_class.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='#{customized_class}' AND #{db_table}.customized_id=#{customized_class.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE #{where})"
|
||||
end
|
||||
|
||||
# Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
|
||||
|
||||
@@ -24,11 +24,15 @@ class TimeEntryActivity < Enumeration
|
||||
OptionName
|
||||
end
|
||||
|
||||
def objects
|
||||
TimeEntry.where(:activity_id => self_and_descendants(1).map(&:id))
|
||||
end
|
||||
|
||||
def objects_count
|
||||
time_entries.count
|
||||
objects.count
|
||||
end
|
||||
|
||||
def transfer_relations(to)
|
||||
time_entries.update_all("activity_id = #{to.id}")
|
||||
objects.update_all(:activity_id => to.id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -100,6 +100,15 @@ class TimeEntryQuery < Query
|
||||
@default_columns_names ||= [:project, :spent_on, :user, :activity, :issue, :comments, :hours]
|
||||
end
|
||||
|
||||
def results_scope(options={})
|
||||
order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
|
||||
|
||||
TimeEntry.visible.
|
||||
where(statement).
|
||||
order(order_option).
|
||||
joins(joins_for_order_statement(order_option.join(',')))
|
||||
end
|
||||
|
||||
# Accepts :from/:to params as shortcut filters
|
||||
def build_from_params(params)
|
||||
super
|
||||
|
||||
@@ -33,7 +33,7 @@ class UserPreference < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def [](attr_name)
|
||||
if attribute_present? attr_name
|
||||
if has_attribute? attr_name
|
||||
super
|
||||
else
|
||||
others ? others[attr_name] : nil
|
||||
@@ -41,7 +41,7 @@ class UserPreference < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def []=(attr_name, value)
|
||||
if attribute_present? attr_name
|
||||
if has_attribute? attr_name
|
||||
super
|
||||
else
|
||||
h = (read_attribute(:others) || {}).dup
|
||||
@@ -56,4 +56,7 @@ class UserPreference < ActiveRecord::Base
|
||||
|
||||
def warn_on_leaving_unsaved; self[:warn_on_leaving_unsaved] || '1'; end
|
||||
def warn_on_leaving_unsaved=(value); self[:warn_on_leaving_unsaved]=value; end
|
||||
|
||||
def no_self_notified; (self[:no_self_notified] == true || self[:no_self_notified] == '1'); end
|
||||
def no_self_notified=(value); self[:no_self_notified]=value; end
|
||||
end
|
||||
|
||||
@@ -47,7 +47,8 @@ class Version < ActiveRecord::Base
|
||||
'wiki_page_title',
|
||||
'status',
|
||||
'sharing',
|
||||
'custom_field_values'
|
||||
'custom_field_values',
|
||||
'custom_fields'
|
||||
|
||||
# Returns true if +user+ or current user is allowed to view the version
|
||||
def visible?(user=User.current)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<p><em><%=h @document.category.name %><br />
|
||||
<%= format_date @document.created_on %></em></p>
|
||||
<div class="wiki">
|
||||
<%= textilizable @document.description, :attachments => @document.attachments %>
|
||||
<%= textilizable @document, :description, :attachments => @document.attachments %>
|
||||
</div>
|
||||
|
||||
<h3><%= l(:label_attachment_plural) %></h3>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="box">
|
||||
<p><strong><%= l(:text_enumeration_destroy_question, @enumeration.objects_count) %></strong></p>
|
||||
<p><label for='reassign_to_id'><%= l(:text_enumeration_category_reassign_to) %></label>
|
||||
<%= select_tag 'reassign_to_id', (content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---") + options_from_collection_for_select(@enumerations, 'id', 'name')) %></p>
|
||||
<%= select_tag 'reassign_to_id', (content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---", :value => '') + options_from_collection_for_select(@enumerations, 'id', 'name')) %></p>
|
||||
</div>
|
||||
|
||||
<%= submit_tag l(:button_apply) %>
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
<h1><%= link_to(h("#{issue.tracker.name} ##{issue.id}: #{issue.subject}"), issue_url) %></h1>
|
||||
|
||||
<ul>
|
||||
<li><%=l(:field_author)%>: <%=h issue.author %></li>
|
||||
<li><%=l(:field_status)%>: <%=h issue.status %></li>
|
||||
<li><%=l(:field_priority)%>: <%=h issue.priority %></li>
|
||||
<li><%=l(:field_assigned_to)%>: <%=h issue.assigned_to %></li>
|
||||
<li><%=l(:field_category)%>: <%=h issue.category %></li>
|
||||
<li><%=l(:field_fixed_version)%>: <%=h issue.fixed_version %></li>
|
||||
<% issue.custom_field_values.each do |c| %>
|
||||
<li><%=h c.custom_field.name %>: <%=h show_value(c) %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<%= render_email_issue_attributes(issue, true) %>
|
||||
|
||||
<%= textilizable(issue, :description, :only_path => false) %>
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
<%= "#{issue.tracker.name} ##{issue.id}: #{issue.subject}" %>
|
||||
<%= issue_url %>
|
||||
|
||||
* <%=l(:field_author)%>: <%= issue.author %>
|
||||
* <%=l(:field_status)%>: <%= issue.status %>
|
||||
* <%=l(:field_priority)%>: <%= issue.priority %>
|
||||
* <%=l(:field_assigned_to)%>: <%= issue.assigned_to %>
|
||||
* <%=l(:field_category)%>: <%= issue.category %>
|
||||
* <%=l(:field_fixed_version)%>: <%= issue.fixed_version %>
|
||||
<% issue.custom_field_values.each do |c| %>* <%= c.custom_field.name %>: <%= show_value(c) %>
|
||||
<% end -%>
|
||||
<%= render_email_issue_attributes(issue) %>
|
||||
----------------------------------------
|
||||
<%= issue.description %>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<% if @journal.private_notes? %>
|
||||
(<%= l(:field_private_notes) %>)
|
||||
<% end %>
|
||||
<%= l(:text_issue_updated, :id => "##{@issue.id}", :author => h(@journal.user)) %>
|
||||
|
||||
<ul>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= l(:text_issue_updated, :id => "##{@issue.id}", :author => @journal.user) %>
|
||||
<%= "(#{l(:field_private_notes)}) " if @journal.private_notes? -%><%= l(:text_issue_updated, :id => "##{@issue.id}", :author => @journal.user) %>
|
||||
|
||||
<% details_to_strings(@journal.details, true).each do |string| -%>
|
||||
<%= string %>
|
||||
|
||||
@@ -3,7 +3,7 @@ var operatorLabels = <%= raw_json Query.operators_labels %>;
|
||||
var operatorByType = <%= raw_json Query.operators_by_filter_type %>;
|
||||
var availableFilters = <%= raw_json query.available_filters_as_json %>;
|
||||
var labelDayPlural = <%= raw_json l(:label_day_plural) %>;
|
||||
var allProjects = <%= raw query.all_projects_values.to_json %>;
|
||||
var allProjects = <%= raw_json query.all_projects_values %>;
|
||||
$(document).ready(function(){
|
||||
initFilters();
|
||||
<% query.filters.each do |field, options| %>
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
end %>
|
||||
<p><em class="info"><%= l(:text_user_mail_option) %></em></p>
|
||||
<% end %>
|
||||
|
||||
<%= fields_for :pref, @user.pref do |pref_fields| %>
|
||||
<p>
|
||||
<label>
|
||||
<%= l(:label_user_mail_no_self_notified) %>
|
||||
<%= check_box_tag 'no_self_notified', 1, @user.pref[:no_self_notified] %>
|
||||
</label>
|
||||
<%= pref_fields.check_box :no_self_notified %>
|
||||
<label for="pref_no_self_notified"><%= l(:label_user_mail_no_self_notified) %></label>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
@@ -50,8 +50,8 @@ ar:
|
||||
one: "حوالي ساعة"
|
||||
other: "ساعات %{count}حوالي "
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "%{count} ساعة"
|
||||
other: "%{count} ساعات"
|
||||
x_days:
|
||||
one: "يوم"
|
||||
other: "%{count} أيام"
|
||||
|
||||
@@ -109,8 +109,8 @@ az:
|
||||
many: "təxminən %{count} saat"
|
||||
other: "təxminən %{count} saat"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 saat"
|
||||
other: "%{count} saat"
|
||||
x_days:
|
||||
one: "%{count} gün"
|
||||
few: "%{count} gün"
|
||||
|
||||
@@ -51,8 +51,8 @@ bg:
|
||||
one: "около 1 час"
|
||||
other: "около %{count} часа"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 час"
|
||||
other: "%{count} часа"
|
||||
x_days:
|
||||
one: "1 ден"
|
||||
other: "%{count} дена"
|
||||
|
||||
@@ -49,8 +49,8 @@ bs:
|
||||
one: "oko 1 sahat"
|
||||
other: "oko %{count} sahata"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 sahat"
|
||||
other: "%{count} sahata"
|
||||
x_days:
|
||||
one: "1 dan"
|
||||
other: "%{count} dana"
|
||||
|
||||
@@ -53,8 +53,8 @@ ca:
|
||||
one: "aproximadament 1 hora"
|
||||
other: "aproximadament %{count} hores"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 hora"
|
||||
other: "%{count} hores"
|
||||
x_days:
|
||||
one: "1 dia"
|
||||
other: "%{count} dies"
|
||||
|
||||
@@ -55,8 +55,8 @@ cs:
|
||||
one: "asi 1 hodina"
|
||||
other: "asi %{count} hodin"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 hodina"
|
||||
other: "%{count} hodin"
|
||||
x_days:
|
||||
one: "1 den"
|
||||
other: "%{count} dnů"
|
||||
|
||||
@@ -52,8 +52,8 @@ da:
|
||||
one: "cirka en time"
|
||||
other: "cirka %{count} timer"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 time"
|
||||
other: "%{count} timer"
|
||||
x_days:
|
||||
one: "en dag"
|
||||
other: "%{count} dage"
|
||||
|
||||
@@ -53,8 +53,8 @@ de:
|
||||
one: 'etwa 1 Stunde'
|
||||
other: 'etwa %{count} Stunden'
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 Stunde"
|
||||
other: "%{count} Stunden"
|
||||
x_days:
|
||||
one: '1 Tag'
|
||||
other: '%{count} Tagen'
|
||||
|
||||
@@ -52,8 +52,8 @@ el:
|
||||
one: "περίπου 1 ώρα"
|
||||
other: "περίπου %{count} ώρες"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 ώρα"
|
||||
other: "%{count} ώρες"
|
||||
x_days:
|
||||
one: "1 ημέρα"
|
||||
other: "%{count} ημέρες"
|
||||
|
||||
@@ -80,8 +80,8 @@ es:
|
||||
one: "alrededor de 1 hora"
|
||||
other: "alrededor de %{count} horas"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 hora"
|
||||
other: "%{count} horas"
|
||||
x_days:
|
||||
one: "1 día"
|
||||
other: "%{count} días"
|
||||
@@ -1118,8 +1118,8 @@ es:
|
||||
permission_edit_documents: Editar documentos
|
||||
permission_delete_documents: Borrar documentos
|
||||
label_gantt_progress_line: Línea de progreso
|
||||
setting_jsonp_enabled: Enable JSONP support
|
||||
field_inherit_members: Inherit members
|
||||
field_closed_on: Closed
|
||||
setting_default_projects_tracker_ids: Default trackers for new projects
|
||||
setting_jsonp_enabled: Habilitar soporte de JSONP
|
||||
field_inherit_members: Heredar miembros
|
||||
field_closed_on: Cerrada
|
||||
setting_default_projects_tracker_ids: Tipos de petición habilitados por defecto
|
||||
label_total_time: Total
|
||||
|
||||
@@ -67,8 +67,8 @@ et:
|
||||
one: "umbes tund"
|
||||
other: "umbes %{count} tundi"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 tund"
|
||||
other: "%{count} tundi"
|
||||
x_days:
|
||||
one: "1 päev"
|
||||
other: "%{count} päeva"
|
||||
|
||||
@@ -53,8 +53,8 @@ eu:
|
||||
one: "ordu 1 inguru"
|
||||
other: "%{count} ordu inguru"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "ordu 1"
|
||||
other: "%{count} ordu"
|
||||
x_days:
|
||||
one: "egun 1"
|
||||
other: "%{count} egun"
|
||||
|
||||
@@ -50,8 +50,8 @@ fa:
|
||||
one: "نزدیک 1 ساعت"
|
||||
other: "نزدیک %{count} ساعت"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 ساعت"
|
||||
other: "%{count} ساعت"
|
||||
x_days:
|
||||
one: "1 روز"
|
||||
other: "%{count} روز"
|
||||
|
||||
@@ -95,8 +95,8 @@ fi:
|
||||
one: "noin tunti"
|
||||
other: "noin %{count} tuntia"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 tunti"
|
||||
other: "%{count} tuntia"
|
||||
x_days:
|
||||
one: "päivä"
|
||||
other: "%{count} päivää"
|
||||
|
||||
@@ -91,8 +91,8 @@ gl:
|
||||
one: 'aproximadamente unha hora'
|
||||
other: '%{count} horas'
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 hora"
|
||||
other: "%{count} horas"
|
||||
x_days:
|
||||
one: '1 día'
|
||||
other: '%{count} días'
|
||||
|
||||
@@ -56,8 +56,8 @@ he:
|
||||
one: 'בערך שעה אחת'
|
||||
other: 'בערך %{count} שעות'
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 שעה"
|
||||
other: "%{count} שעות"
|
||||
x_days:
|
||||
one: 'יום אחד'
|
||||
other: '%{count} ימים'
|
||||
|
||||
@@ -49,8 +49,8 @@ hr:
|
||||
one: "oko sat vremena"
|
||||
other: "oko %{count} sati"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 sata"
|
||||
other: "%{count} sati"
|
||||
x_days:
|
||||
one: "1 dan"
|
||||
other: "%{count} dana"
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
one: 'csaknem 1 órája'
|
||||
other: 'csaknem %{count} órája'
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 óra"
|
||||
other: "%{count} óra"
|
||||
x_days:
|
||||
one: '1 napja'
|
||||
other: '%{count} napja'
|
||||
|
||||
@@ -47,8 +47,8 @@ id:
|
||||
one: "sekitar sejam"
|
||||
other: "sekitar %{count} jam"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 jam"
|
||||
other: "%{count} jam"
|
||||
x_days:
|
||||
one: "sehari"
|
||||
other: "%{count} hari"
|
||||
|
||||
@@ -55,8 +55,8 @@ it:
|
||||
one: "circa un'ora"
|
||||
other: "circa %{count} ore"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 ora"
|
||||
other: "%{count} ore"
|
||||
x_days:
|
||||
one: "1 giorno"
|
||||
other: "%{count} giorni"
|
||||
@@ -1001,90 +1001,87 @@ it:
|
||||
button_export: Esporta
|
||||
label_export_options: "%{export_format} opzioni per l'export"
|
||||
error_attachment_too_big: Questo file non può essere caricato in quanto la sua dimensione supera la massima consentita (%{max_size})
|
||||
notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
|
||||
notice_failed_to_save_time_entries: "Non ho potuto salvare %{count} registrazioni di tempo impiegato su %{total} selezionate: %{ids}."
|
||||
label_x_issues:
|
||||
zero: 0 segnalazione
|
||||
one: 1 segnalazione
|
||||
other: "%{count} segnalazioni"
|
||||
label_repository_new: New repository
|
||||
field_repository_is_default: Main repository
|
||||
label_copy_attachments: Copy attachments
|
||||
label_repository_new: Nuovo repository
|
||||
field_repository_is_default: Repository principale
|
||||
label_copy_attachments: Copia allegati
|
||||
label_item_position: "%{position}/%{count}"
|
||||
label_completed_versions: Completed versions
|
||||
text_project_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
|
||||
field_multiple: Multiple values
|
||||
text_project_identifier_info: Consentiti solo lettere minuscole (a-z), numeri, trattini e trattini bassi.<br />Una volta salvato, l'identificatore non può essere modificato.
|
||||
field_multiple: Valori multipli
|
||||
setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
|
||||
text_issue_conflict_resolution_add_notes: Add my notes and discard my other changes
|
||||
text_issue_conflict_resolution_overwrite: Apply my changes anyway (previous notes will be kept but some changes may be overwritten)
|
||||
notice_issue_update_conflict: The issue has been updated by an other user while you were editing it.
|
||||
text_issue_conflict_resolution_cancel: Discard all my changes and redisplay %{link}
|
||||
permission_manage_related_issues: Manage related issues
|
||||
field_auth_source_ldap_filter: LDAP filter
|
||||
label_search_for_watchers: Search for watchers to add
|
||||
notice_account_deleted: Your account has been permanently deleted.
|
||||
setting_unsubscribe: Allow users to delete their own account
|
||||
button_delete_my_account: Delete my account
|
||||
text_account_destroy_confirmation: |-
|
||||
Are you sure you want to proceed?
|
||||
Your account will be permanently deleted, with no way to reactivate it.
|
||||
error_session_expired: Your session has expired. Please login again.
|
||||
text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
|
||||
setting_session_lifetime: Session maximum lifetime
|
||||
setting_session_timeout: Session inactivity timeout
|
||||
label_session_expiration: Session expiration
|
||||
permission_close_project: Close / reopen the project
|
||||
label_show_closed_projects: View closed projects
|
||||
button_close: Close
|
||||
button_reopen: Reopen
|
||||
project_status_active: active
|
||||
project_status_closed: closed
|
||||
project_status_archived: archived
|
||||
text_project_closed: This project is closed and read-only.
|
||||
notice_user_successful_create: User %{id} created.
|
||||
field_core_fields: Standard fields
|
||||
field_timeout: Timeout (in seconds)
|
||||
setting_thumbnails_enabled: Display attachment thumbnails
|
||||
setting_thumbnails_size: Thumbnails size (in pixels)
|
||||
label_status_transitions: Status transitions
|
||||
label_fields_permissions: Fields permissions
|
||||
label_readonly: Read-only
|
||||
label_required: Required
|
||||
text_repository_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
|
||||
text_issue_conflict_resolution_add_notes: Aggiunge le mie note e non salvare le mie ulteriori modifiche
|
||||
text_issue_conflict_resolution_overwrite: Applica comunque le mie modifiche (le note precedenti verranno mantenute ma alcuni cambiamenti potrebbero essere sovrascritti)
|
||||
notice_issue_update_conflict: La segnalazione è stata aggiornata da un altro utente mentre la stavi editando.
|
||||
text_issue_conflict_resolution_cancel: Cancella ogni modifica e rivisualizza %{link}
|
||||
permission_manage_related_issues: Gestisci relative segnalazioni
|
||||
field_auth_source_ldap_filter: Filtro LDAP
|
||||
label_search_for_watchers: Cerca osservatori da aggiungere
|
||||
notice_account_deleted: Il tuo account sarà definitivamente rimosso.
|
||||
setting_unsubscribe: Consentire agli utenti di cancellare il proprio account
|
||||
button_delete_my_account: Cancella il mio account
|
||||
text_account_destroy_confirmation: "Sei sicuro di voler procedere?\nIl tuo account sarà definitivamente cancellato, senza alcuna possibilità di ripristino."
|
||||
error_session_expired: "La tua sessione è scaduta. Effettua nuovamente il login."
|
||||
text_session_expiration_settings: "Attenzione: la modifica di queste impostazioni può far scadere le sessioni correnti, compresa la tua."
|
||||
setting_session_lifetime: Massima durata di una sessione
|
||||
setting_session_timeout: Timeout di inattività di una sessione
|
||||
label_session_expiration: Scadenza sessione
|
||||
permission_close_project: Chiusura / riapertura progetto
|
||||
label_show_closed_projects: Vedi progetti chiusi
|
||||
button_close: Chiudi
|
||||
button_reopen: Riapri
|
||||
project_status_active: attivo
|
||||
project_status_closed: chiuso
|
||||
project_status_archived: archiviato
|
||||
text_project_closed: Questo progetto è chiuso e in sola lettura.
|
||||
notice_user_successful_create: Creato utente %{id}.
|
||||
field_core_fields: Campi standard
|
||||
field_timeout: Timeout (in secondi)
|
||||
setting_thumbnails_enabled: Mostra miniature degli allegati
|
||||
setting_thumbnails_size: Dimensioni delle miniature (in pixels)
|
||||
label_status_transitions: Transizioni di stato
|
||||
label_fields_permissions: Permessi sui campi
|
||||
label_readonly: Sola lettura
|
||||
label_required: Richiesto
|
||||
text_repository_identifier_info: Consentiti solo lettere minuscole (a-z), numeri, trattini e trattini bassi.<br />Una volta salvato, ll'identificatore non può essere modificato.
|
||||
field_board_parent: Parent forum
|
||||
label_attribute_of_project: Project's %{name}
|
||||
label_attribute_of_author: Author's %{name}
|
||||
label_attribute_of_assigned_to: Assignee's %{name}
|
||||
label_attribute_of_assigned_to: Assegnatari %{name}
|
||||
label_attribute_of_fixed_version: Target version's %{name}
|
||||
label_copy_subtasks: Copy subtasks
|
||||
label_copied_to: copied to
|
||||
label_copied_from: copied from
|
||||
label_any_issues_in_project: any issues in project
|
||||
label_any_issues_not_in_project: any issues not in project
|
||||
field_private_notes: Private notes
|
||||
permission_view_private_notes: View private notes
|
||||
permission_set_notes_private: Set notes as private
|
||||
label_no_issues_in_project: no issues in project
|
||||
label_copy_subtasks: Copia sottoattività
|
||||
label_copied_to: copia a
|
||||
label_copied_from: copia da
|
||||
label_any_issues_in_project: ogni segnalazione del progetto
|
||||
label_any_issues_not_in_project: ogni segnalazione non nel progetto
|
||||
field_private_notes: Note private
|
||||
permission_view_private_notes: Visualizza note private
|
||||
permission_set_notes_private: Imposta note come private
|
||||
label_no_issues_in_project: progetto privo di segnalazioni
|
||||
label_any: tutti
|
||||
label_last_n_weeks: last %{count} weeks
|
||||
setting_cross_project_subtasks: Allow cross-project subtasks
|
||||
label_last_n_weeks: ultime %{count} settimane
|
||||
setting_cross_project_subtasks: Consenti sottoattività cross-project
|
||||
label_cross_project_descendants: Con sottoprogetti
|
||||
label_cross_project_tree: Con progetto padre
|
||||
label_cross_project_hierarchy: Con gerarchia progetto
|
||||
label_cross_project_system: Con tutti i progetti
|
||||
button_hide: Hide
|
||||
setting_non_working_week_days: Non-working days
|
||||
label_in_the_next_days: in the next
|
||||
label_in_the_past_days: in the past
|
||||
label_attribute_of_user: User's %{name}
|
||||
text_turning_multiple_off: If you disable multiple values, multiple values will be
|
||||
removed in order to preserve only one value per item.
|
||||
label_attribute_of_issue: Issue's %{name}
|
||||
permission_add_documents: Add documents
|
||||
permission_edit_documents: Edit documents
|
||||
permission_delete_documents: Delete documents
|
||||
button_hide: Nascondi
|
||||
setting_non_working_week_days: Giorni non lavorativi
|
||||
label_in_the_next_days: nei prossimi
|
||||
label_in_the_past_days: nei passati
|
||||
label_attribute_of_user: Utente %{name}
|
||||
text_turning_multiple_off: Disabilitando valori multipli, i valori multipli verranno rimossi, in modo da mantenere un solo valore per item.
|
||||
label_attribute_of_issue: Segnalazione %{name}
|
||||
permission_add_documents: Aggiungi documenti
|
||||
permission_edit_documents: Edita documenti
|
||||
permission_delete_documents: Cancella documenti
|
||||
label_gantt_progress_line: Progress line
|
||||
setting_jsonp_enabled: Enable JSONP support
|
||||
field_inherit_members: Inherit members
|
||||
field_closed_on: Closed
|
||||
setting_default_projects_tracker_ids: Default trackers for new projects
|
||||
setting_jsonp_enabled: Abilita supporto a JSONP
|
||||
field_inherit_members: Eredita membri
|
||||
field_closed_on: Chiuso
|
||||
setting_default_projects_tracker_ids: Trackers di default per nuovi progetti
|
||||
label_total_time: Totale
|
||||
|
||||
@@ -50,8 +50,8 @@ ko:
|
||||
one: "약 한시간"
|
||||
other: "약 %{count}시간"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 시간"
|
||||
other: "%{count} 시간"
|
||||
x_days:
|
||||
one: "하루"
|
||||
other: "%{count}일"
|
||||
|
||||
@@ -253,7 +253,6 @@ lt:
|
||||
error_unable_delete_issue_status: 'Negalima ištrinti darbo statuso'
|
||||
error_unable_to_connect: Negalima prisijungti (%{value})
|
||||
error_attachment_too_big: "Ši byla negali būti įkelta, nes viršija maksimalią (%{max_size}) leistiną bylos apimtį"
|
||||
error_attachment_too_big: "This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})"
|
||||
warning_attachments_not_saved: "%{count} byla(ų) negali būti išsaugota."
|
||||
|
||||
mail_subject_lost_password: "Jūsų %{value} slaptažodis"
|
||||
|
||||
@@ -46,8 +46,8 @@ lv:
|
||||
one: "aptuveni 1 stunda"
|
||||
other: "aptuveni %{count} stundas"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 stunda"
|
||||
other: "%{count} stundas"
|
||||
x_days:
|
||||
one: "1 diena"
|
||||
other: "%{count} dienas"
|
||||
@@ -775,7 +775,7 @@ lv:
|
||||
button_cancel: Atcelt
|
||||
button_activate: Aktivizēt
|
||||
button_sort: Kārtot
|
||||
button_log_time: Ilgs laiks
|
||||
button_log_time: Reģistrēt laiku
|
||||
button_rollback: Atjaunot uz šo versiju
|
||||
button_watch: Vērot
|
||||
button_unwatch: Nevērot
|
||||
|
||||
@@ -50,8 +50,8 @@ mk:
|
||||
one: "околу 1 час"
|
||||
other: "околу %{count} часа"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 час"
|
||||
other: "%{count} часа"
|
||||
x_days:
|
||||
one: "1 ден"
|
||||
other: "%{count} дена"
|
||||
|
||||
@@ -51,8 +51,8 @@ mn:
|
||||
one: "1 цаг орчим"
|
||||
other: "ойролцоогоор %{count} цаг"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 цаг"
|
||||
other: "%{count} цаг"
|
||||
x_days:
|
||||
one: "1 өдөр"
|
||||
other: "%{count} өдөр"
|
||||
|
||||
@@ -50,7 +50,7 @@ nl:
|
||||
other: "ongeveer %{count} uren"
|
||||
x_hours:
|
||||
one: "1 uur"
|
||||
other: "%{count} hours"
|
||||
other: "%{count} uren"
|
||||
x_days:
|
||||
one: "1 dag"
|
||||
other: "%{count} dagen"
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
one: "rundt 1 time"
|
||||
other: "rundt %{count} timer"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 time"
|
||||
other: "%{count} timer"
|
||||
x_days:
|
||||
one: "1 dag"
|
||||
other: "%{count} dager"
|
||||
|
||||
@@ -82,8 +82,8 @@ pl:
|
||||
one: "około godziny"
|
||||
other: "około %{count} godzin"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 godzina"
|
||||
other: "%{count} godzin"
|
||||
x_days:
|
||||
one: "1 dzień"
|
||||
other: "%{count} dni"
|
||||
@@ -772,7 +772,7 @@ pl:
|
||||
text_default_administrator_account_changed: Zmieniono domyślne hasło administratora
|
||||
text_destroy_time_entries: Usuń wpisy dziennika
|
||||
text_destroy_time_entries_question: Przepracowano %{hours} godzin przy zagadnieniu, które chcesz usunąć. Co chcesz zrobić?
|
||||
text_email_delivery_not_configured: "Dostarczanie poczty elektronicznej nie zostało skonfigurowane, więc powiadamianie jest nieaktywne.\nSkonfiguruj serwer SMTP w config/email.yml a następnie zrestartuj aplikację i uaktywnij to."
|
||||
text_email_delivery_not_configured: "Dostarczanie poczty elektronicznej nie zostało skonfigurowane, więc powiadamianie jest nieaktywne.\nSkonfiguruj serwer SMTP w config/configuration.yml a następnie zrestartuj aplikację i uaktywnij to."
|
||||
text_enumeration_category_reassign_to: 'Zmień przypisanie na tą wartość:'
|
||||
text_enumeration_destroy_question: "%{count} obiektów jest przypisanych do tej wartości."
|
||||
text_file_repository_writable: Zapisywalne repozytorium plików
|
||||
|
||||
@@ -1105,5 +1105,5 @@ pt-BR:
|
||||
setting_jsonp_enabled: Ativar suporte JSONP
|
||||
field_inherit_members: Herdar membros
|
||||
field_closed_on: Fechado
|
||||
setting_default_projects_tracker_ids: Default trackers for new projects
|
||||
setting_default_projects_tracker_ids: Tipos padrões para novos projeto
|
||||
label_total_time: Total
|
||||
|
||||
@@ -1091,5 +1091,5 @@ pt:
|
||||
setting_jsonp_enabled: Activar suporte JSONP
|
||||
field_inherit_members: Herdar membros
|
||||
field_closed_on: Fechado
|
||||
setting_default_projects_tracker_ids: Default trackers for new projects
|
||||
setting_default_projects_tracker_ids: Tipo de tarefa padrão para novos projectos
|
||||
label_total_time: Total
|
||||
|
||||
@@ -45,8 +45,8 @@ ro:
|
||||
one: "aproximativ o oră"
|
||||
other: "aproximativ %{count} ore"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 oră"
|
||||
other: "%{count} ore"
|
||||
x_days:
|
||||
one: "o zi"
|
||||
other: "%{count} zile"
|
||||
|
||||
@@ -1080,17 +1080,17 @@ ru:
|
||||
label_between: между
|
||||
setting_issue_group_assignment: Разрешить назначение задач группам пользователей
|
||||
label_diff: Разница(diff)
|
||||
text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
|
||||
text_git_repository_note: Хранилище пустое и локальное (т.е. /gitrepo, c:\gitrepo)
|
||||
description_query_sort_criteria_direction: Порядок сортировки
|
||||
description_project_scope: Search scope
|
||||
description_project_scope: Область поиска
|
||||
description_filter: Фильтр
|
||||
description_user_mail_notification: Mail notification settings
|
||||
description_date_from: Enter start date
|
||||
description_message_content: Message content
|
||||
description_available_columns: Available Columns
|
||||
description_date_range_interval: Choose range by selecting start and end date
|
||||
description_issue_category_reassign: Choose issue category
|
||||
description_search: Searchfield
|
||||
description_user_mail_notification: Настройки почтовых оповещений
|
||||
description_date_from: Введите дату начала
|
||||
description_message_content: Содержание сообщения
|
||||
description_available_columns: Доступные столбцы
|
||||
description_date_range_interval: Выберите диапазон, установив дату начала и дату окончания
|
||||
description_issue_category_reassign: Выберите категорию задачи
|
||||
description_search: Поле поиска
|
||||
description_notes: Примечания
|
||||
description_date_range_list: Выберите диапазон из списка
|
||||
description_choose_project: Проекты
|
||||
@@ -1145,7 +1145,7 @@ ru:
|
||||
project_status_active: открытые
|
||||
project_status_closed: закрытые
|
||||
project_status_archived: архивированные
|
||||
text_project_closed: Проект закрыт и находиться в режиме только для чтения.
|
||||
text_project_closed: Проект закрыт и находится в режиме только для чтения.
|
||||
notice_user_successful_create: Пользователь %{id} создан.
|
||||
field_core_fields: Стандартные поля
|
||||
field_timeout: Таймаут (в секундах)
|
||||
|
||||
@@ -46,8 +46,8 @@ sk:
|
||||
one: "okolo 1 hodiny"
|
||||
other: "okolo %{count} hodín"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 hodina"
|
||||
other: "%{count} hodín"
|
||||
x_days:
|
||||
one: "1 deň"
|
||||
other: "%{count} dní"
|
||||
|
||||
@@ -50,8 +50,8 @@ sl:
|
||||
one: "okrog 1. ure"
|
||||
other: "okrog %{count} ur"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 ura"
|
||||
other: "%{count} ur"
|
||||
x_days:
|
||||
one: "1 dan"
|
||||
other: "%{count} dni"
|
||||
|
||||
@@ -50,8 +50,8 @@ sq:
|
||||
one: "about 1 hour"
|
||||
other: "about %{count} hours"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 ore"
|
||||
other: "%{count} ore"
|
||||
x_days:
|
||||
one: "1 day"
|
||||
other: "%{count} days"
|
||||
|
||||
@@ -53,8 +53,8 @@ sr-YU:
|
||||
one: "približno jedan sat"
|
||||
other: "približno %{count} sati"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 sat"
|
||||
other: "%{count} sati"
|
||||
x_days:
|
||||
one: "jedan dan"
|
||||
other: "%{count} dana"
|
||||
|
||||
@@ -51,8 +51,8 @@ sr:
|
||||
one: "приближно један сат"
|
||||
other: "приближно %{count} сати"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 сат"
|
||||
other: "%{count} сати"
|
||||
x_days:
|
||||
one: "један дан"
|
||||
other: "%{count} дана"
|
||||
|
||||
@@ -272,6 +272,7 @@ sv:
|
||||
field_author: Författare
|
||||
field_created_on: Skapad
|
||||
field_updated_on: Uppdaterad
|
||||
field_closed_on: Stängd
|
||||
field_field_format: Format
|
||||
field_is_for_all: För alla projekt
|
||||
field_possible_values: Möjliga värden
|
||||
@@ -372,6 +373,7 @@ sv:
|
||||
field_timeout: "Timeout (i sekunder)"
|
||||
field_board_parent: Förälderforum
|
||||
field_private_notes: Privata anteckningar
|
||||
field_inherit_members: Ärv medlemmar
|
||||
|
||||
setting_app_title: Applikationsrubrik
|
||||
setting_app_subtitle: Applikationsunderrubrik
|
||||
@@ -440,6 +442,8 @@ sv:
|
||||
setting_thumbnails_enabled: Visa miniatyrbilder av bilagor
|
||||
setting_thumbnails_size: Storlek på miniatyrbilder (i pixlar)
|
||||
setting_non_working_week_days: Lediga dagar
|
||||
setting_jsonp_enabled: Aktivera JSONP-stöd
|
||||
setting_default_projects_tracker_ids: Standardärendetyper för nya projekt
|
||||
|
||||
permission_add_project: Skapa projekt
|
||||
permission_add_subprojects: Skapa underprojekt
|
||||
@@ -657,6 +661,7 @@ sv:
|
||||
one: 1 ärende
|
||||
other: "%{count} ärenden"
|
||||
label_total: Total
|
||||
label_total_time: Total tid
|
||||
label_permissions: Behörigheter
|
||||
label_current_status: Nuvarande status
|
||||
label_new_statuses_allowed: Nya tillåtna statusvärden
|
||||
@@ -1120,8 +1125,3 @@ sv:
|
||||
description_date_from: Ange startdatum
|
||||
description_date_to: Ange slutdatum
|
||||
text_repository_identifier_info: 'Endast gemener (a-z), siffror, streck och understreck är tillåtna.<br />När identifieraren sparats kan den inte ändras.'
|
||||
setting_jsonp_enabled: Enable JSONP support
|
||||
field_inherit_members: Inherit members
|
||||
field_closed_on: Closed
|
||||
setting_default_projects_tracker_ids: Default trackers for new projects
|
||||
label_total_time: Total
|
||||
|
||||
@@ -56,8 +56,8 @@ tr:
|
||||
one: 'yaklaşık 1 saat'
|
||||
other: 'yaklaşık %{count} saat'
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 saat"
|
||||
other: "%{count} saat"
|
||||
x_days:
|
||||
one: '1 gün'
|
||||
other: '%{count} gün'
|
||||
|
||||
@@ -123,8 +123,8 @@
|
||||
one: "約 1 小時"
|
||||
other: "約 %{count} 小時"
|
||||
x_hours:
|
||||
one: "1 hour"
|
||||
other: "%{count} hours"
|
||||
one: "1 小時"
|
||||
other: "%{count} 小時"
|
||||
x_days:
|
||||
one: "1 天"
|
||||
other: "%{count} 天"
|
||||
@@ -712,7 +712,7 @@
|
||||
label_nobody: 無名
|
||||
label_next: 下一頁
|
||||
label_previous: 上一頁
|
||||
label_used_by: Used by
|
||||
label_used_by: 已使用專案
|
||||
label_details: 明細
|
||||
label_add_note: 加入一個新筆記
|
||||
label_per_page: 每頁
|
||||
|
||||
@@ -99,9 +99,11 @@ RedmineApp::Application.routes.draw do
|
||||
match 'copy', :via => [:get, :post]
|
||||
end
|
||||
|
||||
resources :memberships, :shallow => true, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
|
||||
collection do
|
||||
get 'autocomplete'
|
||||
shallow do
|
||||
resources :memberships, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
|
||||
collection do
|
||||
get 'autocomplete'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -134,15 +136,19 @@ RedmineApp::Application.routes.draw do
|
||||
get 'report', :on => :collection
|
||||
end
|
||||
resources :queries, :only => [:new, :create]
|
||||
resources :issue_categories, :shallow => true
|
||||
shallow do
|
||||
resources :issue_categories
|
||||
end
|
||||
resources :documents, :except => [:show, :edit, :update, :destroy]
|
||||
resources :boards
|
||||
resources :repositories, :shallow => true, :except => [:index, :show] do
|
||||
member do
|
||||
match 'committers', :via => [:get, :post]
|
||||
shallow do
|
||||
resources :repositories, :except => [:index, :show] do
|
||||
member do
|
||||
match 'committers', :via => [:get, :post]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
|
||||
resources :wiki, :except => [:index, :new, :create], :as => 'wiki_page' do
|
||||
member do
|
||||
@@ -176,7 +182,9 @@ RedmineApp::Application.routes.draw do
|
||||
get 'report'
|
||||
end
|
||||
end
|
||||
resources :relations, :shallow => true, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
|
||||
shallow do
|
||||
resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
|
||||
end
|
||||
end
|
||||
match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
|
||||
|
||||
|
||||
@@ -4,6 +4,55 @@ Redmine - project management software
|
||||
Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
http://www.redmine.org/
|
||||
|
||||
== 2013-09-14 v2.3.3
|
||||
|
||||
* Defect #13008: Usage of attribute_present? in UserPreference
|
||||
* Defect #14340: Autocomplete fields rendering issue with alternate theme
|
||||
* Defect #14366: Spent Time report sorting on custom fields causes error
|
||||
* Defect #14369: Open/closed issue counts on issues summary are not displayed with SQLServer
|
||||
* Defect #14401: Filtering issues on "related to" may ignore other filters
|
||||
* Defect #14415: Spent time details and report should ignore 'Setting.display_subprojects_issues?' when 'Subproject' filter is enabled.
|
||||
* Defect #14422: CVS root_url not recognized when connection string does not include port
|
||||
* Defect #14447: Additional status transitions for assignees do not work if assigned to a group
|
||||
* Defect #14511: warning: class variable access from toplevel on Ruby 2.0
|
||||
* Defect #14562: diff of CJK (Chinese/Japanese/Korean) is broken on Ruby 1.8
|
||||
* Defect #14584: Standard fields disabled for certain trackers still appear in email notifications
|
||||
* Defect #14607: rake redmine:load_default_data Error
|
||||
* Defect #14697: Wrong Russian translation in close project message
|
||||
* Defect #14798: Wrong done_ratio calculation for parent with subtask having estimated_hours=0
|
||||
* Patch #14485: Traditional Chinese translation for 2.3-stable
|
||||
* Patch #14502: Russian translation for 2.3-stable
|
||||
* Patch #14531: Spanish translations for 2.3.x
|
||||
* Patch #14686: Portuguese translation for 2.3-stable
|
||||
|
||||
== 2013-07-14 v2.3.2
|
||||
|
||||
* Defect #9996: configuration.yml in documentation , but redmine ask me to create email.yml
|
||||
* Defect #13692: warning: already initialized constant on Ruby 1.8.7
|
||||
* Defect #13783: Internal error on time tracking activity enumeration deletion
|
||||
* Defect #13821: "obj" parameter is not defined for macros used in description of documents
|
||||
* Defect #13850: Unable to set custom fields for versions using the REST API
|
||||
* Defect #13910: Values of custom fields are not kept in issues when copying a project
|
||||
* Defect #13950: Duplicate Lithuanian "error_attachment_too_big" translation keys
|
||||
* Defect #14015: Ruby hangs when adding a subtask
|
||||
* Defect #14020: Locking and unlocking a user resets the email notification checkbox
|
||||
* Defect #14023: Can't delete relation when Redmine runs in a subpath
|
||||
* Defect #14051: Filtering issues with custom field in date format with NULL(empty) value
|
||||
* Defect #14178: PDF API broken in version 2.3.1
|
||||
* Defect #14186: Project name is not properly escaped in issue filters JSON
|
||||
* Defect #14242: Project auto generation fails when projects created in the same time
|
||||
* Defect #14245: Gem::InstallError: nokogiri requires Ruby version >= 1.9.2.
|
||||
* Defect #14346: Latvian translation for "Log time"
|
||||
* Feature #12888: Adding markings to emails generated by Private comments
|
||||
* Feature #14419: Include RUBY_PATCHLEVEL and RUBY_RELEASE_DATE in info.rb
|
||||
* Patch #14005: Swedish Translation for 2.3-stable
|
||||
* Patch #14101: Receive IMAP by uid's
|
||||
* Patch #14103: Disconnect and logout from IMAP after mail receive
|
||||
* Patch #14145: German translation of x_hours
|
||||
* Patch #14182: pt-BR translation for 2.3-stable
|
||||
* Patch #14196: Italian translation for 2.3-stable
|
||||
* Patch #14221: Translation of x_hours for many languages
|
||||
|
||||
== 2013-05-01 v2.3.1
|
||||
|
||||
* Defect #12650: Lost text after selection in issue list with IE
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
begin
|
||||
require 'zlib'
|
||||
@@__have_zlib = true
|
||||
rescue
|
||||
@@__have_zlib = false
|
||||
# Zlib not available
|
||||
end
|
||||
|
||||
require 'rexml/document'
|
||||
@@ -211,7 +210,7 @@ module SVG
|
||||
@doc.write( data, 0 )
|
||||
|
||||
if @config[:compress]
|
||||
if @@__have_zlib
|
||||
if Object.const_defined?(:Zlib)
|
||||
inp, out = IO.pipe
|
||||
gz = Zlib::GzipWriter.new( out )
|
||||
gz.write data
|
||||
|
||||
@@ -394,7 +394,7 @@ module Redmine
|
||||
|
||||
# write the cells on page
|
||||
issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true)
|
||||
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_width)
|
||||
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width)
|
||||
pdf.SetY(base_y + max_height);
|
||||
|
||||
# rows
|
||||
@@ -474,7 +474,7 @@ module Redmine
|
||||
|
||||
# write the cells on page
|
||||
issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
|
||||
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_width)
|
||||
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width)
|
||||
pdf.SetY(base_y + max_height);
|
||||
|
||||
if query.has_column?(:description) && issue.description?
|
||||
@@ -511,7 +511,10 @@ module Redmine
|
||||
end
|
||||
|
||||
# Draw lines to close the row (MultiCell border drawing in not uniform)
|
||||
def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, col_widths)
|
||||
#
|
||||
# parameter "col_id_width" is not used. it is kept for compatibility.
|
||||
def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
|
||||
col_id_width, col_widths)
|
||||
col_x = top_x
|
||||
pdf.Line(col_x, top_y, col_x, lower_y) # id right border
|
||||
col_widths.each do |width|
|
||||
|
||||
@@ -675,7 +675,7 @@ module Redmine
|
||||
start_date + (end_date - start_date + 1) * (progress / 100.0)
|
||||
end
|
||||
|
||||
# Sorts a collection of issues by start_date, due_date, id for gantt rendering
|
||||
# TODO: Sorts a collection of issues by start_date, due_date, id for gantt rendering
|
||||
def sort_issues!(issues)
|
||||
issues.sort! { |a, b| gantt_issue_compare(a, b) }
|
||||
end
|
||||
|
||||
@@ -29,25 +29,27 @@ module Redmine
|
||||
imap = Net::IMAP.new(host, port, ssl)
|
||||
imap.login(imap_options[:username], imap_options[:password]) unless imap_options[:username].nil?
|
||||
imap.select(folder)
|
||||
imap.search(['NOT', 'SEEN']).each do |message_id|
|
||||
msg = imap.fetch(message_id,'RFC822')[0].attr['RFC822']
|
||||
logger.debug "Receiving message #{message_id}" if logger && logger.debug?
|
||||
imap.uid_search(['NOT', 'SEEN']).each do |uid|
|
||||
msg = imap.uid_fetch(uid,'RFC822')[0].attr['RFC822']
|
||||
logger.debug "Receiving message #{uid}" if logger && logger.debug?
|
||||
if MailHandler.receive(msg, options)
|
||||
logger.debug "Message #{message_id} successfully received" if logger && logger.debug?
|
||||
logger.debug "Message #{uid} successfully received" if logger && logger.debug?
|
||||
if imap_options[:move_on_success]
|
||||
imap.copy(message_id, imap_options[:move_on_success])
|
||||
imap.uid_copy(uid, imap_options[:move_on_success])
|
||||
end
|
||||
imap.store(message_id, "+FLAGS", [:Seen, :Deleted])
|
||||
imap.uid_store(uid, "+FLAGS", [:Seen, :Deleted])
|
||||
else
|
||||
logger.debug "Message #{message_id} can not be processed" if logger && logger.debug?
|
||||
imap.store(message_id, "+FLAGS", [:Seen])
|
||||
logger.debug "Message #{uid} can not be processed" if logger && logger.debug?
|
||||
imap.uid_store(uid, "+FLAGS", [:Seen])
|
||||
if imap_options[:move_on_failure]
|
||||
imap.copy(message_id, imap_options[:move_on_failure])
|
||||
imap.store(message_id, "+FLAGS", [:Deleted])
|
||||
imap.uid_copy(uid, imap_options[:move_on_failure])
|
||||
imap.uid_store(uid, "+FLAGS", [:Deleted])
|
||||
end
|
||||
end
|
||||
end
|
||||
imap.expunge
|
||||
imap.logout
|
||||
imap.disconnect
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -10,16 +10,16 @@ module Redmine
|
||||
s = "Environment:\n"
|
||||
s << [
|
||||
["Redmine version", Redmine::VERSION],
|
||||
["Ruby version", "#{RUBY_VERSION} (#{RUBY_PLATFORM})"],
|
||||
["Ruby version", "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"],
|
||||
["Rails version", Rails::VERSION::STRING],
|
||||
["Environment", Rails.env],
|
||||
["Database adapter", ActiveRecord::Base.connection.adapter_name]
|
||||
].map {|info| " %-40s %s" % info}.join("\n")
|
||||
].map {|info| " %-30s %s" % info}.join("\n")
|
||||
s << "\nRedmine plugins:\n"
|
||||
|
||||
plugins = Redmine::Plugin.all
|
||||
if plugins.any?
|
||||
s << plugins.map {|plugin| " %-40s %s" % [plugin.id.to_s, plugin.version.to_s]}.join("\n")
|
||||
s << plugins.map {|plugin| " %-30s %s" % [plugin.id.to_s, plugin.version.to_s]}.join("\n")
|
||||
else
|
||||
s << " no plugin installed"
|
||||
end
|
||||
|
||||
@@ -335,7 +335,7 @@ module Redmine
|
||||
# :pserver:anonymous@foo.bar:/path => /path
|
||||
# :ext:cvsservername:/path => /path
|
||||
def root_url_path
|
||||
root_url.to_s.gsub(/^:.+:\d*/, '')
|
||||
root_url.to_s.gsub(%r{^:.+?(?=/)}, '')
|
||||
end
|
||||
|
||||
# convert a date/time into the CVS-format
|
||||
|
||||
@@ -33,7 +33,6 @@ module Redmine
|
||||
|
||||
module CodeRay
|
||||
require 'coderay'
|
||||
require 'coderay/helpers/file_type'
|
||||
|
||||
class << self
|
||||
# Highlights +text+ as the content of +filename+
|
||||
|
||||
@@ -205,12 +205,20 @@ module Redmine
|
||||
end
|
||||
end
|
||||
ending = -1
|
||||
while ending >= -(max - starting) && line_left[ending] == line_right[ending]
|
||||
while ending >= -(max - starting) && (line_left[ending] == line_right[ending])
|
||||
ending -= 1
|
||||
end
|
||||
if (! "".respond_to?(:force_encoding)) && ending > (-1 * line_left.size)
|
||||
while line_left[ending].ord.between?(128, 191) && ending > -1
|
||||
ending -= 1
|
||||
while line_left[ending].ord.between?(128, 255) && ending < -1
|
||||
if line_left[ending].ord.between?(128, 191)
|
||||
if line_left[ending + 1].ord.between?(128, 191)
|
||||
ending += 1
|
||||
else
|
||||
break
|
||||
end
|
||||
else
|
||||
ending += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
unless starting == 0 && ending == -1
|
||||
|
||||
@@ -4,7 +4,7 @@ module Redmine
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 1
|
||||
TINY = 3
|
||||
|
||||
# Branch values:
|
||||
# * official release: nil
|
||||
|
||||
@@ -2,6 +2,7 @@ desc 'Load Redmine default configuration data. Language is chosen interactively
|
||||
|
||||
namespace :redmine do
|
||||
task :load_default_data => :environment do
|
||||
require 'custom_field'
|
||||
include Redmine::I18n
|
||||
set_language_if_valid('en')
|
||||
|
||||
|
||||
@@ -575,7 +575,7 @@ table.members td.group { padding-left: 20px; background: url(../images/group.png
|
||||
input#principal_search, input#user_search {width:90%}
|
||||
|
||||
input.autocomplete {
|
||||
background: #fff url(../images/magnifier.png) no-repeat 2px 50%; padding-left:20px;
|
||||
background: #fff url(../images/magnifier.png) no-repeat 2px 50%; padding-left:20px !important;
|
||||
border:1px solid #9EB1C2; border-radius:2px; height:1.5em;
|
||||
}
|
||||
input.autocomplete.ajax-loading {
|
||||
|
||||
7
test/fixtures/diffs/issue-13644-3.diff
vendored
Normal file
7
test/fixtures/diffs/issue-13644-3.diff
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
--- a.txt 2013-07-27 06:03:49.133257759 +0900
|
||||
+++ b.txt 2013-07-27 06:03:58.791221118 +0900
|
||||
@@ -1,3 +1,3 @@
|
||||
aaaa
|
||||
-日本記
|
||||
+日本娘
|
||||
bbbb
|
||||
7
test/fixtures/diffs/issue-13644-4.diff
vendored
Normal file
7
test/fixtures/diffs/issue-13644-4.diff
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
--- a.txt 2013-07-27 04:20:45.973229414 +0900
|
||||
+++ b.txt 2013-07-27 04:20:52.366228105 +0900
|
||||
@@ -1,3 +1,3 @@
|
||||
aaaa
|
||||
-日本記
|
||||
+日本誘
|
||||
bbbb
|
||||
7
test/fixtures/diffs/issue-13644-5.diff
vendored
Normal file
7
test/fixtures/diffs/issue-13644-5.diff
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
--- a.txt 2013-07-27 05:52:11.415223830 +0900
|
||||
+++ b.txt 2013-07-27 05:52:18.249190358 +0900
|
||||
@@ -1,3 +1,3 @@
|
||||
aaaa
|
||||
-日本記ok
|
||||
+日本誘ok
|
||||
bbbb
|
||||
@@ -83,7 +83,7 @@ class AdminControllerTest < ActionController::TestCase
|
||||
|
||||
def test_test_email
|
||||
user = User.find(1)
|
||||
user.pref[:no_self_notified] = '1'
|
||||
user.pref.no_self_notified = '1'
|
||||
user.pref.save!
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
||||
|
||||
@@ -126,4 +126,11 @@ class EnumerationsControllerTest < ActionController::TestCase
|
||||
# check that the issue was reassign
|
||||
assert_equal 6, issue.reload.priority_id
|
||||
end
|
||||
|
||||
def test_destroy_enumeration_in_use_with_blank_reassignment
|
||||
assert_no_difference 'IssuePriority.count' do
|
||||
delete :destroy, :id => 4, :reassign_to_id => ''
|
||||
end
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
@@ -209,7 +209,7 @@ class ProjectsControllerTest < ActionController::TestCase
|
||||
assert_response :success
|
||||
project = assigns(:project)
|
||||
assert_kind_of Project, project
|
||||
assert_not_nil project.errors[:parent_id]
|
||||
assert_not_equal [], project.errors[:parent_id]
|
||||
end
|
||||
|
||||
test "#create by non-admin user with add_subprojects permission should create a project with a parent_id" do
|
||||
@@ -244,7 +244,7 @@ class ProjectsControllerTest < ActionController::TestCase
|
||||
assert_response :success
|
||||
project = assigns(:project)
|
||||
assert_kind_of Project, project
|
||||
assert_not_nil project.errors[:parent_id]
|
||||
assert_not_equal [], project.errors[:parent_id]
|
||||
end
|
||||
|
||||
test "#create by non-admin user with add_subprojects permission should fail with unauthorized parent_id" do
|
||||
@@ -265,7 +265,7 @@ class ProjectsControllerTest < ActionController::TestCase
|
||||
assert_response :success
|
||||
project = assigns(:project)
|
||||
assert_kind_of Project, project
|
||||
assert_not_nil project.errors[:parent_id]
|
||||
assert_not_equal [], project.errors[:parent_id]
|
||||
end
|
||||
|
||||
def test_create_subproject_with_inherit_members_should_inherit_members
|
||||
|
||||
@@ -54,6 +54,24 @@ class ReportsControllerTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
def test_get_issue_report_details_by_tracker_should_show_issue_count
|
||||
Issue.delete_all
|
||||
Issue.generate!(:tracker_id => 1)
|
||||
Issue.generate!(:tracker_id => 1)
|
||||
Issue.generate!(:tracker_id => 1, :status_id => 5)
|
||||
Issue.generate!(:tracker_id => 2)
|
||||
|
||||
get :issue_report_details, :id => 1, :detail => 'tracker'
|
||||
assert_select 'table.list tbody :nth-child(1)' do
|
||||
assert_select 'td', :text => 'Bug'
|
||||
assert_select ':nth-child(2)', :text => '2' # status:1
|
||||
assert_select ':nth-child(3)', :text => '-' # status:2
|
||||
assert_select ':nth-child(8)', :text => '2' # open
|
||||
assert_select ':nth-child(9)', :text => '1' # closed
|
||||
assert_select ':nth-child(10)', :text => '3' # total
|
||||
end
|
||||
end
|
||||
|
||||
def test_get_issue_report_details_by_priority
|
||||
get :issue_report_details, :id => 1, :detail => 'priority'
|
||||
assert_equal IssuePriority.all.reverse, assigns(:rows)
|
||||
|
||||
@@ -443,6 +443,28 @@ class TimelogControllerTest < ActionController::TestCase
|
||||
:attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
|
||||
end
|
||||
|
||||
def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
|
||||
entry = TimeEntry.generate!(:project => Project.find(3))
|
||||
|
||||
with_settings :display_subprojects_issues => '0' do
|
||||
get :index, :project_id => 'ecookbook'
|
||||
assert_response :success
|
||||
assert_template 'index'
|
||||
assert_not_include entry, assigns(:entries)
|
||||
end
|
||||
end
|
||||
|
||||
def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
|
||||
entry = TimeEntry.generate!(:project => Project.find(3))
|
||||
|
||||
with_settings :display_subprojects_issues => '0' do
|
||||
get :index, :project_id => 'ecookbook', :subproject_id => 3
|
||||
assert_response :success
|
||||
assert_template 'index'
|
||||
assert_include entry, assigns(:entries)
|
||||
end
|
||||
end
|
||||
|
||||
def test_index_at_project_level_with_date_range
|
||||
get :index, :project_id => 'ecookbook',
|
||||
:f => ['spent_on'],
|
||||
@@ -540,6 +562,24 @@ class TimelogControllerTest < ActionController::TestCase
|
||||
assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
|
||||
end
|
||||
|
||||
def test_index_with_time_entry_custom_field_sorting
|
||||
field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
|
||||
TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
|
||||
TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
|
||||
TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
|
||||
field_name = "cf_#{field.id}"
|
||||
|
||||
get :index, :c => ["hours", field_name], :sort => field_name
|
||||
assert_response :success
|
||||
assert_include field_name.to_sym, assigns(:query).column_names
|
||||
assert_select "th a.sort", :text => 'String Field'
|
||||
|
||||
# Make sure that values are properly sorted
|
||||
values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
|
||||
assert_equal 3, values.size
|
||||
assert_equal values.sort, values
|
||||
end
|
||||
|
||||
def test_index_atom_feed
|
||||
get :index, :project_id => 1, :format => 'atom'
|
||||
assert_response :success
|
||||
|
||||
@@ -325,6 +325,18 @@ class UsersControllerTest < ActionController::TestCase
|
||||
assert_equal [1, 2], u.notified_projects_ids.sort
|
||||
end
|
||||
|
||||
def test_update_status_should_not_update_attributes
|
||||
user = User.find(2)
|
||||
user.pref[:no_self_notified] = '1'
|
||||
user.pref.save
|
||||
|
||||
put :update, :id => 2, :user => {:status => 3}
|
||||
assert_response 302
|
||||
user = User.find(2)
|
||||
assert_equal 3, user.status
|
||||
assert_equal '1', user.pref[:no_self_notified]
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
assert_difference 'User.count', -1 do
|
||||
delete :destroy, :id => 2
|
||||
|
||||
@@ -82,6 +82,29 @@ class Redmine::ApiTest::VersionsTest < Redmine::ApiTest::Base
|
||||
assert_tag 'version', :child => {:tag => 'id', :content => version.id.to_s}
|
||||
end
|
||||
|
||||
should "create the version with custom fields" do
|
||||
field = VersionCustomField.generate!
|
||||
|
||||
assert_difference 'Version.count' do
|
||||
post '/projects/1/versions.xml', {
|
||||
:version => {
|
||||
:name => 'API test',
|
||||
:custom_fields => [
|
||||
{'id' => field.id.to_s, 'value' => 'Some value'}
|
||||
]
|
||||
}
|
||||
}, credentials('jsmith')
|
||||
end
|
||||
|
||||
version = Version.first(:order => 'id DESC')
|
||||
assert_equal 'API test', version.name
|
||||
assert_equal 'Some value', version.custom_field_value(field)
|
||||
|
||||
assert_response :created
|
||||
assert_equal 'application/xml', @response.content_type
|
||||
assert_select 'version>custom_fields>custom_field[id=?]>value', field.id.to_s, 'Some value'
|
||||
end
|
||||
|
||||
context "with failure" do
|
||||
should "return the errors" do
|
||||
assert_no_difference('Version.count') do
|
||||
|
||||
@@ -107,10 +107,11 @@ module ObjectHelpers
|
||||
def TimeEntry.generate!(attributes={})
|
||||
entry = TimeEntry.new(attributes)
|
||||
entry.user ||= User.find(2)
|
||||
entry.issue ||= Issue.find(1)
|
||||
entry.issue ||= Issue.find(1) unless entry.project
|
||||
entry.project ||= entry.issue.project
|
||||
entry.activity ||= TimeEntryActivity.first
|
||||
entry.spent_on ||= Date.today
|
||||
entry.hours ||= 1.0
|
||||
entry.save!
|
||||
entry
|
||||
end
|
||||
@@ -147,4 +148,15 @@ module ObjectHelpers
|
||||
attachment.save!
|
||||
attachment
|
||||
end
|
||||
|
||||
def CustomField.generate!(attributes={})
|
||||
@generated_custom_field_name ||= 'Custom field 0'
|
||||
@generated_custom_field_name.succ!
|
||||
field = new(attributes)
|
||||
field.name = @generated_custom_field_name.dup if field.name.blank?
|
||||
field.field_format = 'string' if field.field_format.blank?
|
||||
yield field if block_given?
|
||||
field.save!
|
||||
field
|
||||
end
|
||||
end
|
||||
|
||||
@@ -93,7 +93,7 @@ class AttachmentTest < ActiveSupport::TestCase
|
||||
def test_description_length_should_be_validated
|
||||
a = Attachment.new(:description => 'a' * 300)
|
||||
assert !a.save
|
||||
assert_not_nil a.errors[:description]
|
||||
assert_not_equal [], a.errors[:description]
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
|
||||
@@ -1210,4 +1210,14 @@ RAW
|
||||
def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript
|
||||
assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo)
|
||||
end
|
||||
|
||||
def test_raw_json_should_escape_closing_tags
|
||||
s = raw_json(["<foo>bar</foo>"])
|
||||
assert_equal '["<foo>bar<\/foo>"]', s
|
||||
end
|
||||
|
||||
def test_raw_json_should_be_html_safe
|
||||
s = raw_json(["foo"])
|
||||
assert s.html_safe?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -57,7 +57,7 @@ class IssueNestedSetTest < ActiveSupport::TestCase
|
||||
child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
|
||||
:subject => 'child', :parent_issue_id => issue.id)
|
||||
assert !child.save
|
||||
assert_not_nil child.errors[:parent_issue_id]
|
||||
assert_not_equal [], child.errors[:parent_issue_id]
|
||||
end
|
||||
|
||||
def test_move_a_root_to_child
|
||||
@@ -163,7 +163,7 @@ class IssueNestedSetTest < ActiveSupport::TestCase
|
||||
child.reload
|
||||
child.parent_issue_id = grandchild.id
|
||||
assert !child.save
|
||||
assert_not_nil child.errors[:parent_issue_id]
|
||||
assert_not_equal [], child.errors[:parent_issue_id]
|
||||
end
|
||||
|
||||
def test_destroy_should_destroy_children
|
||||
@@ -302,6 +302,17 @@ class IssueNestedSetTest < ActiveSupport::TestCase
|
||||
assert_equal (50 * 20 + 20 * 10) / 30, parent.reload.done_ratio
|
||||
end
|
||||
|
||||
def test_parent_done_ratio_with_child_estimate_to_0_should_reach_100
|
||||
parent = Issue.generate!
|
||||
issue1 = Issue.generate!(:parent_issue_id => parent.id)
|
||||
issue2 = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 0)
|
||||
assert_equal 0, parent.reload.done_ratio
|
||||
issue1.reload.update_attribute :status_id, 5
|
||||
assert_equal 50, parent.reload.done_ratio
|
||||
issue2.reload.update_attribute :status_id, 5
|
||||
assert_equal 100, parent.reload.done_ratio
|
||||
end
|
||||
|
||||
def test_parent_estimate_should_be_sum_of_leaves
|
||||
parent = Issue.generate!
|
||||
Issue.generate!(:estimated_hours => nil, :parent_issue_id => parent.id)
|
||||
|
||||
@@ -114,7 +114,7 @@ class IssueRelationTest < ActiveSupport::TestCase
|
||||
:relation_type => IssueRelation::TYPE_PRECEDES
|
||||
)
|
||||
assert !r.save
|
||||
assert_not_nil r.errors[:base]
|
||||
assert_not_equal [], r.errors[:base]
|
||||
end
|
||||
|
||||
def test_validates_circular_dependency_of_subtask
|
||||
@@ -165,6 +165,6 @@ class IssueRelationTest < ActiveSupport::TestCase
|
||||
:relation_type => IssueRelation::TYPE_BLOCKED
|
||||
)
|
||||
assert !r.save
|
||||
assert_not_nil r.errors[:base]
|
||||
assert_not_equal [], r.errors[:base]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -469,7 +469,7 @@ class IssueTest < ActiveSupport::TestCase
|
||||
issue.tracker_id = 2
|
||||
issue.subject = 'New subject'
|
||||
assert !issue.save
|
||||
assert_not_nil issue.errors[:tracker_id]
|
||||
assert_not_equal [], issue.errors[:tracker_id]
|
||||
end
|
||||
|
||||
def test_category_based_assignment
|
||||
@@ -488,9 +488,9 @@ class IssueTest < ActiveSupport::TestCase
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
|
||||
:old_status_id => 1, :new_status_id => 3,
|
||||
:author => true, :assignee => false)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
|
||||
:new_status_id => 4, :author => false,
|
||||
:assignee => true)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
|
||||
:old_status_id => 1, :new_status_id => 4,
|
||||
:author => false, :assignee => true)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
|
||||
:old_status_id => 1, :new_status_id => 5,
|
||||
:author => true, :assignee => true)
|
||||
@@ -516,6 +516,26 @@ class IssueTest < ActiveSupport::TestCase
|
||||
:project_id => 1, :author => user,
|
||||
:assigned_to => user)
|
||||
assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
|
||||
|
||||
group = Group.generate!
|
||||
group.users << user
|
||||
issue = Issue.generate!(:tracker => tracker, :status => status,
|
||||
:project_id => 1, :author => user,
|
||||
:assigned_to => group)
|
||||
assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
|
||||
end
|
||||
|
||||
def test_new_statuses_allowed_to_should_consider_group_assignment
|
||||
WorkflowTransition.delete_all
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
|
||||
:old_status_id => 1, :new_status_id => 4,
|
||||
:author => false, :assignee => true)
|
||||
user = User.find(2)
|
||||
group = Group.generate!
|
||||
group.users << user
|
||||
|
||||
issue = Issue.generate!(:author_id => 1, :assigned_to => group)
|
||||
assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
|
||||
end
|
||||
|
||||
def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
|
||||
@@ -1004,7 +1024,7 @@ class IssueTest < ActiveSupport::TestCase
|
||||
:status_id => 1, :fixed_version_id => 1,
|
||||
:subject => 'New issue')
|
||||
assert !issue.save
|
||||
assert_not_nil issue.errors[:fixed_version_id]
|
||||
assert_not_equal [], issue.errors[:fixed_version_id]
|
||||
end
|
||||
|
||||
def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
|
||||
@@ -1012,7 +1032,7 @@ class IssueTest < ActiveSupport::TestCase
|
||||
:status_id => 1, :fixed_version_id => 2,
|
||||
:subject => 'New issue')
|
||||
assert !issue.save
|
||||
assert_not_nil issue.errors[:fixed_version_id]
|
||||
assert_not_equal [], issue.errors[:fixed_version_id]
|
||||
end
|
||||
|
||||
def test_should_be_able_to_assign_a_new_issue_to_an_open_version
|
||||
@@ -1033,7 +1053,7 @@ class IssueTest < ActiveSupport::TestCase
|
||||
issue = Issue.find(11)
|
||||
issue.status_id = 1
|
||||
assert !issue.save
|
||||
assert_not_nil issue.errors[:base]
|
||||
assert_not_equal [], issue.errors[:base]
|
||||
end
|
||||
|
||||
def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
|
||||
@@ -1792,6 +1812,136 @@ class IssueTest < ActiveSupport::TestCase
|
||||
assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_with_subtask
|
||||
IssueRelation.delete_all
|
||||
|
||||
project = Project.generate!(:name => "testproject")
|
||||
|
||||
parentIssue = Issue.generate!(:project => project)
|
||||
childIssue1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
|
||||
childIssue2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
|
||||
|
||||
assert_equal [childIssue1.id, childIssue2.id].sort, parentIssue.all_dependent_issues.collect(&:id).uniq.sort
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_does_not_include_self
|
||||
IssueRelation.delete_all
|
||||
|
||||
project = Project.generate!(:name => "testproject")
|
||||
|
||||
parentIssue = Issue.generate!(:project => project)
|
||||
childIssue = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
|
||||
|
||||
assert_equal [childIssue.id], parentIssue.all_dependent_issues.collect(&:id)
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_with_parenttask_and_sibling
|
||||
IssueRelation.delete_all
|
||||
|
||||
project = Project.generate!(:name => "testproject")
|
||||
|
||||
parentIssue = Issue.generate!(:project => project)
|
||||
childIssue1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
|
||||
childIssue2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
|
||||
|
||||
assert_equal [parentIssue.id].sort, childIssue1.all_dependent_issues.collect(&:id)
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_with_relation_to_leaf_in_other_tree
|
||||
IssueRelation.delete_all
|
||||
|
||||
project = Project.generate!(:name => "testproject")
|
||||
|
||||
parentIssue1 = Issue.generate!(:project => project)
|
||||
childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
|
||||
childIssue1_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
|
||||
|
||||
parentIssue2 = Issue.generate!(:project => project)
|
||||
childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
|
||||
childIssue2_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
|
||||
|
||||
|
||||
assert IssueRelation.create(:issue_from => parentIssue1,
|
||||
:issue_to => childIssue2_2,
|
||||
:relation_type => IssueRelation::TYPE_BLOCKS)
|
||||
|
||||
assert_equal [childIssue1_1.id, childIssue1_2.id, parentIssue2.id, childIssue2_2.id].sort,
|
||||
parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_with_relation_to_parent_in_other_tree
|
||||
IssueRelation.delete_all
|
||||
|
||||
project = Project.generate!(:name => "testproject")
|
||||
|
||||
parentIssue1 = Issue.generate!(:project => project)
|
||||
childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
|
||||
childIssue1_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
|
||||
|
||||
parentIssue2 = Issue.generate!(:project => project)
|
||||
childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
|
||||
childIssue2_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
|
||||
|
||||
|
||||
assert IssueRelation.create(:issue_from => parentIssue1,
|
||||
:issue_to => parentIssue2,
|
||||
:relation_type => IssueRelation::TYPE_BLOCKS)
|
||||
|
||||
assert_equal [childIssue1_1.id, childIssue1_2.id, parentIssue2.id, childIssue2_1.id, childIssue2_2.id].sort,
|
||||
parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_with_transitive_relation
|
||||
IssueRelation.delete_all
|
||||
|
||||
project = Project.generate!(:name => "testproject")
|
||||
|
||||
parentIssue1 = Issue.generate!(:project => project)
|
||||
childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
|
||||
|
||||
parentIssue2 = Issue.generate!(:project => project)
|
||||
childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
|
||||
|
||||
independentIssue = Issue.generate!(:project => project)
|
||||
|
||||
assert IssueRelation.create(:issue_from => parentIssue1,
|
||||
:issue_to => childIssue2_1,
|
||||
:relation_type => IssueRelation::TYPE_RELATES)
|
||||
|
||||
assert IssueRelation.create(:issue_from => childIssue2_1,
|
||||
:issue_to => independentIssue,
|
||||
:relation_type => IssueRelation::TYPE_RELATES)
|
||||
|
||||
assert_equal [childIssue1_1.id, parentIssue2.id, childIssue2_1.id, independentIssue.id].sort,
|
||||
parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_with_transitive_relation2
|
||||
IssueRelation.delete_all
|
||||
|
||||
project = Project.generate!(:name => "testproject")
|
||||
|
||||
parentIssue1 = Issue.generate!(:project => project)
|
||||
childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
|
||||
|
||||
parentIssue2 = Issue.generate!(:project => project)
|
||||
childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
|
||||
|
||||
independentIssue = Issue.generate!(:project => project)
|
||||
|
||||
assert IssueRelation.create(:issue_from => parentIssue1,
|
||||
:issue_to => independentIssue,
|
||||
:relation_type => IssueRelation::TYPE_RELATES)
|
||||
|
||||
assert IssueRelation.create(:issue_from => independentIssue,
|
||||
:issue_to => childIssue2_1,
|
||||
:relation_type => IssueRelation::TYPE_RELATES)
|
||||
|
||||
assert_equal [childIssue1_1.id, parentIssue2.id, childIssue2_1.id, independentIssue.id].sort,
|
||||
parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
|
||||
|
||||
end
|
||||
|
||||
def test_all_dependent_issues_with_persistent_circular_dependency
|
||||
IssueRelation.delete_all
|
||||
assert IssueRelation.create!(:issue_from => Issue.find(1),
|
||||
|
||||
@@ -79,6 +79,22 @@ begin
|
||||
assert_equal "UTF-8", adpt2.path_encoding
|
||||
end
|
||||
|
||||
def test_root_url_path
|
||||
to_test = {
|
||||
':pserver:cvs_user:cvs_password@123.456.789.123:9876/repo' => '/repo',
|
||||
':pserver:cvs_user:cvs_password@123.456.789.123/repo' => '/repo',
|
||||
':pserver:cvs_user:cvs_password@cvs_server:/repo' => '/repo',
|
||||
':pserver:cvs_user:cvs_password@cvs_server:9876/repo' => '/repo',
|
||||
':pserver:cvs_user:cvs_password@cvs_server/repo' => '/repo',
|
||||
':pserver:cvs_user:cvs_password@cvs_server/path/repo' => '/path/repo',
|
||||
':ext:cvsservername:/path' => '/path'
|
||||
}
|
||||
|
||||
to_test.each do |string, expected|
|
||||
assert_equal expected, Redmine::Scm::Adapters::CvsAdapter.new('foo', string).send(:root_url_path), "#{string} failed"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def test_scm_version_for(scm_command_version, version)
|
||||
|
||||
@@ -308,6 +308,54 @@ DIFF
|
||||
end
|
||||
end
|
||||
|
||||
def test_offset_range_japanese_3
|
||||
# UTF-8 The 1st byte differs.
|
||||
ja1 = "\xe6\x97\xa5\xe6\x9c\xac<span>\xe8\xa8\x98</span>"
|
||||
ja1.force_encoding('UTF-8') if ja1.respond_to?(:force_encoding)
|
||||
ja2 = "\xe6\x97\xa5\xe6\x9c\xac<span>\xe5\xa8\x98</span>"
|
||||
ja2.force_encoding('UTF-8') if ja2.respond_to?(:force_encoding)
|
||||
with_settings :repositories_encodings => '' do
|
||||
diff = Redmine::UnifiedDiff.new(
|
||||
read_diff_fixture('issue-13644-3.diff'), :type => 'sbs')
|
||||
assert_equal 1, diff.size
|
||||
assert_equal 3, diff.first.size
|
||||
assert_equal ja1, diff.first[1].html_line_left
|
||||
assert_equal ja2, diff.first[1].html_line_right
|
||||
end
|
||||
end
|
||||
|
||||
def test_offset_range_japanese_4
|
||||
# UTF-8 The 2nd byte differs.
|
||||
ja1 = "\xe6\x97\xa5\xe6\x9c\xac<span>\xe8\xa8\x98</span>"
|
||||
ja1.force_encoding('UTF-8') if ja1.respond_to?(:force_encoding)
|
||||
ja2 = "\xe6\x97\xa5\xe6\x9c\xac<span>\xe8\xaa\x98</span>"
|
||||
ja2.force_encoding('UTF-8') if ja2.respond_to?(:force_encoding)
|
||||
with_settings :repositories_encodings => '' do
|
||||
diff = Redmine::UnifiedDiff.new(
|
||||
read_diff_fixture('issue-13644-4.diff'), :type => 'sbs')
|
||||
assert_equal 1, diff.size
|
||||
assert_equal 3, diff.first.size
|
||||
assert_equal ja1, diff.first[1].html_line_left
|
||||
assert_equal ja2, diff.first[1].html_line_right
|
||||
end
|
||||
end
|
||||
|
||||
def test_offset_range_japanese_5
|
||||
# UTF-8 The 2nd byte differs.
|
||||
ja1 = "\xe6\x97\xa5\xe6\x9c\xac<span>\xe8\xa8\x98</span>ok"
|
||||
ja1.force_encoding('UTF-8') if ja1.respond_to?(:force_encoding)
|
||||
ja2 = "\xe6\x97\xa5\xe6\x9c\xac<span>\xe8\xaa\x98</span>ok"
|
||||
ja2.force_encoding('UTF-8') if ja2.respond_to?(:force_encoding)
|
||||
with_settings :repositories_encodings => '' do
|
||||
diff = Redmine::UnifiedDiff.new(
|
||||
read_diff_fixture('issue-13644-5.diff'), :type => 'sbs')
|
||||
assert_equal 1, diff.size
|
||||
assert_equal 3, diff.first.size
|
||||
assert_equal ja1, diff.first[1].html_line_left
|
||||
assert_equal ja2, diff.first[1].html_line_right
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_diff_fixture(filename)
|
||||
|
||||
@@ -215,14 +215,14 @@ class MailerTest < ActiveSupport::TestCase
|
||||
# Remove members except news author
|
||||
news.project.memberships.each {|m| m.destroy unless m.user == user}
|
||||
|
||||
user.pref[:no_self_notified] = false
|
||||
user.pref.no_self_notified = false
|
||||
user.pref.save
|
||||
User.current = user
|
||||
Mailer.news_added(news.reload).deliver
|
||||
assert_equal 1, last_email.bcc.size
|
||||
|
||||
# nobody to notify
|
||||
user.pref[:no_self_notified] = true
|
||||
user.pref.no_self_notified = true
|
||||
user.pref.save
|
||||
User.current = user
|
||||
ActionMailer::Base.deliveries.clear
|
||||
@@ -296,7 +296,7 @@ class MailerTest < ActiveSupport::TestCase
|
||||
issue = Issue.find(1)
|
||||
user = User.find(9)
|
||||
# minimal email notification options
|
||||
user.pref[:no_self_notified] = '1'
|
||||
user.pref.no_self_notified = '1'
|
||||
user.pref.save
|
||||
user.mail_notification = false
|
||||
user.save
|
||||
@@ -315,6 +315,29 @@ class MailerTest < ActiveSupport::TestCase
|
||||
assert !last_email.bcc.include?(user.mail)
|
||||
end
|
||||
|
||||
def test_issue_add_should_include_enabled_fields
|
||||
Setting.default_language = 'en'
|
||||
issue = Issue.find(2)
|
||||
assert Mailer.deliver_issue_add(issue)
|
||||
assert_mail_body_match '* Target version: 1.0', last_email
|
||||
assert_select_email do
|
||||
assert_select 'li', :text => 'Target version: 1.0'
|
||||
end
|
||||
end
|
||||
|
||||
def test_issue_add_should_not_include_disabled_fields
|
||||
Setting.default_language = 'en'
|
||||
issue = Issue.find(2)
|
||||
tracker = issue.tracker
|
||||
tracker.core_fields -= ['fixed_version_id']
|
||||
tracker.save!
|
||||
assert Mailer.deliver_issue_add(issue)
|
||||
assert_mail_body_no_match 'Target version', last_email
|
||||
assert_select_email do
|
||||
assert_select 'li', :text => /Target version/, :count => 0
|
||||
end
|
||||
end
|
||||
|
||||
# test mailer methods for each language
|
||||
def test_issue_add
|
||||
issue = Issue.find(1)
|
||||
@@ -361,6 +384,17 @@ class MailerTest < ActiveSupport::TestCase
|
||||
assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
|
||||
end
|
||||
|
||||
def test_issue_edit_should_mark_private_notes
|
||||
journal = Journal.find(2)
|
||||
journal.private_notes = true
|
||||
journal.save!
|
||||
|
||||
with_settings :default_language => 'en' do
|
||||
Mailer.issue_edit(journal).deliver
|
||||
end
|
||||
assert_mail_body_match '(Private notes)', last_email
|
||||
end
|
||||
|
||||
def test_document_added
|
||||
document = Document.find(1)
|
||||
valid_languages.each do |lang|
|
||||
|
||||
@@ -69,6 +69,19 @@ class ProjectCopyTest < ActiveSupport::TestCase
|
||||
assert_equal "Closed", copied_issue.status.name
|
||||
end
|
||||
|
||||
test "#copy should copy issues custom values" do
|
||||
field = IssueCustomField.generate!(:is_for_all => true, :trackers => Tracker.all)
|
||||
issue = Issue.generate!(:project => @source_project, :subject => 'Custom field copy')
|
||||
issue.custom_field_values = {field.id => 'custom'}
|
||||
issue.save!
|
||||
assert_equal 'custom', issue.reload.custom_field_value(field)
|
||||
|
||||
assert @project.copy(@source_project)
|
||||
copy = @project.issues.find_by_subject('Custom field copy')
|
||||
assert copy
|
||||
assert_equal 'custom', copy.reload.custom_field_value(field)
|
||||
end
|
||||
|
||||
test "#copy should copy issues assigned to a locked version" do
|
||||
User.current = User.find(1)
|
||||
assigned_version = Version.generate!(:name => "Assigned Issues")
|
||||
|
||||
@@ -337,6 +337,20 @@ class QueryTest < ActiveSupport::TestCase
|
||||
find_issues_with_query(query)
|
||||
end
|
||||
|
||||
def test_operator_lesser_than_on_date_custom_field
|
||||
f = IssueCustomField.create!(:name => 'filter', :field_format => 'date', :is_filter => true, :is_for_all => true)
|
||||
CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '2013-04-11')
|
||||
CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '2013-05-14')
|
||||
CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
|
||||
|
||||
query = IssueQuery.new(:project => Project.find(1), :name => '_')
|
||||
query.add_filter("cf_#{f.id}", '<=', ['2013-05-01'])
|
||||
issue_ids = find_issues_with_query(query).map(&:id)
|
||||
assert_include 1, issue_ids
|
||||
assert_not_include 2, issue_ids
|
||||
assert_not_include 3, issue_ids
|
||||
end
|
||||
|
||||
def test_operator_between
|
||||
query = IssueQuery.new(:project => Project.find(1), :name => '_')
|
||||
query.add_filter('done_ratio', '><', ['30', '40'])
|
||||
@@ -732,6 +746,21 @@ class QueryTest < ActiveSupport::TestCase
|
||||
assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
|
||||
end
|
||||
|
||||
def test_filter_on_relations_should_not_ignore_other_filter
|
||||
issue = Issue.generate!
|
||||
issue1 = Issue.generate!(:status_id => 1)
|
||||
issue2 = Issue.generate!(:status_id => 2)
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue1)
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue2)
|
||||
|
||||
query = IssueQuery.new(:name => '_')
|
||||
query.filters = {
|
||||
"status_id" => {:operator => '=', :values => ['1']},
|
||||
"relates" => {:operator => '=', :values => [issue.id.to_s]}
|
||||
}
|
||||
assert_equal [issue1], find_issues_with_query(query)
|
||||
end
|
||||
|
||||
def test_statement_should_be_nil_with_no_filters
|
||||
q = IssueQuery.new(:name => '_')
|
||||
q.filters = {}
|
||||
|
||||
@@ -84,5 +84,33 @@ class TimeEntryActivityTest < ActiveSupport::TestCase
|
||||
e.reload
|
||||
assert_equal "0", e.custom_value_for(field).value
|
||||
end
|
||||
end
|
||||
|
||||
def test_system_activity_with_child_in_use_should_be_in_use
|
||||
project = Project.generate!
|
||||
system_activity = TimeEntryActivity.create!(:name => 'Activity')
|
||||
project_activity = TimeEntryActivity.create!(:name => 'Activity', :project => project, :parent_id => system_activity.id)
|
||||
|
||||
TimeEntry.generate!(:project => project, :activity => project_activity)
|
||||
|
||||
assert project_activity.in_use?
|
||||
assert system_activity.in_use?
|
||||
end
|
||||
|
||||
def test_destroying_a_system_activity_should_reassign_children_activities
|
||||
project = Project.generate!
|
||||
system_activity = TimeEntryActivity.create!(:name => 'Activity')
|
||||
project_activity = TimeEntryActivity.create!(:name => 'Activity', :project => project, :parent_id => system_activity.id)
|
||||
|
||||
entries = [
|
||||
TimeEntry.generate!(:project => project, :activity => system_activity),
|
||||
TimeEntry.generate!(:project => project, :activity => project_activity)
|
||||
]
|
||||
|
||||
assert_difference 'TimeEntryActivity.count', -2 do
|
||||
assert_nothing_raised do
|
||||
assert system_activity.destroy(TimeEntryActivity.find_by_name('Development'))
|
||||
end
|
||||
end
|
||||
assert entries.all? {|entry| entry.reload.activity.name == 'Development'}
|
||||
end
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user