Merge branch 'feature/rails3' into feature/wysiwyg_editor_xml

pull/1186/head
Philipp Tessenow 12 years ago
commit 62a32a737c
  1. 28
      Gemfile
  2. 22
      Rakefile
  3. 2
      app/controllers/admin_controller.rb
  4. 14
      app/controllers/application_controller.rb
  5. 4
      app/controllers/auth_sources_controller.rb
  6. 2
      app/controllers/issue_moves_controller.rb
  7. 18
      app/controllers/issues_controller.rb
  8. 2
      app/controllers/ldap_auth_sources_controller.rb
  9. 2
      app/controllers/members_controller.rb
  10. 5
      app/controllers/projects_controller.rb
  11. 2
      app/controllers/repositories_controller.rb
  12. 3
      app/controllers/settings_controller.rb
  13. 2
      app/controllers/sys_controller.rb
  14. 37
      app/controllers/users_controller.rb
  15. 7
      app/helpers/accessibility_helper.rb
  16. 180
      app/helpers/application_helper.rb
  17. 51
      app/helpers/breadcrumb_helper.rb
  18. 6
      app/helpers/calendars_helper.rb
  19. 26
      app/helpers/issues_helper.rb
  20. 17
      app/helpers/journals_helper.rb
  21. 4
      app/helpers/projects_helper.rb
  22. 14
      app/helpers/repositories_helper.rb
  23. 12
      app/helpers/search_helper.rb
  24. 12
      app/helpers/settings_helper.rb
  25. 2
      app/helpers/timelog_helper.rb
  26. 2
      app/helpers/watchers_helper.rb
  27. 4
      app/helpers/wiki_helper.rb
  28. 4
      app/models/changeset.rb
  29. 12
      app/models/custom_field.rb
  30. 11
      app/models/document.rb
  31. 14
      app/models/enumeration.rb
  32. 22
      app/models/group.rb
  33. 31
      app/models/issue.rb
  34. 7
      app/models/issue_relation.rb
  35. 4
      app/models/journal.rb
  36. 11
      app/models/ldap_auth_source.rb
  37. 11
      app/models/mailer.rb
  38. 19
      app/models/member_role.rb
  39. 4
      app/models/message.rb
  40. 2
      app/models/news.rb
  41. 8
      app/models/principal.rb
  42. 10
      app/models/project.rb
  43. 18
      app/models/query.rb
  44. 2
      app/models/query/statement_invalid.rb
  45. 7
      app/models/repository.rb
  46. 2
      app/models/repository/bazaar.rb
  47. 2
      app/models/repository/cvs.rb
  48. 2
      app/models/repository/darcs.rb
  49. 2
      app/models/repository/filesystem.rb
  50. 2
      app/models/repository/git.rb
  51. 2
      app/models/repository/mercurial.rb
  52. 4
      app/models/role.rb
  53. 2
      app/models/time_entry.rb
  54. 20
      app/models/user.rb
  55. 6
      app/models/version.rb
  56. 3
      app/models/wiki_content.rb
  57. 6
      app/models/wiki_page.rb
  58. 0
      app/views/account/_login.html.erb
  59. 0
      app/views/account/login.html.erb
  60. 0
      app/views/account/lost_password.html.erb
  61. 0
      app/views/account/password_recovery.html.erb
  62. 0
      app/views/account/register.html.erb
  63. 0
      app/views/admin/_menu.html.erb
  64. 0
      app/views/admin/_no_data.html.erb
  65. 0
      app/views/admin/info.html.erb
  66. 0
      app/views/admin/plugins.html.erb
  67. 2
      app/views/admin/projects.html.erb
  68. 0
      app/views/attachments/_form.html.erb
  69. 0
      app/views/attachments/_links.html.erb
  70. 0
      app/views/attachments/diff.html.erb
  71. 0
      app/views/attachments/file.html.erb
  72. 7
      app/views/auth_sources/edit.html.erb
  73. 7
      app/views/auth_sources/edit.rhtml
  74. 22
      app/views/auth_sources/index.html.erb
  75. 6
      app/views/auth_sources/new.html.erb
  76. 6
      app/views/auth_sources/new.rhtml
  77. 0
      app/views/boards/_form.html.erb
  78. 0
      app/views/boards/edit.html.erb
  79. 0
      app/views/boards/index.html.erb
  80. 0
      app/views/boards/new.html.erb
  81. 0
      app/views/boards/show.html.erb
  82. 4
      app/views/common/_calendar.html.erb
  83. 0
      app/views/common/_diff.html.erb
  84. 0
      app/views/common/_file.html.erb
  85. 0
      app/views/common/_preview.html.erb
  86. 2
      app/views/common/_tabs.html.erb
  87. 0
      app/views/common/feed.atom.builder
  88. 0
      app/views/custom_fields/_form.html.erb
  89. 0
      app/views/custom_fields/_index.html.erb
  90. 0
      app/views/custom_fields/edit.html.erb
  91. 0
      app/views/custom_fields/index.html.erb
  92. 8
      app/views/custom_fields/new.html.erb
  93. 0
      app/views/documents/_document.html.erb
  94. 0
      app/views/documents/_form.html.erb
  95. 0
      app/views/documents/edit.html.erb
  96. 0
      app/views/documents/index.html.erb
  97. 0
      app/views/documents/new.html.erb
  98. 0
      app/views/documents/show.html.erb
  99. 0
      app/views/enumerations/_form.html.erb
  100. 0
      app/views/enumerations/destroy.html.erb
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,30 +1,38 @@
source :rubygems
gem "rails", "2.3.14"
gem "rails", "~> 3.0.15"
gem "coderay", "~> 0.9.7"
gem "i18n", "~> 0.4.2"
gem "i18n", "~> 0.5.0"
gem "rubytree", "~> 0.5.2", :require => 'tree'
gem "rdoc", ">= 2.4.2"
# Needed only on RUBY_VERSION = 1.8, ruby 1.9+ compatible interpreters should bring their csv
gem "fastercsv", "~> 1.5.0", :platforms => [:ruby_18, :jruby, :mingw_18]
gem 'delayed_job', "~>2.0.4"
gem 'globalize3', :require => 'globalize'
gem "delayed_job_active_record" # that's how delayed job's readme recommends it
# TODO: check that it doesn't break the functionality of acts_as_journalized
gem 'safe_attributes' # allows active record to have a #changes column
gem 'awesome_nested_set'
group :test do
gem 'shoulda', '~> 2.10.3'
gem 'edavis10-object_daddy', :require => 'object_daddy'
gem 'shoulda', '~> 3.1.1'
gem 'object-daddy', :git => 'git://github.com/awebneck/object_daddy.git'
gem 'mocha'
gem "launchy", "~> 2.1.0"
platforms :mri_18, :mingw_18 do gem 'ruby-debug' end
platforms :mri_19, :mingw_19 do gem 'ruby-debug19', :require => 'ruby-debug' end
platforms :mri_19, :mingw_19 do gem 'debugger' end
end
group :openid do
gem "ruby-openid", '~> 2.1.4', :require => 'openid'
end
group :globalize do
gem 'globalize2', :require => 'globalize'
group :development do
gem 'rails-footnotes', '>= 3.7.5.rc4'
gem 'bullet'
end
group :rmagick do
@ -77,6 +85,10 @@ platforms :mri_19, :mingw_19 do
group :sqlite do
gem "sqlite3"
end
group :mysql2 do
gem 'mysql2', '~> 0.2.7'
end
end
platforms :jruby do

@ -1,23 +1,7 @@
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
require File.expand_path('../config/application', __FILE__)
require 'rake'
require 'rake/testtask'
begin
# loading task with rake >= 0.9
require 'rdoc/task'
rescue LoadError
# loading task with rake < 0.9
require 'rake/rdoctask'
end
require 'tasks/rails'
begin
require 'delayed/tasks'
rescue LoadError
STDERR.puts "Run `rake gems:install` to install delayed_job"
end
OpenProject::Application.load_tasks

@ -91,7 +91,7 @@ class AdminController < ApplicationController
@checklist = [
[:text_default_administrator_account_changed, User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
[:text_plugin_assets_writable, File.writable?(Redmine::Plugin.public_directory)],
[:text_rmagick_available, Object.const_defined?(:Magick)]
]
end

@ -23,7 +23,6 @@ class ApplicationController < ActionController::Base
include Redmine::I18n
layout 'base'
exempt_from_layout 'builder', 'rsb'
protect_from_forgery
def handle_unverified_request
@ -62,7 +61,6 @@ class ApplicationController < ActionController::Base
end
before_filter :user_setup, :check_if_login_required, :reset_i18n_fallbacks, :set_localization
filter_parameter_logging :password
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
@ -157,9 +155,9 @@ class ApplicationController < ActionController::Base
respond_to do |format|
format.html { redirect_to :controller => "account", :action => "login", :back_url => url }
format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="ChiliProject API"' }
format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="ChiliProject API"' }
format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="ChiliProject API"' }
format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="OpenProject API"' }
format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="OpenProject API"' }
format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="OpenProject API"' }
end
return false
end
@ -379,7 +377,7 @@ class ApplicationController < ActionController::Base
@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
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
render :template => "common/feed", :layout => false, :content_type => 'application/atom+xml'
end
def self.accept_key_auth(*actions)
@ -464,8 +462,8 @@ class ApplicationController < ActionController::Base
def api_key_from_request
if params[:key].present?
params[:key]
elsif request.headers["X-ChiliProject-API-Key"].present?
request.headers["X-ChiliProject-API-Key"]
elsif request.headers["X-OpenProject-API-Key"].present?
request.headers["X-OpenProject-API-Key"]
end
end

@ -17,10 +17,6 @@ class AuthSourcesController < ApplicationController
before_filter :require_admin
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy, :create, :update ],
:redirect_to => { :template => :index }
def index
@auth_source_pages, @auth_sources = paginate auth_source_class.name.tableize, :per_page => 10
render "auth_sources/index"

@ -64,7 +64,7 @@ class IssueMovesController < ApplicationController
@issues.sort!
@copy = params[:copy_options] && params[:copy_options][:copy]
@allowed_projects = Issue.allowed_target_projects_on_move
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id].to_s} if params[:new_project_id]
@target_project ||= @project
@trackers = @target_project.trackers
@available_statuses = Workflow.available_statuses(@project)

@ -14,7 +14,7 @@
class IssuesController < ApplicationController
EXPORT_FORMATS = %w[atom rss api xls csv pdf]
menu_item :new_issue, :only => [:new, :create]
menu_item :view_all_issues, :only => [:all]
default_search_scope :issues
@ -24,8 +24,8 @@ class IssuesController < ApplicationController
before_filter :check_project_uniqueness, :only => [:move, :perform_move]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index, :all]
before_filter :protect_from_unauthorized_export, :only => [:index, :all]
before_filter :find_optional_project, :only => [:index, :all]
before_filter :protect_from_unauthorized_export, :only => [:index, :all]
before_filter :check_for_default_issue_status, :only => [:new, :create]
before_filter :build_new_issue_from_params, :only => [:new, :create]
before_filter :retrieve_query, :only => [:index, :all]
@ -80,7 +80,7 @@ class IssuesController < ApplicationController
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.html { render :template => 'issues/index', :layout => !request.xhr? }
format.api
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
@ -88,7 +88,7 @@ class IssuesController < ApplicationController
end
else
# Send html if the query is not valid
render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
render(:template => 'issues/index', :layout => !request.xhr?)
end
rescue ActiveRecord::RecordNotFound
render_404
@ -111,7 +111,7 @@ class IssuesController < ApplicationController
@priorities = IssuePriority.all
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.html { render :template => 'issues/show' }
format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
@ -323,12 +323,12 @@ private
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
attributes
end
def protect_from_unauthorized_export
return true unless EXPORT_FORMATS.include? params[:format]
find_optional_project
return true if User.current.allowed_to? :export_issues, @project
find_optional_project if @project.nil?
return true if User.current.allowed_to? :export_issues, @project, :global => @project.nil?
# otherwise deny access
params[:format] = 'html'

@ -18,6 +18,6 @@ class LdapAuthSourcesController < AuthSourcesController
protected
def auth_source_class
AuthSourceLdap
LdapAuthSource
end
end

@ -28,7 +28,7 @@ class MembersController < ApplicationController
respond_to do |format|
if members.present? && members.all? {|m| m.valid? }
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
format.html { redirect_to settings_project_path(@project, :tab => 'members') }
format.js {
render(:update) {|page|

@ -266,7 +266,10 @@ private
def add_current_user_to_project_if_not_admin(project)
unless User.current.admin?
r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
m = Member.new(:user => User.current, :roles => [r])
m = Member.new do |member|
member.user = User.current
member.role_ids = [r].map(&:id) # member.roles = [r] fails, this works
end
project.members << m
end
end

@ -236,7 +236,7 @@ class RepositoriesController < ApplicationController
(render_404; return false) unless @repository
@path = params[:path].join('/') unless params[:path].nil?
@path ||= ''
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
@rev_to = params[:rev_to]
unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)

@ -38,8 +38,7 @@ class SettingsController < ApplicationController
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@deliveries = ActionMailer::Base.perform_deliveries
@guessed_host_and_path = request.host_with_port.dup
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
@guessed_host_and_path = request.host_with_port.dup + Redmine::Utils.relative_url_root.to_s
Redmine::Themes.rescan
end

@ -38,7 +38,7 @@ class SysController < ActionController::Base
def fetch_changesets
projects = []
if params[:id]
projects << Project.active.has_module(:repository).find(params[:id])
projects << Project.active.has_module(:repository).find_by_identifier!(params[:id])
else
projects = Project.active.has_module(:repository).find(:all, :include => :repository)
end

@ -27,7 +27,7 @@ class UsersController < ApplicationController
before_filter :authorize_for_user, :only => [:destroy]
before_filter :check_if_deletion_allowed, :only => [:deletion_info,
:destroy]
accept_key_auth :index, :show, :create, :update
accept_key_auth :index, :show, :create, :update, :destroy
include SortHelper
include CustomFieldsHelper
@ -119,8 +119,8 @@ class UsersController < ApplicationController
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
new_user_path :
edit_user_path(@user)
)
}
format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
@ -216,15 +216,26 @@ class UsersController < ApplicationController
@user.status = User::STATUS_LOCKED
@user.save
@user.delay.destroy
# TODO: use Delayed::Worker.delay_jobs = false in test environment as soon as
# delayed job allows for it
RAILS_ENV == 'test' ?
@user.destroy :
@user.delay.destroy
flash[:notice] = l('account.deleted')
if @user == User.current
logged_user = nil
redirect_to signin_path
else
redirect_to users_path
respond_to do |format|
format.html do
if @user == User.current
logged_user = nil
redirect_to signin_path
else
redirect_to users_path
end
end
format.api do
head :ok
end
end
end
@ -261,7 +272,13 @@ class UsersController < ApplicationController
User.current == User.anonymous) &&
!User.current.admin?
render_403
respond_to do |format|
format.html { render_403 }
format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="OpenProject API"' }
format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="OpenProject API"' }
format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="OpenProject API"' }
end
false
end
end

@ -0,0 +1,7 @@
module AccessibilityHelper
def you_are_here_info(condition = true)
condition ?
"<span class = 'hidden-for-sighted'>#{l(:description_current_position)}</span>".html_safe :
""
end
end

@ -64,9 +64,9 @@ module ApplicationHelper
# Displays a link to user's account page if active or registered
def link_to_user(user, options={})
if user.is_a?(User)
name = h(user.name(options.delete(:format)))
name = user.name(options.delete(:format))
if user.active? || user.registered?
link_to(name, {:controller => 'users', :action => 'show', :id => user}, options)
link_to(name, user, options)
else
name
end
@ -176,18 +176,19 @@ module ApplicationHelper
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
#
def link_to_project(project, options={}, html_options = nil, show_icon = false)
link = ''
if show_icon && User.current.member_of?(project)
icon = image_tag('fav.png', :alt => l(:description_my_project), :title => l(:description_my_project))
else
icon = ""
link << image_tag('fav.png', :alt => l(:description_my_project), :title => l(:description_my_project))
end
if project.active?
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
icon + link_to(h(project), url, html_options)
link << link_to(project.name, project_path(project, options), html_options)
else
icon + h(project)
link << project.name
end
link.html_safe
end
def toggle_link(name, id, options={})
@ -242,22 +243,22 @@ module ApplicationHelper
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
content << link_to(page.pretty_title, project_wiki_path(page.project, page),
:title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
content << "</li>\n"
end
content << "</ul>\n"
end
content
content.html_safe
end
# Renders flash messages
def render_flash_messages
if User.current.impaired?
flash.map { |k,v| content_tag('div', content_tag('a', v, :href => 'javascript:;'), :class => "flash #{k}") }.join
flash.map { |k,v| content_tag('div', content_tag('a', v, :href => 'javascript:;'), :class => "flash #{k}") }.join.html_safe
else
flash.map { |k,v| content_tag('div', v, :class => "flash #{k}") }.join
flash.map { |k,v| content_tag('div', v, :class => "flash #{k}") }.join.html_safe
end
end
@ -281,9 +282,9 @@ module ApplicationHelper
tag_options[:selected] = nil
end
tag_options.merge!(yield(project)) if block_given?
s << content_tag('option', name_prefix + h(project), tag_options)
s << content_tag('option', name_prefix + project.name, tag_options)
end
s
s.html_safe
end
# Yields the given block for each project with its level in the tree
@ -314,15 +315,32 @@ module ApplicationHelper
end
s << ("</li></ul>\n" * ancestors.size)
end
s
s.html_safe
end
def principals_check_box_tags(name, principals)
s = ''
principals.sort.each do |principal|
s << "<label class='#{user_status_class principal}' title='#{user_status_i18n principal}'>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
end
s
labeled_check_box_tags(name, principals,
{ :title => :user_status_i18n,
:class => :user_status_class })
end
def labeled_check_box_tags(name, collection, options = {})
collection.sort.collect do |object|
id = name.gsub(/[\[\]]+/,"_") + object.id.to_s
object_options = options.inject({}) do |h, (k, v)|
h[k] = v.is_a?(Symbol) ?
send(v, object) :
v
h
end
content_tag :div do
check_box_tag(name, object.id, false, :id => id) +
label_tag(id, object, object_options)
end
end.join().html_safe
end
# Truncates and returns the string as a single line
@ -341,11 +359,11 @@ module ApplicationHelper
end
def html_hours(text)
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
end
def authoring(created, author, options={})
l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
end
def time_tag(time)
@ -374,7 +392,7 @@ module ApplicationHelper
html = ''
if paginator.current.previous
html << link_to_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
html << link_to_content_update(l(:label_previous), url_param.merge(page_param => paginator.current.previous), :class => 'navigate-left') + ' '
end
html << (pagination_links_each(paginator, options) do |n|
@ -382,7 +400,7 @@ module ApplicationHelper
end || '')
if paginator.current.next
html << ' ' + link_to_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
html << ' ' + link_to_content_update((l(:label_next)), url_param.merge(page_param => paginator.current.next), :class => 'navigate-right')
end
unless count.nil?
@ -392,7 +410,7 @@ module ApplicationHelper
end
end
html
html.html_safe
end
def per_page_links(selected=nil)
@ -412,43 +430,15 @@ module ApplicationHelper
)
end
def breadcrumb(*args)
elements = args.flatten
elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
end
def breadcrumb_list(*args)
elements = args.flatten
cutme_elements = []
breadcrumb_elements = [content_tag(:li, elements.shift.to_s, :class => 'first-breadcrumb-element', :style => 'list-style-image:none;')]
breadcrumb_elements += elements.collect do |element|
content_tag(:li, element.to_s) if element
end
content_tag(:ul, breadcrumb_elements, :class => 'breadcrumb')
end
def other_formats_links(&block)
concat('<p class="other-formats">' + l(:label_export_to))
yield Redmine::Views::OtherFormatsBuilder.new(self)
concat('</p>')
end
def link_to_project_ancestors(project)
if project && !project.new_record?
ancestors = (project.root? ? [] : project.ancestors.visible)
ancestors << project
ancestors.collect do |p|
if p == project
link_to_project(p, {:jump => current_menu_item}, {:title => p, :class => 'breadcrumb-project-title nocut'})
else
link_to_project(p, {:jump => current_menu_item}, {:title => p})
end
end
content_tag 'p', :class => 'other-formats' do
formats = capture(Redmine::Views::OtherFormatsBuilder.new(self), &block)
(l(:label_export_to) + formats).html_safe
end
end
# this method seems to not be used any more
def page_header_title
if @page_header_title.present?
h(@page_header_title)
@ -472,16 +462,19 @@ module ApplicationHelper
end
def html_title(*args)
title = []
if args.empty?
title = []
title << h(@project.name) if @project
title += @html_title if @html_title
title << h(Setting.app_title)
title.select {|t| !t.blank? }.join(' - ')
else
@html_title ||= []
@html_title += args
title += @html_title
end
title.select {|t| !t.blank? }.join(' - ').html_safe
end
# Returns the theme, controller name, and action as css classes for the
@ -542,7 +535,7 @@ module ApplicationHelper
end
end
text
text.html_safe
end
def parse_non_pre_blocks(text)
@ -867,7 +860,8 @@ module ApplicationHelper
text.to_s.
gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
html_safe
end
def lang_options_for_select(blank=true)
@ -924,13 +918,20 @@ module ApplicationHelper
pcts << (100 - pcts[1] - pcts[0])
width = options[:width] || '100px;'
legend = options[:legend] || ''
content_tag('table',
content_tag('tr',
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
bar = content_tag 'table', { :class => 'progress', :style => "width: #{width};" } do
row = content_tag 'tr' do
((pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
), :class => 'progress', :style => "width: #{width};") +
content_tag('p', legend + " " + l(:total_progress), :class => 'pourcent')
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')).html_safe
end
end
number = content_tag 'p', :class => 'pourcent' do
legend + " " + l(:total_progress)
end
bar + number
end
def checked_image(checked=true)
@ -1002,20 +1003,13 @@ module ApplicationHelper
end
def content_for(name, content = nil, &block)
super(name, content, &block)
# only care for non whitespace contents;
# rails 2.3 specific implementation
if instance_variable_get("@content_for_#{name}").match /\S+/
@has_content ||= {}
@has_content[name] = true
if block_given?
content = capture(&block)
# only care for non whitespace contents;
super(name, content) if content.to_s.match /\S+/
else
super
end
nil
end
def has_content?(name)
(@has_content && @has_content[name]) || false
end
# Returns the avatar image tag for the given +user+ if avatars are enabled
@ -1039,14 +1033,14 @@ module ApplicationHelper
def javascript_heads
tags = javascript_include_tag(:defaults)
unless User.current.pref.warn_on_leaving_unsaved == '0'
tags << "\n" + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
tags.safe_concat "\n" + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
end
tags << "\n" + javascript_include_tag("accessibility.js") if User.current.impaired? and accessibility_js_enabled?
tags.safe_concat("\n" + javascript_include_tag("accessibility.js")) if User.current.impaired? and accessibility_js_enabled?
tags
end
def favicon
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
end
# Add a HTML meta tag to control robots (web spiders)
@ -1054,7 +1048,7 @@ module ApplicationHelper
# @param [optional, String] content the content of the ROBOTS tag.
# defaults to no index, follow, and no archive
def robot_exclusion_tag(content="NOINDEX,FOLLOW,NOARCHIVE")
"<meta name='ROBOTS' content='#{h(content)}' />"
"<meta name='ROBOTS' content='#{h(content)}' />".html_safe
end
# Returns true if arg is expected in the API response
@ -1067,10 +1061,10 @@ module ApplicationHelper
@included_in_api_response.include?(arg.to_s)
end
# Returns options or nil if nometa param or X-ChiliProject-Nometa header
# Returns options or nil if nometa param or X-OpenProject-Nometa header
# was set in the request
def api_meta(options)
if params[:nometa].present? || request.headers['X-ChiliProject-Nometa']
if params[:nometa].present? || request.headers['X-OpenProject-Nometa']
# compatibility mode for activeresource clients that raise
# an error when unserializing an array with attributes
nil
@ -1138,17 +1132,7 @@ module ApplicationHelper
end
def password_complexity_requirements
"<em>" + l(:text_caracters_minimum, :count => Setting.password_min_length) + "</em>"
raw "<em>" + l(:text_caracters_minimum, :count => Setting.password_min_length) + "</em>"
end
def breadcrumb_paths(*args)
if args.nil?
nil
elsif args.empty?
@breadcrumb_paths ||= [default_breadcrumb]
else
@breadcrumb_paths ||= []
@breadcrumb_paths += args
end
end
end

@ -0,0 +1,51 @@
module BreadcrumbHelper
def full_breadcrumb
breadcrumb_list(link_to(l(:label_home), home_path),
link_to_project_ancestors(@project),
*breadcrumb_paths)
end
def breadcrumb(*args)
elements = args.flatten
elements.any? ? content_tag('p', (args.join(' &#187; ') + ' &#187; ').html_safe, :class => 'breadcrumb') : nil
end
def breadcrumb_list(*args)
elements = args.flatten
cutme_elements = []
breadcrumb_elements = [content_tag(:li, elements.shift.to_s, :class => 'first-breadcrumb-element', :style => 'list-style-image:none;')]
breadcrumb_elements += elements.collect do |element|
content_tag(:li, h(element.to_s)) if element
end
content_tag(:ul, breadcrumb_elements.join.html_safe, :class => 'breadcrumb')
end
def breadcrumb_paths(*args)
if args.nil?
nil
elsif args.empty?
@breadcrumb_paths ||= [default_breadcrumb]
else
@breadcrumb_paths ||= []
@breadcrumb_paths += args
end
end
private
def link_to_project_ancestors(project)
if project && !project.new_record?
ancestors = (project.root? ? [] : project.ancestors.visible)
ancestors << project
ancestors.collect do |p|
if p == project
link_to_project(p, {:jump => current_menu_item}, {:title => p, :class => 'breadcrumb-project-title nocut'}).html_safe
else
link_to_project(p, {:jump => current_menu_item}, {:title => p}).html_safe
end
end
end
end
end

@ -26,7 +26,7 @@ module CalendarsHelper
"#{month_name(target_month)}"
end
link_to_month(('&#171; ' + h(name)), target_year, target_month, options)
link_to_month(name, target_year, target_month, options.merge(:class => 'navigate-left'))
end
def link_to_next_month(year, month, options={})
@ -42,10 +42,10 @@ module CalendarsHelper
"#{month_name(target_month)}"
end
link_to_month((h(name) + ' &#187;'), target_year, target_month, options)
link_to_month(name, target_year, target_month, options.merge(:class => 'navigate-right'))
end
def link_to_month(link_name, year, month, options={})
link_to_content_update(link_name, params.merge(:year => year, :month => month))
link_to_content_update(link_name, params.merge(:year => year, :month => month), options)
end
end

@ -43,13 +43,13 @@ module IssuesHelper
@cached_label_priority ||= l(:field_priority)
@cached_label_project ||= l(:field_project)
link_to_issue(issue) + "<br /><br />" +
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
"<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />" +
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
"<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />" +
"<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}"
(link_to_issue(issue).html_safe + "<br /><br />".html_safe +
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
"<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
"<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
"<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe).html_safe
end
# TODO: deprecate and/or remove
@ -119,19 +119,19 @@ module IssuesHelper
# links to #index on issues/show
url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
content_tag('h3', h(title)) +
content_tag('h3', title) +
queries.collect {|query|
link_to(h(query.name), url_params.merge(:query_id => query))
}.join('<br />')
link_to(query.name, url_params.merge(:query_id => query))
}.join('<br />').html_safe
end
def render_sidebar_queries
out = ''
queries = sidebar_queries.select {|q| !q.is_public?}
queries = sidebar_queries.reject(&:is_public?)
out << query_links(l(:label_my_queries), queries) if queries.any?
queries = sidebar_queries.select {|q| q.is_public?}
queries = sidebar_queries.select(&:is_public?)
out << query_links(l(:label_query_plural), queries) if queries.any?
out
out.html_safe
end
# Find the name of an associated record stored in the field attribute

@ -47,17 +47,18 @@ module JournalsHelper
if journal.details.any?
details = content_tag "ul", :class => "details journal-attributes" do
journal.details.collect do |detail|
if d = journal.render_detail(detail)
if d = journal.render_detail(detail).html_safe
content_tag("li", d)
end
end.compact.join(' ')
end.compact.join(' ').html_safe
end
end
notes = <<-HTML
#{render_notes(model, journal, options) unless journal.notes.blank?}
HTML
content_tag("div", "#{header}#{details}#{notes}", :id => "change-#{journal.id}", :class => "journal")
notes = journal.notes.blank? ?
'' :
render_notes(model, journal, options)
content_tag("div", "#{header}#{details}#{notes}".html_safe, :id => "change-#{journal.id}", :class => "journal")
end
def render_notes(model, journal, options={})
@ -87,13 +88,13 @@ module JournalsHelper
end
content = ''
content << content_tag('div', links.join(' '), :class => 'contextual') unless links.empty?
content << content_tag('div', links.join(' '),{ :class => 'contextual' }, false) unless links.empty?
content << textilizable(journal, :notes)
css_classes = "wiki"
css_classes << " editable" if editable
content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => css_classes)
content_tag('div', content, { :id => "journal-#{journal.id}-notes", :class => css_classes }, false)
end
def link_to_in_place_notes_editor(text, field_id, url, options={})

@ -44,7 +44,7 @@ module ProjectsHelper
options = ''
options << "<option value=''></option>" if project.allowed_parents.include?(nil)
options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
content_tag('select', options, :name => 'project[parent_id]', :id => 'project_parent_id')
content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
end
# Renders a tree of projects as a nested set of unordered lists
@ -78,7 +78,7 @@ module ProjectsHelper
s << ("</li></ul>\n" * ancestors.size)
@project = original_project
end
s
s.html_safe
end
# Returns a set of options for a select field, grouped by project.

@ -33,9 +33,9 @@ module RepositoriesHelper
unless properties.nil? || properties.empty?
content = ''
properties.keys.sort.each do |property|
content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>")
content << content_tag('li', raw("<b>#{h property}</b>: <span>#{h properties[property]}</span>"))
end
content_tag('ul', content, :class => 'properties')
content_tag('ul', content.html_safe, :class => 'properties')
end
end
@ -99,18 +99,18 @@ module RepositoriesHelper
:id => @project,
:path => path_param,
:rev => @changeset.identifier) unless c.action == 'D'
text << " - #{h(c.revision)}" unless c.revision.blank?
text << ' (' + link_to(l(:label_diff), :controller => 'repositories',
text << raw(" - #{h(c.revision)}") unless c.revision.blank?
text << raw(' (' + link_to(l(:label_diff), :controller => 'repositories',
:action => 'diff',
:id => @project,
:path => path_param,
:rev => @changeset.identifier) + ') ' if c.action == 'M'
text << ' ' + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank?
:rev => @changeset.identifier) + ') ') if c.action == 'M'
text << raw(' ' + content_tag('span', h(c.from_path), :class => 'copied-from')) unless c.from_path.blank?
output << "<li class='#{style}'>#{text}</li>"
end
end
output << '</ul>'
output
output.html_safe
end
def to_utf8_for_repositories(str)

@ -32,7 +32,7 @@ module SearchHelper
result << content_tag('span', h(words), :class => "highlight token-#{t}")
end
end
result
result.html_safe
end
def type_label(t)
@ -57,16 +57,16 @@ module SearchHelper
text = "#{type_label(t)} (#{c})"
links << link_to(h(text), :q => params[:q], :titles_only => params[:title_only], :all_words => params[:all_words], :scope => params[:scope], t => 1)
end
('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>').html_safe unless links.empty?
end
def link_to_previous_search_page(pagination_previous_date)
link_to_content_update('&#171; ' + l(:label_previous),
params.merge(:previous => 1, :offset => pagination_previous_date.strftime("%Y%m%d%H%M%S")))
link_to_content_update(l(:label_previous),
params.merge(:previous => 1, :offset => pagination_previous_date.strftime("%Y%m%d%H%M%S")), :class => 'navigate-left')
end
def link_to_next_search_page(pagination_next_date)
link_to_content_update(l(:label_next) + ' &#187;',
params.merge(:previous => nil, :offset => pagination_next_date.strftime("%Y%m%d%H%M%S")))
link_to_content_update(l(:label_next),
params.merge(:previous => nil, :offset => pagination_next_date.strftime("%Y%m%d%H%M%S")), :class => 'navigate-right')
end
end

@ -30,8 +30,11 @@ module SettingsHelper
if blank_text = options.delete(:blank)
choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices
end
setting_label(setting, options) +
select_tag("settings[#{setting}]", options_for_select(choices, Setting.send(setting).to_s), options)
ret = select_tag("settings[#{setting}]", options_for_select(choices, Setting.send(setting).to_s), options)
ret = setting_label(setting).safe_concat(ret) unless options[:label] == false
ret
end
def setting_multiselect(setting, choices, options={})
@ -42,11 +45,12 @@ module SettingsHelper
hidden_field_tag("settings[#{setting}][]", '') +
choices.collect do |choice|
text, value = (choice.is_a?(Array) ? choice : [choice, choice])
content_tag('label',
check_box_tag("settings[#{setting}][]", value, Setting.send(setting).include?(value)) + text.to_s,
:class => 'block'
)
end.join
end.join.html_safe
end
def setting_text_field(setting, options={})
@ -67,7 +71,7 @@ module SettingsHelper
def setting_label(setting, options={})
label = options.delete(:label)
label != false ? content_tag("label", l(label || "setting_#{setting}"), :for => "settings_#{setting}" ) : ''
label != false ? content_tag("label", l(label || "setting_#{setting}"), :for => "settings_#{setting}" ) : ''.html_safe
end
# Renders a notification field for a Redmine::Notifiable option

@ -23,7 +23,7 @@ module TimelogHelper
if @issue.visible?
links << link_to_issue(@issue, :subject => false)
else
links << "##{@issue.id}"
links << "##{@issue.id}".html_safe
end
end
breadcrumb links

@ -70,6 +70,6 @@ module WatchersHelper
end
"<li>#{ s }</li>"
end
lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>"
lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>".html_safe
end
end

@ -23,10 +23,10 @@ module WikiHelper
attrs << " selected='selected'" if selected == page
indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
s << "<option #{attrs}>#{indent}#{h(page.pretty_title)}</option>\n" +
wiki_page_options_for_select(pages, selected, page, level + 1)
end
end
s
s.html_safe
end
end

@ -38,8 +38,8 @@ class Changeset < ActiveRecord::Base
validates_uniqueness_of :revision, :scope => :repository_id
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
named_scope :visible, lambda {|*args| { :include => {:repository => :project},
:conditions => Project.allowed_to_condition(args.first || User.current, :view_changesets) } }
scope :visible, lambda {|*args| { :include => { :repository => :project },
:conditions => Project.allowed_to_condition(args.first || User.current, :view_changesets) } }
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)

@ -89,7 +89,7 @@ class CustomField < ActiveRecord::Base
end
else
locale = obj if obj.is_a?(String) || obj.is_a?(Symbol)
attribute = globalize.fetch(locale || self.class.locale || I18n.locale, :possible_values)
attribute = possible_values(locale)
attribute
end
end
@ -99,17 +99,17 @@ class CustomField < ActiveRecord::Base
when 'user'
possible_values_options(obj).collect(&:last)
else
globalize.fetch(obj || self.class.locale || I18n.locale, :possible_values)
options = obj.nil? ? {} : obj
read_attribute(:possible_values, options)
end
end
# Makes possible_values accept a multiline string
def possible_values=(arg)
if arg.is_a?(Array)
value = arg.compact.collect(&:strip).select {|v| !v.blank?}
value = arg.compact.collect(&:strip).select{ |v| !v.blank? }
globalize.write(self.class.locale || I18n.locale, :possible_values, value)
self[:possible_values] = value
write_attribute(:possible_values, value, {})
else
self.possible_values = arg.to_s.split(/[\n\r]+/)
end
@ -182,7 +182,7 @@ end
# for the sake of nested attributes it is necessary to redefine possible_values
# the values get set directly on the translations association
class CustomField::Translation < ActiveRecord::Base
class CustomField::Translation < Globalize::ActiveRecord::Translation
serialize :possible_values
def possible_values=(arg)

@ -25,14 +25,14 @@ class Document < ActiveRecord::Base
end)
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
attr_protected :project_id
validates_presence_of :project, :title, :category
validates_length_of :title, :maximum => 60
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_documents) } }
scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_documents) } }
safe_attributes 'category_id', 'title', 'description'
@ -48,7 +48,10 @@ class Document < ActiveRecord::Base
def updated_on
unless @updated_on
a = attachments.find(:first, :order => 'created_on DESC')
# attachments has a default order that conflicts with `created_on DESC`
# #reorder removes that default order but rather than #unscoped keeps the
# scoping by this document
a = attachments.reorder(nil).order('created_on DESC').first
@updated_on = (a && a.created_on) || created_on
end
@updated_on

@ -22,15 +22,15 @@ class Enumeration < ActiveRecord::Base
acts_as_tree :order => 'position ASC'
before_destroy :check_integrity
attr_protected :project_id
validates_presence_of :name
validates_uniqueness_of :name, :scope => [:type, :project_id]
validates_length_of :name, :maximum => 30
named_scope :shared, :conditions => { :project_id => nil }
named_scope :active, :conditions => { :active => true }
scope :shared, :conditions => { :project_id => nil }
scope :active, :conditions => { :active => true }
def self.default
# Creates a fake default scope so Enumeration.default will check
@ -86,14 +86,6 @@ class Enumeration < ActiveRecord::Base
def to_s; name end
# Returns the Subclasses of Enumeration. Each Subclass needs to be
# required in development mode.
#
# Note: subclasses is protected in ActiveRecord
def self.get_subclasses
@@subclasses[Enumeration]
end
# Does the +new+ Hash override the previous Enumeration?
def self.overridding_change?(new, previous)
if (same_active_state?(new['active'], previous.active)) && same_custom_values?(new,previous)

@ -29,11 +29,25 @@ class Group < Principal
def user_added(user)
members.each do |member|
next if member.project.nil?
user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id)
member.member_roles.each do |member_role|
user_member.member_roles << MemberRole.new(:role => member_role.role, :inherited_from => member_role.id)
user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id)
if user_member.nil?
user_member = Member.new.tap do |m|
m.project_id = member.project_id
m.user_id = user.id
end
member.member_roles.each do |member_role|
user_member.member_roles.build(:role => member_role.role, :inherited_from => member_role.id)
end
user_member.save!
else
member.member_roles.each do |member_role|
user_member.member_roles << MemberRole.new(:role => member_role.role, :inherited_from => member_role.id)
end
end
user_member.save!
end
end

@ -61,7 +61,7 @@ class Issue < ActiveRecord::Base
DONE_RATIO_OPTIONS = %w(issue_field issue_status)
attr_protected :project_id, :author_id
attr_protected :project_id, :author_id, :lft, :rgt
validates_presence_of :subject, :priority, :project, :tracker, :author, :status
@ -69,25 +69,25 @@ class Issue < ActiveRecord::Base
validates_inclusion_of :done_ratio, :in => 0..100
validates_numericality_of :estimated_hours, :allow_nil => true
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Issue.visible_condition(args.first || User.current) } }
scope :visible, lambda {|*args| { :include => :project,
:conditions => Issue.visible_condition(args.first || User.current) } }
named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
named_scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
named_scope :with_limit, lambda { |limit| { :limit => limit} }
named_scope :on_active_project, :include => [:status, :project, :tracker],
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
scope :with_limit, lambda { |limit| { :limit => limit} }
scope :on_active_project, :include => [:status, :project, :tracker],
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
named_scope :without_version, lambda {
scope :without_version, lambda {
{
:conditions => { :fixed_version_id => nil}
}
}
named_scope :with_query, lambda {|query|
scope :with_query, lambda {|query|
{
:conditions => Query.merge_conditions(query.statement)
:conditions => ::Query.merge_conditions(query.statement)
}
}
@ -156,6 +156,11 @@ class Issue < ActiveRecord::Base
issue.fixed_version = nil
end
issue.project = new_project
if !Setting.cross_project_issue_relations? &&
parent && parent.project_id != project_id
self.parent_issue_id = nil
end
end
if new_tracker
issue.tracker = new_tracker
@ -340,7 +345,9 @@ class Issue < ActiveRecord::Base
# Checks parent issue assignment
if @parent_issue
if !new_record?
if !Setting.cross_project_issue_relations? && @parent_issue.project_id != self.project_id
errors.add :parent_issue_id, :not_a_valid_parent
elsif !new_record?
# moving an existing issue
if @parent_issue.root_id != root_id
# we can always move to another tree

@ -96,6 +96,13 @@ class IssueRelation < ActiveRecord::Base
TYPES[self.relation_type][:order] <=> TYPES[relation.relation_type][:order]
end
# delay is an attribute of IssueRelation but its getter is masked by delayed_job's #delay method
# here we overwrite dj's delay method with the one reading the attribute
# since we don't plan to use dj with IssueRelation objects, this should be fine
def delay
self[:delay]
end
private
# Reverses the relation if needed so that it gets stored in the proper way

@ -33,7 +33,7 @@ class Journal < ActiveRecord::Base
belongs_to :user
#attr_protected :user_id
# "touch" the journaled object on creation
after_create :touch_journaled_after_creation
@ -46,7 +46,7 @@ class Journal < ActiveRecord::Base
# Scopes to all journals excluding the initial journal - useful for change
# logs like the history on issue#show
named_scope "changing", :conditions => ["version > 1"]
scope "changing", :conditions => ["version > 1"]
def touch_journaled_after_creation
journaled.touch

@ -15,7 +15,7 @@
require 'net/ldap'
require 'iconv'
class AuthSourceLdap < AuthSource
class LdapAuthSource < AuthSource
validates_presence_of :host, :port, :attr_login
validates_length_of :name, :host, :maximum => 60, :allow_nil => true
validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true
@ -23,10 +23,7 @@ class AuthSourceLdap < AuthSource
validates_numericality_of :port, :only_integer => true
before_validation :strip_ldap_attributes
def after_initialize
self.port = 389 if self.port == 0
end
after_initialize :set_default_port
def authenticate(login, password)
return nil if login.blank? || password.blank?
@ -124,4 +121,8 @@ class AuthSourceLdap < AuthSource
entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
end
end
def set_default_port
self.port = 389 if self.port.to_i == 0
end
end

@ -395,6 +395,11 @@ class Mailer < ActionMailer::Base
h.each { |k,v| headers["X-OpenProject-#{k}"] = v }
end
# Some plugins still call redmine_headers, this preserves backwards
# compatibility and adds a deprecation warning to the logs
alias :redmine_headers :openproject_headers
deprecate :redmine_headers
# Overrides the create_mail method
def create_mail
# Removes the current user from the recipients and cc
@ -421,11 +426,11 @@ class Mailer < ActionMailer::Base
def render_multipart(method_name, body)
if Setting.plain_text_mail?
content_type "text/plain"
body render(:file => "#{method_name}.text.plain.rhtml", :body => body, :layout => 'mailer.text.plain.erb')
body render(:file => "#{method_name}.text.erb", :body => body, :layout => 'mailer.text.erb')
else
content_type "multipart/alternative"
part :content_type => "text/plain", :body => render(:file => "#{method_name}.text.plain.rhtml", :body => body, :layout => 'mailer.text.plain.erb')
part :content_type => "text/html", :body => render_message("#{method_name}.text.html.rhtml", body)
part :content_type => "text/plain", :body => render(:file => "#{method_name}.text.erb", :body => body, :layout => 'mailer.text.erb')
part :content_type => "text/html", :body => render_message("#{method_name}.html.erb", body)
end
end

@ -36,15 +36,26 @@ class MemberRole < ActiveRecord::Base
def add_role_to_group_users
if member && member.principal.is_a?(Group)
member.principal.users.each do |user|
user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id)
user_member.member_roles << MemberRole.new(:role => role, :inherited_from => id)
user_member.save!
user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id)
if user_member.nil?
user_member = Member.new.tap do |m|
m.project_id = member.project_id
m.user_id = user.id
end
user_member.member_roles << MemberRole.new(:role => role, :inherited_from => id)
user_member.save
else
user_member.member_roles << MemberRole.new(:role => role, :inherited_from => id)
end
end
end
end
def remove_role_from_group_users
MemberRole.find(:all, :conditions => { :inherited_from => id }).group_by(&:member).each do |member, member_roles|
MemberRole.all(:conditions => { :inherited_from => id }).group_by(&:member).each do |member, member_roles|
member_roles.each(&:destroy)
if member && member.user
Watcher.prune(:user => member.user, :project => member.project)

@ -47,8 +47,8 @@ class Message < ActiveRecord::Base
after_create :add_author_as_watcher
named_scope :visible, lambda {|*args| { :include => {:board => :project},
:conditions => Project.allowed_to_condition(args.first || User.current, :view_messages) } }
scope :visible, lambda {|*args| { :include => {:board => :project},
:conditions => Project.allowed_to_condition(args.first || User.current, :view_messages) } }
safe_attributes 'subject', 'content'
safe_attributes 'locked', 'sticky',

@ -30,7 +30,7 @@ class News < ActiveRecord::Base
after_create :add_author_as_watcher
named_scope :visible, lambda {|*args| {
scope :visible, lambda {|*args| {
:include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_news)
}}

@ -20,11 +20,11 @@ class Principal < ActiveRecord::Base
has_many :projects, :through => :memberships
# Groups and active users
named_scope :active, :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status = 1)"
scope :active, :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status = 1)"
named_scope :active_or_registered, :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND (#{Principal.table_name}.status = 1 OR #{Principal.table_name}.status = 2))"
named_scope :like, lambda {|q|
scope :active_or_registered, :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND (#{Principal.table_name}.status = 1 OR #{Principal.table_name}.status = 2))"
scope :like, lambda {|q|
s = "%#{q.to_s.strip.downcase}%"
{:conditions => ["LOWER(login) LIKE :s OR LOWER(firstname) LIKE :s OR LOWER(lastname) LIKE :s OR LOWER(mail) LIKE :s", {:s => s}],
:order => 'type, login, lastname, firstname, mail'

@ -78,10 +78,10 @@ class Project < ActiveRecord::Base
before_destroy :delete_all_members
named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } }
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } }
scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
scope :all_public, { :conditions => { :is_public => true } }
scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
def initialize(attributes = nil)
super
@ -809,7 +809,7 @@ class Project < ActiveRecord::Base
# Copies queries from +project+
def copy_queries(project)
project.queries.each do |query|
new_query = Query.new
new_query = ::Query.new
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

@ -235,11 +235,11 @@ class Query < ActiveRecord::Base
def available_columns
return @available_columns if @available_columns
@available_columns = Query.available_columns
@available_columns = ::Query.available_columns
@available_columns += (project ?
project.all_issue_custom_fields :
IssueCustomField.find(:all)
).collect {|cf| QueryCustomFieldColumn.new(cf) }
).collect {|cf| ::QueryCustomFieldColumn.new(cf) }
end
def self.available_columns=(v)
@ -462,7 +462,7 @@ class Query < ActiveRecord::Base
def issue_count
Issue.count(:include => [:status, :project], :conditions => statement)
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise ::Query::StatementInvalid.new(e.message)
end
# Returns the issue count by group or nil if query is not grouped
@ -482,7 +482,7 @@ class Query < ActiveRecord::Base
end
r
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise ::Query::StatementInvalid.new(e.message)
end
# Returns the issues
@ -492,12 +492,12 @@ class Query < ActiveRecord::Base
order_option = nil if order_option.blank?
Issue.find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
:conditions => Query.merge_conditions(statement, options[:conditions]),
:conditions => ::Query.merge_conditions(statement, options[:conditions]),
:order => order_option,
:limit => options[:limit],
:offset => options[:offset]
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise ::Query::StatementInvalid.new(e.message)
end
# Returns the journals
@ -509,16 +509,16 @@ class Query < ActiveRecord::Base
:limit => options[:limit],
:offset => options[:offset]
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise ::Query::StatementInvalid.new(e.message)
end
# Returns the versions
# Valid options are :conditions
def versions(options={})
Version.find :all, :include => :project,
:conditions => Query.merge_conditions(project_statement, options[:conditions])
:conditions => ::Query.merge_conditions(project_statement, options[:conditions])
rescue ::ActiveRecord::StatementInvalid => e
raise Query::StatementInvalid.new(e.message)
raise ::Query::StatementInvalid.new(e.message)
end
private

@ -1,2 +1,2 @@
class Query::StatementInvalid < ActiveRecord::StatementInvalid
class ::Query::StatementInvalid < ActiveRecord::StatementInvalid
end

@ -124,8 +124,9 @@ class Repository < ActiveRecord::Base
# Finds and returns a revision with a number or the beginning of a hash
def find_changeset_by_name(name)
name = name.to_s
return nil if name.blank?
changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name] : ["revision LIKE ?", name + '%']))
end
def latest_changeset
@ -275,8 +276,8 @@ class Repository < ActiveRecord::Base
def before_save
# Strips url and root_url
url.strip!
root_url.strip!
url.strip! if url.present?
root_url.strip! if root_url.present?
true
end

@ -22,7 +22,7 @@ class Repository::Bazaar < Repository
"url" => "Root directory",
"log_encoding" => "Commit messages encoding",
}
def self.human_attribute_name(attribute_key_name)
def self.human_attribute_name(attribute_key_name, options = {})
ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
end

@ -23,7 +23,7 @@ class Repository::Cvs < Repository
"root_url" => "Module",
"log_encoding" => "Commit messages encoding",
}
def self.human_attribute_name(attribute_key_name)
def self.human_attribute_name(attribute_key_name, options = {})
ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
end

@ -21,7 +21,7 @@ class Repository::Darcs < Repository
"url" => "Root directory",
"log_encoding" => "Commit messages encoding",
}
def self.human_attribute_name(attribute_key_name)
def self.human_attribute_name(attribute_key_name, options = {})
ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
end

@ -21,7 +21,7 @@ class Repository::Filesystem < Repository
ATTRIBUTE_KEY_NAMES = {
"url" => "Root directory",
}
def self.human_attribute_name(attribute_key_name)
def self.human_attribute_name(attribute_key_name, options = {})
ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
end

@ -21,7 +21,7 @@ class Repository::Git < Repository
ATTRIBUTE_KEY_NAMES = {
"url" => "Path to repository",
}
def self.human_attribute_name(attribute_key_name)
def self.human_attribute_name(attribute_key_name, options = {})
ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
end

@ -26,7 +26,7 @@ class Repository::Mercurial < Repository
ATTRIBUTE_KEY_NAMES = {
"url" => "Root directory",
}
def self.human_attribute_name(attribute_key_name)
def self.human_attribute_name(attribute_key_name, options = {})
ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
end

@ -17,8 +17,8 @@ class Role < ActiveRecord::Base
BUILTIN_NON_MEMBER = 1
BUILTIN_ANONYMOUS = 2
named_scope :givable, { :conditions => "builtin = 0", :order => 'position' }
named_scope :builtin, lambda { |*args|
scope :givable, { :conditions => "builtin = 0", :order => 'position' }
scope :builtin, lambda { |*args|
compare = 'not' if args.first == true
{ :conditions => "#{compare} builtin = 0" }
}

@ -33,7 +33,7 @@ class TimeEntry < ActiveRecord::Base
validates_numericality_of :hours, :allow_nil => true, :message => :invalid
validates_length_of :comments, :maximum => 255, :allow_nil => true
named_scope :visible, lambda {|*args| {
scope :visible, lambda {|*args| {
:include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_time_entries)
}}

@ -58,8 +58,8 @@ class User < Principal
belongs_to :auth_source
# Active non-anonymous users scope
named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
named_scope :active_or_registered, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE} or #{User.table_name}.status = #{STATUS_REGISTERED}"
scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
scope :active_or_registered, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE} or #{User.table_name}.status = #{STATUS_REGISTERED}"
acts_as_customizable
@ -72,15 +72,15 @@ class User < Principal
:firstname,
:lastname,
:mail,
:if => Proc.new { |user| !(user.is_a?(AnonymousUser) || user.is_a?(DeletedUser)) }
:unless => Proc.new { |user| user.is_a?(AnonymousUser) || user.is_a?(DeletedUser) }
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
validates_uniqueness_of :mail, :allow_blank => true, :case_sensitive => false
# Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :maximum => 30
validates_length_of :firstname, :lastname, :maximum => 30
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_blank => true
validates_length_of :mail, :maximum => 60, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
@ -88,11 +88,11 @@ class User < Principal
before_destroy :delete_associated_public_queries
before_destroy :reassign_associated
named_scope :in_group, lambda {|group|
scope :in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i
{ :conditions => ["#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
}
named_scope :not_in_group, lambda {|group|
scope :not_in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i
{ :conditions => ["#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
}
@ -418,7 +418,7 @@ class User < Principal
# Return true if the user is a member of project
def member_of?(project)
!roles_for_project(project).detect {|role| role.member?}.nil?
roles_for_project(project).any?(&:member?)
end
# Returns a hash of user's projects grouped by roles
@ -641,7 +641,7 @@ class User < Principal
klass.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
end
[TimeEntry, Journal, Query].each do |klass|
[TimeEntry, Journal, ::Query].each do |klass|
klass.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
end
@ -671,7 +671,7 @@ class User < Principal
end
def delete_associated_public_queries
Query.delete_all ['user_id = ? AND is_public = ?', id, false]
::Query.delete_all ['user_id = ? AND is_public = ?', id, false]
end
end

@ -34,9 +34,9 @@ class Version < ActiveRecord::Base
validates_inclusion_of :status, :in => VERSION_STATUSES
validates_inclusion_of :sharing, :in => VERSION_SHARINGS
named_scope :open, :conditions => {:status => 'open'}
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
scope :open, :conditions => {:status => 'open'}
scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
safe_attributes 'name',
'description',

@ -61,8 +61,9 @@ class WikiContent < ActiveRecord::Base
journals
end
# REVIEW
def version
new_record? ? 0 : last_journal.version
new_record? || last_journal.nil? ? 0 : last_journal.version
end
private

@ -39,7 +39,7 @@ class WikiPage < ActiveRecord::Base
validates_associated :content
# eager load information about last updates, without loading text
named_scope :with_updated_on, {
scope :with_updated_on, {
:select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
:joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id"
}
@ -158,6 +158,10 @@ class WikiPage < ActiveRecord::Base
self.parent = parent_page
end
def to_param
title
end
protected
def validate

@ -31,7 +31,7 @@
<tbody>
<% project_tree(@projects) do |project, level| %>
<tr class="<%= cycle("odd", "even") %> <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="name"><span><%= link_to_project(project, {:action => 'settings'}, :title => project.short_description) %></span></td>
<td class="name"><span><%= link_to project, settings_project_path(project), :title => project.short_description %></span></td>
<td align="center"><%= checked_image project.is_public? %></td>
<td align="center"><%= format_date(project.created_on) %></td>
<td class="buttons">

@ -0,0 +1,7 @@
<h2><%=l(:label_auth_source)%> (<%= @auth_source.auth_method_name %>)</h2>
<%= form_for @auth_source, :html => { :class => "tabular" } do %>
<%= render :partial => 'form' %>
<%= submit_tag l(:button_save) %>
<% end %>

@ -1,7 +0,0 @@
<h2><%=l(:label_auth_source)%> (<%= h(@auth_source.auth_method_name) %>)</h2>
<% form_tag({:action => 'update', :id => @auth_source}, :class => "tabular") do %>
<%= render :partial => 'form' %>
<%= submit_tag l(:button_save) %>
<% end %>

@ -1,5 +1,5 @@
<div class="contextual">
<%= link_to l(:label_auth_source_new), {:action => 'new'}, :class => 'icon icon-add' %>
<%= link_to l(:label_auth_source_new), { :action => 'new' }, :class => 'icon icon-add' %>
</div>
<h2><%=l(:label_auth_source_plural)%></h2>
@ -15,17 +15,17 @@
<tbody>
<% for source in @auth_sources %>
<tr class="<%= cycle("odd", "even") %>">
<td><%= link_to(h(source.name), :action => 'edit', :id => source)%></td>
<td align="center"><%= h source.auth_method_name %></td>
<td align="center"><%= h source.host %></td>
<td align="center"><%= h source.users.count %></td>
<td><%= link_to source.name, :action => 'edit', :id => source %></td>
<td align="center"><%= source.auth_method_name %></td>
<td align="center"><%= source.host %></td>
<td align="center"><%= source.users.count %></td>
<td class="buttons">
<%= link_to l(:button_test), :action => 'test_connection', :id => source %>
<%= link_to l(:button_delete), { :action => 'destroy', :id => source },
:method => :post,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del',
:disabled => source.users.any? %>
<%= link_to l(:button_test), { :action => 'test_connection', :id => source } %>
<%= link_to l(:button_delete), { :action => 'destroy', :id => source },
:method => :delete,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del',
:disabled => source.users.any? %>
</td>
</tr>
<% end %>

@ -0,0 +1,6 @@
<h2><%=l(:label_auth_source_new)%> (<%= @auth_source.auth_method_name %>)</h2>
<%= form_for @auth_source, :html => { :class => "tabular" } do %>
<%= render :partial => 'form' %>
<%= submit_tag l(:button_create) %>
<% end %>

@ -1,6 +0,0 @@
<h2><%=l(:label_auth_source_new)%> (<%= h(@auth_source.auth_method_name) %>)</h2>
<% form_tag({:action => 'create'}, :class => "tabular") do %>
<%= render :partial => 'form' %>
<%= submit_tag l(:button_create) %>
<% end %>

@ -6,7 +6,7 @@
<tr>
<% day = calendar.startdt
while day <= calendar.enddt %>
<%= "<td class='week-number' title='#{ l(:label_week) }'>#{(day+(11-day.cwday)%7).cweek}</td>" if day.cwday == calendar.first_wday %>
<%= "<td class='week-number' title='#{ l(:label_week) }'>#{(day+(11-day.cwday)%7).cweek}</td>".html_safe if day.cwday == calendar.first_wday %>
<td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %>">
<p class="day-num"><%= day.day %></p>
<% calendar.events_on(day).each do |i| %>
@ -36,7 +36,7 @@ while day <= calendar.enddt %>
<% end %>
<% end %>
</td>
<%= '</tr><tr>' if day.cwday==calendar.last_wday and day!=calendar.enddt %>
<%= '</tr><tr>'.html_safe if day.cwday==calendar.last_wday and day!=calendar.enddt %>
<% day = day + 1
end %>
</tr>

@ -4,7 +4,7 @@
<ul>
<% tabs.each do |tab| -%>
<li><%=
position_span = (tab[:name] == selected_tab ? "<span class = 'hidden-for-sighted'>#{l(:description_current_position)}</span>" : "")
position_span = you_are_here_info(tab[:name] == selected_tab)
link_to(position_span + l(tab[:label]), { :tab => tab[:name] },
:id => "tab-#{tab[:name]}",
:class => (tab[:name] != selected_tab ? nil : 'selected'),

@ -2,8 +2,8 @@
&#187; <%= link_to l(@custom_field.type_name), :controller => 'custom_fields', :action => 'index', :tab => @custom_field.type %>
&#187; <%= l(:label_custom_field_new) %></h2>
<% labelled_tabular_form_for :custom_field, @custom_field, :url => { :action => "new" } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= hidden_field_tag 'type', @custom_field.type %>
<%= submit_tag l(:button_save) %>
<%= labelled_tabular_form_for :custom_field, @custom_field, :url => { :action => "new" } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= hidden_field_tag 'type', @custom_field.type %>
<%= submit_tag l(:button_save) %>
<% end %>

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

Loading…
Cancel
Save