Commit 6b7650e2 authored by Jean-Philippe Lang's avatar Jean-Philippe Lang

Initial commit

git-svn-id: http://redmine.rubyforge.org/svn/trunk@4 e93f8b46-1217-0410-a6f0-8f06a7374b81
parent 73e0b8f8
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/switchtower.rake, and they will automatically be available to Rake.
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'tasks/rails'
\ No newline at end of file
# 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.
class AccountController < ApplicationController
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]
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
# Log out current user and redirect to welcome page
def logout
session[:user] = nil
redirect_to(:controller => '')
end
def my_page
@user = session[:user]
@reported_issues = Issue.find(:all, :conditions => ["author_id=?", @user.id], :limit => 10, :include => [ :status, :project, :tracker ], :order => 'issues.updated_on DESC')
@assigned_issues = Issue.find(:all, :conditions => ["assigned_to_id=?", @user.id], :limit => 10, :include => [ :status, :project, :tracker ], :order => 'issues.updated_on DESC')
end
# Edit current user's account
def my_account
@user = User.find(session[:user].id)
if request.post? and @user.update_attributes(@params[:user])
flash[:notice] = 'Account was successfully updated.'
session[:user] = @user
set_localization
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
end
# 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.
class AdminController < ApplicationController
layout 'base'
before_filter :require_admin
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
end
def mail_options
@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.save
}
flash[:notice] = "Mail options were successfully updated."
end
end
def info
@adapter_name = ActiveRecord::Base.connection.adapter_name
end
end
# 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.
class ApplicationController < ActionController::Base
before_filter :check_if_login_required, :set_localization
# check if login is globally required to access the application
def check_if_login_required
require_login if RDM_LOGIN_REQUIRED
end
def set_localization
Localization.lang = session[:user].nil? ? RDM_DEFAULT_LANG : (session[:user].language || RDM_DEFAULT_LANG)
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
# 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] ]
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 session[:user].admin? or Permission.allowed_to_role( "%s/%s" % [ @params[:controller], @params[:action] ], session[:user].role_for_project(@project.id) )
return true
end
flash[:notice] = "Acces denied"
redirect_to(:controller => "")
return 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
end
\ No newline at end of file
# 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.
class CustomFieldsController < ApplicationController
layout 'base'
before_filter :require_admin
def index
list
render :action => 'list'
end
def list
@custom_field_pages, @custom_fields = paginate :custom_fields, :per_page => 10
end
def new
if request.get?
@custom_field = CustomField.new
else
@custom_field = CustomField.new(params[:custom_field])
if @custom_field.save
flash[:notice] = 'CustomField was successfully created.'
redirect_to :action => 'list'
end
end
end
def edit
@custom_field = CustomField.find(params[:id])
if request.post? and @custom_field.update_attributes(params[:custom_field])
flash[:notice] = 'CustomField was successfully updated.'
redirect_to :action => 'list'
end
end
def destroy
CustomField.find(params[:id]).destroy
redirect_to :action => 'list'
rescue
flash[:notice] = "Unable to delete custom field"
redirect_to :action => 'list'
end
end
# 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.
class DocumentsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def show
end
def edit
@categories = Enumeration::get_values('DCAT')
if request.post? and @document.update_attributes(params[:document])
flash[:notice] = 'Document was successfully updated.'
redirect_to :action => 'show', :id => @document
end
end
def destroy
@document.destroy
redirect_to :controller => 'projects', :action => 'list_documents', :id => @project
end
def download
@attachment = @document.attachments.find(params[:attachment_id])
@attachment.increment_download
send_file @attachment.diskfile, :filename => @attachment.filename
end
def add_attachment
# Save the attachment
if params[:attachment][:file].size > 0
@attachment = @document.attachments.build(params[:attachment])
@attachment.author_id = session[:user].id unless session[:user].nil?
@attachment.save
end
render :action => 'show'
end
def destroy_attachment
@document.attachments.find(params[:attachment_id]).destroy
render :action => 'show'
end
private
def find_project
@document = Document.find(params[:id])
@project = @document.project
end
end
# 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.
class EnumerationsController < ApplicationController
layout 'base'
before_filter :require_admin
def index
list
render :action => 'list'
end
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy, :create, :update ],
:redirect_to => { :action => :list }
def list
end
def new
@enumeration = Enumeration.new(:opt => params[:opt])
end
def create
@enumeration = Enumeration.new(params[:enumeration])
if @enumeration.save
flash[:notice] = 'Enumeration was successfully created.'
redirect_to :action => 'list', :opt => @enumeration.opt
else
render :action => 'new'
end
end
def edit
@enumeration = Enumeration.find(params[:id])
end
def update
@enumeration = Enumeration.find(params[:id])
if @enumeration.update_attributes(params[:enumeration])
flash[:notice] = 'Enumeration was successfully updated.'
redirect_to :action => 'list', :opt => @enumeration.opt
else
render :action => 'edit'
end
end
def destroy
Enumeration.find(params[:id]).destroy
redirect_to :action => 'list'
rescue
flash[:notice] = "Unable to delete enumeration"
redirect_to :action => 'list'
end
end
# 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.
class HelpController < ApplicationController
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}"
else
redirect_to "/manual/"
end
end
private
def load_help_config
@help_config = YAML::load(File.open("#{RAILS_ROOT}/config/help.yml"))
end
end
# 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.
class IssueCategoriesController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def edit
if request.post? and @category.update_attributes(params[:category])
flash[:notice] = 'Issue category was successfully updated.'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
def destroy
@category.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project
rescue
flash[:notice] = "Categorie can't be deleted"
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
private
def find_project
@category = IssueCategory.find(params[:id])
@project = @category.project
end
end
# 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.
class IssueStatusesController < ApplicationController
layout 'base'
before_filter :require_admin
def index
list
render :action => 'list'
end
def list
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 10
end
def new
@issue_status = IssueStatus.new
end
def create
@issue_status = IssueStatus.new(params[:issue_status])
if @issue_status.save
flash[:notice] = 'IssueStatus was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
def edit
@issue_status = IssueStatus.find(params[:id])
end
def update
@issue_status = IssueStatus.find(params[:id])
if @issue_status.update_attributes(params[:issue_status])
flash[:notice] = 'IssueStatus was successfully updated.'
redirect_to :action => 'list'
else
render :action => 'edit'
end
end
def destroy
IssueStatus.find(params[:id]).destroy
redirect_to :action => 'list'
rescue
flash[:notice] = "Unable to delete issue status"
redirect_to :action => 'list'
end
end
# 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.
class IssuesController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
helper :custom_fields
include CustomFieldsHelper
def show
@status_options = @issue.status.workflows.find(:all, :conditions => ["role_id=? and tracker_id=?", session[:user].role_for_project(@project.id), @issue.tracker.id]).collect{ |w| w.new_status } if session[:user]
end
def edit
@trackers = Tracker.find(:all)
@priorities = Enumeration::get_values('IPRI')
if request.get?
@custom_values = @project.custom_fields_for_issues.collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
else
# Retrieve custom fields and values
@custom_values = @project.custom_fields_for_issues.collect { |x| CustomValue.new(:custom_field => x, :value => params["custom_fields"][x.id.to_s]) }
@issue.custom_values = @custom_values
if @issue.update_attributes(params[:issue])
flash[:notice] = 'Issue was successfully updated.'
redirect_to :action => 'show', :id => @issue
end
end
end
def change_status
@history = @issue.histories.build(params[:history])
@status_options = @issue.status.workflows.find(:all, :conditions => ["role_id=? and tracker_id=?", session[:user].role_for_project(@project.id), @issue.tracker.id]).collect{ |w| w.new_status } if session[:user]
if params[:confirm]
unless session[:user].nil?
@history.author = session[:user]
end
if @history.save
@issue.status = @history.status
@issue.fixed_version_id = (params[:issue][:fixed_version_id])
@issue.assigned_to_id = (params[:issue][:assigned_to_id])
if @issue.save
flash[:notice] = 'Issue was successfully updated.'
Mailer.deliver_issue_change_status(@issue) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
redirect_to :action => 'show', :id => @issue
end
end
end
@assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user }
end
def destroy
@issue.destroy
redirect_to :controller => 'projects', :action => 'list_issues', :id => @project
end
def add_attachment
# Save the attachment
if params[:attachment][:file].size > 0
@attachment = @issue.attachments.build(params[:attachment])
@attachment.author_id = session[:user].id unless session[:user].nil?
@attachment.save
end
redirect_to :action => 'show', :id => @issue
end
def destroy_attachment
@issue.attachments.find(params[:attachment_id]).destroy
redirect_to :action => 'show', :id => @issue
end
# Send the file in stream mode
def download
@attachment = @issue.attachments.find(params[:attachment_id])
send_file @attachment.diskfile, :filename => @attachment.filename
end
private
def find_project
@issue = Issue.find(params[:id])
@project = @issue.project
end
end
# 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.
class MembersController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def edit
if request.post? and @member.update_attributes(params[:member])
flash[:notice] = 'Member was successfully updated.'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
def destroy
@member.destroy
flash[:notice] = 'Member was successfully removed.'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
private
def find_project
@member = Member.find(params[:id])
@project = @member.project
end
end
# 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.
class NewsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def show
end
def edit
if request.post? and @news.update_attributes(params[:news])
flash[:notice] = 'News was successfully updated.'
redirect_to :action => 'show', :id => @news
end
end
def destroy
@news.destroy
redirect_to :controller => 'projects', :action => 'list_news', :id => @project
end
private
def find_project
@news = News.find(params[:id])
@project = @news.project
end
end
# 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.
class ProjectsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize, :except => [ :index, :list, :add ]
before_filter :require_admin, :only => [ :add, :destroy ]
helper :sort
include SortHelper
helper :search_filter
include SearchFilterHelper
helper :custom_fields
include CustomFieldsHelper
def index
list
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,
15,
@params['page']
@projects = Project.find :all, :order => sort_clause,
:conditions => ["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
# Show @project
def show
@members = @project.members.find(:all, :include => [:user, :role])
end
def settings
@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
else
settings
render :action => 'settings'
end
end
end
# Delete @project
def destroy
if request.post? and params[:confirm]
@project.destroy
redirect_to :controller => 'admin', :action => 'projects'
end
end
# Add a new issue category to @project
def add_issue_category
if request.post?
@issue_category = @project.issue_categories.build(params[:issue_category])
if @issue_category.save
redirect_to :action => 'settings', :id => @project
else
settings
render :action => 'settings'
end
end
end
# Add a new version to @project
def add_version
@version = @project.versions.build(params[:version])
if request.post? and @version.save
redirect_to :action => 'settings', :id => @project
end
end
# Add a new member to @project
def add_member
@member = @project.members.build(params[:member])
if request.post?
if @member.save
flash[:notice] = 'Member was successfully added.'
redirect_to :action => 'settings', :id => @project
else
settings
render :action => 'settings'
end
end
end
# Show members list of @project
def list_members
@members = @project.members
end
# Add a new document to @project
def add_document
@categories = Enumeration::get_values('DCAT')
@document = @project.documents.build(params[:document])
if request.post?
# Save the attachment
if params[:attachment][:file].size > 0
@attachment = @document.attachments.build(params[:attachment])
@attachment.author_id = session[:user].id unless session[:user].nil?
end
if @document.save
redirect_to :action => 'list_documents', :id => @project
end
end
end
# Show documents list of @project
def list_documents
@documents = @project.documents
end
# Add a new issue to @project
def add_issue
@trackers = Tracker.find(:all)
@priorities = Enumeration::get_values('IPRI')
if request.get?
@issue = @project.issues.build
@custom_values = @project.custom_fields_for_issues.collect { |x| CustomValue.new(:custom_field => x) }
else
# Create the issue and set the author
@issue = @project.issues.build(params[:issue])
@issue.author = session[:user] unless session[:user].nil?
# Create the document if a file was sent
if params[:attachment][:file].size > 0
@attachment = @issue.attachments.build(params[:attachment])
@attachment.author_id = session[:user].id unless session[:user].nil?
end
@custom_values = @project.custom_fields_for_issues.collect { |x| CustomValue.new(:custom_field => x, :value => params["custom_fields"][x.id.to_s]) }
@issue.custom_values = @custom_values
if @issue.save
flash[:notice] = "Issue was successfully added."
Mailer.deliver_issue_add(@issue) if Permission.find_by_controller_and_action(@params[:controller], @params[:action]).mail_enabled?
redirect_to :action => 'list_issues', :id => @project
end
end
end
# Show issues list of @project
def list_issues
sort_init 'issues.id', 'desc'
sort_update
search_filter_criteria 'issues.tracker_id', :values => "Tracker.find(:all)"
search_filter_criteria 'issues.priority_id', :values => "Enumeration.find(:all, :conditions => ['opt=?','IPRI'])"
search_filter_criteria 'issues.category_id', :values => "@project.issue_categories"
search_filter_criteria 'issues.status_id', :values => "IssueStatus.find(:all)"
search_filter_criteria 'issues.author_id', :values => "User.find(:all)", :label => "display_name"
search_filter_update if params[:set_filter] or request.post?
@issue_count = @project.issues.count(search_filter_clause)
@issue_pages = Paginator.new self, @issue_count,
15,
@params['page']
@issues = @project.issues.find :all, :order => sort_clause,
:include => [ :author, :status, :tracker ],
:conditions => search_filter_clause,
:limit => @issue_pages.items_per_page,
:offset => @issue_pages.current.offset
end
# Add a news to @project
def add_news
@news = @project.news.build(params[:news])
if request.post?
@news.author = session[:user] unless session[:user].nil?
if @news.save
redirect_to :action => 'list_news', :id => @project
end
end
end
# Show news list of @project
def list_news
@news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "news.created_on DESC"
end
def add_file
if request.post?
# Save the attachment
if params[:attachment][:file].size > 0
@attachment = @project.versions.find(params[:version_id]).attachments.build(params[:attachment])
@attachment.author_id = session[:user].id unless session[:user].nil?
if @attachment.save
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
end
end
end
@versions = @project.versions
end
def list_files
@versions = @project.versions
end
# Show changelog of @project
def changelog
@fixed_issues = @project.issues.find(:all,
:include => [ :fixed_version, :status, :tracker ],
:conditions => [ "issue_statuses.is_closed=? and trackers.is_in_chlog=? and issues.fixed_version_id is not null", true, true]
)
end
private
# Find project of id params[:id]
# if not found, redirect to project list
# used as a before_filter
def find_project
@project = Project.find(params[:id])
rescue
flash[:notice] = 'Project not found.'
redirect_to :action => 'list'
end
end
# 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.
class ReportsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def issue_report
@statuses = IssueStatus.find_all
@trackers = Tracker.find_all
@issues_by_tracker =
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
t.id as tracker_id,
count(i.id) as total
from
issues i, issue_statuses s, trackers t
where
i.status_id=s.id
and i.tracker_id=t.id
and i.project_id=#{@project.id}
group by s.id, t.id")
@priorities = Enumeration::get_values('IPRI')
@issues_by_priority =
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
p.id as priority_id,
count(i.id) as total
from
issues i, issue_statuses s, enumerations p
where
i.status_id=s.id
and i.priority_id=p.id
and i.project_id=#{@project.id}
group by s.id, p.id")
@categories = @project.issue_categories
@issues_by_category =
ActiveRecord::Base.connection.select_all("select s.id as status_id,
s.is_closed as closed,
c.id as category_id,
count(i.id) as total
from
issues i, issue_statuses s, issue_categories c
where
i.status_id=s.id
and i.category_id=c.id
and i.project_id=#{@project.id}
group by s.id, c.id")
end
private
# Find project of id params[:id]
def find_project
@project = Project.find(params[:id])
end
end
# 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.
class RolesController < ApplicationController
layout 'base'
before_filter :require_admin
def index
list
render :action => 'list'
end
def list
@role_pages, @roles = paginate :roles, :per_page => 10
end
def new
@role = Role.new(params[:role])
if request.post?
@role.permissions = Permission.find(@params[:permission_ids]) if @params[:permission_ids]
if @role.save
flash[:notice] = 'Role was successfully created.'
redirect_to :action => 'list'
end
end
@permissions = Permission.find(:all, :order => 'sort ASC')
end
def edit
@role = Role.find(params[:id])
if request.post? and @role.update_attributes(params[:role])
@role.permissions = Permission.find(@params[:permission_ids] || [])
Permission.allowed_to_role_expired
flash[:notice] = 'Role was successfully updated.'
redirect_to :action => 'list'
end
@permissions = Permission.find(:all, :order => 'sort ASC')
end
def destroy
@role = Role.find(params[:id])
unless @role.members.empty?
flash[:notice] = 'Some members have this role. Can\'t delete it.'
else
@role.destroy
end
redirect_to :action => 'list'
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])
if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
(params[:issue_status] || []).each { |old, news|
news.each { |new|
@role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
}
}
if @role.save
flash[:notice] = 'Workflow was successfully updated.'
end
end
end
end
# 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.
class TrackersController < ApplicationController
layout 'base'
before_filter :require_admin
def index
list
render :action => 'list'
end
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy ], :redirect_to => { :action => :list }
def list
@tracker_pages, @trackers = paginate :trackers, :per_page => 10
end
def new
@tracker = Tracker.new(params[:tracker])
if request.post? and @tracker.save
flash[:notice] = 'Tracker was successfully created.'
redirect_to :action => 'list'
end
end
def edit
@tracker = Tracker.find(params[:id])
if request.post? and @tracker.update_attributes(params[:tracker])
flash[:notice] = 'Tracker was successfully updated.'
redirect_to :action => 'list'
end
end
def destroy
@tracker = Tracker.find(params[:id])
unless @tracker.issues.empty?
flash[:notice] = "This tracker contains issues and can\'t be deleted."
else
@tracker.destroy
end
redirect_to :action => 'list'
end
end
# 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.
class UsersController < ApplicationController
layout 'base'
before_filter :require_admin
helper :sort
include SortHelper
def index
list
render :action => 'list'
end
def list
sort_init 'users.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,
:limit => @user_pages.items_per_page,
:offset => @user_pages.current.offset
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 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 destroy
User.find(params[:id]).destroy
redirect_to :action => 'list'
rescue
flash[:notice] = "Unable to delete user"
redirect_to :action => 'list'
end
end
# 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.
class VersionsController < ApplicationController
layout 'base'
before_filter :find_project, :authorize
def edit
if request.post? and @version.update_attributes(params[:version])
flash[:notice] = 'Version was successfully updated.'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
def destroy
@version.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project
rescue
flash[:notice] = "Unable to delete version"
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
def download
@attachment = @version.attachments.find(params[:attachment_id])
@attachment.increment_download
send_file @attachment.diskfile, :filename => @attachment.filename
end
def destroy_file
@version.attachments.find(params[:attachment_id]).destroy
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
end
private
def find_project
@version = Version.find(params[:id])
@project = @version.project
end
end
# 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.
class WelcomeController < ApplicationController
layout 'base'
def index
@news = News.latest
@projects = Project.latest
end
end
# 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.
module AccountHelper
end
# 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.
module AdminHelper
end
# 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.
module ApplicationHelper
def loggedin?
session[:user]
end
def admin_loggedin?
session[:user] && session[:user].admin
end
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 ]
return true
end
# check if user is authorized
if session[:user] and (session[:user].admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], session[:user].role_for_project(@project.id) ) )
return true
end
return false
end
def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
end
# Display a link to user's account page
def link_to_user(user)
link_to user.display_name, :controller => 'account', :action => 'show', :id => user
end
def format_date(date)
_('(date)', date) if date
end
def format_time(time)
_('(time)', time) if time
end
def pagination_links_full(paginator, options={}, html_options={})
html =''
html << link_to(('&#171; ' + _('Previous') ), { :page => paginator.current.previous }) + ' ' if paginator.current.previous
html << (pagination_links(paginator, options, html_options) || '')
html << ' ' + link_to((_('Next') + ' &#187;'), { :page => paginator.current.next }) if paginator.current.next
html
end
end
# 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.
module CustomFieldsHelper
def custom_field_tag(custom_value)
custom_field = custom_value.custom_field
field_name = "custom_fields[#{custom_field.id}]"
case custom_field.typ
when 0 .. 2
text_field_tag field_name, custom_value.value
when 3
check_box field_name
when 4
select_tag field_name,
options_for_select(custom_field.possible_values.split('|'),
custom_value.value)
end
end
end
# 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.
module DocumentsHelper
end
# 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.
module EnumerationsHelper
end
# 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.
module HelpHelper
end
# 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.
module IssueCategoriesHelper
end
# 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.
module IssueStatusesHelper
end
# 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.
module IssuesHelper
end
# 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.
module MembersHelper
end
# 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.
module NewsHelper
end
# 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.
module ProjectsHelper
end
# 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.
module ReportsHelper
def aggregate(data, criteria)
a = 0
data.each { |row|
match = 1
criteria.each { |k, v|
match = 0 unless row[k].to_s == v.to_s
} unless criteria.nil?
a = a + row["total"].to_i if match == 1
} unless data.nil?
a
end
end
# 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.
module RolesHelper
end
# 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.
module SearchFilterHelper
def search_filter_criteria(field, options = {})
session[:search_filter] ||= {}
session[:search_filter][field] ||= options
# session[:search_filter][field][:values] = options[:values] unless options[:values].nil?
# session[:search_filter][field][:label] = options[:label] unless options[:label].nil?
end
def search_filter_update
session[:search_filter].each_key {|field| session[:search_filter][field][:value] = params[field] }
#@search_filter[:value] = params[@search_filter[:field]]
end
def search_filter_clause
clause = "1=1"
session[:search_filter].each {|field, criteria| clause = clause + " AND " + field + "='" + session[:search_filter][field][:value] + "'" unless session[:search_filter][field][:value].nil? || session[:search_filter][field][:value].empty? }
clause
#@search_filter[:field] + "='" + @search_filter[:value] + "'" unless @search_filter[:value].nil? || @search_filter[:value].empty?
end
def search_filter_tag(field)
option_values = []
#values = eval @search_filter[:values_expr]
option_values = eval session[:search_filter][field][:values]
content_tag("select",
content_tag("option", "[All]", :value => "") +
options_from_collection_for_select(option_values,
"id",
session[:search_filter][field][:label] || "name",
session[:search_filter][field][:value].to_i
),
:name => field
)
end
end
\ No newline at end of file
# Helpers to sort tables using clickable column headers.
#
# Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
# License: This source code is released under the MIT license.
#
# - Consecutive clicks toggle the column's sort order.
# - Sort state is maintained by a session hash entry.
# - Icon image identifies sort column and state.
# - Typically used in conjunction with the Pagination module.
#
# Example code snippets:
#
# Controller:
#
# helper :sort
# include SortHelper
#
# def list
# sort_init 'last_name'
# sort_update
# @items = Contact.find_all nil, sort_clause
# end
#
# Controller (using Pagination module):
#
# helper :sort
# include SortHelper
#
# def list
# sort_init 'last_name'
# sort_update
# @contact_pages, @items = paginate :contacts,
# :order_by => sort_clause,
# :per_page => 10
# end
#
# View (table header in list.rhtml):
#
# <thead>
# <tr>
# <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
# <%= sort_header_tag('last_name', :caption => 'Name') %>
# <%= sort_header_tag('phone') %>
# <%= sort_header_tag('address', :width => 200) %>
# </tr>
# </thead>
#
# - The ascending and descending sort icon images are sort_asc.png and
# sort_desc.png and reside in the application's images directory.
# - Introduces instance variables: @sort_name, @sort_default.
# - Introduces params :sort_key and :sort_order.
#
module SortHelper
# Initializes the default sort column (default_key) and sort order
# (default_order).
#
# - default_key is a column attribute name.
# - default_order is 'asc' or 'desc'.
# - name is the name of the session hash entry that stores the sort state,
# defaults to '<controller_name>_sort'.
#
def sort_init(default_key, default_order='asc', name=nil)
@sort_name = name || @params[:controller] + @params[:action] + '_sort'
@sort_default = {:key => default_key, :order => default_order}
end
# Updates the sort state. Call this in the controller prior to calling
# sort_clause.
#
def sort_update()
if @params[:sort_key]
sort = {:key => @params[:sort_key], :order => @params[:sort_order]}
elsif @session[@sort_name]
sort = @session[@sort_name] # Previous sort.
else
sort = @sort_default
end
@session[@sort_name] = sort
end
# Returns an SQL sort clause corresponding to the current sort state.
# Use this to sort the controller's table items collection.
#
def sort_clause()
@session[@sort_name][:key] + ' ' + @session[@sort_name][:order]
end
# Returns a link which sorts by the named column.
#
# - column is the name of an attribute in the sorted record collection.
# - The optional caption explicitly specifies the displayed link text.
# - A sort icon image is positioned to the right of the sort link.
#
def sort_link(column, caption=nil)
key, order = @session[@sort_name][:key], @session[@sort_name][:order]
if key == column
if order.downcase == 'asc'
icon = 'sort_asc'
order = 'desc'
else
icon = 'sort_desc'
order = 'asc'
end
else
icon = nil
order = 'desc' # changed for desc order by default
end
caption = titleize(Inflector::humanize(column)) unless caption
params = {:params => {:sort_key => column, :sort_order => order}}
link_to(caption, params) + (icon ? nbsp(2) + image_tag(icon) : '')
end
# Returns a table header <th> tag with a sort link for the named column
# attribute.
#
# Options:
# :caption The displayed link name (defaults to titleized column name).
# :title The tag's 'title' attribute (defaults to 'Sort by :caption').
#
# Other options hash entries generate additional table header tag attributes.
#
# Example:
#
# <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
#
# Renders:
#
# <th title="Sort by contact ID" width="40">
# <a href="/contact/list?sort_order=desc&amp;sort_key=id">Id</a>
# &nbsp;&nbsp;<img alt="Sort_asc" src="/images/sort_asc.png" />
# </th>
#
def sort_header_tag(column, options = {})
if options[:caption]
caption = options[:caption]
options.delete(:caption)
else
caption = titleize(Inflector::humanize(column))
end
options[:title]= "Sort by #{caption}" unless options[:title]
content_tag('th', sort_link(column, caption), options)
end
private
# Return n non-breaking spaces.
def nbsp(n)
'&nbsp;' * n
end
# Return capitalized title.
def titleize(title)
title.split.map {|w| w.capitalize }.join(' ')
end
end
# 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.
module TrackersHelper
end
# 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.
module UsersHelper
end
# 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.
module VersionsHelper
end
# 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.
module WelcomeHelper
end
# 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.
require "digest/md5"
class Attachment < ActiveRecord::Base
belongs_to :container, :polymorphic => true
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
validates_presence_of :filename
def file=(incomming_file)
unless incomming_file.nil?
@temp_file = incomming_file
if @temp_file.size > 0
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
end
end
end
# Copy temp file to its final location
def before_save
if @temp_file && (@temp_file.size > 0)
logger.debug("saving '#{self.diskfile}'")
File.open(diskfile, "wb") do |f|
f.write(@temp_file.read)
end
self.digest = Digest::MD5.hexdigest(File.read(diskfile))
end
end
# Deletes file on the disk
def after_destroy
if self.filename?
File.delete(diskfile) if File.exist?(diskfile)
end
end
# Returns file's location on disk
def diskfile
"#{RDM_STORAGE_PATH}/#{self.disk_filename}"
end
def increment_download
increment!(:downloads)
end
# returns last created projects
def self.most_downloaded
find(:all, :limit => 5, :order => "downloads DESC")
end
private
def sanitize_filename(value)
# get only the filename, not the whole path
just_filename = value.gsub(/^.*(\\|\/)/, '')
# NOTE: File.basename doesn't work right with Windows paths on Unix
# INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
# Finally, replace all non alphanumeric, underscore or periods with underscore
@filename = just_filename.gsub(/[^\w\.\-]/,'_')
end
end
# 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.
class CustomField < ActiveRecord::Base
has_and_belongs_to_many :projects
has_many :custom_values, :dependent => true
has_many :issues, :through => :issue_custom_values
validates_presence_of :name, :typ
validates_uniqueness_of :name
TYPES = [
[ "Integer", 0 ],
[ "String", 1 ],
[ "Date", 2 ],
[ "Boolean", 3 ],
[ "List", 4 ]
].freeze
def self.for_all
find(:all, :conditions => ["is_for_all=?", true])
end
end
# 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.
class CustomValue < ActiveRecord::Base
belongs_to :issue
belongs_to :custom_field
protected
def validate
errors.add(custom_field.name, "can't be blank") if custom_field.is_required? and value.empty?
errors.add(custom_field.name, "is not valid") unless custom_field.regexp.empty? or value =~ Regexp.new(custom_field.regexp)
case custom_field.typ
when 0
errors.add(custom_field.name, "must be an integer") unless value =~ /^[0-9]*$/
when 1
errors.add(custom_field.name, "is too short") if custom_field.min_length > 0 and value.length < custom_field.min_length and value.length > 0
errors.add(custom_field.name, "is too long") if custom_field.max_length > 0 and value.length > custom_field.max_length
when 2
errors.add(custom_field.name, "must be a valid date") unless value =~ /^(\d+)\/(\d+)\/(\d+)$/ or value.empty?
when 3
when 4
errors.add(custom_field.name, "is not a valid value") unless custom_field.possible_values.split('|').include? value or value.empty?
end
end
end
# 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.
class Document < ActiveRecord::Base
belongs_to :project
belongs_to :category, :class_name => "Enumeration", :foreign_key => "category_id"
has_many :attachments, :as => :container, :dependent => true
validates_presence_of :title
end
# 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.
class Enumeration < ActiveRecord::Base
before_destroy :check_integrity
validates_presence_of :opt, :name
OPTIONS = [
["Issue priorities", "IPRI"],
["Document categories", "DCAT"]
].freeze
def self.get_values(option)
find(:all, :conditions => ['opt=?', option])
end
def name
_ self.attributes['name']
end
private
def check_integrity
case self.opt
when "IPRI"
raise "Can't delete enumeration" if Issue.find(:first, :conditions => ["priority_id=?", self.id])
when "DCAT"
raise "Can't delete enumeration" if Document.find(:first, :conditions => ["category_id=?", self.id])
end
end
end
# 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.
class Issue < ActiveRecord::Base
belongs_to :project
belongs_to :tracker
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id'
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
has_many :histories, :class_name => 'IssueHistory', :dependent => true, :order => "issue_histories.created_on DESC", :include => :status
has_many :attachments, :as => :container, :dependent => true
has_many :custom_values, :dependent => true
has_many :custom_fields, :through => :custom_values
validates_presence_of :subject, :descr, :priority, :tracker, :author
# set default status for new issues
def before_create
self.status = IssueStatus.default
build_history
end
def long_id
"%05d" % self.id
end
private
# Creates an history for the issue
def build_history
@history = self.histories.build
@history.status = self.status
@history.author = self.author
end
end
# 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.
class IssueCategory < ActiveRecord::Base
before_destroy :check_integrity
belongs_to :project
validates_presence_of :name
private
def check_integrity
raise "Can't delete category" if Issue.find(:first, :conditions => ["category_id=?", self.id])
end
end
# 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.
class IssueHistory < ActiveRecord::Base
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :status
end
# 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.
class IssueStatus < ActiveRecord::Base
before_destroy :check_integrity
has_many :workflows, :foreign_key => "old_status_id"
validates_presence_of :name
validates_uniqueness_of :name
# 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
def name
_ self.attributes['name']
end
private
def check_integrity
raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id]) or IssueHistory.find(:first, :conditions => ["status_id=?", self.id])
end
end
# 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.
class Mailer < ActionMailer::Base
def issue_change_status(issue)
# Sends to all project members
@recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
@from = 'redmine@somenet.foo'
@subject = "Issue ##{issue.id} has been updated"
@body['issue'] = issue
end
def issue_add(issue)
# Sends to all project members
@recipients = issue.project.members.collect { |m| m.user.mail if m.user.mail_notification }
@from = 'redmine@somenet.foo'
@subject = "Issue ##{issue.id} has been reported"
@body['issue'] = issue
end
end
# 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.
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :role
belongs_to :project
validates_presence_of :role, :user, :project
validates_uniqueness_of :user_id, :scope => :project_id
def name
self.user.display_name
end
end
# 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.
class News < ActiveRecord::Base
belongs_to :project
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :title, :shortdescr, :descr
# returns last created news
def self.latest
find(:all, :limit => 5, :include => [ :author, :project ], :order => "news.created_on DESC")
end
end
# 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.
class Permission < ActiveRecord::Base
has_and_belongs_to_many :roles
validates_presence_of :controller, :action, :descr
GROUPS = {
100 => "Project",
200 => "Membres",
300 => "Versions",
400 => "Issue categories",
1000 => "Issues",
1100 => "News",
1200 => "Documents",
1300 => "Files",
}.freeze
@@cached_perms_for_public = nil
@@cached_perms_for_roles = nil
def name
self.controller + "/" + self.action
end
def group_id
(self.sort / 100)*100
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.include? action
end
def self.allowed_to_role(action, role)
@@cached_perms_for_roles ||=
begin
perms = {}
find(:all, :include => :roles).each {|p| perms.store "#{p.controller}/#{p.action}", p.roles.collect {|r| r.id } }
perms
end
@@cached_perms_for_roles[action] and @@cached_perms_for_roles[action].include? role
end
def self.allowed_to_role_expired
@@cached_perms_for_roles = nil
end
end
# 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.
class Project < ActiveRecord::Base
has_many :versions, :dependent => true, :order => "versions.date DESC"
has_many :members, :dependent => true
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 :issue_categories, :dependent => true
has_and_belongs_to_many :custom_fields
validates_presence_of :name, :descr
# 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
# 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.
class Role < ActiveRecord::Base
before_destroy :check_integrity
has_and_belongs_to_many :permissions
has_many :workflows, :dependent => true
has_many :members
validates_presence_of :name
validates_uniqueness_of :name
private
def check_integrity
raise "Can't delete role" if Member.find(:first, :conditions =>["role_id=?", self.id])
end
end
# 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.
class Tracker < ActiveRecord::Base
before_destroy :check_integrity
has_many :issues
has_many :workflows, :dependent => true
def name
_ self.attributes['name']
end
private
def check_integrity
raise "Can't delete tracker" if Issue.find(:first, :conditions => ["tracker_id=?", self.id])
end
end
# 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.
require "digest/sha1"
class User < ActiveRecord::Base
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
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
def before_create
self.hashed_password = User.hash_password(self.password)
end
def after_create
@password = nil
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
# 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 ||=
begin
roles = {}
self.memberships.each { |m| roles.store m.project_id, m.role_id }
roles
end
@role_for_projects[project_id]
end
private
# Return password digest
def self.hash_password(clear_password)
Digest::SHA1.hexdigest(clear_password)
end
end
# 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.
class Version < ActiveRecord::Base
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
private
def check_integrity
raise "Can't delete version" if self.fixed_issues.find(:first)
end
end
# 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.
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'
validates_presence_of :role, :old_status, :new_status
end
<div class="box">
<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="user_password"><%=_ 'Password' %>:</label><br/>
<input type="password" name="user[password]" id="user_password" size="30"/></p>
<p><input type="submit" name="login" value="<%=_ 'Log in' %> &#187;" class="primary" /></p>
<%= end_form_tag %>
</div>
\ No newline at end of file
<h2><%=_('My account')%></h2>
<p><%=_('Login')%>: <strong><%= @user.login %></strong><br />
<%=_('Created on')%>: <%= format_time(@user.created_on) %>,
<%=_('Last update')%>: <%= format_time(@user.updated_on) %></p>
<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/>
<%= text_field 'user', 'firstname' %></p>
<p><label for="user_lastname"><%=_('Lastname')%> <span class="required">*</span></label><br/>
<%= text_field 'user', 'lastname' %></p>
<p><label for="user_mail"><%=_('Mail')%> <span class="required">*</span></label><br/>
<%= text_field 'user', 'mail' %></p>
<p><label for="user_language"><%=_('Language')%></label><br/>
<%= select("user", "language", Localization.langs) %></p>
<!--[eoform:user]-->
<p><%= check_box 'user', 'mail_notification' %> <label for="user_mail_notification"><%=_('Mail notifications')%></label></p>
<center><%= submit_tag _('Save') %></center>
<%= end_form_tag %>
</div>
</div>
<div class="splitcontentright">
<div class="box">
<h3><%=_('Password')%></h3>
&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="new_password"><%=_('New password')%> <span class="required">*</span></label><br/>
<%= password_field_tag 'new_password' %></p>
<p><label for="new_password_confirmation"><%=_('Confirmation')%> <span class="required">*</span></label><br/>
<%= password_field_tag 'new_password_confirmation' %></p>
<center><%= submit_tag _('Save') %></center>
<%= end_form_tag %>
</div>
</div>
\ No newline at end of file
<h2><%=_('My page') %></h2>
<p>
<%=_('Welcome')%> <b><%= @user.firstname %></b><br />
<% unless @user.last_before_login_on.nil? %>
<%=_('Last login')%>: <%= format_time(@user.last_before_login_on) %>
<% end %>
</p>
<div class="splitcontentleft">
<h3><%=_('Reported issues')%></h3>
<%= render :partial => 'issues/list_simple', :locals => { :issues => @reported_issues } %>
<%= "<p>(Last #{@reported_issues.length} updated)</p>" if @reported_issues.length > 0 %>
</div>
<div class="splitcontentright">
<h3><%=_('Assigned to me')%></h3>
<%= render :partial => 'issues/list_simple', :locals => { :issues => @assigned_issues } %>
<%= "<p>(Last #{@assigned_issues.length} updated)</p>" if @assigned_issues.length > 0 %>
</div>
\ No newline at end of file
<h2><%= @user.display_name %></h2>
<p>
<%= mail_to @user.mail %><br />
<%=_('Registered on')%>: <%= format_date(@user.created_on) %>
</p>
<h3><%=_('Projects')%></h3>
<p>
<% for membership in @user.memberships %>
<%= membership.project.name %> (<%= membership.role.name %>, <%= format_date(membership.created_on) %>)
<br />
<% end %>
</p>
<h3><%=_('Activity')%></h3>
<p>
<%=_('Reported issues')%>: <%= Issue.count( [ "author_id=?", @user.id]) %>
</p>
\ No newline at end of file
<h2><%=_('Administration')%></h2>
<p>
<%= image_tag "projects" %>
<%= link_to _('Projects'), :controller => 'admin', :action => 'projects' %> |
<%= link_to _('New'), :controller => 'projects', :action => 'add' %>
</p>
<p>
<%= image_tag "users" %>
<%= link_to _('Users'), :controller => 'users' %> |
<%= link_to _('New'), :controller => 'users', :action => 'add' %>
</p>
<p>
<%= image_tag "role" %>
<%= link_to _('Roles and permissions'), :controller => 'roles' %>
</p>
<p>
<%= image_tag "tracker" %>
<%= link_to _('Trackers'), :controller => 'trackers' %> |
<%= link_to _('Custom fields'), :controller => 'custom_fields' %>
</p>
<p>
<%= image_tag "workflow" %>
<%= link_to _('Issue Statuses'), :controller => 'issue_statuses' %> |
<%= link_to _('Workflow'), :controller => 'roles', :action => 'workflow' %>
</p>
<p>
<%= image_tag "options" %>
<%= link_to _('Enumerations'), :controller => 'enumerations' %>
</p>
<p>
<%= image_tag "mailer" %>
<%= link_to _('Mail notifications'), :controller => 'admin', :action => 'mail_options' %>
</p>
<p>
<%= image_tag "help" %>
<%= link_to _('Information'), :controller => 'admin', :action => 'info' %>
</p>
\ No newline at end of file
<h2><%=_('Information')%></h2>
<%=_('Version')%>: <%= RDM_APP_NAME %> <%= RDM_APP_VERSION %><br />
<%=_('Database')%>: <%= @adapter_name %>
\ No newline at end of file
<h2><%=_('Mail notifications')%></h2>
<p><%=_('Select actions for which mail notification should be enabled.')%></p>
<%= start_form_tag ({}, :id => 'mail_options_form')%>
<% for action in @actions %>
<%= check_box_tag "action_ids[]", action.id, action.mail_enabled? %>
<%= action.descr %><br />
<% end %>
<br />
<p>
<a href="javascript:checkAll('mail_options_form', true)"><%=_('Check all')%></a> |
<a href="javascript:checkAll('mail_options_form', false)"><%=_('Uncheck all')%></a>
</p>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
\ No newline at end of file
<h2><%=_('Projects')%></h2>
<table width="100%" cellspacing="1" cellpadding="2" class="listTableContent">
<tr class="ListHead">
<%= sort_header_tag('projects.name', :caption => _('Project')) %>
<th><%=_('Description')%></th>
<th><%=_('Public')%></th>
<%= sort_header_tag('projects.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 %>">
<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"><%= format_date(project.created_on) %>
<td align="center">
<%= start_form_tag({:controller => 'projects', :action => 'destroy', :id => project}) %>
<%= submit_tag _('Delete'), :class => "button-small" %>
<%= end_form_tag %>
</td>
</tr>
<% 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' %>
\ No newline at end of file
<%= error_messages_for 'custom_field' %>
<!--[form:custom_field]-->
<p><label for="custom_field_name"><%=_('Name')%></label><br/>
<%= text_field 'custom_field', 'name' %></p>
<p><label for="custom_field_typ"><%=_('Type')%></label><br/>
<%= select("custom_field", "typ", CustomField::TYPES) %></p>
<p><%= check_box 'custom_field', 'is_required' %>
<label for="custom_field_is_required"><%=_('Required')%></label></p>
<p><%= check_box 'custom_field', 'is_for_all' %>
<label for="custom_field_is_for_all"><%=_('For all projects')%></label></p>
<p><label for="custom_field_min_length"><%=_('Min - max length')%></label> (<%=_('0 means no restriction')%>)<br/>
<%= text_field 'custom_field', 'min_length', :size => 5 %> -
<%= text_field 'custom_field', 'max_length', :size => 5 %></p>
<p><label for="custom_field_regexp"><%=_('Regular expression pattern')%></label> (eg. ^[A-Z0-9]+$)<br/>
<%= text_field 'custom_field', 'regexp', :size => 50 %></p>
<p><label for="custom_field_possible_values"><%=_('Possible values')%></label> (separator: |)<br/>
<%= text_area 'custom_field', 'possible_values', :rows => 5, :cols => 60 %></p>
<!--[eoform:custom_field]-->
<h2><%=_('Custom field')%></h2>
<%= start_form_tag :action => 'edit', :id => @custom_field %>
<%= render :partial => 'form' %>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
<h2><%=_('Custom fields')%></h2>
<table border="0" cellspacing="1" cellpadding="2" class="listTableContent">
<tr class="ListHead">
<th><%=_('Name')%></th>
<th><%=_('Type')%></th>
<th><%=_('Required')%></th>
<th><%=_('For all projects')%></th>
<th><%=_('Used by')%></th>
<th></th>
</tr>
<% for custom_field in @custom_fields %>
<tr style="background-color:#CEE1ED">
<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>
<td align="center"><%= image_tag 'true' if custom_field.is_for_all? %></td>
<td align="center"><%= custom_field.projects.count.to_s + ' ' + _('Project') + '(s)' unless custom_field.is_for_all? %></td>
<td align="center">
<%= start_form_tag :action => 'destroy', :id => custom_field %>
<%= submit_tag _('Delete'), :class => "button-small" %>
<%= end_form_tag %> </td>
</tr>
<% end %>
</table>
<%= link_to ('&#171; ' + _('Previous')), { :page => @custom_field_pages.current.previous } if @custom_field_pages.current.previous %>
<%= link_to (_('Next') + ' &#187;'), { :page => @custom_field_pages.current.next } if @custom_field_pages.current.next %>
<br />
<%= link_to ('&#187; ' + _('New custom field')), :action => 'new' %>
<h2><%=_('New custom field')%></h2>
<%= start_form_tag :action => 'new' %>
<%= render :partial => 'form' %>
<%= submit_tag _('Create') %>
<%= end_form_tag %>
<%= error_messages_for 'document' %>
<!--[form:document]-->
<p><label for="document_category_id"><%=_('Category')%></label><br />
<select name="document[category_id]">
<%= options_from_collection_for_select @categories, "id", "name",@document.category_id %>
</select></p>
<p><label for="document_title"><%=_('Title')%> <span class="required">*</span></label><br />
<%= text_field 'document', 'title', :size => 60 %></p>
<p><label for="document_descr"><%=_('Description')%> <span class="required">*</span></label><br />
<%= text_area 'document', 'descr', :cols => 60, :rows => 5 %></p>
<!--[eoform:document]-->
<h2><%=_('Document')%></h2>
<%= start_form_tag :action => 'edit', :id => @document %>
<%= render :partial => 'form' %>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
<h2><%= @document.title %></h2>
<strong><%=_('Description')%>:</strong> <%= @document.descr %><br />
<strong><%=_('Category')%>:</strong> <%= @document.category.name %><br />
<br />
<% if authorize_for('documents', 'edit') %>
<%= start_form_tag ({ :controller => 'documents', :action => 'edit', :id => @document }, :method => 'get' ) %>
<%= submit_tag _('Edit') %>
<%= end_form_tag %>
<% end %>
<% if authorize_for('documents', 'destroy') %>
<%= start_form_tag ({ :controller => 'documents', :action => 'destroy', :id => @document } ) %>
<%= submit_tag _('Delete') %>
<%= end_form_tag %>
<% end %>
<br /><br />
<table border="0" cellspacing="1" cellpadding="2" width="100%">
<% for attachment in @document.attachments %>
<tr style="background-color:#CEE1ED">
<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>
<% 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 %>
</table>
<br />
<% if authorize_for('documents', 'add_attachment') %>
<%= start_form_tag ({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true) %>
<%=_('Add file')%><br /><%= file_field 'attachment', 'file' %>
<%= submit_tag _('Add') %>
<%= end_form_tag %>
<% end %>
<%= error_messages_for 'enumeration' %>
<!--[form:optvalue]-->
<%= hidden_field 'enumeration', 'opt' %>
<p><label for="enumeration_name"><%=_('Name')%></label><br/>
<%= text_field 'enumeration', 'name' %></p>
<!--[eoform:optvalue]-->
<h2><%=_('Enumerations')%></h2>
<%= start_form_tag :action => 'update', :id => @enumeration %>
<%= render :partial => 'form' %>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
<%= start_form_tag :action => 'destroy', :id => @enumeration %>
<%= submit_tag _('Delete') %>
<%= end_form_tag %>
\ No newline at end of file
<h2><%=_('Enumerations')%></h2>
<% for option in Enumeration::OPTIONS %>
<% if @params[:opt]==option[1] %>
<p><%= image_tag 'dir_open' %> <b><%=_ option[0] %></b></p>
<ul>
<% for value in Enumeration::find(:all, :conditions => [ "opt = ?", option[1]]) %>
<li><%= link_to value.name, :action => 'edit', :id => value %></li>
<% end %>
</ul>
<ul>
<li><%= link_to ('&#187; ' + _('New')), :action => 'new', :opt => option[1] %></li>
</ul>
<% else %>
<p><%= image_tag 'dir' %> <%= link_to _(option[0]), :opt => option[1] %></p>
<% end %>
<% end %>
<h2><%=_('New enumeration')%></h2>
<%= start_form_tag :action => 'create' %>
<%= render :partial => 'form' %>
<%= submit_tag _('Create') %>
<%= end_form_tag %>
<%= error_messages_for 'issue_category' %>
<!--[form:issue_category]-->
<p><label for="issue_category_name"><%=_('Name')%></label><br/>
<%= text_field 'issue_category', 'name' %></p>
<!--[eoform:issue_category]-->
<h2>Editing issue category</h2>
<%= start_form_tag :action => 'edit', :id => @category %>
<%= render :partial => 'form' %>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
<%= error_messages_for 'issue_status' %>
<!--[form:issue_status]-->
<p><label for="issue_status_name"><%=_('Name')%></label><br/>
<%= text_field 'issue_status', 'name' %></p>
<p><%= check_box 'issue_status', 'is_closed' %>
<label for="issue_status_is_closed"><%=_('Issue closed')%></label></p>
<p><%= check_box 'issue_status', 'is_default' %>
<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>
<!--[eoform:issue_status]-->
<h2><%=_('Issue status')%></h2>
<%= start_form_tag :action => 'update', :id => @issue_status %>
<%= render :partial => 'form' %>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
<h2><%=_('Issue statuses')%></h2>
<table border="0" cellspacing="1" cellpadding="2" class="listTableContent">
<tr class="ListHead">
<th><%=_('Status')%></th>
<th><%=_('Default status')%></th>
<th><%=_('Issue closed')%></th>
<th><%=_('Color')%></th>
<th></th>
</tr>
<% for status in @issue_statuses %>
<tr style="background-color:#CEE1ED">
<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 bgcolor="#<%= status.html_color %>">&nbsp</td>
<td align="center">
<%= start_form_tag :action => 'destroy', :id => status %>
<%= submit_tag _('Delete'), :class => "button-small" %>
<%= end_form_tag %>
</td>
</tr>
<% end %>
</table>
<%= pagination_links_full @issue_status_pages %>
<br />
<%= link_to '&#187; ' + _('New issue status'), :action => 'new' %>
<h2><%=_('New issue status')%></h2>
<%= start_form_tag :action => 'create' %>
<%= render :partial => 'form' %>
<%= submit_tag _('Create') %>
<%= end_form_tag %>
<% if issues.length > 0 %>
<table cellspacing="0" cellpadding="1" width="100%" border="0" class="listTable">
<tr><td>
<table width="100%" border="0" cellspacing="1" cellpadding="2" class="listTableContent">
<tr class="ListHead">
<th>#</th>
<th><%=_('Tracker')%></th>
<th><%=_('Subject')%></th>
</tr>
<% for issue in issues %>
<tr bgcolor="#<%= issue.status.html_color %>">
<td align="center">
<%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %><br />
</td>
<td><p class="small"><%= issue.project.name %> - <%= issue.tracker.name %><br />
<%= issue.status.name %> - <%= format_time(issue.updated_on) %></p></td>
<td>
<p class="small"><%= link_to issue.subject, :controller => 'issues', :action => 'show', :id => issue %></p>
</td>
</tr>
<% end %>
</table>
</td>
</tr>
</table>
<% else %>
<%=_('No issue')%>
<% end %>
\ No newline at end of file
<h2><%=_('Issue')%> #<%= @issue.id %>: <%= @issue.subject %></h2>
<%= error_messages_for 'history' %>
<%= start_form_tag :action => 'change_status', :id => @issue %>
<%= hidden_field_tag 'confirm', 1 %>
<%= hidden_field 'history', 'status_id' %>
<p><%=_('New status')%>: <b><%= @history.status.name %></b></p>
<div>
<p><label for="issue_assigned_to_id"><%=_('Assigned to')%></label><br/>
<select name="issue[assigned_to_id]">
<option value=""></option>
<%= options_from_collection_for_select @assignable_to, "id", "display_name", @issue.assigned_to_id %></p>
</select></p>
</div>
<p><label for="issue_fixed_version"><%=_('Fixed in version')%></label><br/>
<select name="issue[fixed_version_id]">
<option value="">--none--</option>
<%= options_from_collection_for_select @issue.project.versions, "id", "name", @issue.fixed_version_id %>
</select></p>
<p><label for="history_notes"><%=_('Notes')%></label><br />
<%= text_area 'history', 'notes', :cols => 60, :rows => 10 %></p>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
<h2><%=_('Issue')%> #<%= @issue.id %></h2>
<%= error_messages_for 'issue' %>
<%= start_form_tag :action => 'edit', :id => @issue %>
<!--[form:issue]-->
<p><%=_('Status')%>: <b><%= @issue.status.name %></b></p>
<div style="float:left;margin-right:10px;">
<p><label for="issue_tracker_id"><%=_('Tracker')%> <span class="required">*</span></label><br/>
<select name="issue[tracker_id]">
<%= options_from_collection_for_select @trackers, "id", "name", @issue.tracker_id %></p>
</select></p>
</div>
<div style="float:left;margin-right:10px;">
<p><label for="issue_priority_id"><%=_('Priority')%> <span class="required">*</span></label><br/>
<select name="issue[priority_id]">
<%= options_from_collection_for_select @priorities, "id", "name", @issue.priority_id %></p>
</select></p>
</div>
<div style="float:left;margin-right:10px;">
<p><label for="issue_assigned_to_id"><%=_('Assigned to')%></label><br/>
<select name="issue[assigned_to_id]">
<option value=""></option>
<%= options_from_collection_for_select @issue.project.members, "user_id", "name", @issue.assigned_to_id %></p>
</select></p>
</div>
<div>
<p><label for="issue_category_id"><%=_('Category')%></label><br/>
<select name="issue[category_id]">
<option value=""></option>
<%= options_from_collection_for_select @project.issue_categories, "id", "name", @issue.category_id %></p>
</select></p>
</div>
<p><label for="issue_subject"><%=_('Subject')%></label><span class="required">*</span><br/>
<%= text_field 'issue', 'subject', :size => 60 %></p>
<p><label for="issue_descr"><%=_('Description')%></label><span class="required">*</span><br/>
<%= text_area 'issue', 'descr', :cols => 60, :rows => 10 %></p>
<% for custom_value in @custom_values %>
<p><%= content_tag "label", custom_value.custom_field.name %>
<% if custom_value.custom_field.is_required? %><span class="required">*</span><% end %>
<br />
<%= custom_field_tag custom_value %></p>
<% end %>
<p><label for="issue_fixed_version"><%=_('Fixed in version')%></label><br/>
<select name="issue[fixed_version_id]">
<option value="">--none--</option>
<%= options_from_collection_for_select @project.versions, "id", "name", @issue.fixed_version_id %>
</select></p>
<!--[eoform:issue]-->
<center><%= submit_tag _('Save') %></center>
<%= end_form_tag %>
<h2><%=_('Issue')%> #<%= @issue.id %> - <%= @issue.subject %></h2>
<div class="box">
<p><b><%=_('Tracker')%>:</b> <%= @issue.tracker.name %></p>
<p><b><%=_('Priority')%>:</b> <%= @issue.priority.name %></p>
<p><b><%=_('Category')%>:</b> <%= @issue.category.name unless @issue.category_id.nil? %></p>
<p><b><%=_('Status')%>:</b> <%= @issue.status.name %></p>
<p><b><%=_('Author')%>:</b> <%= @issue.author.display_name %></p>
<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><%=_('Created on')%>:</b> <%= format_date(@issue.created_on) %></p>
<% if authorize_for('issues', 'edit') %>
<%= start_form_tag ({:controller => 'issues', :action => 'edit', :id => @issue}, :method => "get" ) %>
<%= submit_tag _('Edit') %>
<%= end_form_tag %>
&nbsp;&nbsp;
<% end %>
<% if authorize_for('issues', 'change_status') and @status_options and !@status_options.empty? %>
<%= start_form_tag ({:controller => 'issues', :action => 'change_status', :id => @issue}) %>
<label for="history_status_id"><%=_('Change status')%>:</label>
<select name="history[status_id]">
<%= options_from_collection_for_select @status_options, "id", "name" %>
</select>
<%= submit_tag _ "Update..." %>
<%= end_form_tag %>
&nbsp;&nbsp;
<% end %>
<% if authorize_for('issues', 'destroy') %>
<%= start_form_tag ({:controller => 'issues', :action => 'destroy', :id => @issue} ) %>
<%= submit_tag _ "Delete" %>
<%= end_form_tag %>
&nbsp;&nbsp;
<% end %>
</div>
<div class="splitcontentleft">
<div class="box">
<h3><%=_('History')%></h3>
<table width="100%">
<% for history in @issue.histories.find(:all, :include => :author) %>
<tr>
<td><%= format_date(history.created_on) %></td>
<td><%= history.author.display_name %></td>
<td><b><%= history.status.name %></b></td>
</tr>
<% if history.notes? %>
<tr><td colspan=3><div class="notes"><%= history.notes %></td></tr>
<% end %>
<% end %>
</table>
</div>
</div>
<div class="splitcontentright">
<div class="box">
<h3><%=_('Attachments')%></h3>
<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><%= format_date(attachment.created_on) %></td>
<td><%= attachment.author.display_name %></td>
<% if authorize_for('issues', 'destroy_attachment') %>
<td>
<%= start_form_tag :action => 'destroy_attachment', :id => @issue, :attachment_id => attachment %>
<%= submit_tag _('Delete'), :class => "button-small" %>
<%= end_form_tag %>
</td>
<% end %>
</tr>
<% end %>
</table>
<br />
<% if authorize_for('issues', 'add_attachment') %>
<%= start_form_tag ({ :controller => 'issues', :action => 'add_attachment', :id => @issue }, :multipart => true) %>
<%=_('Add file')%>: <%= file_field 'attachment', 'file' %>
<%= submit_tag _('Add') %>
<%= end_form_tag %>
<% end %>
</div>
</div>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>redMine</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="redMine" />
<meta name="keywords" content="issue,bug,tracker" />
<%= stylesheet_link_tag "application" %>
<%= stylesheet_link_tag "rails" %>
<%= javascript_include_tag :defaults %>
</head>
<body>
<div id="container" >
<div id="header">
<div style="float: left;">
<h1><%= RDM_APP_NAME %></h1>
<h2>Project management</h2>
</div>
<div style="float: right; padding-right: 1em; padding-top: 0.2em;">
<% unless session[:user].nil? %><small><%=_('Logged as')%> <b><%= session[:user].login %></b></small><% end %>
</div>
</div>
<div id="navigation">
<ul>
<li class="selected"><%= link_to _('Home'), { :controller => '' }, :class => "picHome" %></li>
<li><%= link_to _('My page'), { :controller => 'account', :action => 'my_page'}, :class => "picUserPage" %></li>
<li><%= link_to _('Projects'), { :controller => 'projects' }, :class => "picProject" %></li>
<% unless session[:user].nil? %>
<li><%= link_to _('My account'), { :controller => 'account', :action => 'my_account' }, :class => "picUser" %></li>
<% end %>
<% if admin_loggedin? %>
<li><%= link_to _('Administration'), { :controller => 'admin' }, :class => "picAdmin" %></li>
<% end %>
<li class="right"><%= link_to _('Help'), { :controller => 'help', :ctrl => @params[:controller], :page => @params[:action] }, :target => "new", :class => "picHelp" %></li>
<% if session[:user].nil? %>
<li class="right"><%= link_to _('Log in'), { :controller => 'account', :action => 'login' }, :class => "picUser" %></li>
<% else %>
<li class="right"><%= link_to _('Logout'), { :controller => 'account', :action => 'logout' }, :class => "picUser" %></li>
<% end %>
</ul>
</div>
<div id="subcontent">
<% unless @project.nil? || @project.id.nil? %>
<h2><%= @project.name %></h2>
<ul class="menublock">
<li><%= link_to _('Overview'), :controller => 'projects', :action => 'show', :id => @project %></li>
<li><%= link_to _('Issues'), :controller => 'projects', :action => 'list_issues', :id => @project %></li>
<li><%= link_to _('Reports'), :controller => 'reports', :action => 'issue_report', :id => @project %></li>
<li><%= link_to _('News'), :controller => 'projects', :action => 'list_news', :id => @project %></li>
<li><%= link_to _('Change log'), :controller => 'projects', :action => 'changelog', :id => @project %></li>
<li><%= link_to _('Documents'), :controller => 'projects', :action => 'list_documents', :id => @project %></li>
<li><%= link_to _('Members'), :controller => 'projects', :action => 'list_members', :id => @project %></li>
<li><%= link_to _('Files'), :controller => 'projects', :action => 'list_files', :id => @project %></li>
<li><%= link_to_if_authorized _('Settings'), :controller => 'projects', :action => 'settings', :id => @project %></li>
</ul>
<% end %>
<% unless session[:user].nil? %>
<h2><%=_('My projects') %></h2>
<ul class="menublock">
<% for membership in session[:user].memberships %>
<li><%= link_to membership.project.name, :controller => 'projects', :action => 'show', :id => membership.project %></li>
<% end %>
</ul>
<% end %>
</div>
<div id="content">
<% if flash[:notice] %><p style="color: green"><%= flash[:notice] %></p><% end %>
<%= @content_for_layout %>
</div>
<div id="footer">
<p><a href="http://redmine.sourceforge.net/" target="_new"><%= RDM_APP_NAME %></a> <%= RDM_APP_VERSION %></p>
</div>
</div>
</body>
</html>
\ No newline at end of file
<%=_('Issue')%> #<%= issue.id %> - <%= issue.subject %>
<%=_('Author')%>: <%= issue.author.display_name %>
<%= issue.descr %>
http://<%= RDM_HOST_NAME %>/issues/show/<%= issue.id %>
\ No newline at end of file
Issue #<%= @issue.id %> has been reported.
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
Issue #<%= @issue.id %> has been updated to "<%= @issue.status.name %>" status.
----------------------------------------
<%= render :file => "_issue", :use_full_path => true, :locals => { :issue => @issue } %>
\ No newline at end of file
<%= error_messages_for 'news' %>
<!--[form:news]-->
<p><label for="news_title"><%=_('Title')%></label> <span class="required">*</span><br/>
<%= text_field 'news', 'title', :size => 60 %></p>
<p><label for="news_shortdescr"><%=_('Summary')%> <span class="required">*</span></label><br/>
<%= text_area 'news', 'shortdescr', :cols => 60, :rows => 2 %></p>
<p><label for="news_descr"><%=_('Description')%> <span class="required">*</span></label><br/>
<%= text_area 'news', 'descr', :cols => 60, :rows => 10 %></p>
<!--[eoform:news]-->
<h2><%=_('News')%></h2>
<%= start_form_tag :action => 'edit', :id => @news %>
<%= render :partial => 'form' %>
<%= submit_tag _('Save') %>
<%= end_form_tag %>
<h2><%= @news.title %></h2>
<p>
<b><%=_('Summary')%></b>: <%= @news.shortdescr %><br />
<b><%=_('By')%></b>: <%= @news.author.display_name %><br />
<b><%=_('Date')%></b>: <%= format_time(@news.created_on) %>
</p>
<%= @news.descr %>
<%= error_messages_for 'project' %>
<!--[form:project]-->
<p><label for="project_name"><%=_('Name')%> <span class="required">*</span></label><br/>
<%= text_field 'project', 'name' %></p>
<p><label for="project_descr"><%=_('Description')%> <span class="required">*</span></label><br/>
<%= text_field 'project', 'descr', :size => 60 %></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>
<fieldset><legend><%=_('Custom fields')%></legend>
<% for custom_field in @custom_fields %>
<input type="checkbox"
name="custom_field_ids[]"
value="<%= custom_field.id %>"
<%if @project.custom_fields.include? custom_field%>checked="checked"<%end%>
> <%= custom_field.name %>
<% end %></fieldset>
<br />
<!--[eoform:project]-->
<h2><%=_('New project')%></h2>
<%= start_form_tag :action => 'add' %>
<%= render :partial => 'form' %>
<%= submit_tag _('Create') %>
<%= end_form_tag %>
<h2><%=_('New document')%></h2>
<%= error_messages_for 'document' %>
<%= start_form_tag( { :action => 'add_document', :id => @project }, :multipart => true) %>
<!--[form:document]-->
<p><label for="document_category_id"><%=_('Category')%></label><br />
<select name="document[category_id]">
<%= options_from_collection_for_select @categories, "id", "name",@document.category_id %>
</select></p>
<p><label for="document_title"><%=_('Title')%> <span class="required">*</span></label><br />
<%= text_field 'document', 'title', :size => 60 %></p>
<p><label for="document_descr"><%=_('Description')%> <span class="required">*</span></label><br />
<%= text_area 'document', 'descr', :cols => 60, :rows => 5 %></p>
<p><label for="attachment_file"><%=_('File')%></label><br/>
<%= file_field 'attachment', 'file' %></p>
<!--[eoform:document]-->
<%= submit_tag _('Create') %>
<%= end_form_tag %>
<h2><%=_('New file')%></h2>
<%= start_form_tag ({ :action => 'add_file', :project => @project }, :multipart => true) %>
<p><label for="version_id"><%=_('Version')%></label><br />
<select name="version_id">
<%= options_from_collection_for_select @versions, "id", "name" %>
</select></p>
<p><b><%=_('File')%><b><br /><%= file_field 'attachment', 'file' %></p>
<br/>
<%= submit_tag _('Add') %>
<%= end_form_tag %>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment