Feature #24013 » Basic_Fb_support.patch
app/controllers/versions_controller.rb (revision fed3e767d8c476e6a3bd414a497e179b5c872788) | ||
---|---|---|
32 | 32 |
respond_to do |format| |
33 | 33 |
format.html { |
34 | 34 |
@trackers = @project.trackers.sorted.to_a |
35 |
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
|
|
35 |
retrieve_selected_tracker_ids(@trackers, @trackers.select { |t| t.is_in_roadmap? })
|
|
36 | 36 |
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') |
37 | 37 |
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id] |
38 | 38 | |
... | ... | |
47 | 47 |
@issues_by_version = {} |
48 | 48 |
if @selected_tracker_ids.any? && @versions.any? |
49 | 49 |
issues = Issue.visible. |
50 |
includes(:project, :tracker).
|
|
51 |
preload(:status, :priority, :fixed_version).
|
|
52 |
where(:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)). |
|
50 |
includes(:project, :tracker). |
|
51 |
preload(:status, :priority, :fixed_version).
|
|
52 |
where(:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)).
|
|
53 |
order("#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") |
|
53 |
order(Project.arel_table[:lft].asc, Tracker.arel_table[:position].asc, Issue.arel_table[:id].asc) |
|
54 | 54 |
@issues_by_version = issues.group_by(&:fixed_version) |
55 | 55 |
end |
56 |
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
|
|
56 |
@versions.reject! { |version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank? }
|
|
57 | 57 |
} |
58 | 58 |
format.api { |
59 | 59 |
@versions = @project.shared_versions.to_a |
... | ... | |
65 | 65 |
respond_to do |format| |
66 | 66 |
format.html { |
67 | 67 |
@issues = @version.fixed_issues.visible. |
68 |
includes(:status, :tracker, :priority). |
|
69 |
preload(:project).
|
|
68 |
includes(:status, :tracker, :priority). |
|
69 |
preload(:project). |
|
70 |
reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id"). |
|
70 |
reorder(Issue.arel_table[:id].asc).
|
|
71 |
to_a |
|
71 |
to_a |
|
72 | 72 |
} |
73 | 73 |
format.api |
74 | 74 |
end |
... | ... | |
107 | 107 |
else |
108 | 108 |
respond_to do |format| |
109 | 109 |
format.html { render :action => 'new' } |
110 |
format.js { render :action => 'new' } |
|
110 |
format.js { render :action => 'new' } |
|
111 |
format.api { render_validation_errors(@version) } |
|
111 |
format.api { render_validation_errors(@version) } |
|
112 | 112 |
end |
113 | 113 |
end |
114 | 114 |
end |
... | ... | |
128 | 128 |
flash[:notice] = l(:notice_successful_update) |
129 | 129 |
redirect_back_or_default settings_project_path(@project, :tab => 'versions') |
130 | 130 |
} |
131 |
format.api { render_api_ok }
|
|
131 |
format.api { render_api_ok } |
|
132 | 132 |
end |
133 | 133 |
else |
134 | 134 |
respond_to do |format| |
135 | 135 |
format.html { render :action => 'edit' } |
136 |
format.api { render_validation_errors(@version) }
|
|
136 |
format.api { render_validation_errors(@version) } |
|
137 | 137 |
end |
138 | 138 |
end |
139 | 139 |
end |
... | ... | |
151 | 151 |
@version.destroy |
152 | 152 |
respond_to do |format| |
153 | 153 |
format.html { redirect_back_or_default settings_project_path(@project, :tab => 'versions') } |
154 |
format.api { render_api_ok }
|
|
154 |
format.api { render_api_ok } |
|
155 | 155 |
end |
156 | 156 |
else |
157 | 157 |
respond_to do |format| |
... | ... | |
159 | 159 |
flash[:error] = l(:notice_unable_delete_version) |
160 | 160 |
redirect_to settings_project_path(@project, :tab => 'versions') |
161 | 161 |
} |
162 |
format.api { head :unprocessable_entity }
|
|
162 |
format.api { head :unprocessable_entity } |
|
163 | 163 |
end |
164 | 164 |
end |
165 | 165 |
end |
... | ... | |
177 | 177 |
if ids = params[:tracker_ids] |
178 | 178 |
@selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s } |
179 | 179 |
else |
180 |
@selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s } |
|
180 |
@selected_tracker_ids = (default_trackers || selectable_trackers).collect { |t| t.id.to_s }
|
|
181 | 181 |
end |
182 | 182 |
end |
183 | 183 |
end |
app/helpers/my_helper.rb (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
37 | 37 |
limit(10). |
38 | 38 |
includes(:status, :project, :tracker, :priority). |
39 | 39 |
references(:status, :project, :tracker, :priority). |
40 |
order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC")
|
|
40 |
order(IssuePriority.arel_table[:position].desc, Issue.arel_table[:updated_on].desc)
|
|
41 | 41 |
end |
42 | 42 | |
43 | 43 |
def issuesreportedbyme_items |
... | ... | |
69 | 69 |
joins(:activity, :project). |
70 | 70 |
references(:issue => [:tracker, :status]). |
71 | 71 |
includes(:issue => [:tracker, :status]). |
72 |
order("#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC").
|
|
72 |
order(TimeEntry.arel_table[:spent_on].desc, Project.arel_table[:name].asc, Tracker.arel_table[:position].asc, Issue.arel_table[:id].asc).
|
|
73 | 73 |
to_a |
74 | 74 |
end |
75 | 75 |
end |
app/models/enumeration.rb (revision 1199798be728002c93d6241bf33b919fdfeb7918) | ||
---|---|---|
18 | 18 |
class Enumeration < ActiveRecord::Base |
19 | 19 |
include Redmine::SubclassFactory |
20 | 20 | |
21 |
default_scope lambda {order(:position)}
|
|
21 |
default_scope lambda { order(:position) }
|
|
22 | 22 | |
23 | 23 |
belongs_to :project |
24 | 24 | |
... | ... | |
27 | 27 |
acts_as_tree |
28 | 28 | |
29 | 29 |
before_destroy :check_integrity |
30 |
before_save :check_default
|
|
30 |
before_save :check_default |
|
31 | 31 | |
32 | 32 |
attr_protected :type |
33 | 33 | |
... | ... | |
39 | 39 |
scope :sorted, lambda { order(:position) } |
40 | 40 |
scope :active, lambda { where(:active => true) } |
41 | 41 |
scope :system, lambda { where(:project_id => nil) } |
42 |
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
|
|
42 |
scope :named, lambda { |arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip) }
|
|
43 | 43 | |
44 | 44 |
def self.default |
45 | 45 |
# Creates a fake default scope so Enumeration.default will check |
... | ... | |
93 | 93 |
position <=> enumeration.position |
94 | 94 |
end |
95 | 95 | |
96 |
def to_s; name end |
|
96 |
def to_s; |
|
97 |
name |
|
98 |
end |
|
97 | 99 | |
98 | 100 |
# Returns the Subclasses of Enumeration. Each Subclass needs to be |
99 | 101 |
# required in development mode. |
... | ... | |
105 | 107 | |
106 | 108 |
# Does the +new+ Hash override the previous Enumeration? |
107 | 109 |
def self.overriding_change?(new, previous) |
108 |
if (same_active_state?(new['active'], previous.active)) && same_custom_values?(new,previous) |
|
110 |
if (same_active_state?(new['active'], previous.active)) && same_custom_values?(new, previous)
|
|
109 | 111 |
return false |
110 | 112 |
else |
111 | 113 |
return true |
... | ... | |
149 | 151 |
def update_position |
150 | 152 |
super |
151 | 153 |
if position_changed? |
152 |
self.class.where.not(:parent_id => nil).update_all( |
|
153 |
"position = coalesce(( |
|
154 |
select position |
|
155 |
from (select id, position from enumerations) as parent |
|
156 |
where parent_id = parent.id), 1)" |
|
157 |
) |
|
154 |
from = Enumeration.select(:id, :position) |
|
155 |
self.class.where.not(:parent_id => nil).update_all(:position => "coalesce(#{Enumeration.where(nil). |
|
156 |
from("(#{from.to_sql}) as parent"). |
|
157 |
where('parent_id = parent.id').select(:position).to_sql}), 1)") |
|
158 | 158 |
end |
159 | 159 |
end |
160 | 160 |
app/models/issue.rb (revision e3cbc3d2f583d6a93fe4c01281991e21d34a88b3) | ||
---|---|---|
1537 | 1537 |
if issue_id && p = Issue.find_by_id(issue_id) |
1538 | 1538 |
if p.priority_derived? |
1539 | 1539 |
# priority = highest priority of open children |
1540 |
if priority_position = p.children.open.joins(:priority).maximum("#{IssuePriority.table_name}.position")
|
|
1540 |
if priority_position = p.children.open.joins(:priority).reorder(nil).select(IssuePriority.arel_table[:position].maximum).first[:max]
|
|
1541 | 1541 |
p.priority = IssuePriority.find_by_position(priority_position) |
1542 | 1542 |
else |
1543 | 1543 |
p.priority = IssuePriority.default |
app/models/issue_query.rb (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
23 | 23 |
self.available_columns = [ |
24 | 24 |
QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => '#', :frozen => true), |
25 | 25 |
QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true), |
26 |
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
|
|
26 |
QueryColumn.new(:tracker, :sortable => Tracker.arel_table[:position].asc.to_sql, :groupable => true),
|
|
27 | 27 |
QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue), |
28 |
QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
|
|
29 |
QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
|
|
28 |
QueryColumn.new(:status, :sortable => IssueStatus.arel_table[:position].asc.to_sql, :groupable => true),
|
|
29 |
QueryColumn.new(:priority, :sortable => IssuePriority.arel_table[:position].desc.to_sql, :groupable => true),
|
|
30 | 30 |
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"), |
31 | 31 |
QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true), |
32 | 32 |
QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true), |
app/models/issue_status.rb (revision 1199798be728002c93d6241bf33b919fdfeb7918) | ||
---|---|---|
33 | 33 |
attr_protected :id |
34 | 34 | |
35 | 35 |
scope :sorted, lambda { order(:position) } |
36 |
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
|
|
36 |
scope :named, lambda { |arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip) }
|
|
37 | 37 | |
38 | 38 |
safe_attributes 'name', |
39 |
'is_closed', |
|
40 |
'position', |
|
41 |
'default_done_ratio' |
|
39 |
'is_closed',
|
|
40 |
'position',
|
|
41 |
'default_done_ratio'
|
|
42 | 42 | |
43 | 43 |
# Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+ |
44 | 44 |
def self.update_issue_done_ratios |
... | ... | |
55 | 55 |
def new_statuses_allowed_to(roles, tracker, author=false, assignee=false) |
56 | 56 |
self.class.new_statuses_allowed(self, roles, tracker, author, assignee) |
57 | 57 |
end |
58 | ||
58 | 59 |
alias :find_new_statuses_allowed_to :new_statuses_allowed_to |
59 | 60 | |
60 | 61 |
def self.new_statuses_allowed(status, roles, tracker, author=false, assignee=false) |
... | ... | |
62 | 63 |
status_id = status.try(:id) || 0 |
63 | 64 | |
64 | 65 |
scope = IssueStatus. |
65 |
joins(:workflow_transitions_as_new_status). |
|
66 |
where(:workflows => {:old_status_id => status_id, :role_id => roles.map(&:id), :tracker_id => tracker.id}) |
|
66 |
joins(:workflow_transitions_as_new_status).
|
|
67 |
where(:workflows => {:old_status_id => status_id, :role_id => roles.map(&:id), :tracker_id => tracker.id})
|
|
67 | 68 | |
68 | 69 |
unless author && assignee |
69 | 70 |
if author || assignee |
... | ... | |
83 | 84 |
position <=> status.position |
84 | 85 |
end |
85 | 86 | |
86 |
def to_s; name end |
|
87 |
def to_s; |
|
88 |
name |
|
89 |
end |
|
87 | 90 | |
88 | 91 |
private |
89 | 92 | |
... | ... | |
92 | 95 |
if is_closed_changed? && is_closed == true |
93 | 96 |
# First we update issues that have a journal for when the current status was set, |
94 | 97 |
# a subselect is used to update all issues with a single query |
95 |
subselect = "SELECT MAX(j.created_on) FROM #{Journal.table_name} j" + |
|
96 |
" JOIN #{JournalDetail.table_name} d ON d.journal_id = j.id" + |
|
97 |
" WHERE j.journalized_type = 'Issue' AND j.journalized_id = #{Issue.table_name}.id" + |
|
98 |
" AND d.property = 'attr' AND d.prop_key = 'status_id' AND d.value = :status_id" |
|
98 |
subselect = Journal.joins(:details).where(:journalized_type => 'Issue').where("#{Journal.table_name}. |
|
99 |
journalized_id = #{Issue.table_name}.id").where(:journal_details => {:property => 'attr', :prop_key => 'status_id', :value => ':status_id'}). |
|
100 |
select("MAX(#{Journal.table_name}.created_on)").to_sql.gsub(/':status_id'/, ":status_id") |
|
99 | 101 |
Issue.where(:status_id => id, :closed_on => nil). |
100 |
update_all(["closed_on = (#{subselect})", {:status_id => id.to_s}]) |
|
102 |
update_all(["closed_on = (#{subselect})", {:status_id => id.to_s}])
|
|
101 | 103 | |
102 | 104 |
# Then we update issues that don't have a journal which means the |
103 | 105 |
# current status was set on creation |
app/models/project.rb (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
44 | 44 |
has_many :documents, :dependent => :destroy |
45 | 45 |
has_many :news, lambda {includes(:author)}, :dependent => :destroy |
46 | 46 |
has_many :issue_categories, lambda {order("#{IssueCategory.table_name}.name")}, :dependent => :delete_all |
47 |
has_many :boards, lambda {order("position ASC")}, :dependent => :destroy
|
|
47 |
has_many :boards, lambda { order(:position) }, :dependent => :destroy
|
|
48 | 48 |
has_one :repository, lambda {where(["is_default = ?", true])} |
49 | 49 |
has_many :repositories, :dependent => :destroy |
50 | 50 |
has_many :changesets, :through => :repository |
51 | 51 |
has_one :wiki, :dependent => :destroy |
52 | 52 |
# Custom field for the project issues |
53 | 53 |
has_and_belongs_to_many :issue_custom_fields, |
54 |
lambda {order("#{CustomField.table_name}.position")},
|
|
54 |
lambda { order(CustomField.arel_table[:position].asc) },
|
|
55 | 55 |
:class_name => 'IssueCustomField', |
56 | 56 |
:join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", |
57 | 57 |
:association_foreign_key => 'custom_field_id' |
app/models/query.rb (revision b795289685e02d24a79a08eacd9d55cabc8bec9e) | ||
---|---|---|
823 | 823 | |
824 | 824 |
def sql_for_custom_field(field, operator, value, custom_field_id) |
825 | 825 |
db_table = CustomValue.table_name |
826 |
db_field = 'value'
|
|
826 |
db_field = ActiveRecord::Base.connection.quote_column_name('value')
|
|
827 | 827 |
filter = @available_filters[field] |
828 | 828 |
return nil unless filter |
829 | 829 |
if filter[:field].format.target_class && filter[:field].format.target_class <= User |
... | ... | |
868 | 868 |
int_values = value.first.to_s.scan(/[+-]?\d+/).map(&:to_i).join(",") |
869 | 869 |
if int_values.present? |
870 | 870 |
if is_custom_filter |
871 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) IN (#{int_values}))"
|
|
871 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(18,3)) IN (#{int_values}))"
|
|
872 | 872 |
else |
873 | 873 |
sql = "#{db_table}.#{db_field} IN (#{int_values})" |
874 | 874 |
end |
... | ... | |
877 | 877 |
end |
878 | 878 |
when :float |
879 | 879 |
if is_custom_filter |
880 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5})"
|
|
880 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(18,3)) BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5})"
|
|
881 | 881 |
else |
882 | 882 |
sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}" |
883 | 883 |
end |
... | ... | |
906 | 906 |
sql = date_clause(db_table, db_field, parse_date(value.first), nil, is_custom_filter) |
907 | 907 |
else |
908 | 908 |
if is_custom_filter |
909 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) >= #{value.first.to_f})"
|
|
909 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(18,3)) >= #{value.first.to_f})"
|
|
910 | 910 |
else |
911 | 911 |
sql = "#{db_table}.#{db_field} >= #{value.first.to_f}" |
912 | 912 |
end |
... | ... | |
916 | 916 |
sql = date_clause(db_table, db_field, nil, parse_date(value.first), is_custom_filter) |
917 | 917 |
else |
918 | 918 |
if is_custom_filter |
919 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) <= #{value.first.to_f})"
|
|
919 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(18,3)) <= #{value.first.to_f})"
|
|
920 | 920 |
else |
921 | 921 |
sql = "#{db_table}.#{db_field} <= #{value.first.to_f}" |
922 | 922 |
end |
... | ... | |
926 | 926 |
sql = date_clause(db_table, db_field, parse_date(value[0]), parse_date(value[1]), is_custom_filter) |
927 | 927 |
else |
928 | 928 |
if is_custom_filter |
929 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
|
|
929 |
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(18,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
|
|
930 | 930 |
else |
931 | 931 |
sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_f} AND #{value[1].to_f}" |
932 | 932 |
end |
app/models/repository.rb (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
420 | 420 |
# Notes: |
421 | 421 |
# - this hash honnors the users mapping defined for the repository |
422 | 422 |
def stats_by_author |
423 |
commits = Changeset.where("repository_id = ?", id).select("committer, user_id, count(*) as count").group("committer, user_id")
|
|
423 |
commits = Changeset.where("repository_id = ?", id).select("committer, user_id, count(*)").group("committer, user_id") |
|
424 | 424 | |
425 | 425 |
#TODO: restore ordering ; this line probably never worked |
426 | 426 |
#commits.to_a.sort! {|x, y| x.last <=> y.last} |
427 | 427 | |
428 |
changes = Change.joins(:changeset).where("#{Changeset.table_name}.repository_id = ?", id).select("committer, user_id, count(*) as count").group("committer, user_id")
|
|
428 |
changes = Change.joins(:changeset).where("#{Changeset.table_name}.repository_id = ?", id).select("committer, user_id, count(*)").group("committer, user_id") |
|
429 | 429 | |
430 | 430 |
user_ids = changesets.map(&:user_id).compact.uniq |
431 | 431 |
authors_names = User.where(:id => user_ids).inject({}) do |memo, user| |
app/models/time_entry_query.rb (revision 4d10162d2a58c14765bf6abc8b5ed12ea4af73d2) | ||
---|---|---|
25 | 25 |
QueryColumn.new(:spent_on, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :default_order => 'desc', :groupable => true), |
26 | 26 |
QueryColumn.new(:tweek, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :caption => l(:label_week)), |
27 | 27 |
QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true), |
28 |
QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true),
|
|
28 |
QueryColumn.new(:activity, :sortable => TimeEntryActivity.arel_table[:position].asc.to_sql, :groupable => true),
|
|
29 | 29 |
QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"), |
30 |
QueryAssociationColumn.new(:issue, :tracker, :caption => :field_tracker, :sortable => "#{Tracker.table_name}.position"),
|
|
31 |
QueryAssociationColumn.new(:issue, :status, :caption => :field_status, :sortable => "#{IssueStatus.table_name}.position"),
|
|
30 |
QueryAssociationColumn.new(:issue, :tracker, :caption => :field_tracker, :sortable => Tracker.arel_table[:position].asc.to_sql),
|
|
31 |
QueryAssociationColumn.new(:issue, :status, :caption => :field_status, :sortable => IssueStatus.arel_table[:position].asc.to_sql),
|
|
32 | 32 |
QueryColumn.new(:comments), |
33 | 33 |
QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours", :totalable => true), |
34 | 34 |
] |
app/models/user.rb (revision f1a9cc676b43afdf7de6c12ef3b40c192f98cfed) | ||
---|---|---|
683 | 683 |
# Returns true if the user is allowed to delete the user's own account |
684 | 684 |
def own_account_deletable? |
685 | 685 |
Setting.unsubscribe? && |
686 |
(!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
|
|
686 |
(!admin? || User.active.where("id <> ?", id).where(:admin => true).exists?)
|
|
687 | 687 |
end |
688 | 688 | |
689 | 689 |
safe_attributes 'firstname', |
db/migrate/007_create_journals.rb (revision f1a9cc676b43afdf7de6c12ef3b40c192f98cfed) | ||
---|---|---|
22 | 22 |
end |
23 | 23 | |
24 | 24 |
# indexes |
25 |
add_index "journals", ["journalized_id", "journalized_type"], :name => "journals_journalized_id"
|
|
25 |
add_index "journals", ["journalized_id", "journalized_type"] |
|
26 | 26 |
add_index "journal_details", ["journal_id"], :name => "journal_details_journal_id" |
27 | 27 | |
28 | 28 |
Permission.create :controller => "issues", :action => "history", :description => "label_history", :sort => 1006, :is_public => true, :mail_option => 0, :mail_enabled => 0 |
db/migrate/107_add_open_id_authentication_tables.rb (revision f1a9cc676b43afdf7de6c12ef3b40c192f98cfed) | ||
---|---|---|
1 | 1 |
class AddOpenIdAuthenticationTables < ActiveRecord::Migration |
2 | 2 |
def self.up |
3 |
create_table :open_id_authentication_associations, :force => true do |t|
|
|
3 |
create_table :open_id_auth_associations, :force => true do |t| |
|
4 | 4 |
t.integer :issued, :lifetime |
5 | 5 |
t.string :handle, :assoc_type |
6 | 6 |
t.binary :server_url, :secret |
7 | 7 |
end |
8 | 8 | |
9 |
create_table :open_id_authentication_nonces, :force => true do |t|
|
|
9 |
create_table :open_id_auth_nonces, :force => true do |t| |
|
10 | 10 |
t.integer :timestamp, :null => false |
11 | 11 |
t.string :server_url, :null => true |
12 | 12 |
t.string :salt, :null => false |
... | ... | |
14 | 14 |
end |
15 | 15 | |
16 | 16 |
def self.down |
17 |
drop_table :open_id_authentication_associations
|
|
18 |
drop_table :open_id_authentication_nonces
|
|
17 |
drop_table :open_id_auth_associations |
|
18 |
drop_table :open_id_auth_nonces |
|
19 | 19 |
end |
20 | 20 |
end |
db/migrate/20130201184705_add_unique_index_on_tokens_value.rb (revision f1a9cc676b43afdf7de6c12ef3b40c192f98cfed) | ||
---|---|---|
2 | 2 |
def up |
3 | 3 |
say_with_time "Adding unique index on tokens, this may take some time..." do |
4 | 4 |
# Just in case |
5 |
duplicates = Token.connection.select_values("SELECT value FROM #{Token.table_name} GROUP BY value HAVING COUNT(id) > 1")
|
|
5 |
duplicates = Token.group(:value).having('COUNT(id) > 1').select(:value).to_a
|
|
6 | 6 |
Token.where(:value => duplicates).delete_all |
7 | 7 |
|
8 | 8 |
add_index :tokens, :value, :unique => true, :name => 'tokens_value' |
db/migrate/20130215111141_populate_issues_closed_on.rb (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
10 | 10 |
" AND #{Journal.table_name}.journalized_type = 'Issue' AND #{Journal.table_name}.journalized_id = #{Issue.table_name}.id" + |
11 | 11 |
" AND #{JournalDetail.table_name}.property = 'attr' AND #{JournalDetail.table_name}.prop_key = 'status_id'" + |
12 | 12 |
" AND #{JournalDetail.table_name}.old_value NOT IN (#{closed_status_values})" + |
13 |
" AND #{JournalDetail.table_name}.value IN (#{closed_status_values})"
|
|
13 |
" AND " + JournalDetail.arel_table[:value].in(closed_status_values).to_sql
|
|
14 | 14 |
Issue.update_all "closed_on = (#{subselect})" |
15 | 15 | |
16 | 16 |
# Then set closed_on for closed issues that weren't up updated by the above UPDATE |
lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
27 | 27 |
return if self.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods) |
28 | 28 |
cattr_accessor :customizable_options |
29 | 29 |
self.customizable_options = options |
30 |
has_many :custom_values, lambda {includes(:custom_field).order("#{CustomField.table_name}.position")},
|
|
30 |
has_many :custom_values, lambda { joins(:custom_field).merge(CustomField.order(:position)) },
|
|
31 | 31 |
:as => :customized, |
32 | 32 |
:dependent => :delete_all, |
33 | 33 |
:validate => false |
lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb (revision 08bc581cf543157c294f6194f9062d42fcb1652a) | ||
---|---|---|
89 | 89 | |
90 | 90 |
unless options[:attachments] == 'only' |
91 | 91 |
r = fetch_ranks_and_ids( |
92 |
search_scope(user, projects, options). |
|
93 |
where(search_tokens_condition(columns, tokens, options[:all_words])), |
|
94 |
options[:limit] |
|
92 |
search_scope(user, projects, options).
|
|
93 |
where(search_tokens_condition(columns, tokens, options[:all_words])),
|
|
94 |
options[:limit]
|
|
95 | 95 |
) |
96 | 96 |
queries += 1 |
97 | 97 | |
98 | 98 |
if !options[:titles_only] && searchable_options[:search_custom_fields] |
99 | 99 |
searchable_custom_fields = CustomField.where(:type => "#{self.name}CustomField", :searchable => true).to_a |
100 |
|
|
100 | ||
101 | 101 |
if searchable_custom_fields.any? |
102 |
fields_by_visibility = searchable_custom_fields.group_by {|field| |
|
102 |
fields_by_visibility = searchable_custom_fields.group_by { |field|
|
|
103 | 103 |
field.visibility_by_project_condition(searchable_options[:project_key], user, "#{CustomValue.table_name}.custom_field_id") |
104 | 104 |
} |
105 | 105 |
clauses = [] |
... | ... | |
107 | 107 |
clauses << "(#{CustomValue.table_name}.custom_field_id IN (#{fields.map(&:id).join(',')}) AND (#{visibility}))" |
108 | 108 |
end |
109 | 109 |
visibility = clauses.join(' OR ') |
110 |
|
|
110 | ||
111 | 111 |
r |= fetch_ranks_and_ids( |
112 |
search_scope(user, projects, options).
|
|
113 |
joins(:custom_values). |
|
114 |
where(visibility).
|
|
112 |
search_scope(user, projects, options). |
|
113 |
joins(:custom_values). |
|
114 |
where(visibility).
|
|
115 |
where(search_tokens_condition(["#{CustomValue.table_name}.value"], tokens, options[:all_words])), |
|
115 |
where(search_tokens_condition(["#{ActiveRecord::Base.connection.quote_table_name(CustomValue.table_name)}.#{ActiveRecord::Base.connection.quote_column_name('value')}"], tokens, options[:all_words])),
|
|
116 |
options[:limit] |
|
116 |
options[:limit] |
|
117 | 117 |
) |
118 | 118 |
queries += 1 |
119 | 119 |
end |
... | ... | |
121 | 121 | |
122 | 122 |
if !options[:titles_only] && searchable_options[:search_journals] |
123 | 123 |
r |= fetch_ranks_and_ids( |
124 |
search_scope(user, projects, options). |
|
125 |
joins(:journals). |
|
126 |
where("#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes)})", false). |
|
127 |
where(search_tokens_condition(["#{Journal.table_name}.notes"], tokens, options[:all_words])), |
|
128 |
options[:limit] |
|
124 |
search_scope(user, projects, options).
|
|
125 |
joins(:journals).
|
|
126 |
where("#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes)})", false).
|
|
127 |
where(search_tokens_condition(["#{Journal.table_name}.notes"], tokens, options[:all_words])),
|
|
128 |
options[:limit]
|
|
129 | 129 |
) |
130 | 130 |
queries += 1 |
131 | 131 |
end |
... | ... | |
133 | 133 | |
134 | 134 |
if searchable_options[:search_attachments] && (options[:titles_only] ? options[:attachments] == 'only' : options[:attachments] != '0') |
135 | 135 |
r |= fetch_ranks_and_ids( |
136 |
search_scope(user, projects, options). |
|
137 |
joins(:attachments). |
|
138 |
where(search_tokens_condition(["#{Attachment.table_name}.filename", "#{Attachment.table_name}.description"], tokens, options[:all_words])), |
|
139 |
options[:limit] |
|
136 |
search_scope(user, projects, options).
|
|
137 |
joins(:attachments).
|
|
138 |
where(search_tokens_condition(["#{Attachment.table_name}.filename", "#{Attachment.table_name}.description"], tokens, options[:all_words])),
|
|
139 |
options[:limit]
|
|
140 | 140 |
) |
141 | 141 |
queries += 1 |
142 | 142 |
end |
... | ... | |
152 | 152 |
end |
153 | 153 | |
154 | 154 |
def search_tokens_condition(columns, tokens, all_words) |
155 |
token_clauses = columns.map {|column| "(#{search_token_match_statement(column)})"} |
|
155 |
token_clauses = columns.map { |column| |
|
156 |
column = "lower(#{column})" |
|
157 |
"(#{search_token_match_statement(column)})" |
|
158 |
} |
|
156 | 159 |
sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(all_words ? ' AND ' : ' OR ') |
157 |
[sql, * (tokens.collect {|w| "%#{w}%"} * token_clauses.size).sort]
|
|
160 |
[sql, * (tokens.collect { |w| "%#{w}%" } * token_clauses.size).sort]
|
|
158 | 161 |
end |
162 | ||
159 | 163 |
private :search_tokens_condition |
160 | 164 | |
161 | 165 |
def search_token_match_statement(column, value='?') |
162 | 166 |
Redmine::Database.like(column, value) |
163 | 167 |
end |
168 | ||
164 | 169 |
private :search_token_match_statement |
165 | 170 | |
166 | 171 |
def fetch_ranks_and_ids(scope, limit) |
167 | 172 |
scope. |
168 |
reorder(searchable_options[:date_column] => :desc, :id => :desc).
|
|
169 |
limit(limit).
|
|
170 |
distinct.
|
|
171 |
pluck(searchable_options[:date_column], :id).
|
|
172 |
# converts timestamps to integers for faster sort |
|
173 |
reorder(searchable_options[:date_column] => :desc, :id => :desc). |
|
174 |
limit(limit).
|
|
175 |
distinct.
|
|
176 |
pluck(searchable_options[:date_column], :id).
|
|
177 |
# converts timestamps to integers for faster sort
|
|
173 |
map {|timestamp, id| [timestamp.to_i, id]} |
|
178 |
map { |timestamp, id| [timestamp.to_i, id] } |
|
174 | 179 |
end |
180 | ||
175 | 181 |
private :fetch_ranks_and_ids |
176 | 182 | |
177 | 183 |
# Returns the search scope for user and projects |
... | ... | |
198 | 204 |
end |
199 | 205 |
scope |
200 | 206 |
end |
207 | ||
201 | 208 |
private :search_scope |
202 | 209 | |
203 | 210 |
# Returns search results of given ids |
lib/plugins/open_id_authentication/lib/open_id_authentication/association.rb (revision f1a9cc676b43afdf7de6c12ef3b40c192f98cfed) | ||
---|---|---|
1 | 1 |
module OpenIdAuthentication |
2 | 2 |
class Association < ActiveRecord::Base |
3 |
self.table_name = :open_id_authentication_associations
|
|
3 |
self.table_name = :open_id_auth_associations |
|
4 | 4 | |
5 | 5 |
def from_record |
6 | 6 |
OpenID::Association.new(handle, secret, issued, lifetime, assoc_type) |
lib/redmine/acts/positioned.rb (revision f1a9cc676b43afdf7de6c12ef3b40c192f98cfed) | ||
---|---|---|
85 | 85 |
end |
86 | 86 | |
87 | 87 |
def insert_position |
88 |
position_scope.where("position >= ? AND id <> ?", position, id).update_all("position = position + 1")
|
|
88 |
position_scope.where(self.class.arel_table[:position].gteq(position)).where('id <> ?', id).update_all(:position => (self.class.arel_table[:position] + 1).to_sql)
|
|
89 | 89 |
end |
90 | 90 | |
91 | 91 |
def remove_position |
92 |
position_scope_was.where("position >= ? AND id <> ?", position_was, id).update_all("position = position - 1")
|
|
92 |
position_scope_was.where(self.class.arel_table[:position].gteq(position_was)).where('id <> ?', id).update_all(:position => (self.class.arel_table[:position] - 1).to_sql)
|
|
93 | 93 |
end |
94 | 94 | |
95 | 95 |
def position_scope_changed? |
... | ... | |
99 | 99 |
def shift_positions |
100 | 100 |
offset = position_was <=> position |
101 | 101 |
min, max = [position, position_was].sort |
102 |
r = position_scope.where("id <> ? AND position BETWEEN ? AND ?", id, min, max).update_all("position = position + #{offset}")
|
|
102 |
r = position_scope.where('id <> ?', id).where(self.class.arel_table[:position].between(min..max)).update_all(:position => (self.class.arel_table[:position] + offset).to_sql)
|
|
103 | 103 |
if r != max - min |
104 | 104 |
reset_positions_in_list |
105 | 105 |
end |
lib/redmine/field_format.rb (revision 08bc581cf543157c294f6194f9062d42fcb1652a) | ||
---|---|---|
265 | 265 |
# Returns nil if the custom field can not be used for sorting. |
266 | 266 |
def order_statement(custom_field) |
267 | 267 |
# COALESCE is here to make sure that blank and NULL values are sorted equally |
268 |
"COALESCE(#{join_alias custom_field}.value, '')"
|
|
268 |
"COALESCE(#{join_alias custom_field}.#{value_column}, '')"
|
|
269 | 269 |
end |
270 | 270 | |
271 | 271 |
# Returns a GROUP BY clause that can used to group by custom value |
... | ... | |
283 | 283 |
" AND #{alias_name}.customized_id = #{custom_field.class.customized_class.table_name}.id" + |
284 | 284 |
" AND #{alias_name}.custom_field_id = #{custom_field.id}" + |
285 | 285 |
" AND (#{custom_field.visibility_by_project_condition})" + |
286 |
" AND #{alias_name}.value <> ''" +
|
|
286 |
" AND #{alias_name}.#{value_column} <> ''" +
|
|
287 | 287 |
" AND #{alias_name}.id = (SELECT max(#{alias_name}_2.id) FROM #{CustomValue.table_name} #{alias_name}_2" + |
288 | 288 |
" WHERE #{alias_name}_2.customized_type = #{alias_name}.customized_type" + |
289 | 289 |
" AND #{alias_name}_2.customized_id = #{alias_name}.customized_id" + |
... | ... | |
293 | 293 |
def join_alias(custom_field) |
294 | 294 |
"cf_#{custom_field.id}" |
295 | 295 |
end |
296 | ||
297 |
def value_column |
|
298 |
ActiveRecord::Base.connection.quote_column_name('value') |
|
299 |
end |
|
296 | 300 |
protected :join_alias |
297 | 301 |
end |
298 | 302 | |
... | ... | |
402 | 406 |
# Make the database cast values into numeric |
403 | 407 |
# Postgresql will raise an error if a value can not be casted! |
404 | 408 |
# CustomValue validations should ensure that it doesn't occur |
405 |
"CAST(CASE #{join_alias custom_field}.value WHEN '' THEN '0' ELSE #{join_alias custom_field}.value END AS decimal(30,3))"
|
|
409 |
"CAST(CASE #{join_alias custom_field}.#{value_column} WHEN '' THEN '0' ELSE #{join_alias custom_field}.#{value_column} END AS decimal(18,3))"
|
|
406 | 410 |
end |
407 | 411 | |
408 | 412 |
# Returns totals for the given scope |
... | ... | |
410 | 414 |
scope.joins(:custom_values). |
411 | 415 |
where(:custom_values => {:custom_field_id => custom_field.id}). |
412 | 416 |
where.not(:custom_values => {:value => ''}). |
413 |
sum("CAST(#{CustomValue.table_name}.value AS decimal(30,3))")
|
|
417 |
sum("CAST(#{CustomValue.table_name}.#{value_column} AS decimal(18,3))")
|
|
414 | 418 |
end |
415 | 419 | |
416 | 420 |
def cast_total_value(custom_field, value) |
... | ... | |
690 | 694 |
end |
691 | 695 | |
692 | 696 |
def group_statement(custom_field) |
693 |
"COALESCE(#{join_alias custom_field}.value, '')"
|
|
697 |
"COALESCE(#{join_alias custom_field}.#{value_column}, '')"
|
|
694 | 698 |
end |
695 | 699 | |
696 | 700 |
def join_for_order_statement(custom_field) |
... | ... | |
701 | 705 |
" AND #{alias_name}.customized_id = #{custom_field.class.customized_class.table_name}.id" + |
702 | 706 |
" AND #{alias_name}.custom_field_id = #{custom_field.id}" + |
703 | 707 |
" AND (#{custom_field.visibility_by_project_condition})" + |
704 |
" AND #{alias_name}.value <> ''" +
|
|
708 |
" AND #{alias_name}.#{value_column} <> ''" +
|
|
705 | 709 |
" AND #{alias_name}.id = (SELECT max(#{alias_name}_2.id) FROM #{CustomValue.table_name} #{alias_name}_2" + |
706 | 710 |
" WHERE #{alias_name}_2.customized_type = #{alias_name}.customized_type" + |
707 | 711 |
" AND #{alias_name}_2.customized_id = #{alias_name}.customized_id" + |
708 | 712 |
" AND #{alias_name}_2.custom_field_id = #{alias_name}.custom_field_id)" + |
709 | 713 |
" LEFT OUTER JOIN #{target_class.table_name} #{value_join_alias custom_field}" + |
710 |
" ON CAST(CASE #{alias_name}.value WHEN '' THEN '0' ELSE #{alias_name}.value END AS decimal(30,0)) = #{value_join_alias custom_field}.id"
|
|
714 |
" ON CAST(CASE #{alias_name}.#{value_column} WHEN '' THEN '0' ELSE #{alias_name}.#{value_column} END AS decimal(18,0)) = #{value_join_alias custom_field}.id"
|
|
711 | 715 |
end |
712 | 716 | |
713 | 717 |
def value_join_alias(custom_field) |
lib/tasks/migrate_from_mantis.rake (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
52 | 52 |
TRACKER_BUG = Tracker.find_by_position(1) |
53 | 53 |
TRACKER_FEATURE = Tracker.find_by_position(2) |
54 | 54 | |
55 |
roles = Role.where(:builtin => 0).order('position ASC').all
|
|
55 |
roles = Role.where(:builtin => 0).order(:position).all
|
|
56 | 56 |
manager_role = roles[0] |
57 | 57 |
developer_role = roles[1] |
58 | 58 |
DEFAULT_ROLE = roles.last |
lib/tasks/migrate_from_trac.rake (revision 615eb5fb585c9864694385bf7a3e2b382a9dd52a) | ||
---|---|---|
60 | 60 |
'patch' =>TRACKER_FEATURE |
61 | 61 |
} |
62 | 62 | |
63 |
roles = Role.where(:builtin => 0).order('position ASC').all
|
|
63 |
roles = Role.where(:builtin => 0).order(:position).all
|
|
64 | 64 |
manager_role = roles[0] |
65 | 65 |
developer_role = roles[1] |
66 | 66 |
DEFAULT_ROLE = roles.last |