Commit 8cece46d authored by Tim Felgentreff's avatar Tim Felgentreff

Merge branch 'master' into master-journalized

Conflicts:
	app/controllers/application_controller.rb
	app/controllers/context_menus_controller.rb
	app/controllers/gantts_controller.rb
	app/controllers/issue_moves_controller.rb
	app/controllers/issues_controller.rb
	app/controllers/journals_controller.rb
	app/controllers/previews_controller.rb
	app/controllers/timelog_controller.rb
	app/helpers/issues_helper.rb
	app/helpers/journals_helper.rb
	app/models/issue.rb
	app/models/journal.rb
	app/models/journal_observer.rb
	app/views/admin/projects.rhtml
	app/views/context_menus/issues.html.erb
	app/views/issues/_action_menu.rhtml
	app/views/issues/_history.rhtml
	app/views/news/_news.rhtml
	app/views/news/index.rhtml
	app/views/repositories/diff.rhtml
	config/locales/bg.yml
	config/locales/bs.yml
	config/locales/ca.yml
	config/locales/cs.yml
	config/locales/da.yml
	config/locales/de.yml
	config/locales/el.yml
	config/locales/en-GB.yml
	config/locales/es.yml
	config/locales/eu.yml
	config/locales/fi.yml
	config/locales/fr.yml
	config/locales/gl.yml
	config/locales/he.yml
	config/locales/hr.yml
	config/locales/hu.yml
	config/locales/id.yml
	config/locales/it.yml
	config/locales/ko.yml
	config/locales/lt.yml
	config/locales/lv.yml
	config/locales/mn.yml
	config/locales/nl.yml
	config/locales/no.yml
	config/locales/pl.yml
	config/locales/pt-BR.yml
	config/locales/pt.yml
	config/locales/ro.yml
	config/locales/ru.yml
	config/locales/sk.yml
	config/locales/sl.yml
	config/locales/sr-YU.yml
	config/locales/sr.yml
	config/locales/sv.yml
	config/locales/th.yml
	config/locales/tr.yml
	config/locales/uk.yml
	config/locales/vi.yml
	config/locales/zh-TW.yml
	config/locales/zh.yml
	config/routes.rb
	doc/CHANGELOG
	lib/redmine.rb
	lib/redmine/export/pdf.rb
	lib/redmine/helpers/gantt.rb
	lib/redmine/version.rb
	public/stylesheets/application.css
	test/functional/context_menus_controller_test.rb
	test/functional/issues_controller_test.rb
	test/functional/journals_controller_test.rb
	test/functional/previews_controller_test.rb
	test/functional/users_controller_test.rb
	test/functional/wiki_controller_test.rb
	test/integration/routing_test.rb
	test/unit/issue_test.rb
	test/unit/mailer_test.rb
	test/unit/query_test.rb
parents f0c65744 d3381fb5
class ActivitiesController < ApplicationController
menu_item :activity
before_filter :find_optional_project
accept_key_auth :index
def index
@days = Setting.activity_days_default.to_i
if params[:from]
begin; @date_to = params[:from].to_date + 1; rescue; end
end
@date_to ||= Date.today + 1
@date_from = @date_to - @days
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
@author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
@activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
:with_subprojects => @with_subprojects,
:author => @author)
@activity.scope_select {|t| !params["show_#{t}"].nil?}
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
events = @activity.events(@date_from, @date_to)
if events.empty? || stale?(:etag => [events.first, User.current])
respond_to do |format|
format.html {
@events_by_day = events.group_by(&:event_date)
render :layout => false if request.xhr?
}
format.atom {
title = l(:label_activity)
if @author
title = @author.name
elsif @activity.scope.size == 1
title = l("label_#{@activity.scope.first.singularize}_plural")
end
render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
}
end
end
rescue ActiveRecord::RecordNotFound
render_404
end
private
# TODO: refactor, duplicated in projects_controller
def find_optional_project
return true unless params[:id]
@project = Project.find(params[:id])
authorize
rescue ActiveRecord::RecordNotFound
render_404
end
end
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
...@@ -23,29 +23,29 @@ class ApplicationController < ActionController::Base ...@@ -23,29 +23,29 @@ class ApplicationController < ActionController::Base
layout 'base' layout 'base'
exempt_from_layout 'builder' exempt_from_layout 'builder'
# Remove broken cookie after upgrade from 0.8.x (#4292) # Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360 # See https://rails.lighthouseapp.com/projects/8994/tickets/3360
# TODO: remove it when Rails is fixed # TODO: remove it when Rails is fixed
before_filter :delete_broken_cookies before_filter :delete_broken_cookies
def delete_broken_cookies def delete_broken_cookies
if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/ if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
cookies.delete '_redmine_session' cookies.delete '_redmine_session'
redirect_to home_path redirect_to home_path
return false return false
end end
end end
before_filter :user_setup, :check_if_login_required, :set_localization before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password filter_parameter_logging :password
protect_from_forgery protect_from_forgery
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
include Redmine::Search::Controller include Redmine::Search::Controller
include Redmine::MenuManager::MenuController include Redmine::MenuManager::MenuController
helper Redmine::MenuManager::MenuHelper helper Redmine::MenuManager::MenuHelper
Redmine::Scm::Base.all.each do |scm| Redmine::Scm::Base.all.each do |scm|
require_dependency "repository/#{scm.underscore}" require_dependency "repository/#{scm.underscore}"
end end
...@@ -56,7 +56,7 @@ class ApplicationController < ActionController::Base ...@@ -56,7 +56,7 @@ class ApplicationController < ActionController::Base
# Find the current user # Find the current user
User.current = find_current_user User.current = find_current_user
end end
# Returns the current user or nil if no user is logged in # Returns the current user or nil if no user is logged in
# and starts a session if needed # and starts a session if needed
def find_current_user def find_current_user
...@@ -94,14 +94,14 @@ class ApplicationController < ActionController::Base ...@@ -94,14 +94,14 @@ class ApplicationController < ActionController::Base
User.current = User.anonymous User.current = User.anonymous
end end
end end
# check if login is globally required to access the application # check if login is globally required to access the application
def check_if_login_required def check_if_login_required
# no check needed if user is already logged in # no check needed if user is already logged in
return true if User.current.logged? return true if User.current.logged?
require_login if Setting.login_required? require_login if Setting.login_required?
end end
def set_localization def set_localization
lang = nil lang = nil
if User.current.logged? if User.current.logged?
...@@ -117,7 +117,7 @@ class ApplicationController < ActionController::Base ...@@ -117,7 +117,7 @@ class ApplicationController < ActionController::Base
lang ||= Setting.default_language lang ||= Setting.default_language
set_language_if_valid(lang) set_language_if_valid(lang)
end end
def require_login def require_login
if !User.current.logged? if !User.current.logged?
# Extract only the basic url parameters on non-GET requests # Extract only the basic url parameters on non-GET requests
...@@ -146,14 +146,14 @@ class ApplicationController < ActionController::Base ...@@ -146,14 +146,14 @@ class ApplicationController < ActionController::Base
end end
true true
end end
def deny_access def deny_access
User.current.logged? ? render_403 : require_login User.current.logged? ? render_403 : require_login
end end
# Authorize the user for the requested action # Authorize the user for the requested action
def authorize(ctrl = params[:controller], action = params[:action], global = false) def authorize(ctrl = params[:controller], action = params[:action], global = false)
allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, :global => global) allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
allowed ? true : deny_access allowed ? true : deny_access
end end
...@@ -169,6 +169,13 @@ class ApplicationController < ActionController::Base ...@@ -169,6 +169,13 @@ class ApplicationController < ActionController::Base
render_404 render_404
end end
# Find project of id params[:project_id]
def find_project_by_project_id
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
# Find a project based on params[:project_id] # Find a project based on params[:project_id]
# TODO: some subclasses override this, see about merging their logic # TODO: some subclasses override this, see about merging their logic
def find_optional_project def find_optional_project
...@@ -182,7 +189,7 @@ class ApplicationController < ActionController::Base ...@@ -182,7 +189,7 @@ class ApplicationController < ActionController::Base
# Finds and sets @project based on @object.project # Finds and sets @project based on @object.project
def find_project_from_association def find_project_from_association
render_404 unless @object.present? render_404 unless @object.present?
@project = @object.project @project = @object.project
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
...@@ -206,18 +213,21 @@ class ApplicationController < ActionController::Base ...@@ -206,18 +213,21 @@ class ApplicationController < ActionController::Base
def find_issues def find_issues
@issues = Issue.find_all_by_id(params[:id] || params[:ids]) @issues = Issue.find_all_by_id(params[:id] || params[:ids])
raise ActiveRecord::RecordNotFound if @issues.empty? raise ActiveRecord::RecordNotFound if @issues.empty?
projects = @issues.collect(&:project).compact.uniq @projects = @issues.collect(&:project).compact.uniq
if projects.size == 1 @project = @projects.first if @projects.size == 1
@project = projects.first rescue ActiveRecord::RecordNotFound
else render_404
end
# Check if project is unique before bulk operations
def check_project_uniqueness
unless @project
# TODO: let users bulk edit/move/destroy issues from different projects # TODO: let users bulk edit/move/destroy issues from different projects
render_error 'Can not bulk edit/move/destroy issues from different projects' render_error 'Can not bulk edit/move/destroy issues from different projects'
return false return false
end end
rescue ActiveRecord::RecordNotFound
render_404
end end
# make sure that the user is a member of the project (or admin) if project is private # make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project # used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy def check_project_privacy
...@@ -254,7 +264,7 @@ class ApplicationController < ActionController::Base ...@@ -254,7 +264,7 @@ class ApplicationController < ActionController::Base
end end
redirect_to default redirect_to default
end end
def render_403 def render_403
@project = nil @project = nil
respond_to do |format| respond_to do |format|
...@@ -266,7 +276,7 @@ class ApplicationController < ActionController::Base ...@@ -266,7 +276,7 @@ class ApplicationController < ActionController::Base
end end
return false return false
end end
def render_404 def render_404
respond_to do |format| respond_to do |format|
format.html { render :template => "common/404", :layout => use_layout, :status => 404 } format.html { render :template => "common/404", :layout => use_layout, :status => 404 }
...@@ -277,10 +287,10 @@ class ApplicationController < ActionController::Base ...@@ -277,10 +287,10 @@ class ApplicationController < ActionController::Base
end end
return false return false
end end
def render_error(msg) def render_error(msg)
respond_to do |format| respond_to do |format|
format.html { format.html {
flash.now[:error] = msg flash.now[:error] = msg
render :text => '', :layout => use_layout, :status => 500 render :text => '', :layout => use_layout, :status => 500
} }
...@@ -297,31 +307,31 @@ class ApplicationController < ActionController::Base ...@@ -297,31 +307,31 @@ class ApplicationController < ActionController::Base
def use_layout def use_layout
request.xhr? ? false : 'base' request.xhr? ? false : 'base'
end end
def invalid_authenticity_token def invalid_authenticity_token
if api_request? if api_request?
logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)." logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
end end
render_error "Invalid form authenticity token." render_error "Invalid form authenticity token."
end end
def render_feed(items, options={}) def render_feed(items, options={})
@items = items || [] @items = items || []
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime } @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i) @items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title @title = options[:title] || Setting.app_title
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml' render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end end
def self.accept_key_auth(*actions) def self.accept_key_auth(*actions)
actions = actions.flatten.map(&:to_s) actions = actions.flatten.map(&:to_s)
write_inheritable_attribute('accept_key_auth_actions', actions) write_inheritable_attribute('accept_key_auth_actions', actions)
end end
def accept_key_auth_actions def accept_key_auth_actions
self.class.read_inheritable_attribute('accept_key_auth_actions') || [] self.class.read_inheritable_attribute('accept_key_auth_actions') || []
end end
# Returns the number of objects that should be displayed # Returns the number of objects that should be displayed
# on the paginated list # on the paginated list
def per_page_option def per_page_option
...@@ -357,12 +367,12 @@ class ApplicationController < ActionController::Base ...@@ -357,12 +367,12 @@ class ApplicationController < ActionController::Base
rescue rescue
nil nil
end end
# Returns a string that can be used as filename value in Content-Disposition header # Returns a string that can be used as filename value in Content-Disposition header
def filename_for_content_disposition(name) def filename_for_content_disposition(name)
request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
end end
def api_request? def api_request?
%w(xml json).include? params[:format] %w(xml json).include? params[:format]
end end
...@@ -401,5 +411,5 @@ class ApplicationController < ActionController::Base ...@@ -401,5 +411,5 @@ class ApplicationController < ActionController::Base
{ attribute => error } { attribute => error }
end.to_json end.to_json
end end
end end
...@@ -8,6 +8,8 @@ class CalendarsController < ApplicationController ...@@ -8,6 +8,8 @@ class CalendarsController < ApplicationController
helper :projects helper :projects
helper :queries helper :queries
include QueriesHelper include QueriesHelper
helper :sort
include SortHelper
def show def show
if params[:year] and params[:year].to_i > 1900 if params[:year] and params[:year].to_i > 1900
...@@ -32,8 +34,11 @@ class CalendarsController < ApplicationController ...@@ -32,8 +34,11 @@ class CalendarsController < ApplicationController
@calendar.events = events @calendar.events = events
end end
render :layout => false if request.xhr? render :action => 'show', :layout => false if request.xhr?
end end
def update
show
end
end end
class CommentsController < ApplicationController
default_search_scope :news
model_object News
before_filter :find_model_object
before_filter :find_project_from_association
before_filter :authorize
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@comment = Comment.new(params[:comment])
@comment.author = User.current
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
end
redirect_to :controller => 'news', :action => 'show', :id => @news
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
@news.comments.find(params[:comment_id]).destroy
redirect_to :controller => 'news', :action => 'show', :id => @news
end
private
# ApplicationController's find_model_object sets it based on the controller
# name so it needs to be overriden and set to @news instead
def find_model_object
super
@news = @object
@comment = nil
@news
end
end
class ContextMenusController < ApplicationController class ContextMenusController < ApplicationController
helper :watchers helper :watchers
def issues def issues
@issues = Issue.find_all_by_id(params[:ids], :include => :project) @issues = Issue.find_all_by_id(params[:ids], :include => :project)
if (@issues.size == 1) if (@issues.size == 1)
@issue = @issues.first @issue = @issues.first
@allowed_statuses = @issue.new_statuses_allowed_to(User.current) @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
else
@allowed_statuses = @issues.map do |i|
i.new_statuses_allowed_to(User.current)
end.inject do |memo,s|
memo & s
end
end end
projects = @issues.collect(&:project).compact.uniq @projects = @issues.collect(&:project).compact.uniq
@project = projects.first if projects.size == 1 @project = @projects.first if @projects.size == 1
@can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)), @can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
:log_time => (@project && User.current.allowed_to?(:log_time, @project)), :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
:update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))), :update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)),
:move => (@project && User.current.allowed_to?(:move_issues, @project)), :move => (@project && User.current.allowed_to?(:move_issues, @project)),
:copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
:delete => (@project && User.current.allowed_to?(:delete_issues, @project)) :delete => User.current.allowed_to?(:delete_issues, @projects)
} }
if @project if @project
@assignables = @project.assignable_users @assignables = @project.assignable_users
@assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to) @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
@trackers = @project.trackers @trackers = @project.trackers
else
#when multiple projects, we only keep the intersection of each set
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end end
@priorities = IssuePriority.all.reverse @priorities = IssuePriority.all.reverse
@statuses = IssueStatus.find(:all, :order => 'position') @statuses = IssueStatus.find(:all, :order => 'position')
@back = back_url @back = back_url
render :layout => false render :layout => false
end end
end end
class FilesController < ApplicationController
menu_item :files
before_filter :find_project_by_project_id
before_filter :authorize
helper :sort
include SortHelper
def index
sort_init 'filename', 'asc'
sort_update 'filename' => "#{Attachment.table_name}.filename",
'created_on' => "#{Attachment.table_name}.created_on",
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
@containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end
def new
@versions = @project.versions.sort
end
def create
container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
attachments = Attachment.attach_files(container, params[:attachments])
render_attachment_warning_if_needed(container)
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
Mailer.deliver_attachments_added(attachments[:files])
end
redirect_to project_files_path(@project)
end
end
...@@ -4,6 +4,7 @@ class GanttsController < ApplicationController ...@@ -4,6 +4,7 @@ class GanttsController < ApplicationController
rescue_from Query::StatementInvalid, :with => :query_statement_invalid rescue_from Query::StatementInvalid, :with => :query_statement_invalid
helper :gantt
helper :issues helper :issues
helper :projects helper :projects
helper :queries helper :queries
...@@ -11,36 +12,25 @@ class GanttsController < ApplicationController ...@@ -11,36 +12,25 @@ class GanttsController < ApplicationController
helper :sort helper :sort
include SortHelper include SortHelper
include Redmine::Export::PDF include Redmine::Export::PDF
def show def show
@gantt = Redmine::Helpers::Gantt.new(params) @gantt = Redmine::Helpers::Gantt.new(params)
@gantt.project = @project
retrieve_query retrieve_query
@query.group_by = nil @query.group_by = nil
if @query.valid? @gantt.query = @query if @query.valid?
events = []
# Issues that have start and due dates
events += @query.issues(:include => [:tracker, :assigned_to, :priority],
:order => "start_date, due_date",
:conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
)
# Issues that don't have a due date but that are assigned to a version with a date
events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
:order => "start_date, effective_date",
:conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
)
# Versions
events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
@gantt.events = events
end
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt' basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
respond_to do |format| respond_to do |format|
format.html { render :action => "show", :layout => !request.xhr? } format.html { render :action => "show", :layout => !request.xhr? }
format.png { send_data(@gantt.to_image(@project), :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image') format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") } format.pdf { send_data(@gantt.to_pdf, :type => 'application/pdf', :filename => "#{basename}.pdf") }
end end
end end
def update
show
end
end end
class IssueMovesController < ApplicationController class IssueMovesController < ApplicationController
default_search_scope :issues default_search_scope :issues
before_filter :find_issues before_filter :find_issues, :check_project_uniqueness
before_filter :authorize before_filter :authorize
def new def new
prepare_for_issue_move prepare_for_issue_move
render :layout => false if request.xhr? render :layout => false if request.xhr?
...@@ -47,7 +47,7 @@ class IssueMovesController < ApplicationController ...@@ -47,7 +47,7 @@ class IssueMovesController < ApplicationController
@copy = params[:copy_options] && params[:copy_options][:copy] @copy = params[:copy_options] && params[:copy_options][:copy]
@allowed_projects = Issue.allowed_target_projects_on_move @allowed_projects = Issue.allowed_target_projects_on_move
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id] @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
@target_project ||= @project @target_project ||= @project
@trackers = @target_project.trackers @trackers = @target_project.trackers
@available_statuses = Workflow.available_statuses(@project) @available_statuses = Workflow.available_statuses(@project)
end end
......
...@@ -20,14 +20,14 @@ class IssuesController < ApplicationController ...@@ -20,14 +20,14 @@ class IssuesController < ApplicationController
default_search_scope :issues default_search_scope :issues
before_filter :find_issue, :only => [:show, :edit, :update] before_filter :find_issue, :only => [:show, :edit, :update]
before_filter :find_issues, :only => [:bulk_edit, :move, :perform_move, :destroy] before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy]
before_filter :check_project_uniqueness, :only => [:move, :perform_move]
before_filter :find_project, :only => [:new, :create] before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index, :changes] before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
before_filter :find_optional_project, :only => [:index, :changes]
before_filter :check_for_default_issue_status, :only => [:new, :create] before_filter :check_for_default_issue_status, :only => [:new, :create]
before_filter :build_new_issue_from_params, :only => [:new, :create] before_filter :build_new_issue_from_params, :only => [:new, :create]
accept_key_auth :index, :show, :changes accept_key_auth :index, :show
rescue_from Query::StatementInvalid, :with => :query_statement_invalid rescue_from Query::StatementInvalid, :with => :query_statement_invalid
...@@ -49,6 +49,7 @@ class IssuesController < ApplicationController ...@@ -49,6 +49,7 @@ class IssuesController < ApplicationController
include SortHelper include SortHelper
include IssuesHelper include IssuesHelper
helper :timelog helper :timelog
helper :gantt
include Redmine::Export::PDF include Redmine::Export::PDF
verify :method => [:post, :delete], verify :method => [:post, :delete],
...@@ -56,6 +57,7 @@ class IssuesController < ApplicationController ...@@ -56,6 +57,7 @@ class IssuesController < ApplicationController
:render => { :nothing => true, :status => :method_not_allowed } :render => { :nothing => true, :status => :method_not_allowed }
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed } verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed }
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed } verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def index def index
...@@ -96,22 +98,7 @@ class IssuesController < ApplicationController ...@@ -96,22 +98,7 @@ class IssuesController < ApplicationController
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
def changes
retrieve_query
sort_init 'id', 'desc'
sort_update(@query.sortable_columns)
if @query.valid?
@journals = @query.issue_journals(:order => "#{Journal.table_name}.created_at DESC",
:limit => 25)
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
render :layout => false, :content_type => 'application/atom+xml'
rescue ActiveRecord::RecordNotFound
render_404
end
def show def show
@journals = @issue.journals.find(:all, :include => [:user], :order => "#{Journal.table_name}.created_at ASC") @journals = @issue.journals.find(:all, :include => [:user], :order => "#{Journal.table_name}.created_at ASC")
@journals.reverse! if User.current.wants_comments_in_reverse_order? @journals.reverse! if User.current.wants_comments_in_reverse_order?
...@@ -125,7 +112,7 @@ class IssuesController < ApplicationController ...@@ -125,7 +112,7 @@ class IssuesController < ApplicationController
format.html { render :template => 'issues/show.rhtml' } format.html { render :template => 'issues/show.rhtml' }
format.xml { render :layout => false } format.xml { render :layout => false }
format.json { render :text => @issue.to_json, :layout => false } format.json { render :text => @issue.to_json, :layout => false }
format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' } format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") } format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end end
end end
...@@ -148,7 +135,7 @@ class IssuesController < ApplicationController ...@@ -148,7 +135,7 @@ class IssuesController < ApplicationController
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue}) call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
respond_to do |format| respond_to do |format|
format.html { format.html {
redirect_to(params[:continue] ? { :action => 'new', :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } : redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue }) { :action => 'show', :id => @issue })
} }
format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) } format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
...@@ -208,30 +195,31 @@ class IssuesController < ApplicationController ...@@ -208,30 +195,31 @@ class IssuesController < ApplicationController
# Bulk edit a set of issues # Bulk edit a set of issues
def bulk_edit def bulk_edit
@issues.sort! @issues.sort!
if request.post? @available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
attributes = (params[:issue] || {}).reject {|k,v| v.blank?} @custom_fields = @projects.map{|p|p.all_issue_custom_fields}.inject{|memo,c|memo & c}
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end
unsaved_issue_ids = [] def bulk_update
@issues.each do |issue| @issues.sort!
issue.reload attributes = parse_params_for_bulk_issue_attributes(params)
journal = issue.init_journal(User.current, params[:notes])
issue.safe_attributes = attributes unsaved_issue_ids = []
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) @issues.each do |issue|
unless issue.save issue.reload
# Keep unsaved issue ids to display them in flash error journal = issue.init_journal(User.current, params[:notes])
unsaved_issue_ids << issue.id issue.safe_attributes = attributes
end call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
unless issue.save
# Keep unsaved issue ids to display them in flash error
unsaved_issue_ids << issue.id
end end
set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
return
end end
@available_statuses = Workflow.available_statuses(@project) set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
@custom_fields = @project.all_issue_custom_fields redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
end end
def destroy def destroy
@hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
if @hours > 0 if @hours > 0
...@@ -257,7 +245,7 @@ class IssuesController < ApplicationController ...@@ -257,7 +245,7 @@ class IssuesController < ApplicationController
end end
@issues.each(&:destroy) @issues.each(&:destroy)
respond_to do |format| respond_to do |format|
format.html { redirect_to :action => 'index', :project_id => @project } format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.xml { head :ok } format.xml { head :ok }
format.json { head :ok } format.json { head :ok }
end end
...@@ -270,7 +258,7 @@ private ...@@ -270,7 +258,7 @@ private
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
def find_project def find_project
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id) @project = Project.find(project_id)
...@@ -286,8 +274,8 @@ private ...@@ -286,8 +274,8 @@ private
@priorities = IssuePriority.all @priorities = IssuePriority.all
@edit_allowed = User.current.allowed_to?(:edit_issues, @project) @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new @time_entry = TimeEntry.new
@notes = params[:notes] @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@issue.init_journal(User.current, @notes) @issue.init_journal(User.current, @notes)
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue] if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
...@@ -300,6 +288,7 @@ private ...@@ -300,6 +288,7 @@ private
end end
# TODO: Refactor, lots of extra code in here # TODO: Refactor, lots of extra code in here
# TODO: Changing tracker on an existing issue should not trigger this
def build_new_issue_from_params def build_new_issue_from_params
if params[:id].blank? if params[:id].blank?
@issue = Issue.new @issue = Issue.new
...@@ -308,7 +297,7 @@ private ...@@ -308,7 +297,7 @@ private
else else
@issue = @project.issues.visible.find(params[:id]) @issue = @project.issues.visible.find(params[:id])
end end
@issue.project = @project @issue.project = @project
# Tracker must be set before custom field values # Tracker must be set before custom field values
@issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first) @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
...@@ -318,7 +307,9 @@ private ...@@ -318,7 +307,9 @@ private
end end
if params[:issue].is_a?(Hash) if params[:issue].is_a?(Hash)
@issue.safe_attributes = params[:issue] @issue.safe_attributes = params[:issue]
@issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project) if User.current.allowed_to?(:add_issue_watchers, @project) && @issue.new_record?
@issue.watcher_user_ids = params[:issue]['watcher_user_ids']
end
end end
@issue.author = User.current @issue.author = User.current
@issue.start_date ||= Date.today @issue.start_date ||= Date.today
...@@ -332,4 +323,11 @@ private ...@@ -332,4 +323,11 @@ private
return false return false
end end
end end
def parse_params_for_bulk_issue_attributes(params)
attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
attributes
end
end end
...@@ -54,7 +54,7 @@ class MyController < ApplicationController ...@@ -54,7 +54,7 @@ class MyController < ApplicationController
@pref = @user.pref @pref = @user.pref
if request.post? if request.post?
@user.attributes = params[:user] @user.attributes = params[:user]
@user.mail_notification = (params[:notification_option] == 'all') @user.mail_notification = params[:notification_option] || 'only_my_events'
@user.pref.attributes = params[:pref] @user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1') @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save if @user.save
...@@ -66,12 +66,8 @@ class MyController < ApplicationController ...@@ -66,12 +66,8 @@ class MyController < ApplicationController
return return
end end
end end
@notification_options = [[l(:label_user_mail_option_all), 'all'], @notification_options = @user.valid_notification_options
[l(:label_user_mail_option_none), 'none']] @notification_option = @user.mail_notification #? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
# Only users that belong to more than 1 project can select projects for which they are notified
# Note that @user.membership.size would fail since AR ignores :include association option when doing a count
@notification_options.insert 1, [l(:label_user_mail_option_selected), 'selected'] if @user.memberships.length > 1
@notification_option = @user.mail_notification? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
end end
# Manage user's password # Manage user's password
......
...@@ -18,10 +18,10 @@ ...@@ -18,10 +18,10 @@
class NewsController < ApplicationController class NewsController < ApplicationController
default_search_scope :news default_search_scope :news
model_object News model_object News
before_filter :find_model_object, :except => [:new, :index, :preview] before_filter :find_model_object, :except => [:new, :create, :index]
before_filter :find_project_from_association, :except => [:new, :index, :preview] before_filter :find_project_from_association, :except => [:new, :create, :index]
before_filter :find_project, :only => [:new, :preview] before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index, :preview] before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => :index before_filter :find_optional_project, :only => :index
accept_key_auth :index accept_key_auth :index
...@@ -46,49 +46,38 @@ class NewsController < ApplicationController ...@@ -46,49 +46,38 @@ class NewsController < ApplicationController
def new def new
@news = News.new(:project => @project, :author => User.current) @news = News.new(:project => @project, :author => User.current)
end
def create
@news = News.new(:project => @project, :author => User.current)
if request.post? if request.post?
@news.attributes = params[:news] @news.attributes = params[:news]
if @news.save if @news.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'news', :action => 'index', :project_id => @project redirect_to :controller => 'news', :action => 'index', :project_id => @project
else
render :action => 'new'
end end
end end
end end
def edit def edit
if request.post? and @news.update_attributes(params[:news])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @news
end
end end
def add_comment def update
@comment = Comment.new(params[:comment]) if request.put? and @news.update_attributes(params[:news])
@comment.author = User.current flash[:notice] = l(:notice_successful_update)
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
redirect_to :action => 'show', :id => @news redirect_to :action => 'show', :id => @news
else else
show render :action => 'edit'
render :action => 'show'
end end
end end
def destroy_comment
@news.comments.find(params[:comment_id]).destroy
redirect_to :action => 'show', :id => @news
end
def destroy def destroy
@news.destroy @news.destroy
redirect_to :action => 'index', :project_id => @project redirect_to :action => 'index', :project_id => @project
end end
def preview
@text = (params[:news] ? params[:news][:description] : nil)
render :partial => 'common/preview'
end
private private
def find_project def find_project
@project = Project.find(params[:project_id]) @project = Project.find(params[:project_id])
......
...@@ -16,13 +16,18 @@ class PreviewsController < ApplicationController ...@@ -16,13 +16,18 @@ class PreviewsController < ApplicationController
render :layout => false render :layout => false
end end
def news
@text = (params[:news] ? params[:news][:description] : nil)
render :partial => 'common/preview'
end
private private
def find_project def find_project
project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
@project = Project.find(project_id) @project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
end end
class ProjectEnumerationsController < ApplicationController
before_filter :find_project_by_project_id
before_filter :authorize
def update
if request.put? && params[:enumerations]
Project.transaction do
params[:enumerations].each do |id, activity|
@project.update_or_create_time_entry_activity(id, activity)
end
end
flash[:notice] = l(:notice_successful_update)
end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
def destroy
@project.time_entry_activities.each do |time_entry_activity|
time_entry_activity.destroy(time_entry_activity.parent)
end
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
end
end
This diff is collapsed.
...@@ -26,7 +26,7 @@ class SettingsController < ApplicationController ...@@ -26,7 +26,7 @@ class SettingsController < ApplicationController
end end
def edit def edit
@notifiables = %w(issue_added issue_updated news_added document_added file_added message_posted wiki_content_added wiki_content_updated) @notifiables = Redmine::Notifiable.all
if request.post? && params[:settings] && params[:settings].is_a?(Hash) if request.post? && params[:settings] && params[:settings].is_a?(Hash)
settings = (params[:settings] || {}).dup.symbolize_keys settings = (params[:settings] || {}).dup.symbolize_keys
settings.each do |name, value| settings.each do |name, value|
......
class TimeEntryReportsController < ApplicationController
menu_item :issues
before_filter :find_optional_project
before_filter :load_available_criterias
helper :sort
include SortHelper
helper :issues
helper :timelog
include TimelogHelper
helper :custom_fields
include CustomFieldsHelper
def report
@criterias = params[:criterias] || []
@criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria}
@criterias.uniq!
@criterias = @criterias[0,3]
@columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month'
retrieve_date_range
unless @criterias.empty?
sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ')
sql_group_by = @criterias.collect{|criteria| @available_criterias[criteria][:sql]}.join(', ')
sql_condition = ''
if @project.nil?
sql_condition = Project.allowed_to_condition(User.current, :view_time_entries)
elsif @issue.nil?
sql_condition = @project.project_condition(Setting.display_subprojects_issues?)
else
sql_condition = "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
end
sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours"
sql << " FROM #{TimeEntry.table_name}"
sql << time_report_joins
sql << " WHERE"
sql << " (%s) AND" % sql_condition
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)]
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
@hours = ActiveRecord::Base.connection.select_all(sql)
@hours.each do |row|
case @columns
when 'year'
row['year'] = row['tyear']
when 'month'
row['month'] = "#{row['tyear']}-#{row['tmonth']}"
when 'week'
row['week'] = "#{row['tyear']}-#{row['tweek']}"
when 'day'
row['day'] = "#{row['spent_on']}"
end
end
@total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f}
@periods = []
# Date#at_beginning_of_ not supported in Rails 1.2.x
date_from = @from.to_time
# 100 columns max
while date_from <= @to.to_time && @periods.length < 100
case @columns
when 'year'
@periods << "#{date_from.year}"
date_from = (date_from + 1.year).at_beginning_of_year
when 'month'
@periods << "#{date_from.year}-#{date_from.month}"
date_from = (date_from + 1.month).at_beginning_of_month
when 'week'
@periods << "#{date_from.year}-#{date_from.to_date.cweek}"
date_from = (date_from + 7.day).at_beginning_of_week
when 'day'
@periods << "#{date_from.to_date}"
date_from = date_from + 1.day
end
end
end
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.csv { send_data(report_to_csv(@criterias, @periods, @hours), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
end
end
private
# TODO: duplicated in TimelogController
def find_optional_project
if !params[:issue_id].blank?
@issue = Issue.find(params[:issue_id])
@project = @issue.project
elsif !params[:project_id].blank?
@project = Project.find(params[:project_id])
end
deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
end
# Retrieves the date range based on predefined ranges or specific from/to param dates
# TODO: duplicated in TimelogController
def retrieve_date_range
@free_period = false
@from, @to = nil, nil
if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
case params[:period].to_s
when 'today'
@from = @to = Date.today
when 'yesterday'
@from = @to = Date.today - 1
when 'current_week'
@from = Date.today - (Date.today.cwday - 1)%7
@to = @from + 6
when 'last_week'
@from = Date.today - 7 - (Date.today.cwday - 1)%7
@to = @from + 6
when '7_days'
@from = Date.today - 7
@to = Date.today
when 'current_month'
@from = Date.civil(Date.today.year, Date.today.month, 1)
@to = (@from >> 1) - 1
when 'last_month'
@from = Date.civil(Date.today.year, Date.today.month, 1) << 1
@to = (@from >> 1) - 1
when '30_days'
@from = Date.today - 30
@to = Date.today
when 'current_year'
@from = Date.civil(Date.today.year, 1, 1)
@to = Date.civil(Date.today.year, 12, 31)
end
elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
@free_period = true
else
# default
end
@from, @to = @to, @from if @from && @to && @from > @to
@from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
end
def load_available_criterias
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
:klass => Project,
:label => :label_project},
'version' => {:sql => "#{Issue.table_name}.fixed_version_id",
:klass => Version,
:label => :label_version},
'category' => {:sql => "#{Issue.table_name}.category_id",
:klass => IssueCategory,
:label => :field_category},
'member' => {:sql => "#{TimeEntry.table_name}.user_id",
:klass => User,
:label => :label_member},
'tracker' => {:sql => "#{Issue.table_name}.tracker_id",
:klass => Tracker,
:label => :label_tracker},
'activity' => {:sql => "#{TimeEntry.table_name}.activity_id",
:klass => TimeEntryActivity,
:label => :label_activity},
'issue' => {:sql => "#{TimeEntry.table_name}.issue_id",
:klass => Issue,
:label => :label_issue}
}
# Add list and boolean custom fields as available criterias
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields)
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id)",
:format => cf.field_format,
:label => cf.name}
end if @project
# Add list and boolean time entry custom fields
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id)",
:format => cf.field_format,
:label => cf.name}
end
# Add list and boolean time entry activity custom fields
TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id)",
:format => cf.field_format,
:label => cf.name}
end
call_hook(:controller_timelog_available_criterias, { :available_criterias => @available_criterias, :project => @project })
@available_criterias
end
def time_report_joins
sql = ''
sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id"
sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id"
# TODO: rename hook
call_hook(:controller_timelog_time_report_joins, {:sql => sql} )
sql
end
end
This diff is collapsed.
...@@ -71,51 +71,96 @@ class UsersController < ApplicationController ...@@ -71,51 +71,96 @@ class UsersController < ApplicationController
render_404 render_404
end end
def add def new
if request.get? @notification_options = User::MAIL_NOTIFICATION_OPTIONS
@user = User.new(:language => Setting.default_language) @notification_option = Setting.default_notification_option
@user = User.new(:language => Setting.default_language)
@auth_sources = AuthSource.find(:all)
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@notification_options = User::MAIL_NOTIFICATION_OPTIONS
@notification_option = Setting.default_notification_option
@user = User.new(params[:user])
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
# TODO: Similar to My#account
@user.mail_notification = params[:notification_option] || 'only_my_events'
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? {:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user})
return
else else
@user = User.new(params[:user]) @auth_sources = AuthSource.find(:all)
@user.admin = params[:user][:admin] || false @notification_option = @user.mail_notification
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id render :action => 'new'
if @user.save
Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? {:controller => 'users', :action => 'add'} :
{:controller => 'users', :action => 'edit', :id => @user})
return
end
end end
@auth_sources = AuthSource.find(:all)
end end
def edit def edit
@user = User.find(params[:id]) @user = User.find(params[:id])
if request.post? @notification_options = @user.valid_notification_options
@user.admin = params[:user][:admin] if params[:user][:admin] @notification_option = @user.mail_notification
@user.login = params[:user][:login] if params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
@user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
@user.attributes = params[:user]
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
if @user.save
if was_activated
Mailer.deliver_account_activated(@user)
elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
Mailer.deliver_account_information(@user, params[:password])
end
flash[:notice] = l(:notice_successful_update)
redirect_to :back
end
end
@auth_sources = AuthSource.find(:all) @auth_sources = AuthSource.find(:all)
@membership ||= Member.new @membership ||= Member.new
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@user = User.find(params[:id])
@notification_options = @user.valid_notification_options
@notification_option = @user.mail_notification
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
if params[:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
end
@user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
@user.attributes = params[:user]
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
# TODO: Similar to My#account
@user.mail_notification = params[:notification_option] || 'only_my_events'
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
if was_activated
Mailer.deliver_account_activated(@user)
elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
Mailer.deliver_account_information(@user, params[:password])
end
flash[:notice] = l(:notice_successful_update)
redirect_to :back
else
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
render :action => :edit
end
rescue ::ActionController::RedirectBackError rescue ::ActionController::RedirectBackError
redirect_to :controller => 'users', :action => 'edit', :id => @user redirect_to :controller => 'users', :action => 'edit', :id => @user
end end
def edit_membership def edit_membership
@user = User.find(params[:id]) @user = User.find(params[:id])
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user) @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
......
...@@ -18,13 +18,37 @@ ...@@ -18,13 +18,37 @@
class VersionsController < ApplicationController class VersionsController < ApplicationController
menu_item :roadmap menu_item :roadmap
model_object Version model_object Version
before_filter :find_model_object, :except => [:new, :close_completed] before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
before_filter :find_project_from_association, :except => [:new, :close_completed] before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
before_filter :find_project, :only => [:new, :close_completed] before_filter :find_project, :only => [:index, :new, :create, :close_completed]
before_filter :authorize before_filter :authorize
helper :custom_fields helper :custom_fields
helper :projects helper :projects
def index
@trackers = @project.trackers.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
@versions = @project.shared_versions || []
@versions += @project.rolled_up_versions.visible if @with_subprojects
@versions = @versions.uniq.sort
@versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
@issues_by_version = {}
unless @selected_tracker_ids.empty?
@versions.each do |version|
issues = version.fixed_issues.visible.find(:all,
:include => [:project, :status, :tracker, :priority],
:conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids},
:order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
@issues_by_version[version] = issues
end
end
@versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
end
def show def show
@issues = @version.fixed_issues.visible.find(:all, @issues = @version.fixed_issues.visible.find(:all,
...@@ -39,6 +63,17 @@ class VersionsController < ApplicationController ...@@ -39,6 +63,17 @@ class VersionsController < ApplicationController
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing']) attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes @version.attributes = attributes
end end
end
def create
# TODO: refactor with code above in #new
@version = @project.versions.build
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes
end
if request.post? if request.post?
if @version.save if @version.save
respond_to do |format| respond_to do |format|
...@@ -55,7 +90,7 @@ class VersionsController < ApplicationController ...@@ -55,7 +90,7 @@ class VersionsController < ApplicationController
end end
else else
respond_to do |format| respond_to do |format|
format.html format.html { render :action => 'new' }
format.js do format.js do
render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) } render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
end end
...@@ -63,9 +98,12 @@ class VersionsController < ApplicationController ...@@ -63,9 +98,12 @@ class VersionsController < ApplicationController
end end
end end
end end
def edit def edit
if request.post? && params[:version] end
def update
if request.put? && params[:version]
attributes = params[:version].dup attributes = params[:version].dup
attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing']) attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
if @version.update_attributes(attributes) if @version.update_attributes(attributes)
...@@ -76,7 +114,7 @@ class VersionsController < ApplicationController ...@@ -76,7 +114,7 @@ class VersionsController < ApplicationController
end end
def close_completed def close_completed
if request.post? if request.put?
@project.close_completed_versions @project.close_completed_versions
end end
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
...@@ -105,4 +143,13 @@ private ...@@ -105,4 +143,13 @@ private
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
if ids = params[:tracker_ids]
@selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
else
@selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
end
end
end end
...@@ -79,7 +79,7 @@ class WikiController < ApplicationController ...@@ -79,7 +79,7 @@ class WikiController < ApplicationController
attachments = Attachment.attach_files(@page, params[:attachments]) attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page) render_attachment_warning_if_needed(@page)
# don't save if text wasn't changed # don't save if text wasn't changed
redirect_to :action => 'index', :id => @project, :page => @page.title redirect_to :action => 'index', :project_id => @project, :page => @page.title
return return
end end
#@content.text = params[:content][:text] #@content.text = params[:content][:text]
...@@ -92,7 +92,7 @@ class WikiController < ApplicationController ...@@ -92,7 +92,7 @@ class WikiController < ApplicationController
attachments = Attachment.attach_files(@page, params[:attachments]) attachments = Attachment.attach_files(@page, params[:attachments])
render_attachment_warning_if_needed(@page) render_attachment_warning_if_needed(@page)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page}) call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
redirect_to :action => 'index', :id => @project, :page => @page.title redirect_to :action => 'index', :project_id => @project, :page => @page.title
end end
end end
rescue ActiveRecord::StaleObjectError rescue ActiveRecord::StaleObjectError
...@@ -108,13 +108,13 @@ class WikiController < ApplicationController ...@@ -108,13 +108,13 @@ class WikiController < ApplicationController
@original_title = @page.pretty_title @original_title = @page.pretty_title
if request.post? && @page.update_attributes(params[:wiki_page]) if request.post? && @page.update_attributes(params[:wiki_page])
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'index', :id => @project, :page => @page.title redirect_to :action => 'index', :project_id => @project, :page => @page.title
end end
end end
def protect def protect
@page.update_attribute :protected, params[:protected] @page.update_attribute :protected, params[:protected]
redirect_to :action => 'index', :id => @project, :page => @page.title redirect_to :action => 'index', :project_id => @project, :page => @page.title
end end
# show page history # show page history
...@@ -167,37 +167,26 @@ class WikiController < ApplicationController ...@@ -167,37 +167,26 @@ class WikiController < ApplicationController
end end
end end
@page.destroy @page.destroy
redirect_to :action => 'special', :id => @project, :page => 'Page_index' redirect_to :action => 'page_index', :project_id => @project
end end
# display special pages # Export wiki to a single html file
def special def export
page_title = params[:page].downcase if User.current.allowed_to?(:export_wiki_pages, @project)
case page_title @pages = @wiki.pages.find :all, :order => 'title'
# show pages index, sorted by title export = render_to_string :action => 'export_multiple', :layout => false
when 'page_index', 'date_index' send_data(export, :type => 'text/html', :filename => "wiki.html")
# eager load information about last updates, without loading text
@pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
:joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
:order => 'title'
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
@pages_by_parent_id = @pages.group_by(&:parent_id)
# export wiki to a single html file
when 'export'
if User.current.allowed_to?(:export_wiki_pages, @project)
@pages = @wiki.pages.find :all, :order => 'title'
export = render_to_string :action => 'export_multiple', :layout => false
send_data(export, :type => 'text/html', :filename => "wiki.html")
else
redirect_to :action => 'index', :id => @project, :page => nil
end
return
else else
# requested special page doesn't exist, redirect to default page redirect_to :action => 'index', :project_id => @project, :page => nil
redirect_to :action => 'index', :id => @project, :page => nil
return
end end
render :action => "special_#{page_title}" end
def page_index
load_pages_grouped_by_date_without_content
end
def date_index
load_pages_grouped_by_date_without_content
end end
def preview def preview
...@@ -222,7 +211,7 @@ class WikiController < ApplicationController ...@@ -222,7 +211,7 @@ class WikiController < ApplicationController
private private
def find_wiki def find_wiki
@project = Project.find(params[:id]) @project = Project.find(params[:project_id])
@wiki = @project.wiki @wiki = @project.wiki
render_404 unless @wiki render_404 unless @wiki
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
...@@ -246,4 +235,14 @@ private ...@@ -246,4 +235,14 @@ private
extend helper unless self.instance_of?(helper) extend helper unless self.instance_of?(helper)
helper.instance_method(:initial_page_content).bind(self).call(page) helper.instance_method(:initial_page_content).bind(self).call(page)
end end
# eager load information about last updates, without loading text
def load_pages_grouped_by_date_without_content
@pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
:joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
:order => 'title'
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
@pages_by_parent_id = @pages.group_by(&:parent_id)
end
end end
...@@ -20,12 +20,4 @@ module AdminHelper ...@@ -20,12 +20,4 @@ module AdminHelper
options_for_select([[l(:label_all), ''], options_for_select([[l(:label_all), ''],
[l(:status_active), 1]], selected) [l(:status_active), 1]], selected)
end end
def css_project_classes(project)
s = 'project'
s << ' root' if project.root?
s << ' child' if project.child?
s << (project.leaf? ? ' leaf' : ' parent')
s
end
end end
...@@ -32,6 +32,11 @@ module ApplicationHelper ...@@ -32,6 +32,11 @@ module ApplicationHelper
end end
# Display a link if user is authorized # Display a link if user is authorized
#
# @param [String] name Anchor text (passed to link_to)
# @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
# @param [optional, Hash] html_options Options passed to link_to
# @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) 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] || params[:controller], options[:action]) link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
end end
...@@ -102,6 +107,28 @@ module ApplicationHelper ...@@ -102,6 +107,28 @@ module ApplicationHelper
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision)) link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
end end
def link_to_project(project, options={})
options[:class] ||= 'project'
link_to(h(project), {:controller => 'projects', :action => 'show', :id => project}, :class => options[:class])
end
# Generates a link to a project if active
# Examples:
#
# link_to_project(project) # => link to the specified project overview
# link_to_project(project, :action=>'settings') # => link to project settings
# link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
#
def link_to_project(project, options={}, html_options = nil)
if project.active?
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
link_to(h(project), url, html_options)
else
h(project)
end
end
# Generates a link to a project if active # Generates a link to a project if active
# Examples: # Examples:
...@@ -172,7 +199,7 @@ module ApplicationHelper ...@@ -172,7 +199,7 @@ module ApplicationHelper
content << "<ul class=\"pages-hierarchy\">\n" content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page| pages[node].each do |page|
content << "<li>" content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title}, content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :project_id => page.project, :page => page.title},
:title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil)) :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id] content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
content << "</li>\n" content << "</li>\n"
...@@ -302,7 +329,7 @@ module ApplicationHelper ...@@ -302,7 +329,7 @@ module ApplicationHelper
def time_tag(time) def time_tag(time)
text = distance_of_time_in_words(Time.now, time) text = distance_of_time_in_words(Time.now, time)
if @project if @project
link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time)) link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
else else
content_tag('acronym', text, :title => format_time(time)) content_tag('acronym', text, :title => format_time(time))
end end
...@@ -541,7 +568,7 @@ module ApplicationHelper ...@@ -541,7 +568,7 @@ module ApplicationHelper
when :local; "#{title}.html" when :local; "#{title}.html"
when :anchor; "##{title}" # used for single-file wiki export when :anchor; "##{title}" # used for single-file wiki export
else else
url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => link_project, :page => Wiki.titleize(page), :anchor => anchor) url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :project_id => link_project, :page => Wiki.titleize(page), :anchor => anchor)
end end
link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new'))) link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
else else
...@@ -805,7 +832,7 @@ module ApplicationHelper ...@@ -805,7 +832,7 @@ module ApplicationHelper
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>') # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
def avatar(user, options = { }) def avatar(user, options = { })
if Setting.gravatar_enabled? if Setting.gravatar_enabled?
options.merge!({:ssl => Setting.protocol == 'https', :default => Setting.gravatar_default}) options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
email = nil email = nil
if user.respond_to?(:mail) if user.respond_to?(:mail)
email = user.mail email = user.mail
...@@ -813,6 +840,8 @@ module ApplicationHelper ...@@ -813,6 +840,8 @@ module ApplicationHelper
email = $1 email = $1
end end
return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
else
''
end end
end end
......
module CalendarsHelper
def link_to_previous_month(year, month, options={})
target_year, target_month = if month == 1
[year - 1, 12]
else
[year, month - 1]
end
name = if target_month == 12
"#{month_name(target_month)} #{target_year}"
else
"#{month_name(target_month)}"
end
link_to_month(('&#171; ' + name), target_year, target_month, options)
end
def link_to_next_month(year, month, options={})
target_year, target_month = if month == 12
[year + 1, 1]
else
[year, month + 1]
end
name = if target_month == 1
"#{month_name(target_month)} #{target_year}"
else
"#{month_name(target_month)}"
end
link_to_month((name + ' &#187;'), target_year, target_month, options)
end
def link_to_month(link_name, year, month, options={})
project_id = options[:project].present? ? options[:project].to_param : nil
link_target = calendar_path(:year => year, :month => month, :project_id => project_id)
link_to_remote(link_name,
{:update => "content", :url => link_target, :method => :put},
{:href => link_target})
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 GanttHelper
def number_of_issues_on_versions(gantt)
versions = gantt.events.collect {|event| (event.is_a? Version) ? event : nil}.compact
versions.sum {|v| v.fixed_issues.for_gantt.with_query(@query).count}
end
end
...@@ -28,15 +28,26 @@ module IssuesHelper ...@@ -28,15 +28,26 @@ module IssuesHelper
ancestors << issue unless issue.leaf? ancestors << issue unless issue.leaf?
end end
end end
# Renders a HTML/CSS tooltip
#
# To use, a trigger div is needed. This is a div with the class of "tooltip"
# that contains this method wrapped in a span with the class of "tip"
#
# <div class="tooltip"><%= link_to_issue(issue) %>
# <span class="tip"><%= render_issue_tooltip(issue) %></span>
# </div>
#
def render_issue_tooltip(issue) def render_issue_tooltip(issue)
@cached_label_status ||= l(:field_status) @cached_label_status ||= l(:field_status)
@cached_label_start_date ||= l(:field_start_date) @cached_label_start_date ||= l(:field_start_date)
@cached_label_due_date ||= l(:field_due_date) @cached_label_due_date ||= l(:field_due_date)
@cached_label_assigned_to ||= l(:field_assigned_to) @cached_label_assigned_to ||= l(:field_assigned_to)
@cached_label_priority ||= l(:field_priority) @cached_label_priority ||= l(:field_priority)
@cached_label_project ||= l(:field_project)
link_to_issue(issue) + "<br /><br />" + link_to_issue(issue) + "<br /><br />" +
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
"<strong>#{@cached_label_status}</strong>: #{issue.status.name}<br />" + "<strong>#{@cached_label_status}</strong>: #{issue.status.name}<br />" +
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" + "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" + "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
...@@ -171,7 +182,7 @@ module IssuesHelper ...@@ -171,7 +182,7 @@ module IssuesHelper
when :in when :in
if gantt.zoom < 4 if gantt.zoom < 4
link_to_remote(l(:text_zoom_in) + image_tag('zoom_in.png', img_attributes.merge(:alt => l(:text_zoom_in))), link_to_remote(l(:text_zoom_in) + image_tag('zoom_in.png', img_attributes.merge(:alt => l(:text_zoom_in))),
{:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :update => 'content'}, {:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom+1)))}) {:href => url_for(gantt.params.merge(:zoom => (gantt.zoom+1)))})
else else
l(:text_zoom_in) + l(:text_zoom_in) +
...@@ -181,7 +192,7 @@ module IssuesHelper ...@@ -181,7 +192,7 @@ module IssuesHelper
when :out when :out
if gantt.zoom > 1 if gantt.zoom > 1
link_to_remote(l(:text_zoom_out) + image_tag('zoom_out.png', img_attributes.merge(:alt => l(:text_zoom_out))), link_to_remote(l(:text_zoom_out) + image_tag('zoom_out.png', img_attributes.merge(:alt => l(:text_zoom_out))),
{:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :update => 'content'}, {:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :method => :get, :update => 'content'},
{:href => url_for(gantt.params.merge(:zoom => (gantt.zoom-1)))}) {:href => url_for(gantt.params.merge(:zoom => (gantt.zoom-1)))})
else else
l(:text_zoom_out) + l(:text_zoom_out) +
......
...@@ -71,4 +71,14 @@ module SettingsHelper ...@@ -71,4 +71,14 @@ module SettingsHelper
label = options.delete(:label) label = options.delete(:label)
label != false ? content_tag("label", l(label || "setting_#{setting}")) : '' label != false ? content_tag("label", l(label || "setting_#{setting}")) : ''
end end
# Renders a notification field for a Redmine::Notifiable option
def notification_field(notifiable)
return content_tag(:label,
check_box_tag('settings[notified_events][]',
notifiable.name,
Setting.notified_events.include?(notifiable.name)) +
l_or_humanize(notifiable.name, :prefix => 'label_'),
:class => notifiable.parent.present? ? "parent" : '')
end
end end
...@@ -34,14 +34,14 @@ module UsersHelper ...@@ -34,14 +34,14 @@ module UsersHelper
end end
def change_status_link(user) def change_status_link(user)
url = {:controller => 'users', :action => 'edit', :id => user, :page => params[:page], :status => params[:status], :tab => nil} url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
if user.locked? if user.locked?
link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock' link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
elsif user.registered? elsif user.registered?
link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock' link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
elsif user != User.current elsif user != User.current
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :post, :class => 'icon icon-lock' link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
end end
end end
......
...@@ -64,14 +64,32 @@ class Issue < ActiveRecord::Base ...@@ -64,14 +64,32 @@ class Issue < ActiveRecord::Base
named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
named_scope :recently_updated, :order => "#{self.table_name}.updated_on DESC" named_scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
named_scope :with_limit, lambda { |limit| { :limit => limit} } named_scope :with_limit, lambda { |limit| { :limit => limit} }
named_scope :on_active_project, :include => [:status, :project, :tracker], named_scope :on_active_project, :include => [:status, :project, :tracker],
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"] :conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
named_scope :for_gantt, lambda {
{
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
:order => "#{Issue.table_name}.due_date ASC, #{Issue.table_name}.start_date ASC, #{Issue.table_name}.id ASC"
}
}
named_scope :without_version, lambda {
{
:conditions => { :fixed_version_id => nil}
}
}
named_scope :with_query, lambda {|query|
{
:conditions => Query.merge_conditions(query.statement)
}
}
before_create :default_assign before_create :default_assign
before_save :reschedule_following_issues, :close_duplicates, :update_done_ratio_from_issue_status before_save :close_duplicates, :update_done_ratio_from_issue_status
after_save :update_nested_set_attributes, :update_parent_attributes after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
after_destroy :destroy_children after_destroy :destroy_children
after_destroy :update_parent_attributes after_destroy :update_parent_attributes
...@@ -247,7 +265,7 @@ class Issue < ActiveRecord::Base ...@@ -247,7 +265,7 @@ class Issue < ActiveRecord::Base
end end
def done_ratio def done_ratio
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio? if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
status.default_done_ratio status.default_done_ratio
else else
read_attribute(:done_ratio) read_attribute(:done_ratio)
...@@ -310,7 +328,7 @@ class Issue < ActiveRecord::Base ...@@ -310,7 +328,7 @@ class Issue < ActiveRecord::Base
# Set the done_ratio using the status if that setting is set. This will keep the done_ratios # Set the done_ratio using the status if that setting is set. This will keep the done_ratios
# even if the user turns off the setting later # even if the user turns off the setting later
def update_done_ratio_from_issue_status def update_done_ratio_from_issue_status
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio? if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
self.done_ratio = status.default_done_ratio self.done_ratio = status.default_done_ratio
end end
end end
...@@ -355,10 +373,24 @@ class Issue < ActiveRecord::Base ...@@ -355,10 +373,24 @@ class Issue < ActiveRecord::Base
def overdue? def overdue?
!due_date.nil? && (due_date < Date.today) && !status.is_closed? !due_date.nil? && (due_date < Date.today) && !status.is_closed?
end end
# Is the amount of work done less than it should for the due date
def behind_schedule?
return false if start_date.nil? || due_date.nil?
done_date = start_date + ((due_date - start_date+1)* done_ratio/100).floor
return done_date <= Date.today
end
# Does this issue have children?
def children?
!leaf?
end
# Users the issue can be assigned to # Users the issue can be assigned to
def assignable_users def assignable_users
project.assignable_users users = project.assignable_users
users << author if author
users.uniq.sort
end end
# Versions that the issue can be assigned to # Versions that the issue can be assigned to
...@@ -383,9 +415,10 @@ class Issue < ActiveRecord::Base ...@@ -383,9 +415,10 @@ class Issue < ActiveRecord::Base
# Returns the mail adresses of users that should be notified # Returns the mail adresses of users that should be notified
def recipients def recipients
notified = project.notified_users notified = project.notified_users
# Author and assignee are always notified unless they have been locked # Author and assignee are always notified unless they have been
notified << author if author && author.active? # locked or don't want to be notified
notified << assigned_to if assigned_to && assigned_to.active? notified << author if author && author.active? && author.notify_about?(self)
notified << assigned_to if assigned_to && assigned_to.active? && assigned_to.notify_about?(self)
notified.uniq! notified.uniq!
# Remove users that can not view the issue # Remove users that can not view the issue
notified.reject! {|user| !visible?(user)} notified.reject! {|user| !visible?(user)}
...@@ -681,7 +714,7 @@ class Issue < ActiveRecord::Base ...@@ -681,7 +714,7 @@ class Issue < ActiveRecord::Base
end end
# done ratio = weighted average ratio of leaves # done ratio = weighted average ratio of leaves
unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio? unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
leaves_count = p.leaves.count leaves_count = p.leaves.count
if leaves_count > 0 if leaves_count > 0
average = p.leaves.average(:estimated_hours).to_f average = p.leaves.average(:estimated_hours).to_f
...@@ -782,7 +815,7 @@ class Issue < ActiveRecord::Base ...@@ -782,7 +815,7 @@ class Issue < ActiveRecord::Base
j.id as #{select_field}, j.id as #{select_field},
count(i.id) as total count(i.id) as total
from from
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} as j #{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} j
where where
i.status_id=s.id i.status_id=s.id
and #{where} and #{where}
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'ar_condition'
class Mailer < ActionMailer::Base class Mailer < ActionMailer::Base
layout 'mailer' layout 'mailer'
helper :application helper :application
...@@ -307,13 +309,16 @@ class Mailer < ActionMailer::Base ...@@ -307,13 +309,16 @@ class Mailer < ActionMailer::Base
# * :days => how many days in the future to remind about (defaults to 7) # * :days => how many days in the future to remind about (defaults to 7)
# * :tracker => id of tracker for filtering issues (defaults to all trackers) # * :tracker => id of tracker for filtering issues (defaults to all trackers)
# * :project => id or identifier of project to process (defaults to all projects) # * :project => id or identifier of project to process (defaults to all projects)
# * :users => array of user ids who should be reminded
def self.reminders(options={}) def self.reminders(options={})
days = options[:days] || 7 days = options[:days] || 7
project = options[:project] ? Project.find(options[:project]) : nil project = options[:project] ? Project.find(options[:project]) : nil
tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
user_ids = options[:users]
s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ?", false, days.day.from_now.to_date] s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ?", false, days.day.from_now.to_date]
s << "#{Issue.table_name}.assigned_to_id IS NOT NULL" s << "#{Issue.table_name}.assigned_to_id IS NOT NULL"
s << ["#{Issue.table_name}.assigned_to_id IN (?)", user_ids] if user_ids.present?
s << "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}" s << "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}"
s << "#{Issue.table_name}.project_id = #{project.id}" if project s << "#{Issue.table_name}.project_id = #{project.id}" if project
s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
......
...@@ -33,7 +33,11 @@ class Principal < ActiveRecord::Base ...@@ -33,7 +33,11 @@ class Principal < ActiveRecord::Base
} }
before_create :set_default_empty_values before_create :set_default_empty_values
def name(formatter = nil)
to_s
end
def <=>(principal) def <=>(principal)
if self.class.name == principal.class.name if self.class.name == principal.class.name
self.to_s.downcase <=> principal.to_s.downcase self.to_s.downcase <=> principal.to_s.downcase
......
...@@ -382,12 +382,13 @@ class Project < ActiveRecord::Base ...@@ -382,12 +382,13 @@ class Project < ActiveRecord::Base
# Returns the mail adresses of users that should be always notified on project events # Returns the mail adresses of users that should be always notified on project events
def recipients def recipients
members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail} notified_users.collect {|user| user.mail}
end end
# Returns the users that should be notified on project events # Returns the users that should be notified on project events
def notified_users def notified_users
members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user} # TODO: User part should be extracted to User#notify_about?
members.select {|m| m.mail_notification? || m.user.mail_notification == 'all'}.collect {|m| m.user}
end end
# Returns an array of all custom fields enabled for project issues # Returns an array of all custom fields enabled for project issues
...@@ -412,6 +413,58 @@ class Project < ActiveRecord::Base ...@@ -412,6 +413,58 @@ class Project < ActiveRecord::Base
def short_description(length = 255) def short_description(length = 255)
description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
end end
def css_classes
s = 'project'
s << ' root' if root?
s << ' child' if child?
s << (leaf? ? ' leaf' : ' parent')
s
end
# The earliest start date of a project, based on it's issues and versions
def start_date
if module_enabled?(:issue_tracking)
[
issues.minimum('start_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.minimum('start_date')}
].flatten.compact.min
end
end
# The latest due date of an issue or version
def due_date
if module_enabled?(:issue_tracking)
[
issues.maximum('due_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.maximum('due_date')}
].flatten.compact.max
end
end
def overdue?
active? && !due_date.nil? && (due_date < Date.today)
end
# Returns the percent completed for this project, based on the
# progress on it's versions.
def completed_percent(options={:include_subprojects => false})
if options.delete(:include_subprojects)
total = self_and_descendants.collect(&:completed_percent).sum
total / self_and_descendants.count
else
if versions.count > 0
total = versions.collect(&:completed_pourcent).sum
total / versions.count
else
100
end
end
end
# Return true if this project is allowed to do the specified action. # Return true if this project is allowed to do the specified action.
# action can be: # action can be:
...@@ -441,6 +494,15 @@ class Project < ActiveRecord::Base ...@@ -441,6 +494,15 @@ class Project < ActiveRecord::Base
enabled_modules.clear enabled_modules.clear
end end
end end
# Returns an array of projects that are in this project's hierarchy
#
# Example: parents, children, siblings
def hierarchy
parents = project.self_and_ancestors || []
descendants = project.descendants || []
project_hierarchy = parents | descendants # Set union
end
# Returns an auto-generated project identifier based on the last identifier used # Returns an auto-generated project identifier based on the last identifier used
def self.next_identifier def self.next_identifier
......
...@@ -195,6 +195,12 @@ class Query < ActiveRecord::Base ...@@ -195,6 +195,12 @@ class Query < ActiveRecord::Base
end end
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty? @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
@available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty? @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
@available_filters["member_of_group"] = { :type => :list_optional, :order => 6, :values => group_values } unless group_values.empty?
role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
@available_filters["assigned_to_role"] = { :type => :list_optional, :order => 7, :values => role_values } unless role_values.empty?
if User.current.logged? if User.current.logged?
@available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] } @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
...@@ -432,6 +438,47 @@ class Query < ActiveRecord::Base ...@@ -432,6 +438,47 @@ class Query < ActiveRecord::Base
db_field = 'user_id' db_field = 'user_id'
sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND "
sql << sql_for_field(field, '=', v, db_table, db_field) + ')' sql << sql_for_field(field, '=', v, db_table, db_field) + ')'
elsif field == "member_of_group" # named field
if operator == '*' # Any group
groups = Group.all
operator = '=' # Override the operator since we want to find by assigned_to
elsif operator == "!*"
groups = Group.all
operator = '!' # Override the operator since we want to find by assigned_to
else
groups = Group.find_all_by_id(v)
end
groups ||= []
members_of_groups = groups.inject([]) {|user_ids, group|
if group && group.user_ids.present?
user_ids << group.user_ids
end
user_ids.flatten.uniq.compact
}.sort.collect(&:to_s)
sql << '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
elsif field == "assigned_to_role" # named field
if operator == "*" # Any Role
roles = Role.givable
operator = '=' # Override the operator since we want to find by assigned_to
elsif operator == "!*" # No role
roles = Role.givable
operator = '!' # Override the operator since we want to find by assigned_to
else
roles = Role.givable.find_all_by_id(v)
end
roles ||= []
members_of_roles = roles.inject([]) {|user_ids, role|
if role && role.members
user_ids << role.members.collect(&:user_id)
end
user_ids.flatten.uniq.compact
}.sort.collect(&:to_s)
sql << '(' + sql_for_field("assigned_to_id", operator, members_of_roles, Issue.table_name, "assigned_to_id", false) + ')'
else else
# regular field # regular field
db_table = Issue.table_name db_table = Issue.table_name
......
...@@ -77,4 +77,20 @@ class TimeEntry < ActiveRecord::Base ...@@ -77,4 +77,20 @@ class TimeEntry < ActiveRecord::Base
yield yield
end end
end end
def self.earilest_date_for_project(project=nil)
finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries))
if project
finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)]
end
TimeEntry.minimum(:spent_on, :include => :project, :conditions => finder_conditions.conditions)
end
def self.latest_date_for_project(project=nil)
finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries))
if project
finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)]
end
TimeEntry.maximum(:spent_on, :include => :project, :conditions => finder_conditions.conditions)
end
end end
...@@ -33,6 +33,15 @@ class User < Principal ...@@ -33,6 +33,15 @@ class User < Principal
:username => '#{login}' :username => '#{login}'
} }
MAIL_NOTIFICATION_OPTIONS = [
[:all, :label_user_mail_option_all],
[:selected, :label_user_mail_option_selected],
[:none, :label_user_mail_option_none],
[:only_my_events, :label_user_mail_option_only_my_events],
[:only_assigned, :label_user_mail_option_only_assigned],
[:only_owner, :label_user_mail_option_only_owner]
]
has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)}, has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
:after_remove => Proc.new {|user, group| group.user_removed(user)} :after_remove => Proc.new {|user, group| group.user_removed(user)}
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
...@@ -65,7 +74,7 @@ class User < Principal ...@@ -65,7 +74,7 @@ class User < Principal
validates_confirmation_of :password, :allow_nil => true validates_confirmation_of :password, :allow_nil => true
def before_create def before_create
self.mail_notification = false self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
true true
end end
...@@ -250,6 +259,17 @@ class User < Principal ...@@ -250,6 +259,17 @@ class User < Principal
notified_projects_ids notified_projects_ids
end end
# Only users that belong to more than 1 project can select projects for which they are notified
def valid_notification_options
# Note that @user.membership.size would fail since AR ignores
# :include association option when doing a count
if memberships.length < 1
MAIL_NOTIFICATION_OPTIONS.delete_if {|option| option.first == :selected}
else
MAIL_NOTIFICATION_OPTIONS
end
end
# Find a user account by matching the exact login and then a case-insensitive # Find a user account by matching the exact login and then a case-insensitive
# version. Exact matches will be given priority. # version. Exact matches will be given priority.
def self.find_by_login(login) def self.find_by_login(login)
...@@ -324,23 +344,35 @@ class User < Principal ...@@ -324,23 +344,35 @@ class User < Principal
!roles_for_project(project).detect {|role| role.member?}.nil? !roles_for_project(project).detect {|role| role.member?}.nil?
end end
# Return true if the user is allowed to do the specified action on project # Return true if the user is allowed to do the specified action on a specific context
# action can be: # Action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project) # * a permission Symbol (eg. :edit_project)
def allowed_to?(action, project, options={}) # Context can be:
if project # * a project : returns true if user is allowed to do the specified action on this project
# * a group of projects : returns true if user is allowed on every project
# * nil with options[:global] set : check if user has at least one role allowed for this action,
# or falls back to Non Member / Anonymous permissions depending if the user is logged
def allowed_to?(action, context, options={})
if context && context.is_a?(Project)
# No action allowed on archived projects # No action allowed on archived projects
return false unless project.active? return false unless context.active?
# No action allowed on disabled modules # No action allowed on disabled modules
return false unless project.allows_to?(action) return false unless context.allows_to?(action)
# Admin users are authorized for anything else # Admin users are authorized for anything else
return true if admin? return true if admin?
roles = roles_for_project(project) roles = roles_for_project(context)
return false unless roles return false unless roles
roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)} roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
elsif context && context.is_a?(Array)
# Authorize if user is authorized on every element of the array
context.map do |project|
allowed_to?(action,project,options)
end.inject do |memo,allowed|
memo && allowed
end
elsif options[:global] elsif options[:global]
# Admin users are always authorized # Admin users are always authorized
return true if admin? return true if admin?
...@@ -352,6 +384,47 @@ class User < Principal ...@@ -352,6 +384,47 @@ class User < Principal
false false
end end
end end
# Is the user allowed to do the specified action on any project?
# See allowed_to? for the actions and valid options.
def allowed_to_globally?(action, options)
allowed_to?(action, nil, options.reverse_merge(:global => true))
end
# Utility method to help check if a user should be notified about an
# event.
#
# TODO: only supports Issue events currently
def notify_about?(object)
case mail_notification.to_sym
when :all
true
when :selected
# Handled by the Project
when :none
false
when :only_my_events
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
else
false
end
when :only_assigned
if object.is_a?(Issue) && object.assigned_to == self
true
else
false
end
when :only_owner
if object.is_a?(Issue) && object.author == self
true
else
false
end
else
false
end
end
def self.current=(user) def self.current=(user)
@current_user = user @current_user = user
......
...@@ -74,6 +74,18 @@ class Version < ActiveRecord::Base ...@@ -74,6 +74,18 @@ class Version < ActiveRecord::Base
def completed? def completed?
effective_date && (effective_date <= Date.today) && (open_issues_count == 0) effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
end end
def behind_schedule?
if completed_pourcent == 100
return false
elsif due_date && fixed_issues.present? && fixed_issues.minimum('start_date') # TODO: should use #start_date but that method is wrong...
start_date = fixed_issues.minimum('start_date')
done_date = start_date + ((due_date - start_date+1)* completed_pourcent/100).floor
return done_date <= Date.today
else
false # No issues so it's not late
end
end
# Returns the completion percentage of this version based on the amount of open/closed issues # Returns the completion percentage of this version based on the amount of open/closed issues
# and the time spent on the open issues. # and the time spent on the open issues.
...@@ -124,6 +136,10 @@ class Version < ActiveRecord::Base ...@@ -124,6 +136,10 @@ class Version < ActiveRecord::Base
end end
def to_s; name end def to_s; name end
def to_s_with_project
"#{project} - #{name}"
end
# Versions are sorted by effective_date and "Project Name - Version name" # Versions are sorted by effective_date and "Project Name - Version name"
# Those with no effective_date are at the end, sorted by "Project Name - Version name" # Those with no effective_date are at the end, sorted by "Project Name - Version name"
......
<div class="contextual"> <div class="contextual">
<%= link_to l(:label_project_new), {:controller => 'projects', :action => 'add'}, :class => 'icon icon-add' %> <%= link_to l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add' %>
</div> </div>
<h2><%=l(:label_project_plural)%></h2> <h2><%=l(:label_project_plural)%></h2>
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
</tr></thead> </tr></thead>
<tbody> <tbody>
<% project_tree(@projects) do |project, level| %> <% project_tree(@projects) do |project, level| %>
<tr class="<%= cycle("odd", "even") %> <%= css_project_classes(project) %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> <tr class="<%= cycle("odd", "even") %> <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="name"><%= link_to_project(project, :action => 'settings') %></td> <td class="name"><%= link_to_project(project, :action => 'settings') %></td>
<td><%= textilizable project.short_description, :project => project %></td> <td><%= textilizable project.short_description, :project => project %></td>
<td align="center"><%= checked_image project.is_public? %></td> <td align="center"><%= checked_image project.is_public? %></td>
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
<%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project, :status => params[:status] }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') if project.active? %> <%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project, :status => params[:status] }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') if project.active? %>
<%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project, :status => params[:status] }, :method => :post, :class => 'icon icon-unlock') if !project.active? && (project.parent.nil? || project.parent.active?) %> <%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project, :status => params[:status] }, :method => :post, :class => 'icon icon-unlock') if !project.active? && (project.parent.nil? || project.parent.active?) %>
<%= link_to(l(:button_copy), { :controller => 'projects', :action => 'copy', :id => project }, :class => 'icon icon-copy') %> <%= link_to(l(:button_copy), { :controller => 'projects', :action => 'copy', :id => project }, :class => 'icon icon-copy') %>
<%= link_to(l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => 'icon icon-del') %> <%= link_to(l(:button_delete), project_destroy_confirm_path(project), :class => 'icon icon-del') %>
</td> </td>
</tr> </tr>
<% end %> <% end %>
......
...@@ -30,11 +30,11 @@ ...@@ -30,11 +30,11 @@
</table> </table>
<% other_formats_links do |f| %> <% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_messages => 1, :key => User.current.rss_key} %> <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_messages => 1, :key => User.current.rss_key} %>
<% end %> <% end %>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:controller => 'projects', :action => 'activity', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key}) %> <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key}) %>
<% end %> <% end %>
<% html_title l(:label_board_plural) %> <% html_title l(:label_board_plural) %>
<h2><%= l(:label_calendar) %></h2> <h2><%= l(:label_calendar) %></h2>
<% form_tag({}, :id => 'query_form') do %> <% form_tag(calendar_path, :method => :put, :id => 'query_form') do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project%>
<fieldset id="filters" class="collapsible"> <fieldset id="filters" class="collapsible">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend> <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div> <div>
...@@ -9,14 +10,7 @@ ...@@ -9,14 +10,7 @@
</fieldset> </fieldset>
<p style="float:right;"> <p style="float:right;">
<%= link_to_remote ('&#171; ' + (@month==1 ? "#{month_name(12)} #{@year-1}" : "#{month_name(@month-1)}")), <%= link_to_previous_month(@year, @month, :project => @project) %> | <%= link_to_next_month(@year, @month, :project => @project) %>
{:update => "content", :url => { :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1) }},
{:href => url_for(:action => 'show', :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1))}
%> |
<%= link_to_remote ((@month==12 ? "#{month_name(1)} #{@year+1}" : "#{month_name(@month+1)}") + ' &#187;'),
{:update => "content", :url => { :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1) }},
{:href => url_for(:action => 'show', :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1))}
%>
</p> </p>
<p class="buttons"> <p class="buttons">
...@@ -32,7 +26,8 @@ ...@@ -32,7 +26,8 @@
}, :class => 'icon icon-checked' %> }, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear), <%= link_to_remote l(:button_clear),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) }, { :url => { :project_id => @project, :set_filter => (@query.new_record? ? 1 : nil) },
:method => :put,
:update => "content", :update => "content",
}, :class => 'icon icon-reload' if @query.new_record? %> }, :class => 'icon icon-reload' if @query.new_record? %>
</p> </p>
...@@ -43,9 +38,9 @@ ...@@ -43,9 +38,9 @@
<%= render :partial => 'common/calendar', :locals => {:calendar => @calendar} %> <%= render :partial => 'common/calendar', :locals => {:calendar => @calendar} %>
<p class="legend cal"> <p class="legend cal">
<span class="starting"><%= l(:text_tip_task_begin_day) %></span> <span class="starting"><%= l(:text_tip_issue_begin_day) %></span>
<span class="ending"><%= l(:text_tip_task_end_day) %></span> <span class="ending"><%= l(:text_tip_issue_end_day) %></span>
<span class="starting ending"><%= l(:text_tip_task_begin_end_day) %></span> <span class="starting ending"><%= l(:text_tip_issue_begin_end_day) %></span>
</p> </p>
<% end %> <% end %>
......
...@@ -4,19 +4,22 @@ ...@@ -4,19 +4,22 @@
<% if !@issue.nil? -%> <% if !@issue.nil? -%>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
:class => 'icon-edit', :disabled => !@can[:edit] %></li> :class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% else %>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
<% unless @allowed_statuses.empty? %>
<li class="folder"> <li class="folder">
<a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a> <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
<ul> <ul>
<% @statuses.each do |s| -%> <% @statuses.each do |s| -%>
<li><%= context_menu_link s.name, {:controller => 'issues', :action => 'update', :id => @issue, :issue => {:status_id => s}, :back_url => @back}, :method => :put, <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post,
:selected => (s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li> :selected => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
<% end -%> <% end -%>
</ul> </ul>
</li> </li>
<% else %> <% end %>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
<% unless @trackers.nil? %> <% unless @trackers.nil? %>
<li class="folder"> <li class="folder">
...@@ -29,6 +32,7 @@ ...@@ -29,6 +32,7 @@
</ul> </ul>
</li> </li>
<% end %> <% end %>
<li class="folder"> <li class="folder">
<a href="#" class="submenu"><%= l(:field_priority) %></a> <a href="#" class="submenu"><%= l(:field_priority) %></a>
<ul> <ul>
...@@ -38,6 +42,8 @@ ...@@ -38,6 +42,8 @@
<% end -%> <% end -%>
</ul> </ul>
</li> </li>
<% #TODO: allow editing versions when multiple projects %>
<% unless @project.nil? || @project.shared_versions.open.empty? -%> <% unless @project.nil? || @project.shared_versions.open.empty? -%>
<li class="folder"> <li class="folder">
<a href="#" class="submenu"><%= l(:field_fixed_version) %></a> <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
...@@ -77,6 +83,7 @@ ...@@ -77,6 +83,7 @@
</ul> </ul>
</li> </li>
<% end -%> <% end -%>
<% if Issue.use_field_for_done_ratio? %> <% if Issue.use_field_for_done_ratio? %>
<li class="folder"> <li class="folder">
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a> <a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
...@@ -88,6 +95,7 @@ ...@@ -88,6 +95,7 @@
</ul> </ul>
</li> </li>
<% end %> <% end %>
<% if !@issue.nil? %> <% if !@issue.nil? %>
<% if @can[:log_time] -%> <% if @can[:log_time] -%>
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
...@@ -106,7 +114,7 @@ ...@@ -106,7 +114,7 @@
:class => 'icon-copy', :disabled => !@can[:move] %></li> :class => 'icon-copy', :disabled => !@can[:move] %></li>
<li><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)), <li><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)),
:class => 'icon-move', :disabled => !@can[:move] %></li> :class => 'icon-move', :disabled => !@can[:move] %></li>
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)}, <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id), :back_url => @back},
:method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li> :method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %> <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
......
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:label_attachment_new), {:controller => 'projects', :action => 'add_file', :id => @project}, :class => 'icon icon-add' %> <%= link_to_if_authorized l(:label_attachment_new), new_project_file_path(@project), :class => 'icon icon-add' %>
</div> </div>
<h2><%=l(:label_attachment_plural)%></h2> <h2><%=l(:label_attachment_plural)%></h2>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<%= error_messages_for 'attachment' %> <%= error_messages_for 'attachment' %>
<div class="box"> <div class="box">
<% form_tag({ :action => 'add_file', :id => @project }, :multipart => true, :class => "tabular") do %> <% form_tag(project_files_path(@project), :multipart => true, :class => "tabular") do %>
<% if @versions.any? %> <% if @versions.any? %>
<p><label for="version_id"><%=l(:field_version)%></label> <p><label for="version_id"><%=l(:field_version)%></label>
......
<% @gantt.view = self %>
<h2><%= l(:label_gantt) %></h2> <h2><%= l(:label_gantt) %></h2>
<% form_tag(params.merge(:month => nil, :year => nil, :months => nil), :id => 'query_form') do %> <% form_tag(gantt_path(:month => params[:month], :year => params[:year], :months => params[:months]), :method => :put, :id => 'query_form') do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project%>
<fieldset id="filters" class="collapsible"> <fieldset id="filters" class="collapsible">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend> <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div> <div>
...@@ -27,7 +29,8 @@ ...@@ -27,7 +29,8 @@
}, :class => 'icon icon-checked' %> }, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear), <%= link_to_remote l(:button_clear),
{ :url => { :set_filter => (@query.new_record? ? 1 : nil) }, { :url => { :project_id => @project, :set_filter => (@query.new_record? ? 1 : nil) },
:method => :put,
:update => "content", :update => "content",
}, :class => 'icon icon-reload' if @query.new_record? %> }, :class => 'icon icon-reload' if @query.new_record? %>
</p> </p>
...@@ -54,11 +57,12 @@ if @gantt.zoom >1 ...@@ -54,11 +57,12 @@ if @gantt.zoom >1
end end
end end
# Width of the entire chart
g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom
g_height = [(20 * @gantt.events.length + 6)+150, 206].max # Collect the number of issues on Versions
g_height = [(20 * (@gantt.number_of_rows + 6))+150, 206].max
t_height = g_height + headers_height t_height = g_height + headers_height
%> %>
<table width="100%" style="border:0; border-collapse: collapse;"> <table width="100%" style="border:0; border-collapse: collapse;">
<tr> <tr>
<td style="width:<%= subject_width %>px; padding:0px;"> <td style="width:<%= subject_width %>px; padding:0px;">
...@@ -66,26 +70,10 @@ t_height = g_height + headers_height ...@@ -66,26 +70,10 @@ t_height = g_height + headers_height
<div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;"> <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
<div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div> <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
<div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div> <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
<% <% top = headers_height + 8 %>
#
# Tasks subjects <%= @gantt.subjects(:headers_height => headers_height, :top => top, :g_width => g_width) %>
#
top = headers_height + 8
@gantt.events.each do |i|
left = 4 + (i.is_a?(Issue) ? i.level * 16 : 0)
%>
<div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:<%= left %>px;overflow:hidden;"><small>
<% if i.is_a? Issue %>
<%= h("#{i.project} -") unless @project && @project == i.project %>
<%= link_to_issue i %>
<% else %>
<span class="icon icon-package">
<%= link_to_version i %>
</span>
<% end %>
</small></div>
<% top = top + 20
end %>
</div> </div>
</td> </td>
<td> <td>
...@@ -163,53 +151,9 @@ if show_days ...@@ -163,53 +151,9 @@ if show_days
end end
end %> end %>
<% <% top = headers_height + 10 %>
#
# Tasks <%= @gantt.lines(:top => top, :zoom => zoom, :g_width => g_width ) %>
#
top = headers_height + 10
@gantt.events.each do |i|
if i.is_a? Issue
i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from )
i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to )
i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date )
i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date )
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
i_left = ((i_start_date - @gantt.date_from)*zoom).floor
i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
css = "task " + (i.leaf? ? 'leaf' : 'parent')
%>
<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="<%= css %> task_todo"><div class="left"></div>&nbsp;<div class="right"></div></div>
<% if l_width > 0 %>
<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="<%= css %> task_late">&nbsp;</div>
<% end %>
<% if d_width > 0 %>
<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="<%= css %> task_done">&nbsp;</div>
<% end %>
<div style="top:<%= top %>px;left:<%= i_left + i_width + 8 %>px;background:#fff;" class="<%= css %>">
<%= i.status.name %>
<%= (i.done_ratio).to_i %>%
</div>
<div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
<span class="tip">
<%= render_issue_tooltip i %>
</span></div>
<% else
i_left = ((i.start_date - @gantt.date_from)*zoom).floor
%>
<div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
<div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
<strong><%= format_version_name i %></strong>
</div>
<% end %>
<% top = top + 20
end %>
<% <%
# #
...@@ -226,8 +170,8 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %> ...@@ -226,8 +170,8 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
<table width="100%"> <table width="100%">
<tr> <tr>
<td align="left"><%= link_to_remote ('&#171; ' + l(:label_previous)), {:url => @gantt.params_previous, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %></td> <td align="left"><%= link_to_remote ('&#171; ' + l(:label_previous)), {:url => @gantt.params_previous, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %></td>
<td align="right"><%= link_to_remote (l(:label_next) + ' &#187;'), {:url => @gantt.params_next, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td> <td align="right"><%= link_to_remote (l(:label_next) + ' &#187;'), {:url => @gantt.params_next, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td>
</tr> </tr>
</table> </table>
......
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %> <%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time-add' %> <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %>
<% replace_watcher ||= 'watcher' %> <% replace_watcher ||= 'watcher' %>
<%= watcher_tag(@issue, User.current, {:id => replace_watcher, :replace => ['watcher','watcher2']}) %> <%= watcher_tag(@issue, User.current, {:id => replace_watcher, :replace => ['watcher','watcher2']}) %>
<%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %> <%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %>
<%= link_to_if_authorized l(:button_copy), new_issue_move_path(:id => @issue, :copy_options => {:copy => 't'}), :class => 'icon icon-copy' %> <%= link_to_if_authorized l(:button_copy), {:controller => 'issue_moves', :action => 'new', :id => @issue, :copy_options => {:copy => 't'}}, :class => 'icon icon-copy' %>
<%= link_to_if_authorized l(:button_move), new_issue_move_path(:id => @issue), :class => 'icon icon-move' %> <%= link_to_if_authorized l(:button_move), {:controller => 'issue_moves', :action => 'new', :id => @issue}, :class => 'icon icon-move' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => (@issue.leaf? ? l(:text_are_you_sure) : l(:text_are_you_sure_with_children)), :method => :post, :class => 'icon icon-del' %>
</div> </div>
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'), <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
l(:label_version_new), l(:label_version_new),
'version[name]', 'version[name]',
{:controller => 'versions', :action => 'new', :project_id => @project}, {:controller => 'versions', :action => 'create', :project_id => @project},
:title => l(:label_version_new), :title => l(:label_version_new),
:tabindex => 200) if authorize_for('versions', 'new') %> :tabindex => 200) if authorize_for('versions', 'new') %>
</p> </p>
...@@ -40,6 +40,6 @@ ...@@ -40,6 +40,6 @@
</div> </div>
<div style="clear:both;"> </div> <div style="clear:both;"> </div>
<%= render :partial => 'form_custom_fields' %> <%= render :partial => 'issues/form_custom_fields' %>
<% end %> <% end %>
<%= call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f }) %>
<div id="issue_descr_fields" <%= 'style="display:none"' unless @issue.new_record? || @issue.errors.any? %>> <div id="issue_descr_fields" <%= 'style="display:none"' unless @issue.new_record? || @issue.errors.any? %>>
<p><%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p> <p><%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
<%= observe_field :issue_tracker_id, :url => { :action => :new, :project_id => @project, :id => @issue }, <%= observe_field :issue_tracker_id, :url => { :action => :new, :project_id => @project, :id => @issue },
...@@ -20,7 +22,7 @@ ...@@ -20,7 +22,7 @@
</div> </div>
<div id="attributes" class="attributes"> <div id="attributes" class="attributes">
<%= render :partial => 'attributes' %> <%= render :partial => 'issues/attributes' %>
</div> </div>
<% if @issue.new_record? %> <% if @issue.new_record? %>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="autoscroll"> <div class="autoscroll">
<table class="list issues"> <table class="list issues">
<thead><tr> <thead><tr>
<th><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;', <th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %> :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
</th> </th>
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %> <%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
<% previous_group = group %> <% previous_group = group %>
<% end %> <% end %>
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> <tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
<td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td> <td class="checkbox hide-when-print"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
<td class="id"><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td> <td class="id"><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
<% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %> <% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %>
</tr> </tr>
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
<% remote_form_for(:relation, @relation, <% remote_form_for(:relation, @relation,
:url => {:controller => 'issue_relations', :action => 'new', :issue_id => @issue}, :url => {:controller => 'issue_relations', :action => 'new', :issue_id => @issue},
:method => :post, :method => :post,
:complete => "Form.Element.focus('relation_issue_to_id');",
:html => {:id => 'new-relation-form', :style => (@relation ? '' : 'display: none;')}) do |f| %> :html => {:id => 'new-relation-form', :style => (@relation ? '' : 'display: none;')}) do |f| %>
<%= render :partial => 'issue_relations/form', :locals => {:f => f}%> <%= render :partial => 'issue_relations/form', :locals => {:f => f}%>
<% end %> <% end %>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.tracker} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n") %></ul> <ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.tracker} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n") %></ul>
<% form_tag() do %> <% form_tag(:action => 'bulk_update') do %>
<%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %> <%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
<div class="box tabular"> <div class="box tabular">
<fieldset class="attributes"> <fieldset class="attributes">
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<div class="splitcontentleft"> <div class="splitcontentleft">
<p> <p>
<label><%= l(:field_tracker) %></label> <label><%= l(:field_tracker) %></label>
<%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@project.trackers, :id, :name)) %> <%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, :id, :name)) %>
</p> </p>
<% if @available_statuses.any? %> <% if @available_statuses.any? %>
<p> <p>
...@@ -27,20 +27,25 @@ ...@@ -27,20 +27,25 @@
<label><%= l(:field_assigned_to) %></label> <label><%= l(:field_assigned_to) %></label>
<%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') + <%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') + content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@project.assignable_users, :id, :name)) %> options_from_collection_for_select(@assignables, :id, :name)) %>
</p> </p>
<% if @project %>
<p> <p>
<label><%= l(:field_category) %></label> <label><%= l(:field_category) %></label>
<%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') + <%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') + content_tag('option', l(:label_none), :value => 'none') +
options_from_collection_for_select(@project.issue_categories, :id, :name)) %> options_from_collection_for_select(@project.issue_categories, :id, :name)) %>
</p> </p>
<% end %>
<% #TODO: allow editing versions when multiple projects %>
<% if @project %>
<p> <p>
<label><%= l(:field_fixed_version) %></label> <label><%= l(:field_fixed_version) %></label>
<%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') + <%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') + content_tag('option', l(:label_none), :value => 'none') +
version_options_for_select(@project.shared_versions.open)) %> version_options_for_select(@project.shared_versions.open)) %>
</p> </p>
<% end %>
<% @custom_fields.each do |custom_field| %> <% @custom_fields.each do |custom_field| %>
<p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('issue', custom_field) %></p> <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('issue', custom_field) %></p>
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
{ :url => { :set_filter => 1 }, { :url => { :set_filter => 1 },
:before => 'selectAllOptions("selected_columns");', :before => 'selectAllOptions("selected_columns");',
:update => "content", :update => "content",
:complete => "apply_filters_observer()",
:with => "Form.serialize('query_form')" :with => "Form.serialize('query_form')"
}, :class => 'icon icon-checked' %> }, :class => 'icon icon-checked' %>
...@@ -78,7 +79,7 @@ ...@@ -78,7 +79,7 @@
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %> <%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %>
<%= auto_discovery_link_tag(:atom, {:action => 'changes', :query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_changes_details)) %> <%= auto_discovery_link_tag(:atom, {:controller => 'journals', :action => 'index', :query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_changes_details)) %>
<% end %> <% end %>
<%= context_menu issues_context_menu_path %> <%= context_menu issues_context_menu_path %>
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
<th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h @issue.category ? @issue.category.name : "-" %></td> <th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h @issue.category ? @issue.category.name : "-" %></td>
<% if User.current.allowed_to?(:view_time_entries, @project) %> <% if User.current.allowed_to?(:view_time_entries, @project) %>
<th class="spent-time"><%=l(:label_spent_time)%>:</th> <th class="spent-time"><%=l(:label_spent_time)%>:</th>
<td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}) : "-" %></td> <td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
<% end %> <% end %>
</tr> </tr>
<tr> <tr>
...@@ -44,18 +44,19 @@ ...@@ -44,18 +44,19 @@
<%= render_custom_fields_rows(@issue) %> <%= render_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %> <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</table> </table>
<hr />
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<div class="contextual"> <div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') unless @issue.description.blank? %> <%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') if @issue.description? %>
</div> </div>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki"> <div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %> <%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div> </div>
<%= link_to_attachments @issue %> <%= link_to_attachments @issue %>
<% end -%>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %> <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
......
...@@ -32,24 +32,12 @@ ...@@ -32,24 +32,12 @@
<div class="splitcontentright"> <div class="splitcontentright">
<h3><%=l(:field_mail_notification)%></h3> <h3><%=l(:field_mail_notification)%></h3>
<div class="box"> <div class="box">
<%= select_tag 'notification_option', options_for_select(@notification_options, @notification_option), <%= render :partial => 'users/mail_notifications' %>
:onchange => 'if ($("notification_option").value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
<% content_tag 'div', :id => 'notified-projects', :style => (@notification_option == 'selected' ? '' : 'display:none;') do %>
<p><% User.current.projects.each do |project| %>
<label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%=h project.name %></label><br />
<% end %></p>
<p><em><%= l(:text_user_mail_option) %></em></p>
<% end %>
<p><label><%= check_box_tag 'no_self_notified', 1, @user.pref[:no_self_notified] %> <%= l(:label_user_mail_no_self_notified) %></label></p>
</div> </div>
<h3><%=l(:label_preferences)%></h3> <h3><%=l(:label_preferences)%></h3>
<div class="box tabular"> <div class="box tabular">
<% fields_for :pref, @user.pref, :builder => TabularFormBuilder, :lang => current_language do |pref_fields| %> <%= render :partial => 'users/preferences' %>
<p><%= pref_fields.check_box :hide_mail %></p>
<p><%= pref_fields.select :time_zone, ActiveSupport::TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :include_blank => true %></p>
<p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
<% end %>
</div> </div>
</div> </div>
......
...@@ -40,7 +40,7 @@ entries_by_day = entries.group_by(&:spent_on) ...@@ -40,7 +40,7 @@ entries_by_day = entries.group_by(&:spent_on)
:title => l(:button_edit) %> :title => l(:button_edit) %>
<%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry}, <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry},
:confirm => l(:text_are_you_sure), :confirm => l(:text_are_you_sure),
:method => :post, :method => :delete,
:title => l(:button_delete) %> :title => l(:button_delete) %>
<% end -%> <% end -%>
</td> </td>
......
<p><%= link_to_project(news.project) + ': ' unless @project %> <p><%= link_to_project(news.project) + ': ' unless @project %>
<%= link_to h(news.title), :controller => 'news', :action => 'show', :id => news %> <%= link_to h(news.title), news_path(news) %>
<%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %> <%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %>
<br /> <br />
<% unless news.summary.blank? %><span class="summary"><%=h news.summary %></span><br /><% end %> <% unless news.summary.blank? %><span class="summary"><%=h news.summary %></span><br /><% end %>
......
<h2><%=l(:label_news)%></h2> <h2><%=l(:label_news)%></h2>
<% labelled_tabular_form_for :news, @news, :url => { :action => "edit" }, <% labelled_tabular_form_for :news, @news, :url => news_path(@news),
:html => { :id => 'news-form' } do |f| %> :html => { :id => 'news-form', :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<%= link_to_remote l(:label_preview), <%= link_to_remote l(:label_preview),
{ :url => { :controller => 'news', :action => 'preview', :project_id => @project }, { :url => preview_news_path(:project_id => @project),
:method => 'post', :method => 'get',
:update => 'preview', :update => 'preview',
:with => "Form.serialize('news-form')" :with => "Form.serialize('news-form')"
}, :accesskey => accesskey(:preview) %> }, :accesskey => accesskey(:preview) %>
<% end %> <% end %>
<div id="preview" class="wiki"></div> <div id="preview" class="wiki"></div>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized(l(:label_news_new), <%= link_to_if_authorized(l(:label_news_new),
{:controller => 'news', :action => 'new', :project_id => @project}, new_project_news_path(@project),
:class => 'icon icon-add', :class => 'icon icon-add',
:onclick => 'Element.show("add-news"); Form.Element.focus("news_title"); return false;') if @project %> :onclick => 'Element.show("add-news"); Form.Element.focus("news_title"); return false;') if @project %>
</div> </div>
<div id="add-news" style="display:none;"> <div id="add-news" style="display:none;">
<h2><%=l(:label_news_new)%></h2> <h2><%=l(:label_news_new)%></h2>
<% labelled_tabular_form_for :news, @news, :url => { :controller => 'news', :action => 'new', :project_id => @project }, <% labelled_tabular_form_for :news, @news, :url => project_news_index_path(@project),
:html => { :id => 'news-form' } do |f| %> :html => { :id => 'news-form' } do |f| %>
<%= render :partial => 'news/form', :locals => { :f => f } %> <%= render :partial => 'news/form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %> <%= submit_tag l(:button_create) %>
<%= link_to_remote l(:label_preview), <%= link_to_remote l(:label_preview),
{ :url => { :controller => 'news', :action => 'preview', :project_id => @project }, { :url => preview_news_path(:project_id => @project),
:method => 'post', :method => 'get',
:update => 'preview', :update => 'preview',
:with => "Form.serialize('news-form')" :with => "Form.serialize('news-form')"
}, :accesskey => accesskey(:preview) %> | }, :accesskey => accesskey(:preview) %> |
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
<% else %> <% else %>
<% @newss.each do |news| %> <% @newss.each do |news| %>
<h3><%= link_to_project(news.project) + ': ' unless news.project == @project %> <h3><%= link_to_project(news.project) + ': ' unless news.project == @project %>
<%= link_to h(news.title), :controller => 'news', :action => 'show', :id => news %> <%= link_to h(news.title), news_path(news) %>
<%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %></h3> <%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %></h3>
<p class="author"><%= authoring news.created_on, news.author %></p> <p class="author"><%= authoring news.created_on, news.author %></p>
<div class="wiki"> <div class="wiki">
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %> <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %>
<%= stylesheet_link_tag 'scm' %>
<% end %> <% end %>
<% html_title(l(:label_news_plural)) -%> <% html_title(l(:label_news_plural)) -%>
<h2><%=l(:label_news_new)%></h2> <h2><%=l(:label_news_new)%></h2>
<% labelled_tabular_form_for :news, @news, :url => { :controller => 'news', :action => 'new', :project_id => @project }, <% labelled_tabular_form_for :news, @news, :url => project_news_index_path(@project),
:html => { :id => 'news-form' } do |f| %> :html => { :id => 'news-form' } do |f| %>
<%= render :partial => 'news/form', :locals => { :f => f } %> <%= render :partial => 'news/form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %> <%= submit_tag l(:button_create) %>
<%= link_to_remote l(:label_preview), <%= link_to_remote l(:label_preview),
{ :url => { :controller => 'news', :action => 'preview', :project_id => @project }, { :url => preview_news_path(:project_id => @project),
:method => 'post', :method => 'get',
:update => 'preview', :update => 'preview',
:with => "Form.serialize('news-form')" :with => "Form.serialize('news-form')"
}, :accesskey => accesskey(:preview) %> }, :accesskey => accesskey(:preview) %>
......
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_edit), <%= link_to_if_authorized l(:button_edit),
{:controller => 'news', :action => 'edit', :id => @news}, edit_news_path(@news),
:class => 'icon icon-edit', :class => 'icon icon-edit',
:accesskey => accesskey(:edit), :accesskey => accesskey(:edit),
:onclick => 'Element.show("edit-news"); return false;' %> :onclick => 'Element.show("edit-news"); return false;' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'news', :action => 'destroy', :id => @news}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <%= link_to_if_authorized l(:button_delete), news_path(@news), :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
</div> </div>
<h2><%= avatar(@news.author, :size => "24") %><%=h @news.title %></h2> <h2><%= avatar(@news.author, :size => "24") %><%=h @news.title %></h2>
<% if authorize_for('news', 'edit') %> <% if authorize_for('news', 'edit') %>
<div id="edit-news" style="display:none;"> <div id="edit-news" style="display:none;">
<% labelled_tabular_form_for :news, @news, :url => { :action => "edit", :id => @news }, <% labelled_tabular_form_for :news, @news, :url => news_path(@news),
:html => { :id => 'news-form' } do |f| %> :html => { :id => 'news-form', :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<%= link_to_remote l(:label_preview), <%= link_to_remote l(:label_preview),
{ :url => { :controller => 'news', :action => 'preview', :project_id => @project }, { :url => preview_news_path(:project_id => @project),
:method => 'post', :method => 'get',
:update => 'preview', :update => 'preview',
:with => "Form.serialize('news-form')" :with => "Form.serialize('news-form')"
}, :accesskey => accesskey(:preview) %> | }, :accesskey => accesskey(:preview) %> |
...@@ -39,17 +39,17 @@ ...@@ -39,17 +39,17 @@
<% @comments.each do |comment| %> <% @comments.each do |comment| %>
<% next if comment.new_record? %> <% next if comment.new_record? %>
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized image_tag('delete.png'), {:controller => 'news', :action => 'destroy_comment', :id => @news, :comment_id => comment}, <%= link_to_if_authorized image_tag('delete.png'), {:controller => 'comments', :action => 'destroy', :id => @news, :comment_id => comment},
:confirm => l(:text_are_you_sure), :method => :post, :title => l(:button_delete) %> :confirm => l(:text_are_you_sure), :method => :delete, :title => l(:button_delete) %>
</div> </div>
<h4><%= avatar(comment.author, :size => "24") %><%= authoring comment.created_on, comment.author %></h4> <h4><%= avatar(comment.author, :size => "24") %><%= authoring comment.created_on, comment.author %></h4>
<%= textilizable(comment.comments) %> <%= textilizable(comment.comments) %>
<% end if @comments.any? %> <% end if @comments.any? %>
</div> </div>
<% if authorize_for 'news', 'add_comment' %> <% if authorize_for 'comments', 'create' %>
<p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p> <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
<% form_tag({:action => 'add_comment', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %> <% form_tag({:controller => 'comments', :action => 'create', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
<div class="box"> <div class="box">
<%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %> <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'comment_comments' %> <%= wikitoolbar_for 'comment_comments' %>
......
<% labelled_tabular_form_for :project, @project, :url => { :action => "edit", :id => @project } do |f| %> <% labelled_tabular_form_for :project, @project, :url => project_path(@project), :html => {:method => (@project.new_record? ? :post : :put) } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<% end %> <% end %>
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<% end %> <% end %>
</p> </p>
<p> <p>
<% form_tag({:controller => 'projects', :action => 'destroy', :id => @project_to_destroy}) do %> <% form_tag(project_path(@project_to_destroy), :method => :delete) do %>
<label><%= check_box_tag 'confirm', 1 %> <%= l(:general_text_Yes) %></label> <label><%= check_box_tag 'confirm', 1 %> <%= l(:general_text_Yes) %></label>
<%= submit_tag l(:button_delete) %> <%= submit_tag l(:button_delete) %>
<% end %> <% end %>
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
<% end %> <% end %>
<div class="contextual"> <div class="contextual">
<%= link_to(l(:label_project_new), {:controller => 'projects', :action => 'add'}, :class => 'icon icon-add') + ' |' if User.current.allowed_to?(:add_project, nil, :global => true) %> <%= link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add') + ' |' if User.current.allowed_to?(:add_project, nil, :global => true) %>
<%= link_to(l(:label_issue_view_all), { :controller => 'issues' }) + ' |' if User.current.allowed_to?(:view_issues, nil, :global => true) %> <%= link_to(l(:label_issue_view_all), { :controller => 'issues' }) + ' |' if User.current.allowed_to?(:view_issues, nil, :global => true) %>
<%= link_to(l(:label_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %> <%= link_to(l(:label_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %>
<%= link_to l(:label_overall_activity), { :controller => 'projects', :action => 'activity' }%> <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%>
</div> </div>
<h2><%=l(:label_project_plural)%></h2> <h2><%=l(:label_project_plural)%></h2>
......
<h2><%=l(:label_project_new)%></h2> <h2><%=l(:label_project_new)%></h2>
<% labelled_tabular_form_for :project, @project, :url => { :action => "add" } do |f| %> <% labelled_tabular_form_for :project, @project, :url => { :action => "create" } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend> <fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
......
<% form_tag({:controller => 'projects', :action => 'save_activities', :id => @project}, :class => "tabular") do %> <% form_tag(project_project_enumerations_path(@project), :method => :put, :class => "tabular") do %>
<table class="list"> <table class="list">
<thead><tr> <thead><tr>
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
</table> </table>
<div class="contextual"> <div class="contextual">
<%= link_to(l(:button_reset), {:controller => 'projects', :action => 'reset_activities', :id => @project}, <%= link_to(l(:button_reset), project_project_enumerations_path(@project),
:method => :delete, :method => :delete,
:confirm => l(:text_are_you_sure), :confirm => l(:text_are_you_sure),
:class => 'icon icon-del') %> :class => 'icon icon-del') %>
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
<td class="buttons"> <td class="buttons">
<% if version.project == @project %> <% if version.project == @project %>
<%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %> <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
<% end %> <% end %>
</td> </td>
</tr> </tr>
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
<div class="contextual"> <div class="contextual">
<% if @project.versions.any? %> <% if @project.versions.any? %>
<%= link_to l(:label_close_versions), {:controller => 'versions', :action => 'close_completed', :project_id => @project}, :method => :post %> <%= link_to l(:label_close_versions), close_completed_project_versions_path(@project), :method => :put %>
<% end %> <% end %>
</div> </div>
......
<div class="contextual"> <div class="contextual">
<% if User.current.allowed_to?(:add_subprojects, @project) %> <% if User.current.allowed_to?(:add_subprojects, @project) %>
<%= link_to l(:label_subproject_new), {:controller => 'projects', :action => 'add', :parent_id => @project}, :class => 'icon icon-add' %> <%= link_to l(:label_subproject_new), {:controller => 'projects', :action => 'new', :parent_id => @project}, :class => 'icon icon-add' %>
<% end %> <% end %>
</div> </div>
...@@ -67,14 +67,14 @@ ...@@ -67,14 +67,14 @@
<% if @total_hours && User.current.allowed_to?(:view_time_entries, @project) %> <% if @total_hours && User.current.allowed_to?(:view_time_entries, @project) %>
<h3><%= l(:label_spent_time) %></h3> <h3><%= l(:label_spent_time) %></h3>
<p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p> <p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p>
<p><%= link_to(l(:label_details), {:controller => 'timelog', :action => 'details', :project_id => @project}) %> | <p><%= link_to(l(:label_details), {:controller => 'timelog', :action => 'index', :project_id => @project}) %> |
<%= link_to(l(:label_report), {:controller => 'timelog', :action => 'report', :project_id => @project}) %></p> <%= link_to(l(:label_report), {:controller => 'time_entry_reports', :action => 'report', :project_id => @project}) %></p>
<% end %> <% end %>
<%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %> <%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %>
<% end %> <% end %>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:action => 'activity', :id => @project, :format => 'atom', :key => User.current.rss_key}) %> <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :key => User.current.rss_key}) %>
<% end %> <% end %>
<% html_title(l(:label_overview)) -%> <% html_title(l(:label_overview)) -%>
...@@ -53,6 +53,18 @@ function toggle_multi_select(field) { ...@@ -53,6 +53,18 @@ function toggle_multi_select(field) {
select.multiple = true; select.multiple = true;
} }
} }
function apply_filters_observer() {
$$("#query_form input[type=text]").invoke("observe", "keypress", function(e){
if(e.keyCode == Event.KEY_RETURN) {
<%= remote_function(:url => { :set_filter => 1},
:update => "content",
:with => "Form.serialize('query_form')",
:complete => "e.stop(); apply_filters_observer()") %>
}
});
}
Event.observe(document,"dom:loaded", apply_filters_observer);
//]]> //]]>
</script> </script>
......
<h2><%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %></h2> <h2><%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %></h2>
<!-- Choose view type --> <!-- Choose view type -->
<% form_tag({:path => @path}, :method => 'get') do %> <% form_tag({:path => to_path_param(@path)}, :method => 'get') do %>
<%= hidden_field_tag('rev', params[:rev]) if params[:rev] %> <%= hidden_field_tag('rev', params[:rev]) if params[:rev] %>
<%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %> <%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %>
<p><label><%= l(:label_view_diff) %></label> <p><label><%= l(:label_view_diff) %></label>
......
...@@ -7,13 +7,18 @@ ...@@ -7,13 +7,18 @@
<p><%= setting_check_box :bcc_recipients %></p> <p><%= setting_check_box :bcc_recipients %></p>
<p><%= setting_check_box :plain_text_mail %></p> <p><%= setting_check_box :plain_text_mail %></p>
</div>
<fieldset class="box settings" id="notified_events"><legend><%=l(:text_select_mail_notifications)%></legend> <p><%= setting_select(:default_notification_option, User::MAIL_NOTIFICATION_OPTIONS.collect {|o| [l(o.last), o.first.to_s]}) %></p>
<%= setting_multiselect(:notified_events,
@notifiables.collect {|notifiable| [l_or_humanize(notifiable, :prefix => 'label_'), notifiable]}, :label => false) %> </div>
<p><%= check_all_links('notified_events') %></p> <fieldset class="box" id="notified_events"><legend><%=l(:text_select_mail_notifications)%></legend>
<%= hidden_field_tag 'settings[notified_events][]', '' %>
<% @notifiables.each do |notifiable| %>
<%= notification_field notifiable %>
<br />
<% end %>
<p><%= check_all_links('notified_events') %></p>
</fieldset> </fieldset>
<fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend> <fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend>
......
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %> <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %>
</div> </div>
<%= render_timelog_breadcrumb %> <%= render_timelog_breadcrumb %>
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<%# TODO: get rid of the project_id field, that should already be in the URL %> <%# TODO: get rid of the project_id field, that should already be in the URL %>
<%= hidden_field_tag('project_id', params[:project_id]) if @project %> <%= hidden_field_tag('project_id', params[:project_id]) if @project %>
<%= hidden_field_tag('issue_id', params[:issue_id]) if @issue %> <%= hidden_field_tag('issue_id', params[:issue_id]) if @issue %>
<%= render :partial => 'date_range' %> <%= render :partial => 'timelog/date_range' %>
<p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], <p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'],
[l(:label_month), 'month'], [l(:label_month), 'month'],
......
...@@ -28,9 +28,9 @@ ...@@ -28,9 +28,9 @@
<div class="tabs"> <div class="tabs">
<% url_params = @free_period ? { :from => @from, :to => @to } : { :period => params[:period] } %> <% url_params = @free_period ? { :from => @from, :to => @to } : { :period => params[:period] } %>
<ul> <ul>
<li><%= link_to(l(:label_details), url_params.merge({:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue }), <li><%= link_to(l(:label_details), url_params.merge({:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue }),
:class => (@controller.action_name == 'details' ? 'selected' : nil)) %></li> :class => (@controller.action_name == 'index' ? 'selected' : nil)) %></li>
<li><%= link_to(l(:label_report), url_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project, :issue_id => @issue}), <li><%= link_to(l(:label_report), url_params.merge({:controller => 'time_entry_reports', :action => 'report', :project_id => @project, :issue_id => @issue}),
:class => (@controller.action_name == 'report' ? 'selected' : nil)) %></li> :class => (@controller.action_name == 'report' ? 'selected' : nil)) %></li>
</ul> </ul>
</div> </div>
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
:title => l(:button_edit) %> :title => l(:button_edit) %>
<%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry, :project_id => nil}, <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry, :project_id => nil},
:confirm => l(:text_are_you_sure), :confirm => l(:text_are_you_sure),
:method => :post, :method => :delete,
:title => l(:button_delete) %> :title => l(:button_delete) %>
<% end -%> <% end -%>
</td> </td>
......
<h2><%= l(:label_spent_time) %></h2> <h2><%= l(:label_spent_time) %></h2>
<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :id => @time_entry, :project_id => @time_entry.project} do |f| %> <% labelled_tabular_form_for(:time_entry, @time_entry, :url => {
:action => (@time_entry.new_record? ? 'create' : 'update'),
:id => @time_entry,
:project_id => @time_entry.project
},
:html => {:method => @time_entry.new_record? ? :post : :put}) do |f| %>
<%= error_messages_for 'time_entry' %> <%= error_messages_for 'time_entry' %>
<%= back_url_hidden_field_tag %> <%= back_url_hidden_field_tag %>
......
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %> <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %>
</div> </div>
<%= render_timelog_breadcrumb %> <%= render_timelog_breadcrumb %>
......
...@@ -32,4 +32,14 @@ ...@@ -32,4 +32,14 @@
<%= password_field_tag 'password_confirmation', nil, :size => 25 %></p> <%= password_field_tag 'password_confirmation', nil, :size => 25 %></p>
</div> </div>
</div> </div>
<div class="box">
<h3><%=l(:field_mail_notification)%></h3>
<%= render :partial => 'users/mail_notifications' %>
</div>
<div class="box tabular">
<h3><%=l(:label_preferences)%></h3>
<%= render :partial => 'users/preferences' %>
</div>
<!--[eoform:user]--> <!--[eoform:user]-->
<% labelled_tabular_form_for :user, @user, :url => { :controller => 'users', :action => "edit", :tab => nil }, :html => { :class => nil } do |f| %> <% labelled_tabular_form_for :user, @user, :url => { :controller => 'users', :action => "update", :tab => nil }, :html => { :method => :put, :class => nil } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<% if @user.active? -%> <% if @user.active? -%>
<p><label><%= check_box_tag 'send_information', 1, true %> <%= l(:label_send_information) %></label> <p><label><%= check_box_tag 'send_information', 1, true %> <%= l(:label_send_information) %></label></p>
<% end -%> <% end -%>
<p><%= submit_tag l(:button_save) %></p> <p><%= submit_tag l(:button_save) %></p>
<% end %> <% end %>
<% form_for(:user, :url => { :action => 'edit' }) do %> <% form_for(:user, :url => { :action => 'update' }, :html => {:method => :put}) do %>
<div class="box"> <div class="box">
<% Group.all.sort.each do |group| %> <% Group.all.sort.each do |group| %>
<label><%= check_box_tag 'user[group_ids][]', group.id, @user.groups.include?(group) %> <%=h group %></label><br /> <label><%= check_box_tag 'user[group_ids][]', group.id, @user.groups.include?(group) %> <%=h group %></label><br />
......
<p>
<%= select_tag 'notification_option', options_for_select(@notification_options.collect {|o| [l(o.last), o.first]}, @notification_option.to_sym),
:onchange => 'if ($("notification_option").value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
</p>
<% content_tag 'div', :id => 'notified-projects', :style => (@notification_option == 'selected' ? '' : 'display:none;') do %>
<p><% @user.projects.each do |project| %>
<label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%=h project.name %></label><br />
<% end %></p>
<p><em><%= l(:text_user_mail_option) %></em></p>
<% end %>
<p><label><%= l(:label_user_mail_no_self_notified) %></label><%= check_box_tag 'no_self_notified', 1, @user.pref[:no_self_notified] %></p>
<% fields_for :pref, @user.pref, :builder => TabularFormBuilder, :lang => current_language do |pref_fields| %>
<p><%= pref_fields.check_box :hide_mail %></p>
<p><%= pref_fields.select :time_zone, ActiveSupport::TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :include_blank => true %></p>
<p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
<% end %>
<div class="contextual"> <div class="contextual">
<%= link_to l(:label_profile), {:controller => 'users', :action => 'show', :id => @user}, :class => 'icon icon-user' %> <%= link_to l(:label_profile), user_path(@user), :class => 'icon icon-user' %>
<%= change_status_link(@user) %> <%= change_status_link(@user) %>
</div> </div>
......
<div class="contextual"> <div class="contextual">
<%= link_to l(:label_user_new), {:action => 'add'}, :class => 'icon icon-add' %> <%= link_to l(:label_user_new), {:action => 'new'}, :class => 'icon icon-add' %>
</div> </div>
<h2><%=l(:label_user_plural)%></h2> <h2><%=l(:label_user_plural)%></h2>
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
<tbody> <tbody>
<% for user in @users -%> <% for user in @users -%>
<tr class="user <%= cycle("odd", "even") %> <%= %w(anon active registered locked)[user.status] %>"> <tr class="user <%= cycle("odd", "even") %> <%= %w(anon active registered locked)[user.status] %>">
<td class="username"><%= avatar(user, :size => "14") %><%= link_to h(user.login), :action => 'edit', :id => user %></td> <td class="username"><%= avatar(user, :size => "14") %><%= link_to h(user.login), edit_user_path(user) %></td>
<td class="firstname"><%= h(user.firstname) %></td> <td class="firstname"><%= h(user.firstname) %></td>
<td class="lastname"><%= h(user.lastname) %></td> <td class="lastname"><%= h(user.lastname) %></td>
<td class="email"><%= mail_to(h(user.mail)) %></td> <td class="email"><%= mail_to(h(user.mail)) %></td>
......
<h2><%= link_to l(:label_user_plural), :controller => 'users', :action => 'index' %> &#187; <%=l(:label_user_new)%></h2> <h2><%= link_to l(:label_user_plural), :controller => 'users', :action => 'index' %> &#187; <%=l(:label_user_new)%></h2>
<% labelled_tabular_form_for :user, @user, :url => { :action => "add" }, :html => { :class => nil } do |f| %> <% labelled_tabular_form_for :user, @user, :url => { :action => "create" }, :html => { :class => nil } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<p><label><%= check_box_tag 'send_information', 1, true %> <%= l(:label_send_information) %></label></p> <p><label><%= check_box_tag 'send_information', 1, true %> <%= l(:label_send_information) %></label></p>
<p> <p>
......
<div class="contextual"> <div class="contextual">
<%= link_to(l(:button_edit), {:controller => 'users', :action => 'edit', :id => @user}, :class => 'icon icon-edit') if User.current.admin? %> <%= link_to(l(:button_edit), edit_user_path(@user), :class => 'icon icon-edit') if User.current.admin? %>
</div> </div>
<h2><%= avatar @user, :size => "50" %> <%=h @user.name %></h2> <h2><%= avatar @user, :size => "50" %> <%=h @user.name %></h2>
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
<div class="splitcontentright"> <div class="splitcontentright">
<% unless @events_by_day.empty? %> <% unless @events_by_day.empty? %>
<h3><%= link_to l(:label_activity), :controller => 'projects', :action => 'activity', :id => nil, :user_id => @user, :from => @events_by_day.keys.first %></h3> <h3><%= link_to l(:label_activity), :controller => 'activities', :action => 'index', :id => nil, :user_id => @user, :from => @events_by_day.keys.first %></h3>
<p> <p>
<%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %> <%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %>
...@@ -57,11 +57,11 @@ ...@@ -57,11 +57,11 @@
</div> </div>
<% other_formats_links do |f| %> <% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => nil, :user_id => @user, :key => User.current.rss_key} %> <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => nil, :user_id => @user, :key => User.current.rss_key} %>
<% end %> <% end %>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key) %> <%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :user_id => @user, :format => :atom, :key => User.current.rss_key) %>
<% end %> <% end %>
<% end %> <% end %>
<%= call_hook :view_account_right_bottom, :user => @user %> <%= call_hook :view_account_right_bottom, :user => @user %>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
select_tag('status_by', select_tag('status_by',
status_by_options_for_select(criteria), status_by_options_for_select(criteria),
:id => 'status_by_select', :id => 'status_by_select',
:onchange => remote_function(:url => { :action => :status_by, :id => version }, :onchange => remote_function(:url => status_by_project_version_path(version.project, version),
:with => "Form.serialize('status_by_form')"))) %> :with => "Form.serialize('status_by_form')"))) %>
</legend> </legend>
<% if counts.empty? %> <% if counts.empty? %>
......
<h2><%=l(:label_version)%></h2> <h2><%=l(:label_version)%></h2>
<% labelled_tabular_form_for :version, @version, :url => { :action => 'edit' } do |f| %> <% labelled_tabular_form_for :version, @version, :url => project_version_path(@project, @version), :html => {:method => :put} do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %> <%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
<% end %> <% end %>
......
...@@ -51,4 +51,4 @@ ...@@ -51,4 +51,4 @@
<% html_title(l(:label_roadmap)) %> <% html_title(l(:label_roadmap)) %>
<%= context_menu :controller => 'issues', :action => 'context_menu' %> <%= context_menu issues_context_menu_path %>
<h2><%=l(:label_version_new)%></h2> <h2><%=l(:label_version_new)%></h2>
<% labelled_tabular_form_for :version, @version, :url => { :action => 'new' } do |f| %> <% labelled_tabular_form_for :version, @version, :url => project_versions_path(@project) do |f| %>
<%= render :partial => 'versions/form', :locals => { :f => f } %> <%= render :partial => 'versions/form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %> <%= submit_tag l(:button_create) %>
<% end %> <% end %>
\ No newline at end of file
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %> <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %>
<%= link_to_if_authorized(l(:button_edit_associated_wikipage, :page_title => @version.wiki_page_title), {:controller => 'wiki', :action => 'edit', :page => Wiki.titleize(@version.wiki_page_title)}, :class => 'icon icon-edit') unless @version.wiki_page_title.blank? || @project.wiki.nil? %>
<%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %> <%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
</div> </div>
......
...@@ -34,6 +34,6 @@ ...@@ -34,6 +34,6 @@
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:controller => 'news', :action => 'index', :key => User.current.rss_key, :format => 'atom'}, <%= auto_discovery_link_tag(:atom, {:controller => 'news', :action => 'index', :key => User.current.rss_key, :format => 'atom'},
:title => "#{Setting.app_title}: #{l(:label_news_latest)}") %> :title => "#{Setting.app_title}: #{l(:label_news_latest)}") %>
<%= auto_discovery_link_tag(:atom, {:controller => 'projects', :action => 'activity', :key => User.current.rss_key, :format => 'atom'}, <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :key => User.current.rss_key, :format => 'atom'},
:title => "#{Setting.app_title}: #{l(:label_activity)}") %> :title => "#{Setting.app_title}: #{l(:label_activity)}") %>
<% end %> <% end %>
...@@ -5,5 +5,5 @@ ...@@ -5,5 +5,5 @@
<h3><%= l(:label_wiki) %></h3> <h3><%= l(:label_wiki) %></h3>
<%= link_to l(:field_start_page), {:action => 'index', :page => nil} %><br /> <%= link_to l(:field_start_page), {:action => 'index', :page => nil} %><br />
<%= link_to l(:label_index_by_title), {:action => 'special', :page => 'Page_index'} %><br /> <%= link_to l(:label_index_by_title), {:action => 'page_index'} %><br />
<%= link_to l(:label_index_by_date), {:action => 'special', :page => 'Date_index'} %><br /> <%= link_to l(:label_index_by_date), {:action => 'date_index'} %><br />
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<% @annotate.lines.each do |line| -%> <% @annotate.lines.each do |line| -%>
<tr class="bloc-<%= colors[line[0]] %>"> <tr class="bloc-<%= colors[line[0]] %>">
<th class="line-num"><%= line_num %></th> <th class="line-num"><%= line_num %></th>
<td class="revision"><%= link_to line[0], :controller => 'wiki', :action => 'index', :id => @project, :page => @page.title, :version => line[0] %></td> <td class="revision"><%= link_to line[0], :controller => 'wiki', :action => 'index', :project_id => @project, :page => @page.title, :version => line[0] %></td>
<td class="author"><%= h(line[1]) %></td> <td class="author"><%= h(line[1]) %></td>
<td class="line-code"><pre><%=h line[2] %></pre></td> <td class="line-code"><pre><%=h line[2] %></pre></td>
</tr> </tr>
......
...@@ -23,11 +23,11 @@ ...@@ -23,11 +23,11 @@
<% unless @pages.empty? %> <% unless @pages.empty? %>
<% other_formats_links do |f| %> <% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %>
<%= f.link_to('HTML', :url => {:action => 'special', :page => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %> <%= f.link_to('HTML', :url => {:action => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %>
<% end %> <% end %>
<% end %> <% end %>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> <%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %>
<% end %> <% end %>
...@@ -15,5 +15,5 @@ ...@@ -15,5 +15,5 @@
</div> </div>
<%= submit_tag l(:button_apply) %> <%= submit_tag l(:button_apply) %>
<%= link_to l(:button_cancel), :controller => 'wiki', :action => 'index', :id => @project, :page => @page.title %> <%= link_to l(:button_cancel), :controller => 'wiki', :action => 'index', :project_id => @project, :page => @page.title %>
<% end %> <% end %>
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<p><%= submit_tag l(:button_save) %> <p><%= submit_tag l(:button_save) %>
<%= link_to_remote l(:label_preview), <%= link_to_remote l(:label_preview),
{ :url => { :controller => 'wiki', :action => 'preview', :id => @project, :page => @page.title }, { :url => { :controller => 'wiki', :action => 'preview', :project_id => @project, :page => @page.title },
:method => 'post', :method => 'post',
:update => 'preview', :update => 'preview',
:with => "Form.serialize('wiki_form')", :with => "Form.serialize('wiki_form')",
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<h3><%= l(:label_history) %></h3> <h3><%= l(:label_history) %></h3>
<% form_tag({:action => "diff"}, :method => :get) do %> <% form_tag({:action => "diff"}, :method => :get) do %>
<%= hidden_field_tag('project_id', h(@project.to_param)) %>
<table class="list"> <table class="list">
<thead><tr> <thead><tr>
<th>#</th> <th>#</th>
......
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
<% unless @pages.empty? %> <% unless @pages.empty? %>
<% other_formats_links do |f| %> <% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %> <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %>
<%= f.link_to('HTML', :url => {:action => 'special', :page => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %> <%= f.link_to('HTML', :url => {:action => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %>
<% end %> <% end %>
<% end %> <% end %>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %> <%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %>
<% end %> <% end %>
...@@ -4,9 +4,17 @@ ...@@ -4,9 +4,17 @@
# Code is not reloaded between requests # Code is not reloaded between requests
config.cache_classes = true config.cache_classes = true
#####
# Customize the default logger (http://ruby-doc.org/core/classes/Logger.html)
#
# Use a different logger for distributed setups # Use a different logger for distributed setups
# config.logger = SyslogLogger.new # config.logger = SyslogLogger.new
#
# Rotate logs bigger than 1MB, keeps no more than 7 rotated logs around.
# When setting a new Logger, make sure to set it's log level too.
#
# config.logger = Logger.new(config.log_path, 7, 1048576)
# config.logger.level = Logger::INFO
# Full error reports are disabled and caching is turned on # Full error reports are disabled and caching is turned on
config.action_controller.consider_all_requests_local = false config.action_controller.consider_all_requests_local = false
......
...@@ -78,3 +78,17 @@ module AsynchronousMailer ...@@ -78,3 +78,17 @@ module AsynchronousMailer
end end
ActionMailer::Base.send :include, AsynchronousMailer ActionMailer::Base.send :include, AsynchronousMailer
# TODO: Hack to support i18n 4.x on Rails 2.3.5. Remove post 2.3.6.
# See http://www.redmine.org/issues/6428 and http://www.redmine.org/issues/5608
module I18n
module Backend
module Base
def warn_syntax_deprecation!
return if @skip_syntax_deprecation
warn "The {{key}} interpolation syntax in I18n messages is deprecated. Please use %{key} instead.\nDowngrade your i18n gem to 0.3.7 (everything above must be deinstalled) to remove this warning, see http://www.redmine.org/issues/5608 for more information."
@skip_syntax_deprecation = true
end
end
end
end
...@@ -8,10 +8,10 @@ bg: ...@@ -8,10 +8,10 @@ bg:
default: "%Y-%m-%d" default: "%Y-%m-%d"
short: "%b %d" short: "%b %d"
long: "%B %d, %Y" long: "%B %d, %Y"
day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
# Don't forget the nil at the beginning; there's no such thing as a 0th month # Don't forget the nil at the beginning; there's no such thing as a 0th month
month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
...@@ -26,7 +26,7 @@ bg: ...@@ -26,7 +26,7 @@ bg:
long: "%B %d, %Y %H:%M" long: "%B %d, %Y %H:%M"
am: "am" am: "am"
pm: "pm" pm: "pm"
datetime: datetime:
distance_in_words: distance_in_words:
half_a_minute: "half a minute" half_a_minute: "half a minute"
...@@ -69,27 +69,27 @@ bg: ...@@ -69,27 +69,27 @@ bg:
separator: "." separator: "."
delimiter: "" delimiter: ""
precision: 3 precision: 3
human: human:
format: format:
precision: 1 precision: 1
delimiter: "" delimiter: ""
storage_units: storage_units:
format: "%n %u" format: "%n %u"
units: units:
byte: byte:
one: Byte one: Byte
other: Bytes other: Bytes
kb: "KB" kb: "KB"
mb: "MB" mb: "MB"
gb: "GB" gb: "GB"
tb: "TB" tb: "TB"
# Used in array.to_sentence. # Used in array.to_sentence.
support: support:
array: array:
sentence_connector: "and" sentence_connector: "and"
skip_last_comma: false skip_last_comma: false
activerecord: activerecord:
errors: errors:
messages: messages:
...@@ -116,9 +116,10 @@ bg: ...@@ -116,9 +116,10 @@ bg:
greater_than_start_date: "трябва да е след началната дата" greater_than_start_date: "трябва да е след началната дата"
not_same_project: "не е от същия проект" not_same_project: "не е от същия проект"
circular_dependency: "Тази релация ще доведе до безкрайна зависимост" circular_dependency: "Тази релация ще доведе до безкрайна зависимост"
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
actionview_instancetag_blank_option: Изберете actionview_instancetag_blank_option: Изберете
general_text_No: 'Не' general_text_No: 'Не'
general_text_Yes: 'Да' general_text_Yes: 'Да'
general_text_no: 'не' general_text_no: 'не'
...@@ -129,7 +130,7 @@ bg: ...@@ -129,7 +130,7 @@ bg:
general_csv_encoding: UTF-8 general_csv_encoding: UTF-8
general_pdf_encoding: UTF-8 general_pdf_encoding: UTF-8
general_first_day_of_week: '1' general_first_day_of_week: '1'
notice_account_updated: Профилът е обновен успешно. notice_account_updated: Профилът е обновен успешно.
notice_account_invalid_creditentials: Невалиден потребител или парола. notice_account_invalid_creditentials: Невалиден потребител или парола.
notice_account_password_updated: Паролата е успешно променена. notice_account_password_updated: Паролата е успешно променена.
...@@ -149,18 +150,18 @@ bg: ...@@ -149,18 +150,18 @@ bg:
notice_email_sent: "Изпратен e-mail на {{value}}" notice_email_sent: "Изпратен e-mail на {{value}}"
notice_email_error: "Грешка при изпращане на e-mail ({{value}})" notice_email_error: "Грешка при изпращане на e-mail ({{value}})"
notice_feeds_access_key_reseted: Вашия ключ за RSS достъп беше променен. notice_feeds_access_key_reseted: Вашия ключ за RSS достъп беше променен.
error_scm_not_found: Несъществуващ обект в хранилището. error_scm_not_found: Несъществуващ обект в хранилището.
error_scm_command_failed: "Грешка при опит за комуникация с хранилище: {{value}}" error_scm_command_failed: "Грешка при опит за комуникация с хранилище: {{value}}"
mail_subject_lost_password: "Вашата парола ({{value}})" mail_subject_lost_password: "Вашата парола ({{value}})"
mail_body_lost_password: 'За да смените паролата си, използвайте следния линк:' mail_body_lost_password: 'За да смените паролата си, използвайте следния линк:'
mail_subject_register: "Активация на профил ({{value}})" mail_subject_register: "Активация на профил ({{value}})"
mail_body_register: 'За да активирате профила си използвайте следния линк:' mail_body_register: 'За да активирате профила си използвайте следния линк:'
gui_validation_error: 1 грешка gui_validation_error: 1 грешка
gui_validation_error_plural: "{{count}} грешки" gui_validation_error_plural: "{{count}} грешки"
field_name: Име field_name: Име
field_description: Описание field_description: Описание
field_summary: Групиран изглед field_summary: Групиран изглед
...@@ -240,7 +241,7 @@ bg: ...@@ -240,7 +241,7 @@ bg:
field_redirect_existing_links: Пренасочване на съществуващи линкове field_redirect_existing_links: Пренасочване на съществуващи линкове
field_estimated_hours: Изчислено време field_estimated_hours: Изчислено време
field_default_value: Стойност по подразбиране field_default_value: Стойност по подразбиране
setting_app_title: Заглавие setting_app_title: Заглавие
setting_app_subtitle: Описание setting_app_subtitle: Описание
setting_welcome_text: Допълнителен текст setting_welcome_text: Допълнителен текст
...@@ -261,7 +262,7 @@ bg: ...@@ -261,7 +262,7 @@ bg:
setting_autologin: Автоматичен вход setting_autologin: Автоматичен вход
setting_date_format: Формат на датата setting_date_format: Формат на датата
setting_cross_project_issue_relations: Релации на задачи между проекти setting_cross_project_issue_relations: Релации на задачи между проекти
label_user: Потребител label_user: Потребител
label_user_plural: Потребители label_user_plural: Потребители
label_user_new: Нов потребител label_user_new: Нов потребител
...@@ -517,7 +518,7 @@ bg: ...@@ -517,7 +518,7 @@ bg:
label_added_time_by: "Публикувана от {{author}} преди {{age}}" label_added_time_by: "Публикувана от {{author}} преди {{age}}"
label_updated_time: "Обновена преди {{value}}" label_updated_time: "Обновена преди {{value}}"
label_jump_to_a_project: Проект... label_jump_to_a_project: Проект...
button_login: Вход button_login: Вход
button_submit: Прикачване button_submit: Прикачване
button_save: Запис button_save: Запис
...@@ -550,20 +551,20 @@ bg: ...@@ -550,20 +551,20 @@ bg:
button_unarchive: Разархивиране button_unarchive: Разархивиране
button_reset: Генериране наново button_reset: Генериране наново
button_rename: Преименуване button_rename: Преименуване
status_active: активен status_active: активен
status_registered: регистриран status_registered: регистриран
status_locked: заключен status_locked: заключен
text_select_mail_notifications: Изберете събития за изпращане на e-mail. text_select_mail_notifications: Изберете събития за изпращане на e-mail.
text_regexp_info: пр. ^[A-Z0-9]+$ text_regexp_info: пр. ^[A-Z0-9]+$
text_min_max_length_info: 0 - без ограничения text_min_max_length_info: 0 - без ограничения
text_project_destroy_confirmation: Сигурни ли сте, че искате да изтриете проекта и данните в него? text_project_destroy_confirmation: Сигурни ли сте, че искате да изтриете проекта и данните в него?
text_workflow_edit: Изберете роля и тракер за да редактирате работния процес text_workflow_edit: Изберете роля и тракер за да редактирате работния процес
text_are_you_sure: Сигурни ли сте? text_are_you_sure: Сигурни ли сте?
text_tip_task_begin_day: задача започваща този ден text_tip_issue_begin_day: задача започваща този ден
text_tip_task_end_day: задача завършваща този ден text_tip_issue_end_day: задача завършваща този ден
text_tip_task_begin_end_day: задача започваща и завършваща този ден text_tip_issue_begin_end_day: задача започваща и завършваща този ден
text_project_identifier_info: 'Позволени са малки букви (a-z), цифри и тирета.<br />Невъзможна промяна след запис.' text_project_identifier_info: 'Позволени са малки букви (a-z), цифри и тирета.<br />Невъзможна промяна след запис.'
text_caracters_maximum: "До {{count}} символа." text_caracters_maximum: "До {{count}} символа."
text_length_between: "От {{min}} до {{max}} символа." text_length_between: "От {{min}} до {{max}} символа."
...@@ -577,7 +578,7 @@ bg: ...@@ -577,7 +578,7 @@ bg:
text_issue_category_destroy_question: "Има задачи ({{count}}) обвързани с тази категория. Какво ще изберете?" text_issue_category_destroy_question: "Има задачи ({{count}}) обвързани с тази категория. Какво ще изберете?"
text_issue_category_destroy_assignments: Премахване на връзките с категорията text_issue_category_destroy_assignments: Премахване на връзките с категорията
text_issue_category_reassign_to: Преобвързване с категория text_issue_category_reassign_to: Преобвързване с категория
default_role_manager: Мениджър default_role_manager: Мениджър
default_role_developer: Разработчик default_role_developer: Разработчик
default_role_reporter: Публикуващ default_role_reporter: Публикуващ
...@@ -599,7 +600,7 @@ bg: ...@@ -599,7 +600,7 @@ bg:
default_priority_immediate: Веднага default_priority_immediate: Веднага
default_activity_design: Дизайн default_activity_design: Дизайн
default_activity_development: Разработка default_activity_development: Разработка
enumeration_issue_priorities: Приоритети на задачи enumeration_issue_priorities: Приоритети на задачи
enumeration_doc_categories: Категории документи enumeration_doc_categories: Категории документи
enumeration_activities: Дейности (time tracking) enumeration_activities: Дейности (time tracking)
...@@ -621,7 +622,6 @@ bg: ...@@ -621,7 +622,6 @@ bg:
text_user_mail_option: "За неизбраните проекти, ще получавате известия само за наблюдавани дейности или в които участвате (т.е. автор или назначени на мен)." text_user_mail_option: "За неизбраните проекти, ще получавате известия само за наблюдавани дейности или в които участвате (т.е. автор или назначени на мен)."
label_user_mail_option_selected: "За всички събития само в избраните проекти..." label_user_mail_option_selected: "За всички събития само в избраните проекти..."
label_user_mail_option_all: "За всяко събитие в проектите, в които участвам" label_user_mail_option_all: "За всяко събитие в проектите, в които участвам"
label_user_mail_option_none: "Само за наблюдавани или в които участвам (автор или назначени на мен)"
setting_emails_footer: Подтекст за e-mail setting_emails_footer: Подтекст за e-mail
label_float: Дробно label_float: Дробно
button_copy: Копиране button_copy: Копиране
...@@ -904,3 +904,14 @@ bg: ...@@ -904,3 +904,14 @@ bg:
notice_unable_delete_time_entry: Unable to delete time log entry. notice_unable_delete_time_entry: Unable to delete time log entry.
label_overall_spent_time: Overall spent time label_overall_spent_time: Overall spent time
field_time_entries: Log time field_time_entries: Log time
project_module_gantt: Gantt
project_module_calendar: Calendar
field_member_of_group: Member of Group
field_assigned_to_role: Member of Role
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of
setting_default_notification_option: Default notification option
label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
label_user_mail_option_only_assigned: Only for things I am assigned to
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