Merged rails-4.1 branch (#14534).

git-svn-id: http://svn.redmine.org/redmine/trunk@13482 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang
2014-10-22 17:37:16 +00:00
parent a6ec78a4dc
commit 2d1866d966
331 changed files with 2540 additions and 6138 deletions

3
.travis.run-test.sh Normal file
View File

@@ -0,0 +1,3 @@
#! /bin/sh
JRUBY_OPTS=-J-Xmx1024m bundle exec rake test:${TEST_SUITE}

View File

@@ -3,8 +3,6 @@
# You can also run tests on your environment.
language: ruby
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- 2.0
- 2.1
@@ -34,6 +32,7 @@ script:
- "bundle install"
- "RUN_ON_NOT_OFFICIAL='' RUBY_VER=1.9 BRANCH=trunk bundle exec rake config/database.yml"
- "bundle install"
- "JRUBY_OPTS=-J-Xmx1024m bundle exec rake ci"
- "bundle exec rake ci:setup"
- "sh .travis.run-test.sh"
notifications:
email: false

34
Gemfile
View File

@@ -1,12 +1,17 @@
source 'https://rubygems.org'
gem "rails", "3.2.19"
gem "rails", "4.1.6"
gem "jquery-rails", "~> 3.1.1"
gem "coderay", "~> 1.1.0"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "builder", ">= 3.0.4"
gem "request_store", "1.0.5"
gem "mime-types"
gem "awesome_nested_set", "3.0.0"
gem "protected_attributes"
gem "actionpack-action_caching"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :jruby]
gem "rbpdf", "~> 1.18.1"
# Optional gem for LDAP authentication
@@ -23,16 +28,12 @@ end
platforms :mri, :mingw do
# Optional gem for exporting the gantt to a PNG file, not supported with jruby
group :rmagick do
# RMagick 2 supports ruby 1.9
# RMagick 1 would be fine for ruby 1.8 but Bundler does not support
# different requirements for the same gem on different platforms
gem "rmagick", ">= 2.0.0"
end
# Optional Markdown support, not for JRuby
group :markdown do
# TODO: upgrade to redcarpet 3.x when ruby1.8 support is dropped
gem "redcarpet", "~> 2.3.0"
gem "redcarpet", "~> 3.1.2"
end
end
@@ -57,7 +58,6 @@ if File.exist?(database_file)
gem "mysql2", "~> 0.3.11", :platforms => [:mri, :mingw]
gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
when 'mysql'
gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw]
gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
when /postgresql/
gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw]
@@ -85,24 +85,20 @@ group :development do
end
group :test do
gem "shoulda", "~> 3.3.2"
gem "shoulda-matchers", "1.4.1"
gem "minitest"
gem "shoulda-context"
gem "mocha", "~> 1.0.0", :require => 'mocha/api'
if RUBY_VERSION >= '1.9.3'
gem "capybara", "~> 2.1.0"
gem "selenium-webdriver"
end
# For running UI tests
gem "capybara", "~> 2.1.0"
gem "selenium-webdriver"
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
if File.exists?(local_gemfile)
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
instance_eval File.read(local_gemfile)
eval_gemfile local_gemfile
end
# Load plugins' Gemfiles
Dir.glob File.expand_path("../plugins/*/{Gemfile,PluginGemfile}", __FILE__) do |file|
puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
#TODO: switch to "eval_gemfile file" when bundler >= 1.2.0 will be required (rails 4)
instance_eval File.read(file), file
eval_gemfile file
end

View File

@@ -34,7 +34,7 @@ class AdminController < ApplicationController
scope = Project.status(@status).order('lft')
scope = scope.like(params[:name]) if params[:name].present?
@projects = scope.all
@projects = scope.to_a
render :action => "projects", :layout => false if request.xhr?
end

View File

@@ -496,7 +496,7 @@ class ApplicationController < ActionController::Base
end
def render_feed(items, options={})
@items = items || []
@items = (items || []).to_a
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title

View File

@@ -26,7 +26,7 @@ class AutoCompletesController < ApplicationController
if q.match(/\A#?(\d+)\z/)
@issues << scope.find_by_id($1.to_i)
end
@issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%").order("#{Issue.table_name}.id DESC").limit(10).all
@issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%").order("#{Issue.table_name}.id DESC").limit(10).to_a
@issues.compact!
end
render :layout => false

View File

@@ -25,7 +25,7 @@ class BoardsController < ApplicationController
helper :watchers
def index
@boards = @project.boards.includes(:project, :last_message => :author).all
@boards = @project.boards.preload(:project, :last_message => :author).to_a
# show the board if there is only one
if @boards.size == 1
@board = @boards.first
@@ -45,12 +45,13 @@ class BoardsController < ApplicationController
@topic_pages = Paginator.new @topic_count, per_page_option, params['page']
@topics = @board.topics.
reorder("#{Message.table_name}.sticky DESC").
includes(:last_reply).
joins("LEFT OUTER JOIN #{Message.table_name} last_replies_messages ON last_replies_messages.id = #{Message.table_name}.last_reply_id").
references(:last_reply).
limit(@topic_pages.per_page).
offset(@topic_pages.offset).
order(sort_clause).
preload(:author, {:last_reply => :author}).
all
to_a
@message = Message.new(:board => @board)
render :action => 'show', :layout => !request.xhr?
}
@@ -59,7 +60,7 @@ class BoardsController < ApplicationController
reorder('created_on DESC').
includes(:author, :board).
limit(Setting.feeds_limit.to_i).
all
to_a
render_feed(@messages, :title => "#{@project}: #{@board}")
}
end

View File

@@ -27,7 +27,7 @@ class DocumentsController < ApplicationController
def index
@sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category'
documents = @project.documents.includes(:attachments, :category).all
documents = @project.documents.includes(:attachments, :category).to_a
case @sort_by
when 'date'
@grouped = documents.group_by {|d| d.updated_on.to_date }
@@ -43,7 +43,7 @@ class DocumentsController < ApplicationController
end
def show
@attachments = @document.attachments.all
@attachments = @document.attachments.to_a
end
def new
@@ -69,7 +69,7 @@ class DocumentsController < ApplicationController
def update
@document.safe_attributes = params[:document]
if request.put? and @document.save
if @document.save
flash[:notice] = l(:notice_successful_update)
redirect_to document_path(@document)
else

View File

@@ -32,7 +32,7 @@ class EnumerationsController < ApplicationController
format.api {
@klass = Enumeration.get_subclass(params[:type])
if @klass
@enumerations = @klass.shared.sorted.all
@enumerations = @klass.shared.sorted.to_a
else
render_404
end
@@ -56,7 +56,7 @@ class EnumerationsController < ApplicationController
end
def update
if request.put? && @enumeration.update_attributes(params[:enumeration])
if @enumeration.update_attributes(params[:enumeration])
flash[:notice] = l(:notice_successful_update)
redirect_to enumerations_path
else
@@ -75,7 +75,7 @@ class EnumerationsController < ApplicationController
redirect_to enumerations_path
return
end
@enumerations = @enumeration.class.system.all - [@enumeration]
@enumerations = @enumeration.class.system.to_a - [@enumeration]
end
private

View File

@@ -31,8 +31,10 @@ class FilesController < ApplicationController
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@containers = [ Project.includes(:attachments).reorder(sort_clause).find(@project.id)]
@containers += @project.versions.includes(:attachments).reorder(sort_clause).all.sort.reverse
@containers = [Project.includes(:attachments).
references(:attachments).reorder(sort_clause).find(@project.id)]
@containers += @project.versions.includes(:attachments).
references(:attachments).reorder(sort_clause).to_a.sort.reverse
render :layout => !request.xhr?
end

View File

@@ -27,13 +27,13 @@ class GroupsController < ApplicationController
def index
respond_to do |format|
format.html {
@groups = Group.sorted.all
@groups = Group.sorted.to_a
@user_count_by_group_id = user_count_by_group_id
}
format.api {
scope = Group.sorted
scope = scope.givable unless params[:builtin] == '1'
@groups = scope.all
@groups = scope.to_a
}
end
end
@@ -95,7 +95,7 @@ class GroupsController < ApplicationController
end
def add_users
@users = User.where(:id => (params[:user_id] || params[:user_ids])).all
@users = User.where(:id => (params[:user_id] || params[:user_ids])).to_a
@group.users << @users if request.post?
respond_to do |format|
format.html { redirect_to edit_group_path(@group, :tab => 'users') }

View File

@@ -27,7 +27,7 @@ class IssueCategoriesController < ApplicationController
def index
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.api { @categories = @project.issue_categories.all }
format.api { @categories = @project.issue_categories.to_a }
end
end

View File

@@ -29,7 +29,7 @@ class IssueStatusesController < ApplicationController
render :action => "index", :layout => false if request.xhr?
}
format.api {
@issue_statuses = IssueStatus.order('position').all
@issue_statuses = IssueStatus.order('position').to_a
}
end
end

View File

@@ -104,15 +104,16 @@ class IssuesController < ApplicationController
end
def show
@journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
@journals = @issue.journals.includes(:user, :details).
references(:user, :details).
reorder("#{Journal.table_name}.id ASC").to_a
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
Journal.preload_journals_details_custom_fields(@journals)
# TODO: use #select! when ruby1.8 support is dropped
@journals.reject! {|journal| !journal.notes? && journal.visible_details.empty?}
@journals.select! {|journal| journal.notes? || journal.visible_details.any?}
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all
@changesets = @issue.changesets.visible.to_a
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@@ -189,7 +190,7 @@ class IssuesController < ApplicationController
rescue ActiveRecord::StaleObjectError
@conflict = true
if params[:last_journal_id]
@conflict_journals = @issue.journals_after(params[:last_journal_id]).all
@conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
@conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
end
end
@@ -301,7 +302,7 @@ class IssuesController < ApplicationController
else
@saved_issues = @issues
@unsaved_issues = unsaved_issues
@issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).all
@issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
bulk_edit
render :action => 'bulk_edit'
end
@@ -375,7 +376,9 @@ class IssuesController < ApplicationController
def update_issue_from_params
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@time_entry.attributes = params[:time_entry]
if params[:time_entry]
@time_entry.attributes = params[:time_entry]
end
@issue.init_journal(User.current)
@@ -422,7 +425,9 @@ class IssuesController < ApplicationController
@issue.project = @project
@issue.author ||= User.current
# Tracker must be set before custom field values
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
tracker_id = (params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id]
tracker = tracker_id.present? ? @project.trackers.find(tracker_id) : @project.trackers.first
@issue.tracker ||= tracker
if @issue.tracker.nil?
render_error l(:error_no_tracker_in_project)
return false

View File

@@ -34,7 +34,6 @@ class JournalsController < ApplicationController
retrieve_query
sort_init 'id', 'desc'
sort_update(@query.sortable_columns)
if @query.valid?
@journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
:limit => 25)

View File

@@ -32,7 +32,7 @@ class MembersController < ApplicationController
order("#{Member.table_name}.id").
limit(@limit).
offset(@offset).
all
to_a
respond_to do |format|
format.html { head 406 }
format.api
@@ -63,7 +63,10 @@ class MembersController < ApplicationController
respond_to do |format|
format.html { redirect_to_settings_in_projects }
format.js { @members = members }
format.js {
@members = members
@member = Member.new
}
format.api {
@member = members.first
if @member.valid?

View File

@@ -43,10 +43,10 @@ class MessagesController < ApplicationController
@reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
@replies = @topic.children.
includes(:author, :attachments, {:board => :project}).
reorder("#{Message.table_name}.created_on ASC").
reorder("#{Message.table_name}.created_on ASC, #{Message.table_name}.id ASC").
limit(@reply_pages.per_page).
offset(@reply_pages.offset).
all
to_a
@reply = Message.new(:subject => "RE: #{@message.subject}")
render :action => "show", :layout => false if request.xhr?

View File

@@ -53,8 +53,8 @@ class MyController < ApplicationController
@user = User.current
@pref = @user.pref
if request.post?
@user.safe_attributes = params[:user]
@user.pref.attributes = params[:pref]
@user.safe_attributes = params[:user] if params[:user]
@user.pref.attributes = params[:pref] if params[:pref]
if @user.save
@user.pref.save
set_language_if_valid @user.language

View File

@@ -46,7 +46,7 @@ class NewsController < ApplicationController
order("#{News.table_name}.created_on DESC").
limit(@limit).
offset(@offset).
all
to_a
respond_to do |format|
format.html {
@news = News.new # for adding news inline

View File

@@ -20,7 +20,7 @@ class ProjectEnumerationsController < ApplicationController
before_filter :authorize
def update
if request.put? && params[:enumerations]
if params[:enumerations]
Project.transaction do
params[:enumerations].each do |id, activity|
@project.update_or_create_time_entry_activity(id, activity)

View File

@@ -53,30 +53,30 @@ class ProjectsController < ApplicationController
unless params[:closed]
scope = scope.active
end
@projects = scope.visible.order('lft').all
@projects = scope.visible.order('lft').to_a
}
format.api {
@offset, @limit = api_offset_and_limit
@project_count = Project.visible.count
@projects = Project.visible.offset(@offset).limit(@limit).order('lft').all
@projects = Project.visible.offset(@offset).limit(@limit).order('lft').to_a
}
format.atom {
projects = Project.visible.order('created_on DESC').limit(Setting.feeds_limit.to_i).all
projects = Project.visible.order('created_on DESC').limit(Setting.feeds_limit.to_i).to_a
render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
}
end
end
def new
@issue_custom_fields = IssueCustomField.sorted.all
@trackers = Tracker.sorted.all
@issue_custom_fields = IssueCustomField.sorted.to_a
@trackers = Tracker.sorted.to_a
@project = Project.new
@project.safe_attributes = params[:project]
end
def create
@issue_custom_fields = IssueCustomField.sorted.all
@trackers = Tracker.sorted.all
@issue_custom_fields = IssueCustomField.sorted.to_a
@trackers = Tracker.sorted.to_a
@project = Project.new
@project.safe_attributes = params[:project]
@@ -109,8 +109,8 @@ class ProjectsController < ApplicationController
end
def copy
@issue_custom_fields = IssueCustomField.sorted.all
@trackers = Tracker.sorted.all
@issue_custom_fields = IssueCustomField.sorted.to_a
@trackers = Tracker.sorted.to_a
@source_project = Project.find(params[:id])
if request.get?
@project = Project.copy_from(@source_project)
@@ -145,8 +145,8 @@ class ProjectsController < ApplicationController
end
@users_by_role = @project.users_by_role
@subprojects = @project.children.visible.all
@news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").all
@subprojects = @project.children.visible.to_a
@news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").to_a
@trackers = @project.rolled_up_trackers
cond = @project.project_condition(Setting.display_subprojects_issues?)
@@ -167,10 +167,10 @@ class ProjectsController < ApplicationController
end
def settings
@issue_custom_fields = IssueCustomField.sorted.all
@issue_custom_fields = IssueCustomField.sorted.to_a
@issue_category ||= IssueCategory.new
@member ||= @project.members.new
@trackers = Tracker.sorted.all
@trackers = Tracker.sorted.to_a
@wiki ||= @project.wiki
end

View File

@@ -37,8 +37,9 @@ class QueriesController < ApplicationController
order("#{Query.table_name}.name").
limit(@limit).
offset(@offset).
all
to_a
respond_to do |format|
format.html {render_error :status => 406}
format.api
end
end

View File

@@ -90,6 +90,6 @@ class ReportsController < ApplicationController
private
def find_issue_statuses
@statuses = IssueStatus.sorted.all
@statuses = IssueStatus.sorted.to_a
end
end

View File

@@ -69,7 +69,7 @@ class RepositoriesController < ApplicationController
@repository.merge_extra_info(attrs[:attrs_extra])
end
@repository.project = @project
if request.put? && @repository.save
if @repository.save
redirect_to settings_project_path(@project, :tab => 'repositories')
else
render :action => 'edit'
@@ -94,7 +94,7 @@ class RepositoriesController < ApplicationController
@committers = @repository.committers
@users = @project.users
additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
@users += User.where(:id => additional_user_ids).all unless additional_user_ids.empty?
@users += User.where(:id => additional_user_ids).to_a unless additional_user_ids.empty?
@users.compact!
@users.sort!
if request.post? && params[:committers].is_a?(Hash)
@@ -145,7 +145,7 @@ class RepositoriesController < ApplicationController
limit(@changeset_pages.per_page).
offset(@changeset_pages.offset).
includes(:user, :repository, :parents).
all
to_a
respond_to do |format|
format.html { render :layout => false if request.xhr? }

View File

@@ -30,7 +30,7 @@ class RolesController < ApplicationController
render :action => "index", :layout => false if request.xhr?
}
format.api {
@roles = Role.givable.all
@roles = Role.givable.to_a
}
end
end
@@ -47,7 +47,7 @@ class RolesController < ApplicationController
if params[:copy].present? && @copy_from = Role.find_by_id(params[:copy])
@role.copy_from(@copy_from)
end
@roles = Role.sorted.all
@roles = Role.sorted.to_a
end
def create
@@ -60,7 +60,7 @@ class RolesController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to roles_path
else
@roles = Role.sorted.all
@roles = Role.sorted.to_a
render :action => 'new'
end
end
@@ -69,7 +69,7 @@ class RolesController < ApplicationController
end
def update
if request.put? and @role.update_attributes(params[:role])
if @role.update_attributes(params[:role])
flash[:notice] = l(:notice_successful_update)
redirect_to roles_path
else
@@ -86,7 +86,7 @@ class RolesController < ApplicationController
end
def permissions
@roles = Role.sorted.all
@roles = Role.sorted.to_a
@permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
if request.post?
@roles.each do |role|

View File

@@ -31,7 +31,7 @@ class SearchController < ApplicationController
when 'my_projects'
User.current.memberships.collect(&:project)
when 'subprojects'
@project ? (@project.self_and_descendants.active.all) : nil
@project ? (@project.self_and_descendants.active.to_a) : nil
else
@project
end

View File

@@ -19,7 +19,8 @@ class SysController < ActionController::Base
before_filter :check_enabled
def projects
p = Project.active.has_module(:repository).order("#{Project.table_name}.identifier").preload(:repository).all
p = Project.active.has_module(:repository).
order("#{Project.table_name}.identifier").preload(:repository).to_a
# extra_info attribute from repository breaks activeresource client
render :xml => p.to_xml(
:only => [:id, :identifier, :name, :is_public, :status],
@@ -56,7 +57,7 @@ class SysController < ActionController::Base
raise ActiveRecord::RecordNotFound unless project
projects << project
else
projects = scope.all
projects = scope.to_a
end
projects.each do |project|
project.repositories.each do |repository|

View File

@@ -52,7 +52,7 @@ class TimelogController < ApplicationController
format.html {
@entry_count = scope.count
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
@entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).all
@entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).to_a
@total_hours = scope.sum(:hours).to_f
render :layout => !request.xhr?
@@ -60,15 +60,15 @@ class TimelogController < ApplicationController
format.api {
@entry_count = scope.count
@offset, @limit = api_offset_and_limit
@entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).all
@entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).to_a
}
format.atom {
entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").all
entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").to_a
render_feed(entries, :title => l(:label_spent_time))
}
format.csv {
# Export all entries
@entries = scope.all
@entries = scope.to_a
send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
@@ -232,7 +232,7 @@ private
end
def find_time_entries
@time_entries = TimeEntry.where(:id => params[:id] || params[:ids]).all
@time_entries = TimeEntry.where(:id => params[:id] || params[:ids]).to_a
raise ActiveRecord::RecordNotFound if @time_entries.empty?
@projects = @time_entries.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1

View File

@@ -29,14 +29,14 @@ class TrackersController < ApplicationController
render :action => "index", :layout => false if request.xhr?
}
format.api {
@trackers = Tracker.sorted.all
@trackers = Tracker.sorted.to_a
}
end
end
def new
@tracker ||= Tracker.new(params[:tracker])
@trackers = Tracker.sorted.all
@trackers = Tracker.sorted.to_a
@projects = Project.all
end
@@ -95,7 +95,7 @@ class TrackersController < ApplicationController
redirect_to fields_trackers_path
return
end
@trackers = Tracker.sorted.all
@trackers = Tracker.sorted.to_a
@custom_fields = IssueCustomField.all.sort
end
end

View File

@@ -47,7 +47,7 @@ class UsersController < ApplicationController
@user_count = scope.count
@user_pages = Paginator.new @user_count, @limit, params['page']
@offset ||= @user_pages.offset
@users = scope.order(sort_clause).limit(@limit).offset(@offset).all
@users = scope.order(sort_clause).limit(@limit).offset(@offset).to_a
respond_to do |format|
format.html {
@@ -60,7 +60,7 @@ class UsersController < ApplicationController
def show
# show projects based on current user visibility
@memberships = @user.memberships.where(Project.visible_condition(User.current)).all
@memberships = @user.memberships.where(Project.visible_condition(User.current)).to_a
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
@@ -90,7 +90,7 @@ class UsersController < ApplicationController
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
@user.pref.attributes = params[:pref]
@user.pref.attributes = params[:pref] if params[:pref]
if @user.save
Mailer.account_information(@user, @user.password).deliver if params[:send_information]
@@ -134,7 +134,7 @@ class UsersController < ApplicationController
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@user.pref.attributes = params[:pref] if params[:pref]
if @user.save
@user.pref.save

View File

@@ -31,7 +31,7 @@ class VersionsController < ApplicationController
def index
respond_to do |format|
format.html {
@trackers = @project.trackers.sorted.all
@trackers = @project.trackers.sorted.to_a
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@@ -56,7 +56,7 @@ class VersionsController < ApplicationController
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
}
format.api {
@versions = @project.shared_versions.all
@versions = @project.shared_versions.to_a
}
end
end
@@ -67,7 +67,7 @@ class VersionsController < ApplicationController
@issues = @version.fixed_issues.visible.
includes(:status, :tracker, :priority).
reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
all
to_a
}
format.api
end
@@ -117,7 +117,7 @@ class VersionsController < ApplicationController
end
def update
if request.put? && params[:version]
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
@version.safe_attributes = attributes

View File

@@ -53,7 +53,7 @@ class WatchersController < ApplicationController
def append
if params[:watcher].is_a?(Hash)
user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
@users = User.active.where(:id => user_ids).all
@users = User.active.where(:id => user_ids).to_a
end
if @users.blank?
render :nothing => true
@@ -92,7 +92,7 @@ class WatchersController < ApplicationController
def find_watchables
klass = Object.const_get(params[:object_type].camelcase) rescue nil
if klass && klass.respond_to?('watched_by')
@watchables = klass.where(:id => Array.wrap(params[:object_id])).all
@watchables = klass.where(:id => Array.wrap(params[:object_id])).to_a
raise Unauthorized if @watchables.any? {|w|
if w.respond_to?(:visible?)
!w.visible?

View File

@@ -219,7 +219,7 @@ class WikiController < ApplicationController
reorder('version DESC').
limit(@version_pages.per_page + 1).
offset(@version_pages.offset).
all
to_a
render :layout => false if request.xhr?
end
@@ -280,7 +280,7 @@ class WikiController < ApplicationController
@pages = @wiki.pages.
order('title').
includes([:content, {:attachments => :author}]).
all
to_a
respond_to do |format|
format.html {
export = render_to_string :action => 'export_multiple', :layout => false
@@ -327,7 +327,7 @@ private
def find_existing_or_new_page
@page = @wiki.find_or_new_page(params[:id])
if @wiki.page_found_with_redirect?
redirect_to params.update(:id => @page.title)
redirect_to_page @page
end
end
@@ -339,10 +339,14 @@ private
return
end
if @wiki.page_found_with_redirect?
redirect_to params.update(:id => @page.title)
redirect_to_page @page
end
end
def redirect_to_page(page)
redirect_to :action => action_name, :project_id => page.wiki.project, :id => page.title
end
# Returns true if the current user is allowed to edit the page, otherwise false
def editable?(page = @page)
page.editable_by?(User.current)
@@ -360,6 +364,6 @@ private
reorder("#{WikiPage.table_name}.title").
includes(:wiki => :project).
includes(:parent).
all
to_a
end
end

View File

@@ -86,9 +86,9 @@ class WorkflowsController < ApplicationController
@source_role = Role.find_by_id(params[:source_role_id].to_i)
end
@target_trackers = params[:target_tracker_ids].blank? ?
nil : Tracker.where(:id => params[:target_tracker_ids]).all
nil : Tracker.where(:id => params[:target_tracker_ids]).to_a
@target_roles = params[:target_role_ids].blank? ?
nil : Role.where(:id => params[:target_role_ids]).all
nil : Role.where(:id => params[:target_role_ids]).to_a
if request.post?
if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
flash.now[:error] = l(:error_workflow_copy_source)
@@ -113,9 +113,9 @@ class WorkflowsController < ApplicationController
def find_roles
ids = Array.wrap(params[:role_id])
if ids == ['all']
@roles = Role.sorted.all
@roles = Role.sorted.to_a
elsif ids.present?
@roles = Role.where(:id => ids).all
@roles = Role.where(:id => ids).to_a
end
@roles = nil if @roles.blank?
end
@@ -123,9 +123,9 @@ class WorkflowsController < ApplicationController
def find_trackers
ids = Array.wrap(params[:tracker_id])
if ids == ['all']
@trackers = Tracker.sorted.all
@trackers = Tracker.sorted.to_a
elsif ids.present?
@trackers = Tracker.where(:id => ids).all
@trackers = Tracker.where(:id => ids).to_a
end
@trackers = nil if @trackers.blank?
end
@@ -135,6 +135,6 @@ class WorkflowsController < ApplicationController
if @trackers && @used_statuses_only
@statuses = @trackers.map(&:issue_statuses).flatten.uniq.sort.presence
end
@statuses ||= IssueStatus.sorted.all
@statuses ||= IssueStatus.sorted.to_a
end
end

View File

@@ -138,9 +138,7 @@ module ApplicationHelper
if project.archived?
h(project.name)
elsif options.key?(:action)
ActiveSupport::Deprecation.warn "#link_to_project with :action option is deprecated and will be removed in Redmine 3.0."
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
link_to project.name, url, html_options
raise "#link_to_project no longer accepts :action option in Redmine 3.0"
else
link_to project.name, project_path(project, options), html_options
end
@@ -157,13 +155,6 @@ module ApplicationHelper
end
end
# Generates a link to a version
def link_to_version(version, options = {})
return '' unless version && version.is_a?(Version)
options = {:title => format_date(version.effective_date)}.merge(options)
link_to_if version.visible?, format_version_name(version), version_path(version), options
end
# Helper that formats object for html or text rendering
def format_object(object, html=true, &block)
if block_given?
@@ -185,7 +176,7 @@ module ApplicationHelper
when 'Project'
html ? link_to_project(object) : object.to_s
when 'Version'
html ? link_to_version(object) : object.to_s
html ? link_to(object.name, version_path(object)) : object.to_s
when 'TrueClass'
l(:general_text_Yes)
when 'FalseClass'
@@ -247,7 +238,7 @@ module ApplicationHelper
end
def format_version_name(version)
if !version.shared? || version.project == @project
if version.project == @project
h(version)
else
h("#{version.project} - #{version}")
@@ -502,7 +493,7 @@ module ApplicationHelper
h(Setting.app_title)
else
b = []
ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
ancestors = (@project.root? ? [] : @project.ancestors.visible.to_a)
if ancestors.any?
root = ancestors.shift
b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
@@ -1217,7 +1208,7 @@ module ApplicationHelper
source
end
end
super sources, options
super *sources, options
end
# Overrides Rails' image_tag with themes and plugins support.
@@ -1250,7 +1241,7 @@ module ApplicationHelper
end
end
end
super sources, options
super *sources, options
end
# TODO: remove this in 2.5.0
@@ -1288,12 +1279,7 @@ module ApplicationHelper
end
def sanitize_anchor_name(anchor)
if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
else
# TODO: remove when ruby1.8 is no longer supported
anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
end
anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
end
# Returns the javascript tags that are included in the html layout head

View File

@@ -30,7 +30,7 @@ module GroupsHelper
scope = User.active.sorted.not_in_group(group).like(params[:q])
principal_count = scope.count
principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).to_a
s = content_tag('div', principals_check_box_tags('user_ids[]', principals), :id => 'principals')

View File

@@ -63,7 +63,7 @@ module IssuesHelper
def render_issue_subject_with_tree(issue)
s = ''
ancestors = issue.root? ? [] : issue.ancestors.visible.all
ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
ancestors.each do |ancestor|
s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
end
@@ -204,7 +204,7 @@ module IssuesHelper
order("#{Query.table_name}.name ASC").
# Project specific queries and global queries
where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
all
to_a
end
@sidebar_queries
end
@@ -408,7 +408,7 @@ module IssuesHelper
if association
record = association.class_name.constantize.find_by_id(id)
if record
record.name.force_encoding('UTF-8') if record.name.respond_to?(:force_encoding)
record.name.force_encoding('UTF-8')
return record.name
end
end

View File

@@ -22,7 +22,7 @@ module MembersHelper
scope = Principal.active.sorted.not_member_of(project).like(params[:q])
principal_count = scope.count
principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).to_a
s = content_tag('div', principals_check_box_tags('membership[user_ids][]', principals), :id => 'principals')

View File

@@ -23,11 +23,12 @@ module MyHelper
where(:project_id => User.current.projects.map(&:id)).
where("(start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)", startdt, enddt, startdt, enddt).
includes(:project, :tracker, :priority, :assigned_to).
all
references(:project, :tracker, :priority, :assigned_to).
to_a
end
def documents_items
Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).all
Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).to_a
end
def issuesassignedtome_items
@@ -35,8 +36,9 @@ module MyHelper
where(:assigned_to_id => ([User.current.id] + User.current.group_ids)).
limit(10).
includes(:status, :project, :tracker, :priority).
references(:status, :project, :tracker, :priority).
order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC").
all
to_a
end
def issuesreportedbyme_items
@@ -44,12 +46,13 @@ module MyHelper
where(:author_id => User.current.id).
limit(10).
includes(:status, :project, :tracker).
references(:status, :project, :tracker).
order("#{Issue.table_name}.updated_on DESC").
all
to_a
end
def issueswatched_items
Issue.visible.on_active_project.watched_by(User.current.id).recently_updated.limit(10).all
Issue.visible.on_active_project.watched_by(User.current.id).recently_updated.limit(10).to_a
end
def news_items
@@ -57,15 +60,17 @@ module MyHelper
where(:project_id => User.current.projects.map(&:id)).
limit(10).
includes(:project, :author).
references(:project, :author).
order("#{News.table_name}.created_on DESC").
all
to_a
end
def timelog_items
TimeEntry.
where("#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", User.current.id, Date.today - 6, Date.today).
includes(:activity, :project, {:issue => [:tracker, :status]}).
joins(:activity, :project, {:issue => [:tracker, :status]}).
references(:activity, :project, {:issue => [:tracker, :status]}).
order("#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC").
all
to_a
end
end

View File

@@ -18,6 +18,11 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module ProjectsHelper
def link_to_version(version, options = {})
return '' unless version && version.is_a?(Version)
link_to_if version.visible?, format_version_name(version), version_path(version), options
end
def project_settings_tabs
tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
{:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},

View File

@@ -143,7 +143,7 @@ module QueriesHelper
end
end
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
export = CSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# csv header fields
csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
# csv lines

View File

@@ -29,7 +29,6 @@ module SearchHelper
result << '...'
break
end
words = words.mb_chars
if i.even?
result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
else

View File

@@ -84,7 +84,8 @@ module SortHelper
def to_sql
sql = @criteria.collect do |k,o|
if s = @available_criteria[k]
(o ? s.to_a : s.to_a.collect {|c| append_desc(c)})
s = [s] unless s.is_a?(Array)
(o ? s : s.collect {|c| append_desc(c)})
end
end.flatten.compact
sql.blank? ? nil : sql

View File

@@ -105,7 +105,7 @@ module TimelogHelper
def report_to_csv(report)
decimal_separator = l(:general_csv_decimal_separator)
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
export = CSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
# Column headers
headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
headers += report.periods

View File

@@ -27,6 +27,7 @@ class Attachment < ActiveRecord::Base
validates_length_of :disk_filename, :maximum => 255
validates_length_of :description, :maximum => 255
validate :validate_max_file_size
attr_protected :id
acts_as_event :title => :filename,
:url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
@@ -34,16 +35,16 @@ class Attachment < ActiveRecord::Base
acts_as_activity_provider :type => 'files',
:permission => :view_files,
:author_key => :author_id,
:find_options => {:select => "#{Attachment.table_name}.*",
:joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
:scope => select("#{Attachment.table_name}.*").
joins("LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )")
acts_as_activity_provider :type => 'documents',
:permission => :view_documents,
:author_key => :author_id,
:find_options => {:select => "#{Attachment.table_name}.*",
:joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
:scope => select("#{Attachment.table_name}.*").
joins("LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id")
cattr_accessor :storage_path
@@storage_path = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, "files")
@@ -74,7 +75,7 @@ class Attachment < ActiveRecord::Base
if @temp_file.size > 0
if @temp_file.respond_to?(:original_filename)
self.filename = @temp_file.original_filename
self.filename.force_encoding("UTF-8") if filename.respond_to?(:force_encoding)
self.filename.force_encoding("UTF-8")
end
if @temp_file.respond_to?(:content_type)
self.content_type = @temp_file.content_type.to_s.chomp

View File

@@ -29,6 +29,7 @@ class AuthSource < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 60
attr_protected :id
def authenticate(login, password)
end

View File

@@ -18,8 +18,8 @@
class Board < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
has_many :topics, lambda {where("#{Message.table_name}.parent_id IS NULL").order("#{Message.table_name}.created_on DESC")}, :class_name => 'Message'
has_many :messages, lambda {order("#{Message.table_name}.created_on DESC")}, :dependent => :destroy
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
acts_as_tree :dependent => :nullify
acts_as_list :scope => '(project_id = #{project_id} AND parent_id #{parent_id ? "= #{parent_id}" : "IS NULL"})'
@@ -29,9 +29,12 @@ class Board < ActiveRecord::Base
validates_length_of :name, :maximum => 30
validates_length_of :description, :maximum => 255
validate :validate_board
attr_protected :id
scope :visible, lambda {|*args|
includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
joins(:project).
references(:project).
where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
}
safe_attributes 'name', 'description', 'parent_id', 'move_to'

View File

@@ -21,6 +21,7 @@ class Change < ActiveRecord::Base
validates_presence_of :changeset_id, :action, :path
before_save :init_path
before_validation :replace_invalid_utf8_of_path
attr_protected :id
def relative_path
changeset.repository.relative_path(path)

View File

@@ -35,20 +35,23 @@ class Changeset < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}}
acts_as_searchable :columns => 'comments',
:include => {:repository => :project},
:scope => preload(:repository => :project),
:project_key => "#{Repository.table_name}.project_id",
:date_column => 'committed_on'
acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
:author_key => :user_id,
:find_options => {:include => [:user, {:repository => :project}]}
:scope => preload(:user, {:repository => :project})
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
validates_uniqueness_of :revision, :scope => :repository_id
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
attr_protected :id
scope :visible, lambda {|*args|
includes(:repository => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
joins(:repository => :project).
references(:repository => :project).
where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
}
after_create :scan_for_issues

View File

@@ -21,6 +21,7 @@ class Comment < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :commented, :author, :comments
attr_protected :id
after_create :send_notification

View File

@@ -29,6 +29,7 @@ class CustomField < ActiveRecord::Base
validates_length_of :name, :maximum => 30
validates_inclusion_of :field_format, :in => Proc.new { Redmine::FieldFormat.available_formats }
validate :validate_custom_field
attr_protected :id
before_validation :set_searchable
before_save do |field|
@@ -117,7 +118,7 @@ class CustomField < ActiveRecord::Base
values = read_attribute(:possible_values)
if values.is_a?(Array)
values.each do |value|
value.force_encoding('UTF-8') if value.respond_to?(:force_encoding)
value.force_encoding('UTF-8')
end
values
else
@@ -218,7 +219,7 @@ class CustomField < ActiveRecord::Base
# to move in project_custom_field
def self.for_all
where(:is_for_all => true).order('position').all
where(:is_for_all => true).order('position').to_a
end
def type_name

View File

@@ -18,6 +18,7 @@
class CustomValue < ActiveRecord::Base
belongs_to :custom_field
belongs_to :customized, :polymorphic => true
attr_protected :id
def initialize(attributes=nil, *args)
super

View File

@@ -21,19 +21,23 @@ class Document < ActiveRecord::Base
belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id"
acts_as_attachable :delete_permission => :delete_documents
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
acts_as_searchable :columns => ['title', "#{table_name}.description"],
:scope => preload(:project)
acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
:author => Proc.new {|o| o.attachments.reorder("#{Attachment.table_name}.created_on ASC").first.try(:author) },
:url => Proc.new {|o| {:controller => 'documents', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => :project}
acts_as_activity_provider :scope => preload(:project)
validates_presence_of :project, :title, :category
validates_length_of :title, :maximum => 60
attr_protected :id
after_create :send_notification
scope :visible, lambda {|*args|
includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_documents, *args))
joins(:project).
references(:project).
where(Project.allowed_to_condition(args.shift || User.current, :view_documents, *args))
}
safe_attributes 'category_id', 'title', 'description'

View File

@@ -21,6 +21,7 @@ class EnabledModule < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name, :scope => :project_id
attr_protected :id
after_create :module_enabled

View File

@@ -18,7 +18,7 @@
class Enumeration < ActiveRecord::Base
include Redmine::SubclassFactory
default_scope :order => "#{Enumeration.table_name}.position ASC"
default_scope lambda {order("#{Enumeration.table_name}.position ASC")}
belongs_to :project

View File

@@ -28,6 +28,7 @@ class Group < Principal
validates_presence_of :lastname
validates_uniqueness_of :lastname, :case_sensitive => false
validates_length_of :lastname, :maximum => 255
attr_protected :id
before_destroy :remove_references_before_destroy
@@ -81,7 +82,8 @@ class Group < Principal
def user_removed(user)
members.each do |member|
MemberRole.
includes(:member).
joins(:member).
references(:member).
where("#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids).
each(&:destroy)
end
@@ -95,10 +97,6 @@ class Group < Principal
super(attr_name, *args)
end
def self.builtin_id(arg)
(arg.anonymous? ? GroupAnonymous : GroupNonMember).instance_id
end
def self.anonymous
GroupAnonymous.load_instance
end

View File

@@ -23,8 +23,4 @@ class GroupAnonymous < GroupBuiltin
def builtin_type
"anonymous"
end
def self.instance_id
@@instance_id ||= load_instance.id
end
end

View File

@@ -37,7 +37,7 @@ class GroupBuiltin < Group
class << self
def load_instance
return nil if self == GroupBuiltin
instance = first(:order => 'id') || create_instance
instance = order('id').first || create_instance
end
def create_instance

View File

@@ -23,8 +23,4 @@ class GroupNonMember < GroupBuiltin
def builtin_type
"non_member"
end
def self.instance_id
@@instance_id ||= load_instance.id
end
end

View File

@@ -31,15 +31,12 @@ class Issue < ActiveRecord::Base
has_many :journals, :as => :journalized, :dependent => :destroy
has_many :visible_journals,
lambda {where(["(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))", false])},
:class_name => 'Journal',
:as => :journalized,
:conditions => Proc.new {
["(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))", false]
},
:readonly => true
:as => :journalized
has_many :time_entries, :dependent => :destroy
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
has_and_belongs_to_many :changesets, lambda {order("#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC")}
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
@@ -49,14 +46,19 @@ class Issue < ActiveRecord::Base
acts_as_customizable
acts_as_watchable
acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
:include => [:project, :visible_journals],
# sort by id so that limited eager loading doesn't break with postgresql
:order_column => "#{table_name}.id"
:order_column => "#{table_name}.id",
:scope => lambda { joins(:project).
joins("LEFT OUTER JOIN #{Journal.table_name} ON #{Journal.table_name}.journalized_type='Issue'" +
" AND #{Journal.table_name}.journalized_id = #{Issue.table_name}.id" +
" AND (#{Journal.table_name}.private_notes = #{connection.quoted_false}" +
" OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))") }
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
:type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
acts_as_activity_provider :scope => preload(:project, :author, :tracker),
:author_key => :author_id
DONE_RATIO_OPTIONS = %w(issue_field issue_status)
@@ -72,19 +74,26 @@ class Issue < ActiveRecord::Base
validates :start_date, :date => true
validates :due_date, :date => true
validate :validate_issue, :validate_required_fields
attr_protected :id
scope :visible, lambda {|*args|
includes(:project).where(Issue.visible_condition(args.shift || User.current, *args))
joins(:project).
references(:project).
where(Issue.visible_condition(args.shift || User.current, *args))
}
scope :open, lambda {|*args|
is_closed = args.size > 0 ? !args.first : false
includes(:status).where("#{IssueStatus.table_name}.is_closed = ?", is_closed)
joins(:status).
references(:status).
where("#{IssueStatus.table_name}.is_closed = ?", is_closed)
}
scope :recently_updated, lambda { order("#{Issue.table_name}.updated_on DESC") }
scope :on_active_project, lambda {
includes(:status, :project, :tracker).where("#{Project.table_name}.status = ?", Project::STATUS_ACTIVE)
joins(:project).
references(:project).
where("#{Project.table_name}.status = ?", Project::STATUS_ACTIVE)
}
scope :fixed_version, lambda {|versions|
ids = [versions].flatten.compact.map {|v| v.is_a?(Version) ? v.id : v}
@@ -107,7 +116,7 @@ class Issue < ActiveRecord::Base
# Returns a SQL conditions string used to find all issues visible by the specified user
def self.visible_condition(user, options={})
Project.allowed_to_condition(user, :view_issues, options) do |role, user|
if user.logged?
if user.id && user.logged?
case role.issues_visibility
when 'all'
nil
@@ -351,6 +360,10 @@ class Issue < ActiveRecord::Base
# Do not redefine alias chain on reload (see #4838)
alias_method_chain(:assign_attributes, :project_and_tracker_first) unless method_defined?(:assign_attributes_without_project_and_tracker_first)
def attributes=(new_attributes)
assign_attributes new_attributes
end
def estimated_hours=(h)
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
end
@@ -423,7 +436,7 @@ class Issue < ActiveRecord::Base
def safe_attributes=(attrs, user=User.current)
return unless attrs.is_a?(Hash)
attrs = attrs.dup
attrs = attrs.deep_dup
# Project and Tracker must be set before since new_statuses_allowed_to depends on it.
if (p = attrs.delete('project_id')) && safe_attribute?('project_id')
@@ -458,14 +471,12 @@ class Issue < ActiveRecord::Base
if attrs['custom_field_values'].present?
editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
# TODO: use #select when ruby1.8 support is dropped
attrs['custom_field_values'] = attrs['custom_field_values'].reject {|k, v| !editable_custom_field_ids.include?(k.to_s)}
attrs['custom_field_values'].select! {|k, v| editable_custom_field_ids.include?(k.to_s)}
end
if attrs['custom_fields'].present?
editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
# TODO: use #select when ruby1.8 support is dropped
attrs['custom_fields'] = attrs['custom_fields'].reject {|c| !editable_custom_field_ids.include?(c['id'].to_s)}
attrs['custom_fields'].select! {|c| editable_custom_field_ids.include?(c['id'].to_s)}
end
# mass-assignment security bypass
@@ -733,7 +744,7 @@ class Issue < ActiveRecord::Base
def assignable_versions
return @assignable_versions if @assignable_versions
versions = project.shared_versions.open.all
versions = project.shared_versions.open.to_a
if fixed_version
if fixed_version_id_changed?
# nothing to do
@@ -879,10 +890,14 @@ class Issue < ActiveRecord::Base
if issues.any?
issue_ids = issues.map(&:id)
# Relations with issue_from in given issues and visible issue_to
relations_from = IssueRelation.includes(:issue_to => [:status, :project]).where(visible_condition(user)).where(:issue_from_id => issue_ids).all
relations_from = IssueRelation.joins(:issue_to => :project).
references(:issue_to => :project).
where(visible_condition(user)).where(:issue_from_id => issue_ids).to_a
# Relations with issue_to in given issues and visible issue_from
relations_to = IssueRelation.includes(:issue_from => [:status, :project]).where(visible_condition(user)).where(:issue_to_id => issue_ids).all
relations_to = IssueRelation.joins(:issue_from => :project).
references(:issue_from => :project).
where(visible_condition(user)).
where(:issue_to_id => issue_ids).to_a
issues.each do |issue|
relations =
relations_from.select {|relation| relation.issue_from_id == issue.id} +
@@ -1121,6 +1136,7 @@ class Issue < ActiveRecord::Base
def parent_issue_id=(arg)
s = arg.to_s.strip.presence
if s && (m = s.match(%r{\A#?(\d+)\z})) && (@parent_issue = Issue.find_by_id(m[1]))
@parent_issue.id
@invalid_parent_issue_id = nil
elsif s.blank?
@parent_issue = nil
@@ -1349,7 +1365,7 @@ class Issue < ActiveRecord::Base
self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
cond = ["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt]
self.class.base_class.select('id').lock(true).where(cond)
offset = right_most_bound + 1 - lft
offset = rdm_right_most_bound + 1 - lft
Issue.where(cond).
update_all(["root_id = ?, lft = lft + ?, rgt = rgt + ?", root_id, offset, offset])
end
@@ -1367,6 +1383,14 @@ class Issue < ActiveRecord::Base
recalculate_attributes_for(former_parent_id) if former_parent_id
end
def rdm_right_most_bound
right_most_node =
self.class.base_class.unscoped.
order("#{quoted_right_column_full_name} desc").limit(1).lock(true).first
right_most_node ? (right_most_node[right_column_name] || 0 ) : 0
end
private :rdm_right_most_bound
def update_parent_attributes
recalculate_attributes_for(parent_id) if parent_id
end
@@ -1395,7 +1419,7 @@ class Issue < ActiveRecord::Base
end
done = p.leaves.joins(:status).
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)").to_f
"* (CASE WHEN is_closed = #{self.class.connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
progress = done / (average * leaves_count)
p.done_ratio = progress.round
end
@@ -1415,7 +1439,8 @@ class Issue < ActiveRecord::Base
def self.update_versions(conditions=nil)
# Only need to update issues with a fixed_version from
# a different project and that is not systemwide shared
Issue.includes(:project, :fixed_version).
Issue.joins(:project, :fixed_version).
references(:version, :fixed_version).
where("#{Issue.table_name}.fixed_version_id IS NOT NULL" +
" AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
" AND #{Version.table_name}.sharing <> 'system'").

View File

@@ -24,6 +24,7 @@ class IssueCategory < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name, :scope => [:project_id]
validates_length_of :name, :maximum => 30
attr_protected :id
safe_attributes 'name', 'assigned_to_id'

View File

@@ -32,7 +32,7 @@ class IssueCustomField < CustomField
sql = super
id_column ||= id
tracker_condition = "#{Issue.table_name}.tracker_id IN (SELECT tracker_id FROM #{table_name_prefix}custom_fields_trackers#{table_name_suffix} WHERE custom_field_id = #{id_column})"
project_condition = "EXISTS (SELECT 1 FROM #{CustomField.table_name} ifa WHERE ifa.is_for_all = #{connection.quoted_true} AND ifa.id = #{id_column})" +
project_condition = "EXISTS (SELECT 1 FROM #{CustomField.table_name} ifa WHERE ifa.is_for_all = #{self.class.connection.quoted_true} AND ifa.id = #{id_column})" +
" OR #{Issue.table_name}.project_id IN (SELECT project_id FROM #{table_name_prefix}custom_fields_projects#{table_name_suffix} WHERE custom_field_id = #{id_column})"
"((#{sql}) AND (#{tracker_condition}) AND (#{project_condition}))"

View File

@@ -45,7 +45,9 @@ class IssueQuery < Query
scope :visible, lambda {|*args|
user = args.shift || User.current
base = Project.allowed_to_condition(user, :view_issues, *args)
scope = includes(:project).where("#{table_name}.project_id IS NULL OR (#{base})")
scope = joins("LEFT OUTER JOIN #{Project.table_name} ON #{table_name}.project_id = #{Project.table_name}.id").
references(:project).
where("#{table_name}.project_id IS NULL OR (#{base})")
if user.admin?
scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
@@ -132,17 +134,17 @@ class IssueQuery < Query
if project
principals += project.principals.sort
unless project.leaf?
subprojects = project.descendants.visible.all
subprojects = project.descendants.visible.to_a
principals += Principal.member_of(subprojects)
end
versions = project.shared_versions.all
categories = project.issue_categories.all
versions = project.shared_versions.to_a
categories = project.issue_categories.to_a
issue_custom_fields = project.all_issue_custom_fields
else
if all_projects.any?
principals += Principal.member_of(all_projects)
end
versions = Version.visible.where(:sharing => 'system').all
versions = Version.visible.where(:sharing => 'system').to_a
issue_custom_fields = IssueCustomField.where(:is_for_all => true)
end
principals.uniq!
@@ -339,7 +341,7 @@ class IssueQuery < Query
scope = scope.preload(:author)
end
issues = scope.all
issues = scope.to_a
if has_column?(:spent_hours)
Issue.load_visible_spent_hours(issues)
@@ -360,12 +362,13 @@ class IssueQuery < Query
joins(:status, :project).
where(statement).
includes(([:status, :project] + (options[:include] || [])).uniq).
references(([:status, :project] + (options[:include] || [])).uniq).
where(options[:conditions]).
order(order_option).
joins(joins_for_order_statement(order_option.join(','))).
limit(options[:limit]).
offset(options[:offset]).
find_ids
pluck(:id)
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
@@ -380,7 +383,7 @@ class IssueQuery < Query
limit(options[:limit]).
offset(options[:offset]).
preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}).
all
to_a
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
@@ -392,7 +395,8 @@ class IssueQuery < Query
where(project_statement).
where(options[:conditions]).
includes(:project).
all
references(:project).
to_a
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
@@ -411,7 +415,7 @@ class IssueQuery < Query
groups = Group.givable
operator = '!' # Override the operator since we want to find by assigned_to
else
groups = Group.where(:id => value).all
groups = Group.where(:id => value).to_a
end
groups ||= []
@@ -431,7 +435,7 @@ class IssueQuery < Query
" WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
when "=", "!"
role_cond = value.any? ?
"#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" :
"#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")" :
"1=0"
sw = operator == "!" ? 'NOT' : ''
@@ -443,7 +447,7 @@ class IssueQuery < Query
def sql_for_is_private_field(field, operator, value)
op = (operator == "=" ? 'IN' : 'NOT IN')
va = value.map {|v| v == '0' ? connection.quoted_false : connection.quoted_true}.uniq.join(',')
va = value.map {|v| v == '0' ? self.class.connection.quoted_false : self.class.connection.quoted_true}.uniq.join(',')
"#{Issue.table_name}.is_private #{op} (#{va})"
end
@@ -462,14 +466,14 @@ class IssueQuery < Query
sql = case operator
when "*", "!*"
op = (operator == "*" ? 'IN' : 'NOT IN')
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}')"
when "=", "!"
op = (operator == "=" ? 'IN' : 'NOT IN')
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
when "=p", "=!p", "!p"
op = (operator == "!p" ? 'NOT IN' : 'IN')
comp = (operator == "=!p" ? '<>' : '=')
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
end
if relation_options[:sym] == field && !options[:reverse]

View File

@@ -27,6 +27,7 @@ class IssueStatus < ActiveRecord::Base
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
attr_protected :id
scope :sorted, lambda { order("#{table_name}.position ASC") }
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
@@ -79,7 +80,7 @@ class IssueStatus < ActiveRecord::Base
includes(:new_status).
where(["role_id IN (:role_ids) AND tracker_id = :tracker_id AND (#{conditions})",
{:role_ids => roles.collect(&:id), :tracker_id => tracker.id, :true => true, :false => false}
]).all.
]).to_a.
map(&:new_status).compact.sort
else
[]

View File

@@ -24,6 +24,7 @@ class Journal < ActiveRecord::Base
belongs_to :user
has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
attr_accessor :indice
attr_protected :id
acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" },
:description => :notes,
@@ -34,17 +35,18 @@ class Journal < ActiveRecord::Base
acts_as_activity_provider :type => 'issues',
:author_key => :user_id,
:find_options => {:include => [{:issue => :project}, :details, :user],
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
:scope => preload({:issue => :project}, :user).
joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id").
where("#{Journal.table_name}.journalized_type = 'Issue' AND" +
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')")
before_create :split_private_notes
after_create :send_notification
scope :visible, lambda {|*args|
user = args.shift || User.current
includes(:issue => :project).
joins(:issue => :project).
references(:project).
where(Issue.visible_condition(user, *args)).
where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false)
}

View File

@@ -18,6 +18,7 @@
class JournalDetail < ActiveRecord::Base
belongs_to :journal
before_save :normalize_values
attr_protected :id
def custom_field
if property == 'cf'

View File

@@ -42,7 +42,7 @@ class MailHandler < ActionMailer::Base
@@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
@@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1')
email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding)
email.force_encoding('ASCII-8BIT')
super(email)
end
@@ -417,7 +417,7 @@ class MailHandler < ActionMailer::Base
end
parts.reject! do |part|
part.header[:content_disposition].try(:disposition_type) == 'attachment'
part.attachment?
end
@plain_text_body = parts.map do |p|

View File

@@ -25,6 +25,7 @@ class Member < ActiveRecord::Base
validates_presence_of :principal, :project
validates_uniqueness_of :user_id, :scope => :project_id
validate :validate_role
attr_protected :id
before_destroy :set_issue_category_nil

View File

@@ -26,6 +26,7 @@ class MemberRole < ActiveRecord::Base
validates_presence_of :role
validate :validate_role_member
attr_protected :id
def validate_role_member
errors.add :role_id, :invalid if role && !role.member?

View File

@@ -22,9 +22,10 @@ class Message < ActiveRecord::Base
acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
acts_as_attachable
belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
attr_protected :id
acts_as_searchable :columns => ['subject', 'content'],
:include => {:board => :project},
:scope => preload(:board => :project),
:project_key => "#{Board.table_name}.project_id",
:date_column => "#{table_name}.created_on"
acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
@@ -34,7 +35,7 @@ class Message < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
{:id => o.parent_id, :r => o.id, :anchor => "message-#{o.id}"})}
acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
acts_as_activity_provider :scope => preload({:board => :project}, :author),
:author_key => :author_id
acts_as_watchable
@@ -48,7 +49,9 @@ class Message < ActiveRecord::Base
after_create :send_notification
scope :visible, lambda {|*args|
includes(:board => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
joins(:board => :project).
references(:board => :project).
where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
}
safe_attributes 'subject', 'content'

View File

@@ -19,16 +19,18 @@ class News < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
has_many :comments, lambda {order("created_on")}, :as => :commented, :dependent => :delete_all
validates_presence_of :title, :description
validates_length_of :title, :maximum => 60
validates_length_of :summary, :maximum => 255
attr_protected :id
acts_as_attachable :delete_permission => :manage_news
acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"],
:scope => preload(:project)
acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => [:project, :author]},
acts_as_activity_provider :scope => preload(:project, :author),
:author_key => :author_id
acts_as_watchable
@@ -36,7 +38,9 @@ class News < ActiveRecord::Base
after_create :send_notification
scope :visible, lambda {|*args|
includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_news, *args))
joins(:project).
references([:author, :project]).
where(Project.allowed_to_condition(args.shift || User.current, :view_news, *args))
}
safe_attributes 'title', 'summary', 'description'
@@ -68,7 +72,7 @@ class News < ActiveRecord::Base
# returns latest news for projects visible by user
def self.latest(user = User.current, count = 5)
visible(user).includes([:author, :project]).order("#{News.table_name}.created_on DESC").limit(count).all
visible(user).joins([:author, :project]).order("#{News.table_name}.created_on DESC").limit(count).to_a
end
private

View File

@@ -25,11 +25,13 @@ class Principal < ActiveRecord::Base
STATUS_LOCKED = 3
has_many :members, :foreign_key => 'user_id', :dependent => :destroy
has_many :memberships, :class_name => 'Member',
:foreign_key => 'user_id',
:include => [:project, :roles],
:conditions => "#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}",
:order => "#{Project.table_name}.name"
has_many :memberships,
lambda {preload(:project, :roles).
joins(:project).
where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}").
order("#{Project.table_name}.name")},
:class_name => 'Member',
:foreign_key => 'user_id'
has_many :projects, :through => :memberships
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
@@ -56,8 +58,8 @@ class Principal < ActiveRecord::Base
# Principals that are members of a collection of projects
scope :member_of, lambda {|projects|
projects = [projects] unless projects.is_a?(Array)
if projects.empty?
projects = [projects] if projects.is_a?(Project)
if projects.blank?
where("1=0")
else
ids = projects.map(&:id)

View File

@@ -28,31 +28,35 @@ class Project < ActiveRecord::Base
# Specific overridden Activities
has_many :time_entry_activities
has_many :members, :include => [:principal, :roles], :conditions => "#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
has_many :members,
lambda { joins(:principal, :roles).
references(:principal, :roles).
where("#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}") }
has_many :memberships, :class_name => 'Member'
has_many :member_principals, :class_name => 'Member',
:include => :principal,
:conditions => "#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
has_many :member_principals,
lambda { joins(:principal).
references(:principal).
where("#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}")},
:class_name => 'Member'
has_many :enabled_modules, :dependent => :delete_all
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
has_many :issues, :dependent => :destroy, :include => [:status, :tracker]
has_and_belongs_to_many :trackers, lambda {order("#{Tracker.table_name}.position")}
has_many :issues, :dependent => :destroy
has_many :issue_changes, :through => :issues, :source => :journals
has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
has_many :versions, lambda {order("#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC")}, :dependent => :destroy
has_many :time_entries, :dependent => :destroy
has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all
has_many :documents, :dependent => :destroy
has_many :news, :dependent => :destroy, :include => :author
has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
has_many :boards, :dependent => :destroy, :order => "position ASC"
has_one :repository, :conditions => ["is_default = ?", true]
has_many :news, lambda {includes(:author)}, :dependent => :destroy
has_many :issue_categories, lambda {order("#{IssueCategory.table_name}.name")}, :dependent => :delete_all
has_many :boards, lambda {order("position ASC")}, :dependent => :destroy
has_one :repository, lambda {where(["is_default = ?", true])}
has_many :repositories, :dependent => :destroy
has_many :changesets, :through => :repository
has_one :wiki, :dependent => :destroy
# Custom field for the project issues
has_and_belongs_to_many :issue_custom_fields,
lambda {order("#{CustomField.table_name}.position")},
:class_name => 'IssueCustomField',
:order => "#{CustomField.table_name}.position",
:join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
:association_foreign_key => 'custom_field_id'
@@ -126,9 +130,9 @@ class Project < ActiveRecord::Base
if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
default = Setting.default_projects_tracker_ids
if default.is_a?(Array)
self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.all
self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.to_a
else
self.trackers = Tracker.sorted.all
self.trackers = Tracker.sorted.to_a
end
end
end
@@ -144,7 +148,7 @@ class Project < ActiveRecord::Base
# returns latest created projects
# non public projects will be returned only if user is a member of those
def self.latest(user=nil, count=5)
visible(user).limit(count).order("created_on DESC").all
visible(user).limit(count).order("created_on DESC").to_a
end
# Returns true if the project is visible to +user+ or to the current user.
@@ -212,9 +216,9 @@ class Project < ActiveRecord::Base
end
def override_roles(role)
@override_members ||= memberships.where(:user_id => [GroupAnonymous.instance_id, GroupNonMember.instance_id]).all
member = @override_members.detect {|m| role.anonymous? ^ (m.user_id == GroupNonMember.instance_id)}
member ? member.roles : [role]
group_class = role.anonymous? ? GroupAnonymous : GroupNonMember
member = member_principals.where("#{Principal.table_name}.type = ?", group_class.name).first
member ? member.roles.to_a : [role]
end
def principals
@@ -364,7 +368,7 @@ class Project < ActiveRecord::Base
# by the current user
def allowed_parents
return @allowed_parents if @allowed_parents
@allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).all
@allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).to_a
@allowed_parents = @allowed_parents - self_and_descendants
if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?)
@allowed_parents << nil
@@ -435,11 +439,12 @@ class Project < ActiveRecord::Base
@rolled_up_trackers ||=
Tracker.
joins(:projects).
references(:project).
joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'").
select("DISTINCT #{Tracker.table_name}.*").
where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt).
sorted.
all
to_a
end
# Closes open and locked project versions that are completed
@@ -457,7 +462,8 @@ class Project < ActiveRecord::Base
def rolled_up_versions
@rolled_up_versions ||=
Version.
includes(:project).
joins(:project).
references(:project).
where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> ?", lft, rgt, STATUS_ARCHIVED)
end
@@ -465,13 +471,17 @@ class Project < ActiveRecord::Base
def shared_versions
if new_record?
Version.
includes(:project).
joins(:project).
references(:project).
preload(:project).
where("#{Project.table_name}.status <> ? AND #{Version.table_name}.sharing = 'system'", STATUS_ARCHIVED)
else
@shared_versions ||= begin
r = root? ? self : root
Version.
includes(:project).
joins(:project).
references(:project).
preload(:project).
where("#{Project.table_name}.id = #{id}" +
" OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
" #{Version.table_name}.sharing = 'system'" +
@@ -497,7 +507,7 @@ class Project < ActiveRecord::Base
# Deletes all project's members
def delete_all_members
me, mr = Member.table_name, MemberRole.table_name
connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})")
self.class.connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})")
Member.delete_all(['project_id = ?', id])
end
@@ -728,7 +738,7 @@ class Project < ActiveRecord::Base
project = project.is_a?(Project) ? project : Project.find(project)
to_be_copied = %w(wiki versions issue_categories issues members queries boards)
to_be_copied = to_be_copied & options[:only].to_a unless options[:only].nil?
to_be_copied = to_be_copied & Array.wrap(options[:only]) unless options[:only].nil?
Project.transaction do
if save
@@ -740,6 +750,7 @@ class Project < ActiveRecord::Base
save
end
end
true
end
# Returns a new unsaved Project instance with attributes copied from +project+
@@ -958,11 +969,10 @@ class Project < ActiveRecord::Base
def copy_queries(project)
project.queries.each do |query|
new_query = IssueQuery.new
new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria", "user_id", "type")
new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria")
new_query.sort_criteria = query.sort_criteria if query.sort_criteria
new_query.project = self
new_query.user_id = query.user_id
new_query.role_ids = query.role_ids if query.visibility == IssueQuery::VISIBILITY_ROLES
self.queries << new_query
end
end

View File

@@ -288,7 +288,7 @@ class Query < ActiveRecord::Base
end
def trackers
@trackers ||= project.nil? ? Tracker.sorted.all : project.rolled_up_trackers
@trackers ||= project.nil? ? Tracker.sorted.to_a : project.rolled_up_trackers
end
# Returns a hash of localized labels for all filter operators
@@ -306,7 +306,7 @@ class Query < ActiveRecord::Base
end
def all_projects
@all_projects ||= Project.visible.all
@all_projects ||= Project.visible.to_a
end
def all_projects_values
@@ -655,7 +655,7 @@ class Query < ActiveRecord::Base
sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
end
else
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")"
end
else
# IN an empty set
@@ -663,7 +663,7 @@ class Query < ActiveRecord::Base
end
when "!"
if value.any?
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + "))"
else
# NOT IN an empty set
sql = "1=1"
@@ -705,9 +705,9 @@ class Query < ActiveRecord::Base
end
end
when "o"
sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_false})" if field == "status_id"
sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false})" if field == "status_id"
when "c"
sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_true})" if field == "status_id"
sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_true})" if field == "status_id"
when "><t-"
# between today - n days and today
sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
@@ -769,9 +769,9 @@ class Query < ActiveRecord::Base
date = Date.today
sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year)
when "~"
sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{self.class.connection.quote_string(value.first.to_s.downcase)}%'"
when "!~"
sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{self.class.connection.quote_string(value.first.to_s.downcase)}%'"
else
raise "Unknown query operator #{operator}"
end
@@ -834,7 +834,7 @@ class Query < ActiveRecord::Base
if self.class.default_timezone == :utc
from = from.utc
end
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from)])
s << ("#{table}.#{field} > '%s'" % [self.class.connection.quoted_date(from)])
end
if to
if to.is_a?(Date)
@@ -843,7 +843,7 @@ class Query < ActiveRecord::Base
if self.class.default_timezone == :utc
to = to.utc
end
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to)])
s << ("#{table}.#{field} <= '%s'" % [self.class.connection.quoted_date(to)])
end
s.join(' AND ')
end

View File

@@ -25,7 +25,7 @@ class Repository < ActiveRecord::Base
IDENTIFIER_MAX_LENGTH = 255
belongs_to :project
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
has_many :changesets, lambda{order("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC")}
has_many :filechanges, :class_name => 'Change', :through => :changesets
serialize :extra_info
@@ -45,6 +45,7 @@ class Repository < ActiveRecord::Base
validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
# Checks if the SCM is enabled when creating a repository
validate :repo_create_validation, :on => :create
attr_protected :id
safe_attributes 'identifier',
'login',
@@ -264,7 +265,7 @@ class Repository < ActiveRecord::Base
reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
limit(limit).
preload(:user).
all
to_a
else
filechanges.
where("path = ?", path.with_leading_slash).
@@ -313,7 +314,8 @@ class Repository < ActiveRecord::Base
return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
user = nil
c = changesets.where(:committer => committer).includes(:user).first
c = changesets.where(:committer => committer).
includes(:user).references(:user).first
if c && c.user
user = c.user
elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
@@ -484,10 +486,10 @@ class Repository < ActiveRecord::Base
ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
self.class.connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
self.class.connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
self.class.connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
self.class.connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
clear_extra_info_of_changesets
end

View File

@@ -199,7 +199,7 @@ class Repository::Cvs < Repository
# Need to retrieve existing revision numbers to sort them as integers
sql = "SELECT revision FROM #{Changeset.table_name} "
sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
@current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
@current_revision_number ||= (self.class.connection.select_values(sql).collect(&:to_i).max || 0)
@current_revision_number += 1
end
end

View File

@@ -241,7 +241,7 @@ class Repository::Git < Repository
def latest_changesets(path,rev,limit=10)
revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
return [] if revisions.nil? || revisions.empty?
changesets.where(:scmid => revisions.map {|c| c.scmid}).all
changesets.where(:scmid => revisions.map {|c| c.scmid}).to_a
end
def clear_extra_info_of_changesets

View File

@@ -20,7 +20,7 @@ require 'redmine/scm/adapters/mercurial_adapter'
class Repository::Mercurial < Repository
# sort changesets by revision number
has_many :changesets,
:order => "#{Changeset.table_name}.id DESC",
lambda {order("#{Changeset.table_name}.id DESC")},
:foreign_key => 'repository_id'
attr_protected :root_url
@@ -117,9 +117,10 @@ class Repository::Mercurial < Repository
changesets.
includes(:user).
where(latest_changesets_cond(path, rev, limit)).
references(:user).
limit(limit).
order("#{Changeset.table_name}.id DESC").
all
to_a
end
def is_short_id_in_db?

View File

@@ -42,7 +42,7 @@ class Repository::Subversion < Repository
revisions = scm.revisions(path, rev, nil, :limit => limit)
if revisions
identifiers = revisions.collect(&:identifier).compact
changesets.where(:revision => identifiers).reorder("committed_on DESC").includes(:repository, :user).all
changesets.where(:revision => identifiers).reorder("committed_on DESC").includes(:repository, :user).to_a
else
[]
end

View File

@@ -166,7 +166,7 @@ class Role < ActiveRecord::Base
# Find all the roles that can be given to a project member
def self.find_all_givable
Role.givable.all
Role.givable.to_a
end
# Return the builtin 'non member' role. If the role doesn't exist,

View File

@@ -86,6 +86,7 @@ class Setting < ActiveRecord::Base
validates_numericality_of :value, :only_integer => true, :if => Proc.new { |setting|
(s = @@available_settings[setting.name]) && s['format'] == 'int'
}
attr_protected :id
# Hash used to cache setting values
@cached_settings = {}
@@ -142,6 +143,7 @@ END_SRC
def self.set_from_params(name, params)
params = params.dup
params.delete_if {|v| v.blank? } if params.is_a?(Array)
params.symbolize_keys! if params.is_a?(Hash)
m = "#{name}_from_params"
if respond_to? m

View File

@@ -35,7 +35,7 @@ class TimeEntry < ActiveRecord::Base
acts_as_activity_provider :timestamp => "#{table_name}.created_on",
:author_key => :user_id,
:find_options => {:include => :project}
:scope => preload(:project)
validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
validates_numericality_of :hours, :allow_nil => true, :message => :invalid
@@ -45,13 +45,19 @@ class TimeEntry < ActiveRecord::Base
validate :validate_time_entry
scope :visible, lambda {|*args|
includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args))
joins(:project).
references(:project).
where(Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args))
}
scope :on_issue, lambda {|issue|
includes(:issue).where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
joins(:issue).
references(:issue).
where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
}
scope :on_project, lambda {|project, include_subprojects|
includes(:project).where(project.project_condition(include_subprojects))
joins(:project).
references(:project).
where(project.project_condition(include_subprojects))
}
scope :spent_between, lambda {|from, to|
if from && to

View File

@@ -42,7 +42,7 @@ class TimeEntryQuery < Query
if project
principals += project.principals.sort
unless project.leaf?
subprojects = project.descendants.visible.all
subprojects = project.descendants.visible.to_a
if subprojects.any?
add_available_filter "subproject_id",
:type => :list_subprojects,
@@ -109,7 +109,8 @@ class TimeEntryQuery < Query
where(statement).
order(order_option).
joins(joins_for_order_statement(order_option.join(','))).
includes(:activity)
includes(:activity).
references(:activity)
end
def sql_for_activity_id_field(field, operator, value)

View File

@@ -18,6 +18,7 @@
class Token < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :value
attr_protected :id
before_create :delete_previous_tokens, :generate_new_token

View File

@@ -63,7 +63,7 @@ class Tracker < ActiveRecord::Base
connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{WorkflowTransition.table_name} WHERE tracker_id = #{id} AND type = 'WorkflowTransition'").
flatten.
uniq
@issue_statuses = IssueStatus.where(:id => ids).all.sort
@issue_statuses = IssueStatus.where(:id => ids).to_a.sort
end
def disabled_core_fields
@@ -92,7 +92,7 @@ class Tracker < ActiveRecord::Base
# Returns the fields that are disabled for all the given trackers
def self.disabled_core_fields(trackers)
if trackers.present?
trackers.uniq.map(&:disabled_core_fields).reduce(:&)
trackers.map(&:disabled_core_fields).reduce(:&)
else
[]
end

View File

@@ -79,8 +79,8 @@ class User < Principal
:after_remove => Proc.new {|user, group| group.user_removed(user)}
has_many :changesets, :dependent => :nullify
has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
has_one :rss_token, lambda {where "action='feeds'"}, :class_name => 'Token'
has_one :api_token, lambda {where "action='api'"}, :class_name => 'Token'
belongs_to :auth_source
scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
@@ -105,9 +105,13 @@ class User < Principal
validates_length_of :firstname, :lastname, :maximum => 30
validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
validate :validate_password_length
validate do
if password_confirmation && password != password_confirmation
errors.add(:password, :confirmation)
end
end
before_create :set_mail_notification
before_save :generate_password_if_needed, :update_hashed_password
@@ -151,6 +155,15 @@ class User < Principal
write_attribute(:mail, arg.to_s.strip)
end
def self.find_or_initialize_by_identity_url(url)
user = where(:identity_url => url).first
unless user
user = User.new
user.identity_url = url
end
user
end
def identity_url=(url)
if url.blank?
write_attribute(:identity_url, '')
@@ -496,10 +509,12 @@ class User < Principal
hash = Hash.new([])
members = Member.joins(:project).
group_class = anonymous? ? GroupAnonymous : GroupNonMember
members = Member.joins(:project, :principal).
where("#{Project.table_name}.status <> 9").
where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Member.table_name}.user_id = ?)", self.id, true, Group.builtin_id(self)).
preload(:project, :roles)
where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name).
preload(:project, :roles).
to_a
members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
members.each do |member|
@@ -558,6 +573,8 @@ class User < Principal
# Authorize if user is authorized on every element of the array
context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
end
elsif context
raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
elsif options[:global]
# Admin users are always authorized
return true if admin?
@@ -710,17 +727,17 @@ class User < Principal
return if self.id.nil?
substitute = User.anonymous
Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
JournalDetail.
where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
update_all(['old_value = ?', substitute.id.to_s])
JournalDetail.
where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
update_all(['value = ?', substitute.id.to_s])
update_all(['value = ?', substitute.id.to_s])
Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
# Remove private queries and keep public ones

View File

@@ -33,11 +33,14 @@ class Version < ActiveRecord::Base
validates :effective_date, :date => true
validates_inclusion_of :status, :in => VERSION_STATUSES
validates_inclusion_of :sharing, :in => VERSION_SHARINGS
attr_protected :id
scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
scope :open, lambda { where(:status => 'open') }
scope :visible, lambda {|*args|
includes(:project).where(Project.allowed_to_condition(args.first || User.current, :view_issues))
joins(:project).
references(:project).
where(Project.allowed_to_condition(args.first || User.current, :view_issues))
}
safe_attributes 'name',
@@ -230,11 +233,6 @@ class Version < ActiveRecord::Base
end
end
# Returns true if the version is shared, otherwise false
def shared?
sharing != 'none'
end
private
def load_issue_counts

View File

@@ -22,6 +22,7 @@ class Watcher < ActiveRecord::Base
validates_presence_of :user
validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
validate :validate_user
attr_protected :id
# Returns true if at least one object among objects is watched by user
def self.any_watched?(objects, user)

View File

@@ -18,13 +18,14 @@
class Wiki < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
has_many :pages, :class_name => 'WikiPage', :dependent => :destroy, :order => 'title'
has_many :pages, lambda {order('title')}, :class_name => 'WikiPage', :dependent => :destroy
has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
acts_as_watchable
validates_presence_of :start_page
validates_format_of :start_page, :with => /\A[^,\.\/\?\;\|\:]*\z/
attr_protected :id
safe_attributes 'start_page'

View File

@@ -23,6 +23,7 @@ class WikiContent < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :text
validates_length_of :comments, :maximum => 255, :allow_nil => true
attr_protected :id
acts_as_versioned
@@ -68,13 +69,13 @@ class WikiContent < ActiveRecord::Base
:timestamp => "#{WikiContent.versioned_table_name}.updated_on",
:author_key => "#{WikiContent.versioned_table_name}.author_id",
:permission => :view_wiki_edits,
:find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
"#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
"#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
"#{WikiContent.versioned_table_name}.id",
:joins => "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
"LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
"LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
:scope => select("#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
"#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
"#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
"#{WikiContent.versioned_table_name}.id").
joins("LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
"LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
"LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id")
after_destroy :page_update_after_destroy
@@ -104,7 +105,7 @@ class WikiContent < ActiveRecord::Base
# uncompressed data
data
end
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
str.force_encoding("UTF-8")
str
end
end

View File

@@ -33,7 +33,7 @@ class WikiPage < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
:include => [{:wiki => :project}, :content],
:scope => preload(:wiki => :project).joins(:content, {:wiki => :project}),
:permission => :view_wiki_pages,
:project_key => "#{Wiki.table_name}.project_id"
@@ -43,6 +43,7 @@ class WikiPage < ActiveRecord::Base
validates_format_of :title, :with => /\A[^,\.\/\?\;\|\s]*\z/
validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
validates_associated :content
attr_protected :id
validate :validate_parent_title
before_destroy :remove_redirects
@@ -180,12 +181,10 @@ class WikiPage < ActiveRecord::Base
def save_with_content(content)
ret = nil
transaction do
self.content = content
if new_record?
# Rails automatically saves associated content
ret = save
else
ret = save && (content.text_changed? ? content.save : true)
ret = save
if content.text_changed?
self.content = content
ret = ret && content.changed?
end
raise ActiveRecord::Rollback unless ret
end

View File

@@ -20,4 +20,5 @@ class WikiRedirect < ActiveRecord::Base
validates_presence_of :title, :redirects_to
validates_length_of :title, :redirects_to, :maximum => 255
attr_protected :id
end

View File

@@ -24,6 +24,7 @@ class WorkflowRule < ActiveRecord::Base
belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
validates_presence_of :role, :tracker, :old_status
attr_protected :id
# Copies workflows from source to targets
def self.copy(source_tracker, source_role, target_trackers, target_roles)
@@ -34,7 +35,7 @@ class WorkflowRule < ActiveRecord::Base
target_trackers = [target_trackers].flatten.compact
target_roles = [target_roles].flatten.compact
target_trackers = Tracker.sorted.all if target_trackers.empty?
target_trackers = Tracker.sorted.to_a if target_trackers.empty?
target_roles = Role.all if target_roles.empty?
target_trackers.each do |target_tracker|

View File

@@ -40,7 +40,7 @@ class WorkflowTransition < WorkflowRule
roles = Array.wrap roles
transaction do
records = WorkflowTransition.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).all
records = WorkflowTransition.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).to_a
transitions.each do |old_status_id, transitions_by_new_status|
transitions_by_new_status.each do |new_status_id, transition_by_rule|

View File

@@ -8,8 +8,8 @@
<% end -%>
</ul>
<div class="tabs-buttons" style="display:none;">
<button class="tab-left" type="button" onclick="moveTabLeft(this);"></button>
<button class="tab-right" type="button" onclick="moveTabRight(this);"></button>
<button class="tab-left" onclick="moveTabLeft(this); return false;"></button>
<button class="tab-right" onclick="moveTabRight(this); return false;"></button>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<% roles = Role.find_all_givable %>
<% projects = Project.active.all %>
<% projects = Project.active.to_a %>
<div class="splitcontentleft">
<% if @group.memberships.any? %>

View File

@@ -1,6 +1,6 @@
<%= error_messages_for 'member' %>
<% roles = Role.find_all_givable
members = @project.member_principals.includes(:member_roles, :roles, :principal).all.sort %>
members = @project.member_principals.includes(:member_roles, :roles, :principal).to_a.sort %>
<div class="splitcontentleft">
<% if members.any? %>

View File

@@ -8,7 +8,7 @@
<% elsif params[:issue_id] %>
<%= hidden_field_tag 'issue_id', params[:issue_id] %>
<% else %>
<p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).all, :selected => @time_entry.project, :include_blank => true) %></p>
<p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).to_a, :selected => @time_entry.project, :include_blank => true) %></p>
<% end %>
<% end %>
<p>

View File

@@ -1,5 +1,5 @@
<% roles = Role.find_all_givable %>
<% projects = Project.active.all %>
<% projects = Project.active.to_a %>
<div class="splitcontentleft">
<% if @user.memberships.any? %>

View File

@@ -23,7 +23,7 @@
<%= fp.select :parent_id,
content_tag('option', '', :value => '') +
wiki_page_options_for_select(
@wiki.pages.includes(:parent).all -
@wiki.pages.includes(:parent).to_a -
@page.self_and_descendants, @page.parent) %>
</p>
<% end %>

Some files were not shown because too many files have changed in this diff Show More