Anonymous avatar Anonymous committed 00105a7

v0.2.0

Comments (0)

Files changed (152)

redmine/app/controllers/account_controller.rb

 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class AccountController < ApplicationController
-	layout 'base'	
+  layout 'base'	
+  
   # prevents login action to be filtered by check_if_login_required application scope filter
   skip_before_filter :check_if_login_required, :only => :login
-	before_filter :require_login, :except => [:show, :login]
+  before_filter :require_login, :except => [:show, :login]
 
-	def show
-		@user = User.find(params[:id])
-	end
-  
-	# Login request and validation
-	def login
-		if request.get?
-			session[:user] = nil
-			@user = User.new
-		else
-			@user = User.new(params[:user])
-			logged_in_user = @user.try_to_login
-			if logged_in_user
-				session[:user] = logged_in_user
-				redirect_back_or_default :controller => 'account', :action => 'my_page'
-			else
-				flash[:notice] = _('Invalid user/password')
-			end
-		end
-	end
+  def show
+    @user = User.find(params[:id])
+  end
+
+  # Login request and validation
+  def login
+    if request.get?
+      session[:user] = nil
+    else
+      logged_in_user = User.try_to_login(params[:login], params[:password])
+      if logged_in_user
+        session[:user] = logged_in_user
+        redirect_back_or_default :controller => 'account', :action => 'my_page'
+      else
+        flash[:notice] = _('Invalid user/password')
+      end
+    end
+  end
 	
 	# Log out current user and redirect to welcome page
 	def logout
 		end
 	end
 	
-	# Change current user's password
-	def change_password
-		@user = User.find(session[:user].id)		
-		if @user.check_password?(@params[:old_password])		
-			if @params[:new_password] == @params[:new_password_confirmation]
-				if @user.change_password(@params[:old_password], @params[:new_password])
-					flash[:notice] = 'Password was successfully updated.'
-				end
-			else
-				flash[:notice] = 'Password confirmation doesn\'t match!'
-			end
-		else
-			flash[:notice] = 'Wrong password'
-		end
-		render :action => 'my_account'
-	end
+  # Change current user's password
+  def change_password
+    @user = User.find(session[:user].id)
+    if @user.check_password?(@params[:password])
+      @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
+      flash[:notice] = 'Password was successfully updated.' if @user.save
+    else
+      flash[:notice] = 'Wrong password'
+    end
+    render :action => 'my_account'
+  end
 end

redmine/app/controllers/admin_controller.rb

 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class AdminController < ApplicationController
-	layout 'base'	
-	before_filter :require_admin
-  
-	helper :sort
-	include SortHelper	
+  layout 'base'	
+  before_filter :require_admin
 
-	def index	
-	end
+  helper :sort
+  include SortHelper	
+
+  def index	
+  end
 	
   def projects
-		sort_init 'projects.name', 'asc'
-		sort_update		
-    @project_pages, @projects = paginate :projects, :per_page => 15, :order => sort_clause
+    sort_init 'name', 'asc'
+    sort_update		
+    @project_count = Project.count		
+    @project_pages = Paginator.new self, @project_count,
+								15,
+								@params['page']								
+    @projects = Project.find :all, :order => sort_clause,
+						:limit  =>  @project_pages.items_per_page,
+						:offset =>  @project_pages.current.offset		
   end
-  
+
   def mail_options
-    @actions = Permission.find(:all, :conditions => ["mail_option=?", true])  || []
+    @actions = Permission.find(:all, :conditions => ["mail_option=?", true]) || []
     if request.post?
       @actions.each { |a|
-        a.mail_enabled = params[:action_ids].include? a.id.to_s 
+        a.mail_enabled = (params[:action_ids] || []).include? a.id.to_s 
         a.save
       }
       flash[:notice] = "Mail options were successfully updated."
   
   def info
     @adapter_name = ActiveRecord::Base.connection.adapter_name
-  end
-  
+  end  
 end

redmine/app/controllers/application.rb

   end 
   
   def set_localization
-    Localization.lang = session[:user].nil? ? RDM_DEFAULT_LANG : (session[:user].language || RDM_DEFAULT_LANG)
+    Localization.lang = begin
+      if session[:user]
+        session[:user].language
+      elsif request.env['HTTP_ACCEPT_LANGUAGE']
+        accept_lang = HTTPUtils.parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first
+        if Localization.langs.collect{ |l| l[1] }.include? accept_lang
+          accept_lang
+        end
+      end
+    rescue
+      nil
+    end || RDM_DEFAULT_LANG
   end
   
-	def require_login
-		unless session[:user]
-			store_location
-			redirect_to(:controller => "account", :action => "login")
-		end
-	end
+  def require_login
+    unless session[:user]
+      store_location
+      redirect_to(:controller => "account", :action => "login")
+    end
+  end
 
-	def require_admin
-		if session[:user].nil?
-			store_location
-			redirect_to(:controller => "account", :action => "login")
-		else
-			unless session[:user].admin?
-				flash[:notice] = "Acces not allowed"
-				redirect_to(:controller => "projects", :action => "list")
-			end
-		end
-	end
+  def require_admin
+    if session[:user].nil?
+      store_location
+      redirect_to(:controller => "account", :action => "login")
+    else
+      unless session[:user].admin?
+        flash[:notice] = "Acces not allowed"
+        redirect_to(:controller => "projects", :action => "list")
+      end
+    end
+  end
 
-	# authorizes the user for the requested action.
-	def authorize
+  # authorizes the user for the requested action.
+  def authorize
     # check if action is allowed on public projects
-    if @project.public? and Permission.allowed_to_public "%s/%s" % [ @params[:controller], @params[:action] ]
+    if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ @params[:controller], @params[:action] ]
       return true
     end  
-    # if user is not logged in, he is redirect to login form
-		unless session[:user]
-			store_location
-			redirect_to(:controller => "account", :action => "login")
-			return false
-		end
-    # check if user is authorized    
+    # if user not logged in, redirect to login form
+    unless session[:user]
+      store_location
+      redirect_to(:controller => "account", :action => "login")
+      return false
+    end
+    # if logged in, check if authorized    
     if session[:user].admin? or Permission.allowed_to_role( "%s/%s" % [ @params[:controller], @params[:action] ], session[:user].role_for_project(@project.id)  )    
       return true		
-		end		
+    end		
     flash[:notice] = "Acces denied"
     redirect_to(:controller => "")
-    return false
-	end
+    false
+  end
 	
-	# store current uri in  the session.
-	# we can return to this location by calling redirect_back_or_default
-	def store_location
-		session[:return_to] = @request.request_uri
-	end
-	
-	# move to the last store_location call or to the passed default one
-	def redirect_back_or_default(default)
-		if session[:return_to].nil?
-			redirect_to default
-		else
-			redirect_to_url session[:return_to]
-			session[:return_to] = nil
-		end
-	end
-  
+  # store current uri in session.
+  # return to this location by calling redirect_back_or_default
+  def store_location
+    session[:return_to] = @request.request_uri
+  end
+
+  # move to the last store_location call or to the passed default one
+  def redirect_back_or_default(default)
+    if session[:return_to].nil?
+      redirect_to default
+    else
+      redirect_to_url session[:return_to]
+      session[:return_to] = nil
+    end
+  end
 end

redmine/app/controllers/help_controller.rb

 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class HelpController < ApplicationController
+
+  skip_before_filter :check_if_login_required
+  before_filter :load_help_config
+
+  # displays help page for the requested controller/action
+  def index	
+    # select help page to display
+    if @params[:ctrl] and @help_config['pages'][@params[:ctrl]]
+      if @params[:page] and @help_config['pages'][@params[:ctrl]][@params[:page]]
+        template = @help_config['pages'][@params[:ctrl]][@params[:page]]
+      else
+        template = @help_config['pages'][@params[:ctrl]]['index']
+      end
+    end
+    # choose language according to available help translations
+    lang = (@help_config['langs'].include? Localization.lang) ? Localization.lang : @help_config['langs'].first
 	
-  skip_before_filter :check_if_login_required
-	before_filter :load_help_config
-
-	def index	
-		if @params[:ctrl] and @help_config[@params[:ctrl]]
-			if @params[:page] and @help_config[@params[:ctrl]][@params[:page]]
-				template = @help_config[@params[:ctrl]][@params[:page]]
-			else
-				template = @help_config[@params[:ctrl]]['index']
-			end
-		end
-		
     if template
-      redirect_to "/manual/#{template}"
+      redirect_to "/manual/#{lang}/#{template}"
     else
-      redirect_to "/manual/"
+      redirect_to "/manual/#{lang}/"
     end
-	end
+  end
 
 private
-	def load_help_config
-		@help_config = YAML::load(File.open("#{RAILS_ROOT}/config/help.yml"))
-	end	
+  def load_help_config
+    @help_config = YAML::load(File.open("#{RAILS_ROOT}/config/help.yml"))
+  end	
 end

redmine/app/controllers/projects_controller.rb

     render :action => 'list'
   end
 
-	# Lists public projects
-	def list
-		sort_init 'projects.name', 'asc'
-		sort_update		
-		@project_count = Project.count(["public=?", true])		
-		@project_pages = Paginator.new self, @project_count,
+  # Lists public projects
+  def list
+    sort_init 'name', 'asc'
+    sort_update		
+    @project_count = Project.count(["is_public=?", true])		
+    @project_pages = Paginator.new self, @project_count,
 								15,
 								@params['page']								
-		@projects = Project.find :all, :order => sort_clause,
-						:conditions => ["public=?", true],
+    @projects = Project.find :all, :order => sort_clause,
+						:conditions => ["is_public=?", true],
 						:limit  =>  @project_pages.items_per_page,
 						:offset =>  @project_pages.current.offset		
   end
           
   # Add a new project
-	def add
-		@custom_fields = CustomField::find_all
-		@project = Project.new(params[:project])
-		if request.post?
-			@project.custom_fields = CustomField.find(@params[:custom_field_ids]) if @params[:custom_field_ids]
-			if @project.save
-				flash[:notice] = 'Project was successfully created.'
-				redirect_to :controller => 'admin', :action => 'projects'
-			end		
-		end	
-	end
+  def add
+    @custom_fields = CustomField::find_all
+    @root_projects = Project::find(:all, :conditions => "parent_id is null")
+    @project = Project.new(params[:project])
+    if request.post?
+      @project.custom_fields = CustomField.find(@params[:custom_field_ids]) if @params[:custom_field_ids]
+      if @project.save
+        flash[:notice] = 'Project was successfully created.'
+        redirect_to :controller => 'admin', :action => 'projects'
+	  end		
+    end	
+  end
 	
-	# Show @project
-	def show
-    @members = @project.members.find(:all, :include => [:user, :role])
-	end
+	# Show @project
+  def show
+    @members = @project.members.find(:all, :include => [:user, :role])
+    @subprojects = @project.children if @project.children_count > 0
+    @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "news.created_on DESC")
+  end
 
   def settings
-		@custom_fields = CustomField::find_all
-		@issue_category ||= IssueCategory.new
+    @root_projects = Project::find(:all, :conditions => ["parent_id is null and id <> ?", @project.id])
+    @custom_fields = CustomField::find_all
+    @issue_category ||= IssueCategory.new
     @member ||= @project.members.new
     @roles = Role.find_all
     @users = User.find_all - @project.members.find(:all, :include => :user).collect{|m| m.user }
   end
   
-	# Edit @project
-	def edit
-		if request.post?
-			@project.custom_fields = CustomField.find(@params[:custom_field_ids]) if @params[:custom_field_ids]
-			if @project.update_attributes(params[:project])
-				flash[:notice] = 'Project was successfully updated.'
-				redirect_to :action => 'settings', :id => @project
+  # Edit @project
+  def edit
+    if request.post?
+      @project.custom_fields = CustomField.find(@params[:custom_field_ids]) if @params[:custom_field_ids]
+      if @project.update_attributes(params[:project])
+        flash[:notice] = 'Project was successfully updated.'
+        redirect_to :action => 'settings', :id => @project
       else
         settings
         render :action => 'settings'
-			end
-		end
+      end
+    end
   end
   
 	# Delete @project
 		end	
 	end
 	
-  # Show issues list of @project
+  # Show filtered/sorted issues list of @project
   def list_issues
     sort_init 'issues.id', 'desc'
     sort_update
     search_filter_init_list_issues
     search_filter_update if params[:set_filter] or request.post?
 
-    @issue_count = Issue.count(:include => :status, :conditions => search_filter_clause)		
+    @issue_count = Issue.count(:include => [:status, :project], :conditions => search_filter_clause)		
     @issue_pages = Paginator.new self, @issue_count, 15, @params['page']								
     @issues =  Issue.find :all, :order => sort_clause,
-						:include => [ :author, :status, :tracker ],
+						:include => [ :author, :status, :tracker, :project ],
 						:conditions => search_filter_clause,
 						:limit  =>  @issue_pages.items_per_page,
 						:offset =>  @issue_pages.current.offset								
     search_filter_init_list_issues
 					
     @issues =  Issue.find :all, :order => sort_clause,
-						:include => [ :author, :status, :tracker ],
+						:include => [ :author, :status, :tracker, :project ],
 						:conditions => search_filter_clause							
 
     export = StringIO.new

redmine/app/controllers/roles_controller.rb

   end
   
   def workflow
-    @roles = Role.find_all
-    @trackers = Tracker.find_all
-    @statuses = IssueStatus.find_all
     
     @role = Role.find_by_id(params[:role_id])
     @tracker = Tracker.find_by_id(params[:tracker_id])    
         flash[:notice] = 'Workflow was successfully updated.'
       end
     end
+    @roles = Role.find_all
+    @trackers = Tracker.find_all
+    @statuses = IssueStatus.find(:all, :include => :workflows)
   end
 end

redmine/app/controllers/users_controller.rb

 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class UsersController < ApplicationController
-	layout 'base'	
-	before_filter :require_admin
-  
-	helper :sort
-	include SortHelper
-	
-	def index
-		list
-		render :action => 'list'
-	end
+  layout 'base'	
+  before_filter :require_admin
+
+  helper :sort
+  include SortHelper
 
-	def list
-		sort_init 'users.login', 'asc'
-		sort_update
-		@user_count = User.count		
-		@user_pages = Paginator.new self, @user_count,
+  def index
+    list
+    render :action => 'list'
+  end
+
+  def list
+    sort_init 'login', 'asc'
+    sort_update
+    @user_count = User.count		
+    @user_pages = Paginator.new self, @user_count,
 								15,
 								@params['page']								
-		@users =  User.find :all, :order => sort_clause,
+    @users =  User.find :all,:order => sort_clause,
 						:limit  =>  @user_pages.items_per_page,
 						:offset =>  @user_pages.current.offset		
-	end
+  end
 
-	def add
-		if request.get?
-			@user = User.new
-		else
-			@user = User.new(params[:user])
-			@user.admin = params[:user][:admin]
-			if @user.save
-				flash[:notice] = 'User was successfully created.'
-				redirect_to :action => 'list'
-			end
-		end
-	end
+  def add
+    if request.get?
+      @user = User.new
+    else
+      @user = User.new(params[:user])
+      @user.admin = params[:user][:admin] || false
+      @user.login = params[:user][:login]
+      @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
+      if @user.save
+        flash[:notice] = 'User was successfully created.'
+        redirect_to :action => 'list'
+      end
+    end
+  end
 
-	def edit
-		@user = User.find(params[:id])
-		if request.post?
-			@user.admin = params[:user][:admin] if params[:user][:admin]
-			if @user.update_attributes(params[:user])
-				flash[:notice] = 'User was successfully updated.'
-				redirect_to :action => 'list'
-			end
-		end
-	end
+  def edit
+    @user = User.find(params[:id])
+    if request.post?
+      @user.admin = params[:user][:admin] if params[:user][:admin]
+      @user.login = params[:user][:login] if params[:user][:login]
+      @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty?
+      if @user.update_attributes(params[:user])
+        flash[:notice] = 'User was successfully updated.'
+        redirect_to :action => 'list'
+      end
+    end
+  end
 
-	def destroy
-		User.find(params[:id]).destroy
-		redirect_to :action => 'list'
+  def destroy
+    User.find(params[:id]).destroy
+    redirect_to :action => 'list'
   rescue
     flash[:notice] = "Unable to delete user"
     redirect_to :action => 'list'
-	end  
+  end  
 end

redmine/app/controllers/versions_controller.rb

     @attachment = @version.attachments.find(params[:attachment_id])
     @attachment.increment_download
     send_file @attachment.diskfile, :filename => @attachment.filename
+  rescue
+    flash[:notice]="Requested file doesn't exist or has been deleted."
+    redirect_to :controller => 'projects', :action => 'list_files', :id => @project
   end 
   
   def destroy_file

redmine/app/helpers/application_helper.rb

 	
 	def authorize_for(controller, action)  
     # check if action is allowed on public projects
-    if @project.public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
+    if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
       return true
     end  
     # check if user is authorized    

redmine/app/helpers/search_filter_helper.rb

 module SearchFilterHelper
 
   def search_filter_criteria(name, options = {})
-    session[:search_filter] ||= {}
-    session[:search_filter][name] ||= {}
-    unless session[:search_filter][name][:options] and session[:search_filter][name][:conditions]
-      session[:search_filter][name][:options] = []
-      session[:search_filter][name][:conditions] = {}
-      yield.each { |c|
-        session[:search_filter][name][:options] << [c[0], c[1].to_s]
-        session[:search_filter][name][:conditions].store(c[1].to_s, c[2])
-      }
-    end
+    @search_filter ||= {}
+    @search_filter[name] ||= {}
+    @search_filter[name][:options] = []
+    @search_filter[name][:conditions] = {}
+    yield.each { |c|
+      @search_filter[name][:options] << [c[0], c[1].to_s]
+      @search_filter[name][:conditions].store(c[1].to_s, c[2])
+    }
   end
 
   def search_filter_update
-    session[:search_filter].each_key {|field| session[:search_filter][field][:value] = params[field]  }
+    @search_filter.each_key {|field| session[:search_filter][field] = params[field]  }
   end
 	
   def search_filter_clause
-    clause = ["issues.project_id=?", @project.id]
-    session[:search_filter].each { |k, v|
-      v[:value] ||= v[:options][0][1]
-      if (!v[:conditions][v[:value]][0].empty?)
-        clause[0] = clause[0] + " AND " + v[:conditions][v[:value]][0]
-        clause << v[:conditions][v[:value]][1] if !v[:conditions][v[:value]][1].nil?
+    clause = ["1=1"]
+    @search_filter.each { |k, v|
+      filter_value = session[:search_filter][k] || v[:options][0][1]
+      if v[:conditions][filter_value]
+        clause[0] = clause[0] + " AND " + v[:conditions][filter_value].first
+        clause += v[:conditions][filter_value][1..-1]
       end    
     }
     clause
   end
 	
-  def search_filter_tag(criteria)
+  def search_filter_tag(criteria, options = {})
+    options[:name] = criteria
     content_tag("select", 
-				options_for_select(session[:search_filter][criteria][:options], session[:search_filter][criteria][:value]),
-				:name => criteria
+				options_for_select(@search_filter[criteria][:options], session[:search_filter][criteria]),
+				options
 				)
   end
 	
   def search_filter_init_list_issues
 	search_filter_criteria('status_id') { 
-    [ ["[Open]", "O", ["issue_statuses.is_closed=?", false]],
-      ["[All]", "A", ["", false]]
+    [ [_('[Open]'), "O", ["issue_statuses.is_closed=?", false]],
+      [_('[All]'), "A", nil]
     ] + IssueStatus.find(:all).collect {|s| [s.name, s.id, ["issues.status_id=?", s.id]] }                                                      
     }
     
     search_filter_criteria('tracker_id') { 
-    [ ["[All]", "A", ["", false]]
+    [ [_('[All]'), "A", nil]
     ] + Tracker.find(:all).collect {|s| [s.name, s.id, ["issues.tracker_id=?", s.id]] }                                                      
     }
 	
     search_filter_criteria('priority_id') { 
-    [ ["[All]", "A", ["", false]]
+    [ [_('[All]'), "A", nil]
     ] + Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect {|s| [s.name, s.id, ["issues.priority_id=?", s.id]] }                                                      
     }
     
     search_filter_criteria('category_id') { 
-    [ ["[All]", "A", ["", false]],
-      ["[None]", "N", ["issues.category_id is null"]]
+    [ [_('[All]'), "A", nil],
+      [_('[None]'), "N", ["issues.category_id is null"]]
     ] + @project.issue_categories.find(:all).collect {|s| [s.name, s.id, ["issues.category_id=?", s.id]] }                                                      
     }    
 
     search_filter_criteria('assigned_to_id') { 
-    [ ["[All]", "A", ["", false]],
-      ["[Nobody]", "N", ["issues.assigned_to_id is null"]]
-    ] + User.find(:all).collect {|s| [s.display_name, s.id, ["issues.assigned_to_id=?", s.id]] }                                                      
-    }   	
+    [ [_('[All]'), "A", nil],
+      [_('[None]'), "N", ["issues.assigned_to_id is null"]]
+    ] + @project.users.collect {|s| [s.display_name, s.id, ["issues.assigned_to_id=?", s.id]] }                                                      
+    }
+
+    search_filter_criteria('subproject_id') { 
+    [ [_('[None]'), "N", ["issues.project_id=?", @project.id]],
+      [_('[All]'), "A", ["(issues.project_id=? or projects.parent_id=?)", @project.id, @project.id]]
+    ]                                                     
+    }  
   end
 end

redmine/app/models/attachment.rb

 				self.filename = sanitize_filename(@temp_file.original_filename)
 				self.disk_filename = DateTime.now.strftime("%y%m%d%H%M%S") + "_" + self.filename
 				self.content_type = @temp_file.content_type
-				self.size = @temp_file.size
+				self.filesize = @temp_file.size
 			end
 		end
 	end

redmine/app/models/enumeration.rb

   before_destroy :check_integrity
   
 	validates_presence_of :opt, :name
+	validates_uniqueness_of :name, :scope => [:opt]
 	
 	OPTIONS = [
 		["Issue priorities", "IPRI"],

redmine/app/models/issue_category.rb

 
 class IssueCategory < ActiveRecord::Base
   before_destroy :check_integrity  
-	belongs_to :project
-  
+  belongs_to :project
+
   validates_presence_of :name
+  validates_uniqueness_of :name, :scope => [:project_id]
   
 private
   def check_integrity

redmine/app/models/issue_status.rb

 
 class IssueStatus < ActiveRecord::Base
   before_destroy :check_integrity  
-	has_many :workflows, :foreign_key => "old_status_id"
-	
-	validates_presence_of :name
-	validates_uniqueness_of :name
+  has_many :workflows, :foreign_key => "old_status_id"
 
-	# Returns the default status for new issues
-	def self.default
-		find(:first, :conditions =>["is_default=?", true])
-	end
-	
-	# Returns an array of all statuses the given role can switch to
-	def new_statuses_allowed_to(role, tracker)
-		statuses = []
-		for workflow in self.workflows.find(:all, :include => :new_status)
-			statuses << workflow.new_status if workflow.role_id == role.id and workflow.tracker_id == tracker.id
-		end unless role.nil?
-		statuses
-	end
+  validates_presence_of :name
+  validates_uniqueness_of :name
+  validates_length_of :html_color, :is=>6
+  validates_format_of :html_color, :with => /^[a-f0-9]*$/i
+
+  # Returns the default status for new issues
+  def self.default
+    find(:first, :conditions =>["is_default=?", true])
+  end
+
+  # Returns an array of all statuses the given role can switch to
+  def new_statuses_allowed_to(role, tracker)
+    statuses = []
+    for workflow in self.workflows
+      statuses << workflow.new_status if workflow.role_id == role.id and workflow.tracker_id == tracker.id
+    end unless role.nil? or tracker.nil?
+    statuses
+  end
   
   def name
     _ self.attributes['name']

redmine/app/models/permission.rb

   end
   
   def self.allowed_to_public(action)
-    @@cached_perms_for_public ||= find(:all, :conditions => ["public=?", true]).collect {|p| "#{p.controller}/#{p.action}"}
+    @@cached_perms_for_public ||= find(:all, :conditions => ["is_public=?", true]).collect {|p| "#{p.controller}/#{p.action}"}
     @@cached_perms_for_public.include? action
   end
   

redmine/app/models/project.rb

 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class Project < ActiveRecord::Base
-	has_many :versions, :dependent => true, :order => "versions.date DESC"
+	has_many :versions, :dependent => true, :order => "versions.effective_date DESC"
 	has_many :members, :dependent => true
+	has_many :users, :through => :members
 	has_many :issues, :dependent => true, :order => "issues.created_on DESC", :include => :status
 	has_many :documents, :dependent => true
-	has_many :news, :dependent => true, :order => "news.created_on DESC", :include => :author
+	has_many :news, :dependent => true, :include => :author
 	has_many :issue_categories, :dependent => true
 	has_and_belongs_to_many :custom_fields
+	acts_as_tree :order => "name", :counter_cache => true
 	
 	validates_presence_of :name, :descr
+	validates_uniqueness_of :name
 	
 	# returns 5 last created projects
 	def self.latest
 		find(:all, :limit => 5, :order => "created_on DESC")	
 	end	
 	
-	# Returns current version of the project
-	def current_version
-		versions.find(:first, :conditions => [ "date <= ?", Date.today ], :order => "date DESC, id DESC")
-	end
-	
 	# Returns an array of all custom fields enabled for project issues
 	# (explictly associated custom fields and custom fields enabled for all projects)
 	def custom_fields_for_issues
 		(CustomField.for_all + custom_fields).uniq
-	end
+	end
+	
+protected
+  def validate
+    errors.add(parent_id, " must be a root project") if parent and parent.parent
+    errors.add_to_base("A project with subprojects can't be a subproject") if parent and projects_count > 0
+  end
 end

redmine/app/models/tracker.rb

   has_many :issues
   has_many :workflows, :dependent => true
   
+  validates_presence_of :name
+  validates_uniqueness_of :name
+  
   def name
     _ self.attributes['name']
   end

redmine/app/models/user.rb

 require "digest/sha1"
 
 class User < ActiveRecord::Base
-	has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :dependent => true
+  has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :dependent => true
 	
-	attr_accessor :password
-	attr_accessor :last_before_login_on
-	# Prevents unauthorized assignments
-	attr_protected :admin
+  attr_accessor :password, :password_confirmation
+  attr_accessor :last_before_login_on
+  # Prevents unauthorized assignments
+  attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
 	
-	validates_presence_of :login, :firstname, :lastname, :mail
-	validates_uniqueness_of :login, :mail
+  validates_presence_of :login, :firstname, :lastname, :mail
+  validates_uniqueness_of :login, :mail	
+  # Login must contain lettres, numbers, underscores only
+  validates_format_of :login, :with => /^[a-z0-9_]+$/i
+  validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
+  # Password length between 4 and 12
+  validates_length_of :password, :in => 4..12, :allow_nil => true
+  validates_confirmation_of :password, :allow_nil => true
+
+  def before_save
+    # update hashed_password if password was set
+    self.hashed_password = User.hash_password(self.password) if self.password
+  end
 	
-	# Login must contain lettres, numbers, underscores only
-	validates_format_of :login, :with => /^[a-z0-9_]+$/i
-	validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
+  # Returns the user that matches provided login and password, or nil
+  def self.try_to_login(login, password)
+    user = find(:first, :conditions => ["login=? and hashed_password=? and locked=?", login, User.hash_password(password), false])
+    if user
+      user.last_before_login_on = user.last_login_on
+      user.update_attribute(:last_login_on, Time.now)
+    end
+    user
+  end
 	
-	def before_create
-		self.hashed_password = User.hash_password(self.password)
-	end
-	
-	def after_create
-		@password = nil
-	end
+  # Return user's full name for display
+  def display_name
+    firstname + " " + lastname
+  end
 
-	# Returns the user that matches user's login and password
-	def try_to_login
-		@user = User.login(self.login, self.password)
-		unless @user.nil? 
-			@user.last_before_login_on = @user.last_login_on
-			@user.update_attribute(:last_login_on, DateTime.now)
-		end
-		@user
-	end
-	
-	# Return user's full name for display
-	def display_name
-		firstname + " " + lastname #+ (self.admin ? " (Admin)" : "" )
-	end
+  def check_password?(clear_password)
+    User.hash_password(clear_password) == self.hashed_password
+  end
 
-	# Returns the user that matches the given login and password
-	def self.login(login, password)
-		hashed_password = hash_password(password || "")
-		find(:first,
-			:conditions => ["login = ? and hashed_password = ? and locked = ?", login, hashed_password, false])
-	end
-	
-	def check_password?(clear_password)
-		User.hash_password(clear_password) == self.hashed_password
-	end
-	
-	def change_password(current_password, new_password)
-		self.hashed_password = User.hash_password(new_password)
-		save
-	end
   
   def role_for_project(project_id)
     @role_for_projects ||=
   end
 	
 private
-	# Return password digest
-	def self.hash_password(clear_password)
-		Digest::SHA1.hexdigest(clear_password)
-	end
+  # Return password digest
+  def self.hash_password(clear_password)
+    Digest::SHA1.hexdigest(clear_password || "")
+  end
 end

redmine/app/models/version.rb

 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class Version < ActiveRecord::Base
-  before_destroy :check_integrity  
-	belongs_to :project
-	has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
+  before_destroy :check_integrity
+  belongs_to :project
+  has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
   has_many :attachments, :as => :container, :dependent => true
-	
-	validates_presence_of :name, :descr
+
+  validates_presence_of :name
+  validates_uniqueness_of :name, :scope => [:project_id]
   
 private
   def check_integrity

redmine/app/models/workflow.rb

 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class Workflow < ActiveRecord::Base
+  belongs_to :role
+  belongs_to :old_status, :class_name => 'IssueStatus', :foreign_key => 'old_status_id'
+  belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
 
-	belongs_to :role
-	belongs_to :old_status, :class_name => 'IssueStatus', :foreign_key => 'old_status_id'
-	belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
-
-	validates_presence_of :role, :old_status, :new_status
+  validates_presence_of :role, :old_status, :new_status
 end

redmine/app/views/account/login.rhtml

 <div class="box">
-<h2><%=_ 'Please login' %></h2>
+<h2><%=_('Please login') %></h2>
 
 <%= start_form_tag :action=> "login" %>
-<p><label for="user_login"><%=_ 'Login' %>:</label><br/>
-<input type="text" name="user[login]" id="user_login" size="30" /></p>
+<p><label for="login"><%=_ 'Login' %>:</label><br/>
+<%= text_field_tag 'login', nil, :size => 25 %></p>
 
 <p><label for="user_password"><%=_ 'Password' %>:</label><br/>
-<input type="password" name="user[password]" id="user_password" size="30"/></p>
+<%= password_field_tag 'password', nil, :size => 25 %></p>
 
 <p><input type="submit" name="login" value="<%=_ 'Log in' %> &#187;" class="primary" /></p>
 <%= end_form_tag %>

redmine/app/views/account/my_account.rhtml

 <p><%=_('Login')%>: <strong><%= @user.login %></strong><br />
 <%=_('Created on')%>: <%= format_time(@user.created_on) %>,
 <%=_('Last update')%>: <%= format_time(@user.updated_on) %></p>
+
+<%= error_messages_for 'user' %>
 
 <div class="splitcontentleft">
   <div class="box">
   <h3><%=_('Information')%></h3>
   &nbsp;
   <%= start_form_tag :action => 'my_account' %>
-  <%= error_messages_for 'user' %>
 
   <!--[form:user]-->
   <p><label for="user_firstname"><%=_('Firstname')%> <span class="required">*</span></label><br/>
   &nbsp;
   <%= start_form_tag :action => 'change_password' %>
 
-  <p><label for="old_password"><%=_('Password')%> <span class="required">*</span></label><br/>
-  <%= password_field_tag 'old_password' %></p>
+  <p><label for="password"><%=_('Password')%> <span class="required">*</span></label><br/>
+  <%= password_field_tag 'password', nil, :size => 25 %></p>
 
   <p><label for="new_password"><%=_('New password')%> <span class="required">*</span></label><br/>
-  <%= password_field_tag 'new_password' %></p>
+  <%= password_field_tag 'new_password', nil, :size => 25 %></p>
 
   <p><label for="new_password_confirmation"><%=_('Confirmation')%> <span class="required">*</span></label><br/>
-  <%= password_field_tag 'new_password_confirmation' %></p>
+  <%= password_field_tag 'new_password_confirmation', nil, :size => 25 %></p>
 
   <center><%= submit_tag _('Save') %></center>
   <%= end_form_tag %>

redmine/app/views/admin/info.rhtml

 <h2><%=_('Information')%></h2>
 
-<%=_('Version')%>: <%= RDM_APP_NAME %> <%= RDM_APP_VERSION %><br />
-<%=_('Database')%>: <%= @adapter_name %>
+<p><%=_('Version')%>: <strong><%= RDM_APP_NAME %> <%= RDM_APP_VERSION %></strong></p>
+
+Environment:
+<ul>
+<% Rails::Info.properties.each do |name, value| %>
+<li><%= name %>: <%= value %></li>
+<% end %>
+</ul>

redmine/app/views/admin/projects.rhtml

 
 <table width="100%" cellspacing="1" cellpadding="2" class="listTableContent">
   <tr class="ListHead">
-	<%= sort_header_tag('projects.name', :caption => _('Project')) %>
+	<%= sort_header_tag('name', :caption => _('Project')) %>
 	<th><%=_('Description')%></th>
-	<th><%=_('Public')%></th>
-	<%= sort_header_tag('projects.created_on', :caption => _('Created on')) %>
+	<th><%=_('Public')%></th>
+	<th><%=_('Subprojects')%></th>
+	<%= sort_header_tag('created_on', :caption => _('Created on')) %>
   <th></th>
   </tr>
   
-<% odd_or_even = 1
-	for project in @projects
-	odd_or_even = 1 - odd_or_even %>
-  <tr class="ListLine<%= odd_or_even %>">
+<% for project in @projects %>
+  <tr class="<%= cycle("odd", "even") %>">
 	<td><%= link_to project.name, :controller => 'projects', :action => 'settings', :id => project %>
 	<td><%= project.descr %>
-	<td align="center"><%= image_tag 'true' if project.public? %>
+	<td align="center"><%= image_tag 'true' if project.is_public? %>
+	<td align="center"><%= project.projects_count %>
 	<td align="center"><%= format_date(project.created_on) %>
   <td align="center">
     <%= start_form_tag({:controller => 'projects', :action => 'destroy', :id => project}) %>
 <% end %>
 </table>
 
-<%= link_to ('&#171; ' + _('Previous')), { :page => @project_pages.current.previous } if @project_pages.current.previous %>
-<%= pagination_links(@project_pages) %>
-<%= link_to (_('Next') + ' &#187;'), { :page => @project_pages.current.next } if @project_pages.current.next %>
-
-<br />
-
-<%= link_to ('&#187; ' + _('New project')), :controller => 'projects', :action => 'add' %>
+<p><%= pagination_links_full @project_pages %>
+[ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]</p>
+
+<p><%= link_to ('&#187; ' + _('New project')), :controller => 'projects', :action => 'add' %></p>

redmine/app/views/custom_fields/list.rhtml

 	<th></th>
 </tr>  
 <% for custom_field in @custom_fields %>
-  <tr style="background-color:#CEE1ED">
+  <tr class="<%= cycle("odd", "even") %>">
 	<td><%= link_to custom_field.name, :action => 'edit', :id => custom_field %></td>
 	<td align="center"><%= CustomField::TYPES[custom_field.typ][0] %></td>
 	<td align="center"><%= image_tag 'true' if custom_field.is_required? %></td>

redmine/app/views/documents/show.rhtml

   <td><%= link_to attachment.filename, :action => 'download', :id => @document, :attachment_id => attachment %></td>
   <td align="center"><%= format_date(attachment.created_on) %></td>
   <td align="center"><%= attachment.author.display_name %></td>
-  <td><%= human_size(attachment.size) %><br /><%= attachment.downloads %> <%=_('download')%>(s)</td>
+  <td><%= human_size(attachment.filesize) %><br /><%= attachment.downloads %> <%=_('download')%>(s)</td>
+  
   <% if authorize_for('documents', 'destroy_attachment') %>
   <td align="center">
     <%= start_form_tag :action => 'destroy_attachment', :id => @document, :attachment_id => attachment %>
     <%= submit_tag _('Delete'), :class => "button-small" %>
     <%= end_form_tag %>
   </tr>
-  <% end %>
+  <% end %>
+  
 <% end %>
 </table>
 <br />

redmine/app/views/issue_statuses/_form.rhtml

 <%= error_messages_for 'issue_status' %>
 
 <!--[form:issue_status]-->
-<p><label for="issue_status_name"><%=_('Name')%></label><br/>
+<p><label for="issue_status_name"><%=_('Name')%></label> <span class="required">*</span><br/>
 <%= text_field 'issue_status', 'name'  %></p>
 
 <p><%= check_box 'issue_status', 'is_closed' %>
 <label for="issue_status_is_default"><%=_('Default status')%></label></p>
 
 <p><label for="issue_status_html_color"><%=_('Color')%></label>
-#<%= text_field 'issue_status', 'html_color', :size => 6 %></p>
+#<%= text_field 'issue_status', 'html_color', :maxlength => 6 %> <span class="required">*</span></p>
 
 <!--[eoform:issue_status]-->
 

redmine/app/views/issue_statuses/list.rhtml

   </tr>
   
 <% for status in @issue_statuses %>
-  <tr style="background-color:#CEE1ED">
+  <tr class="<%= cycle("odd", "even") %>">
   <td><%= link_to status.name, :action => 'edit', :id => status %></td>
-  <td align="center"><%= image_tag 'true' if status.is_default %></td>
-  <td align="center"><%= image_tag 'true' if status.is_closed %></td>  
+  <td align="center"><%= image_tag 'true' if status.is_default? %></td>
+  <td align="center"><%= image_tag 'true' if status.is_closed? %></td>  
   <td bgcolor="#<%= status.html_color %>">&nbsp</td>  
   <td align="center">
     <%= start_form_tag :action => 'destroy', :id => status %>

redmine/app/views/issues/show.rhtml

 <p><b><%=_('Assigned to')%>:</b> <%= @issue.assigned_to.display_name unless @issue.assigned_to.nil? %></p>
 
 <p><b><%=_('Subject')%>:</b> <%= @issue.subject %></p>
-<p><b><%=_('Description')%>:</b> <%= @issue.descr %></p>
+<p><b><%=_('Description')%>:</b> <%= simple_format auto_link @issue.descr %></p>
 <p><b><%=_('Created on')%>:</b> <%= format_date(@issue.created_on) %></p>
 
 <% if authorize_for('issues', 'edit') %>
 <table width="100%">
 <% for attachment in @issue.attachments %>
 <tr>
-<td><%= link_to attachment.filename, :action => 'download', :id => @issue, :attachment_id => attachment %> (<%= human_size(attachment.size) %>)</td>
+<td><%= link_to attachment.filename, :action => 'download', :id => @issue, :attachment_id => attachment %> (<%= human_size(attachment.filesize) %>)</td>
 <td><%= format_date(attachment.created_on) %></td>
 <td><%= attachment.author.display_name %></td>
 <% if authorize_for('issues', 'destroy_attachment') %>

redmine/app/views/layouts/base.rhtml

 	</div>
 	
 	<div id="footer">
-		<p><a href="http://redmine.sourceforge.net/" target="_new"><%= RDM_APP_NAME %></a> <%= RDM_APP_VERSION %></p>
+		<p><a href="http://redmine.org/" target="_new"><%= RDM_APP_NAME %></a> <%= RDM_APP_VERSION %></p>
 	</div>
 
 </div>

redmine/app/views/news/show.rhtml

 <b><%=_('Date')%></b>: <%= format_time(@news.created_on) %>
 </p>
 
-<%= @news.descr %>
+<%= simple_format auto_link @news.descr %>
 

redmine/app/views/projects/_form.rhtml

 <p><label for="project_name"><%=_('Name')%> <span class="required">*</span></label><br/>
 <%= text_field 'project', 'name'  %></p>
 
+<% if session[:user].admin %>
+<p><label for="project_parent_id"><%=_('Subproject of')%></label><br/>
+<select name="project[parent_id]">
+<option value=""></option>
+<%= options_from_collection_for_select @root_projects, "id", "name", @project.parent_id %>
+</select></p>
+<% end %>
+
 <p><label for="project_descr"><%=_('Description')%> <span class="required">*</span></label><br/>
-<%= text_field 'project', 'descr', :size => 60 %></p>
+<%= text_area 'project', 'descr', :cols => 60, :rows => 3 %></p>
 
 <p><label for="project_homepage"><%=_('Homepage')%></label><br/>
 <%= text_field 'project', 'homepage', :size => 40 %></p>
 
-<p><%= check_box 'project', 'public' %>
-<label for="project_public"><%=_('Public')%></label></p>
-
+<p><%= check_box 'project', 'is_public' %>
+<label for="project_is_public"><%=_('Public')%></label></p>
+
 <fieldset><legend><%=_('Custom fields')%></legend>
 <% for custom_field in @custom_fields %>
 	<input type="checkbox"

redmine/app/views/projects/changelog.rhtml

 
 <% fixed_issues = @fixed_issues.group_by {|i| i.fixed_version } %>
 <% fixed_issues.each do |version, issues| %>
-  <p><strong><%= version.name %></strong> - <%= format_date(version.date) %><br />
+  <p><strong><%= version.name %></strong> - <%= format_date(version.effective_date) %><br />
   <%=h version.descr %></p>
   <ul>  
     <% issues.each do |i| %>

redmine/app/views/projects/list.rhtml

 
 <table width="100%" cellspacing="1" cellpadding="2" class="listTableContent">
   <tr class="ListHead">
-	<%= sort_header_tag('projects.name', :caption => _('Project')) %>
+	<%= sort_header_tag('name', :caption => _('Project')) %>
 	<th>Description</th>
-	<%= sort_header_tag('projects.created_on', :caption => _('Created on')) %>
+	<%= sort_header_tag('created_on', :caption => _('Created on')) %>
   </tr>
   
-<% odd_or_even = 1
-	for project in @projects
-	odd_or_even = 1 - odd_or_even %>
-  <tr class="ListLine<%= odd_or_even %>">
+<% for project in @projects %>
+  <tr class="<%= cycle("odd", "even") %>">
 	<td><%= link_to project.name, :action => 'show', :id => project %>
 	<td><%= project.descr %>
 	<td align="center"><%= format_date(project.created_on) %>

redmine/app/views/projects/list_files.rhtml

   <tr>
     <td colspan="7"><%= image_tag 'package' %> <b><%= version.name %></b></td>
   </tr>
-  <%  odd_or_even = 1
-        for file in version.attachments
-        odd_or_even = 1 - odd_or_even %>		
-  <tr class="ListLine<%= odd_or_even %>">
+  <% for file in version.attachments %>		
+  <tr class="<%= cycle("odd", "even") %>">
     <td></td>
     <td><%= link_to file.filename, :controller => 'versions', :action => 'download', :id => version, :attachment_id => file %></td>
     <td align="center"><%= format_date(file.created_on) %></td>
-    <td align="center"><%= human_size(file.size) %></td>
+    <td align="center"><%= human_size(file.filesize) %></td>
     <td align="center"><%= file.downloads %></td>
     <td align="center"><small><%= file.digest %></small></td>
     <% if delete_allowed %>
     </td>
     <% end %>
   </tr>		
-  <% end %>
+  <% end
+  reset_cycle %>
 <% end %>
 </table>
 

redmine/app/views/projects/list_issues.rhtml

 <form method="post" class="noborder">
 	<table cellpadding=2>
   <tr>
-    <td><%=_('Status')%>:<br /><%= search_filter_tag("status_id") %></td>
-    <td><%=_('Tracker')%>:<br /><%= search_filter_tag("tracker_id") %></td>
-    <td><%=_('Priority')%>:<br /><%= search_filter_tag("priority_id") %></td>
-    <td><%=_('Category')%>:<br /><%= search_filter_tag("category_id") %></td>
-    <td><%=_('Assigned to')%>:<br /><%= search_filter_tag("assigned_to_id") %></td>
+    <td><small><%=_('Status')%>:</small><br /><%= search_filter_tag 'status_id', :class => 'select-small' %></td>
+    <td><small><%=_('Tracker')%>:</small><br /><%= search_filter_tag 'tracker_id', :class => 'select-small' %></td>
+    <td><small><%=_('Priority')%>:</small><br /><%= search_filter_tag 'priority_id', :class => 'select-small' %></td>
+    <td><small><%=_('Category')%>:</small><br /><%= search_filter_tag 'category_id', :class => 'select-small' %></td>
+    <td><small><%=_('Assigned to')%>:</small><br /><%= search_filter_tag 'assigned_to_id', :class => 'select-small' %></td>
+    <td><small><%=_('Subprojects')%>:</small><br /><%= search_filter_tag 'subproject_id', :class => 'select-small' %></td>
+
     <td valign="bottom">
-      <%= submit_tag _('Apply filter') %>
+      <%= submit_tag _('Apply filter'), :class => 'button-small' %>
       <%= end_form_tag %>
       
       <%= start_form_tag %>
-      <%= submit_tag _('Reset') %>
+      <%= submit_tag _('Reset'), :class => 'button-small' %>
       <%= end_form_tag %>
     </td>
   </tr>

redmine/app/views/projects/show.rhtml

     <% for tracker in Tracker.find_all %>    
       <li><%= link_to tracker.name, :controller => 'projects', :action => 'list_issues', :id => @project, 
                                                 :set_filter => 1, 
-                                                "issues.tracker_id" => tracker.id %>:
-      <%= tracker.issues.count(["project_id=?", @project.id]) %> <%=_('open')%>
+                                                "tracker_id" => tracker.id %>:
+      <%= Issue.count(:conditions => ["project_id=? and tracker_id=? and issue_statuses.is_closed=?", @project.id, tracker.id, false], :include => :status) %> <%=_('open')%>
       </li>
     <% end %>
     </ul>
 		<% end %>		
 	</div>
   
+  <% if @subprojects %>
+ 	<div class="box">
+		<h3><%= image_tag "projects" %> <%=_('Subprojects')%></h3>	
+		<% for subproject in @subprojects %>
+		<%= link_to subproject.name, :action => 'show', :id => subproject %><br />		
+		<% end %>		
+	</div>
+  <% end %>
+  
   <div class="box">
     <h3><%=_('Latest news')%></h3>  
-    <% for news in @project.news %>
+    <% for news in @news %>
       <p>
       <b><%= news.title %></b> <small>(<%= link_to_user news.author %> <%= format_time(news.created_on) %>)</small><br />
       <%= news.shortdescr %>

redmine/app/views/roles/list.rhtml

   </tr>
   
 <% for role in @roles %>
-  <tr style="background-color:#CEE1ED">
+  <tr class="<%= cycle("odd", "even") %>">
   <td><%= link_to role.name, :action => 'edit', :id => role %></td>
   <td align="center">
     <%= start_form_tag :action => 'destroy', :id => role %>

redmine/app/views/trackers/list.rhtml

   </tr>
   
 <% for tracker in @trackers %>
-  <tr style="background-color:#CEE1ED">
+  <tr class="<%= cycle("odd", "even") %>">
   <td><%= link_to tracker.name, :action => 'edit', :id => tracker %></td>
   <td align="center">
     <%= start_form_tag :action => 'destroy', :id => tracker %>

redmine/app/views/users/_form.rhtml

 
 <!--[form:user]-->
 <p><label for="user_login"><%=_('Login')%></label><br/>
-<%= text_field 'user', 'login'  %></p>
+<%= text_field 'user', 'login', :size => 25  %></p>
 
-<p><label for="user_password"><%=_('Password')%></label><br/>
-<%= password_field 'user', 'password'  %></p>
+<p><label for="password"><%=_('Password')%></label><br/>
+<%= password_field_tag 'password', nil, :size => 25  %></p>
+
+<p><label for="password_confirmation"><%=_('Confirmation')%></label><br/>
+<%= password_field_tag 'password_confirmation', nil, :size => 25  %></p>
 
 <p><label for="user_firstname"><%=_('Firstname')%></label><br/>
 <%= text_field 'user', 'firstname'  %></p>

redmine/app/views/users/list.rhtml

 
 <table border="0" cellspacing="1" cellpadding="2" class="listTableContent">		
 <tr class="ListHead">
-	<%= sort_header_tag('users.login', :caption => _('Login')) %>
-	<%= sort_header_tag('users.firstname', :caption => _('Firstname')) %>
-	<%= sort_header_tag('users.lastname', :caption => _('Lastname')) %>
+	<%= sort_header_tag('login', :caption => _('Login')) %>
+	<%= sort_header_tag('firstname', :caption => _('Firstname')) %>
+	<%= sort_header_tag('lastname', :caption => _('Lastname')) %>
 	<th><%=_('Mail')%></th>
-	<%= sort_header_tag('users.admin', :caption => _('Admin')) %>
-	<%= sort_header_tag('users.locked', :caption => _('Locked')) %>
-	<%= sort_header_tag('users.created_on', :caption => _('Created on')) %>
-	<%= sort_header_tag('users.last_login_on', :caption => _('Last login')) %>
+	<%= sort_header_tag('admin', :caption => _('Admin')) %>
+	<%= sort_header_tag('locked', :caption => _('Locked')) %>
+	<%= sort_header_tag('created_on', :caption => _('Created on')) %>
+	<%= sort_header_tag('last_login_on', :caption => _('Last login')) %>
   <th></th>
 </tr>  
 <% for user in @users %>
-  <tr style="background-color:#CEE1ED">
+  <tr class="<%= cycle("odd", "even") %>">
 	<td><%= link_to user.login, :action => 'edit', :id => user %></td>
 	<td><%= user.firstname %></td>
 	<td><%= user.lastname %></td>
   <td align="center">
     <%= start_form_tag :action => 'edit', :id => user %>
     <% if user.locked? %>
-      <%= hidden_field_tag 'user[locked]', false %>
+      <%= hidden_field_tag 'user[locked]', 0 %>
       <%= submit_tag _('Unlock'), :class => "button-small"  %>
     <% else %>
-      <%= hidden_field_tag 'user[locked]', true %>
+      <%= hidden_field_tag 'user[locked]', 1 %>
       <%= submit_tag _('Lock'), :class => "button-small"  %>
     <% end %>
     <%= end_form_tag %>  

redmine/app/views/versions/_form.rhtml

 <%= error_messages_for 'version' %>
 
 <!--[form:version]-->
-<p><label for="version_name"><%=_('Version')%></label><br/>
+<p><label for="version_name"><%=_('Version')%></label> <span class="required">*</span><br/>
 <%= text_field 'version', 'name', :size => 20  %></p>
 
 <p><label for="version_descr"><%=_('Description')%></label><br/>
 <%= text_field 'version', 'descr', :size => 60  %></p>
 
-<p><label for="version_date"><%=_('Date')%></label><br/>
-<%= date_select 'version', 'date'  %></p>
+<p><label for="version_effective_date"><%=_('Date')%></label><br/>
+<%= date_select 'version', 'effective_date'  %></p>
 <!--[eoform:version]-->
 

redmine/app/views/welcome/index.rhtml

 	<h2><%=_('Welcome')%> !</h2>
 
   <div class="box">
-	<h3>Latest news</h3>
+	<h3><%=_('Latest news')%></h3>
 		<% for news in @news %>
 			<p>
 			<b><%= news.title %></b> (<%= link_to_user news.author %> <%= format_time(news.created_on) %> - <%= news.project.name %>)<br />
 
 <div class="splitcontentright">
 	<div class="box">
-	<h3>Latest projects</h3>
+	<h3><%=_('Latest projects')%></h3>
 		<ul>
 		<% for project in @projects %>
 			<li>

redmine/config/database.yml

 #   (on OS X: gem install mysql -- --include=/usr/local/lib)
 # And be sure to use new-style password hashing:
 #   http://dev.mysql.com/doc/refman/5.0/en/old-client.html
+
+production:
+  adapter: mysql
+  database: redmine
+  host: localhost
+  username: root
+  password:
+ 
 development:
   adapter: mysql
   database: redmine_development
   host: localhost
   username: postgres
   password: "postgres"
+
+development_oracle:
+  adapter: oci
+  host: 192.168.0.14
+  username: rails
+  password: "rails"
+
+development_sqlserver:
+  adapter: sqlserver
+  host: localhost,1157
+  database: redmine
 
 test:
   adapter: mysql
   host: localhost
   username: root
   password:
-
+
+test_pgsql:
+  adapter: postgresql
+  database: redmine
+  host: localhost
+  username: postgres
+  password: "postgres"
+
+test_oracle:
+  adapter: oci
+  host: 192.168.0.14
+  username: rails_test
+  password: "rails"
+  
+test_sqlserver:
+  adapter: sqlserver
+  host: localhost,1157
+  database: redmine_test
+  
 demo:
   adapter: sqlite3
   dbfile: db/redmine_demo.db
   
-production:
-  adapter: mysql
-  database: redmine
-  host: localhost
-  username: root
-  password:
- 

redmine/config/environment.rb

 # application name
 RDM_APP_NAME = "redMine" 
 # application version
-RDM_APP_VERSION = "0.1.0" 
+RDM_APP_VERSION = "0.2.0" 
 # application host name
 RDM_HOST_NAME = "somenet.foo"
 # file storage path

redmine/config/environments/development_oracle.rb

+# Settings specified here will take precedence over those in config/environment.rb
+
+# In the development environment your application's code is reloaded on
+# every request.  This slows down response time but is perfect for development
+# since you don't have to restart the webserver when you make code changes.
+config.cache_classes     = false
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils        = true
+
+# Enable the breakpoint server that script/breakpointer connects to
+config.breakpoint_server = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+
+# Don't care if the mailer can't send
+config.action_mailer.raise_delivery_errors = false

redmine/config/environments/development_sqlserver.rb

+# Settings specified here will take precedence over those in config/environment.rb
+
+# In the development environment your application's code is reloaded on
+# every request.  This slows down response time but is perfect for development
+# since you don't have to restart the webserver when you make code changes.
+config.cache_classes     = false
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils        = true
+
+# Enable the breakpoint server that script/breakpointer connects to
+config.breakpoint_server = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+
+# Don't care if the mailer can't send
+config.action_mailer.raise_delivery_errors = false

redmine/config/environments/test_oracle.rb

+# Settings specified here will take precedence over those in config/environment.rb
+
+# The test environment is used exclusively to run your application's
+# test suite.  You never need to work with it otherwise.  Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs.  Don't rely on the data there!
+config.cache_classes = true
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils    = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+

redmine/config/environments/test_pgsql.rb

+# Settings specified here will take precedence over those in config/environment.rb
+
+# The test environment is used exclusively to run your application's
+# test suite.  You never need to work with it otherwise.  Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs.  Don't rely on the data there!
+config.cache_classes = true
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils    = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+

redmine/config/environments/test_sqlserver.rb

+# Settings specified here will take precedence over those in config/environment.rb
+
+# The test environment is used exclusively to run your application's
+# test suite.  You never need to work with it otherwise.  Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs.  Don't rely on the data there!
+config.cache_classes = true
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils    = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+

redmine/config/help.yml

-# administration
-admin:
-  index:    administration.html
-  mail_options: administration.html#mail_notifications
-  info:       administration.html#app_info
-users:
-  index:    administration.html#users
-roles:
-  index:    administration.html#roles
-  workflow: administration.html#workflow
-trackers:
-  index:    administration.html#trackers
-issue_statuses:
-  index:    administration.html#issue_statuses  
+# redMine - project management software
+# Copyright (C) 2006  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-# projects
-projects:
-  add:        projects.html#settings
+# available languages for help pages
+langs:
+  - fr
 
-
-# issues
+# mapping between controller/action and help pages
+# if action is not defined here, 'index' page will be displayed
+pages:
+  # administration
+  admin:
+    index:          ch01.html
+    mail_options:   ch01s08.html
+    info:           ch01s09.html
+  users:
+    index:          ch01s01.html
+  roles:
+    index:          ch01s02.html
+    workflow:       ch01s06.html
+  trackers:
+    index:          ch01s03.html
+  issue_statuses:
+    index:          ch01s05.html
+  # projects
+  projects:
+    index:          ch02.html
+    add:            ch02s08.html
+    show:           ch02s01.html
+    add_document:   ch02s06.html
+    list_documents: ch02s06.html
+    add_issue:      ch02s02.html
+    list_issues:    ch02s02.html
+    add_news:       ch02s05.html
+    list_news:      ch02s05.html
+    add_file:       ch02s07.html
+    list_files:     ch02s07.html
+    changelog:      ch02s04.html
+  issues:
+    index:          ch02s02.html
+  documents:
+    index:          ch02s06.html
+  news:
+    index:          ch02s05.html
+  versions:
+    index:          ch02s08.html
+  reports:
+    index:          ch02s03.html

redmine/db/migrate/001_setup.rb

+# redMine - project management software
+# Copyright (C) 2006  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.