Merged r1307 to r1369 from trunk.

git-svn-id: http://redmine.rubyforge.org/svn/branches/0.7-stable@1370 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang committed 2008-04-28 10:36:12 +00:00
1 parent be071deae2
commit 10191813ec
139 files changed
+1936 -874

No files matched your search

+2
View File
@@ -56,6 +56,8 @@ class AccountController < ApplicationController
flash.now[:error] = l(:notice_account_invalid_creditentials)
end
end
rescue User::OnTheFlyCreationFailure
flash.now[:error] = 'Redmine could not retrieve the required information from the LDAP to create your account. Please, contact your Redmine administrator.'
end
# Log out current user and redirect to welcome page
+1
View File
@@ -150,6 +150,7 @@ class ApplicationController < ActionController::Base
def render_feed(items, options={})
@items = items || []
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end
+14 -4
View File
@@ -73,6 +73,8 @@ class IssuesController < ApplicationController
# Send html if the query is not valid
render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
end
rescue ActiveRecord::RecordNotFound
render_404
end
def changes
@@ -87,6 +89,8 @@ class IssuesController < ApplicationController
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
@@ -136,7 +140,9 @@ class IssuesController < ApplicationController
requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
# Check that the user is allowed to apply the requested status
@issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
@custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x,
:customized => @issue,
:value => (params[:custom_fields] ? params[:custom_fields][x.id.to_s] : nil)) }
@issue.custom_values = @custom_values
if @issue.save
attach_files(@issue, params[:attachments])
@@ -338,8 +344,8 @@ class IssuesController < ApplicationController
end
def preview
issue = @project.issues.find_by_id(params[:id])
@attachements = issue.attachments if issue
@issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
@attachements = @issue.attachments if @issue
@text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
render :partial => 'common/preview'
end
@@ -384,7 +390,10 @@ private
# Retrieve query from session or build a new query
def retrieve_query
if !params[:query_id].blank?
@query = Query.find(params[:query_id], :conditions => {:project_id => (@project ? @project.id : nil)})
cond = "project_id IS NULL"
cond << " OR project_id = #{@project.id}" if @project
@query = Query.find(params[:query_id], :conditions => cond)
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id}
else
if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
@@ -404,6 +413,7 @@ private
else
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters])
@query.project = @project
end
end
end
+11 -6
View File
@@ -66,20 +66,20 @@ class ProjectsController < ApplicationController
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
:order => 'name')
@project = Project.new(params[:project])
@project.enabled_module_names = Redmine::AccessControl.available_project_modules
if request.get?
@custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
@project.trackers = Tracker.all
@project.is_public = Setting.default_projects_public?
@project.enabled_module_names = Redmine::AccessControl.available_project_modules
else
@project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
@custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
@project.custom_values = @custom_values
@project.enabled_module_names = params[:enabled_modules]
if @project.save
@project.enabled_module_names = params[:enabled_modules]
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'admin', :action => 'projects'
end
end
end
end
@@ -204,7 +204,10 @@ class ProjectsController < ApplicationController
end
def list_files
@versions = @project.versions.sort.reverse
sort_init "#{Attachment.table_name}.filename", "asc"
sort_update
@versions = @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end
# Show changelog for @project
@@ -338,8 +341,9 @@ class ProjectsController < ApplicationController
:include => [:tracker, :status, :assigned_to, :priority, :project],
:conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?)) AND #{Issue.table_name}.tracker_id IN (#{@selected_tracker_ids.join(',')})", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
) unless @selected_tracker_ids.empty?
events += Version.find(:all, :include => :project,
:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
end
events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
@calendar.events = events
render :layout => false if request.xhr?
@@ -383,8 +387,9 @@ class ProjectsController < ApplicationController
:include => [:tracker, :status, :assigned_to, :priority, :project],
: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 and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
) unless @selected_tracker_ids.empty?
@events += Version.find(:all, :include => :project,
:conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
end
@events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
@events.sort! {|x,y| x.start_date <=> y.start_date }
if params[:format]=='pdf'
+18 -19
View File
@@ -18,19 +18,14 @@
class QueriesController < ApplicationController
layout 'base'
menu_item :issues
before_filter :find_project, :authorize
def index
@queries = @project.queries.find(:all,
:order => "name ASC",
:conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
end
before_filter :find_query, :except => :new
before_filter :find_optional_project, :only => :new
def new
@query = Query.new(params[:query])
@query.project = @project
@query.project = params[:query_is_for_all] ? nil : @project
@query.user = User.current
@query.is_public = false unless current_role.allowed_to?(:manage_public_queries)
@query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin?
@query.column_names = nil if params[:default_columns]
params[:fields].each do |field|
@@ -52,7 +47,8 @@ class QueriesController < ApplicationController
@query.add_filter(field, params[:operators][field], params[:values][field])
end if params[:fields]
@query.attributes = params[:query]
@query.is_public = false unless current_role.allowed_to?(:manage_public_queries)
@query.project = nil if params[:query_is_for_all]
@query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin?
@query.column_names = nil if params[:default_columns]
if @query.save
@@ -64,18 +60,21 @@ class QueriesController < ApplicationController
def destroy
@query.destroy if request.post?
redirect_to :controller => 'queries', :project_id => @project
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1
end
private
def find_project
if params[:id]
@query = Query.find(params[:id])
@project = @query.project
render_403 unless @query.editable_by?(User.current)
else
@project = Project.find(params[:project_id])
end
def find_query
@query = Query.find(params[:id])
@project = @query.project
render_403 unless @query.editable_by?(User.current)
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
@project = Project.find(params[:project_id]) if params[:project_id]
User.current.allowed_to?(:save_queries, @project, :global => true)
rescue ActiveRecord::RecordNotFound
render_404
end
+21 -6
View File
@@ -19,8 +19,8 @@ require 'SVG/Graph/Bar'
require 'SVG/Graph/BarHorizontal'
require 'digest/sha1'
class ChangesetNotFound < Exception
end
class ChangesetNotFound < Exception; end
class InvalidRevisionParam < Exception; end
class RepositoriesController < ApplicationController
layout 'base'
@@ -51,8 +51,8 @@ class RepositoriesController < ApplicationController
def show
# check if new revisions have been committed in the repository
@repository.fetch_changesets if Setting.autofetch_changesets?
# get entries for the browse frame
@entries = @repository.entries('')
# root entries
@entries = @repository.entries('', @rev)
# latest changesets
@changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
show_error_not_found unless @entries || @changesets.any?
@@ -65,7 +65,8 @@ class RepositoriesController < ApplicationController
if request.xhr?
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
else
show_error_not_found unless @entries
show_error_not_found and return unless @entries
render :action => 'browse'
end
rescue Redmine::Scm::Adapters::CommandFailed => e
show_error_command_failed(e.message)
@@ -95,6 +96,12 @@ class RepositoriesController < ApplicationController
end
def entry
@entry = @repository.scm.entry(@path, @rev)
show_error_not_found and return unless @entry
# If the entry is a dir, show the browser
browse and return if @entry.is_dir?
@content = @repository.scm.cat(@path, @rev)
show_error_not_found and return unless @content
if 'raw' == params[:format] || @content.is_binary_data?
@@ -135,7 +142,6 @@ class RepositoriesController < ApplicationController
end
def diff
@rev_to = params[:rev_to]
@diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
@@ -180,6 +186,8 @@ private
render_404
end
REV_PARAM_RE = %r{^[a-f0-9]*$}
def find_repository
@project = Project.find(params[:id])
@repository = @project.repository
@@ -187,8 +195,12 @@ private
@path = params[:path].join('/') unless params[:path].nil?
@path ||= ''
@rev = params[:rev]
@rev_to = params[:rev_to]
raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
rescue ActiveRecord::RecordNotFound
render_404
rescue InvalidRevisionParam
show_error_not_found
end
def show_error_not_found
@@ -255,6 +267,9 @@ private
commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
# Remove email adress in usernames
fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
graph = SVG::Graph::BarHorizontal.new(
:height => 300,
:width => 500,
+123 -110
View File
@@ -26,6 +26,8 @@ class TimelogController < ApplicationController
include SortHelper
helper :issues
include TimelogHelper
helper :custom_fields
include CustomFieldsHelper
def report
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
@@ -45,37 +47,40 @@ class TimelogController < ApplicationController
:label => :label_tracker},
'activity' => {:sql => "#{TimeEntry.table_name}.activity_id",
:klass => Enumeration,
:label => :label_activity}
:label => :label_activity},
'issue' => {:sql => "#{TimeEntry.table_name}.issue_id",
:klass => Issue,
:label => :label_issue}
}
# Add list and boolean custom fields as available criterias
@project.all_custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM custom_values c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = issues.id)",
:format => cf.field_format,
:label => cf.name}
end
@criterias = params[:criterias] || []
@criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria}
@criterias.uniq!
@criterias = @criterias[0,3]
@columns = (params[:period] && %w(year month week).include?(params[:period])) ? params[:period] : 'month'
@columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month'
if params[:date_from]
begin; @date_from = params[:date_from].to_date; rescue; end
end
if params[:date_to]
begin; @date_to = params[:date_to].to_date; rescue; end
end
@date_from ||= Date.civil(Date.today.year, 1, 1)
@date_to ||= (Date.civil(Date.today.year, Date.today.month, 1) >> 1) - 1
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 = "SELECT #{sql_select}, tyear, tmonth, tweek, SUM(hours) AS hours"
sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours"
sql << " FROM #{TimeEntry.table_name}"
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"
sql << " WHERE (%s)" % @project.project_condition(Setting.display_subprojects_issues?)
sql << " AND (%s)" % Project.allowed_to_condition(User.current, :view_time_entries)
sql << " AND spent_on BETWEEN '%s' AND '%s'" % [ActiveRecord::Base.connection.quoted_date(@date_from.to_time), ActiveRecord::Base.connection.quoted_date(@date_to.to_time)]
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek"
sql << " AND spent_on BETWEEN '%s' AND '%s'" % [ActiveRecord::Base.connection.quoted_date(@from.to_time), ActiveRecord::Base.connection.quoted_date(@to.to_time)]
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
@hours = ActiveRecord::Base.connection.select_all(sql)
@@ -87,36 +92,122 @@ class TimelogController < ApplicationController
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}
end
@periods = []
date_from = @date_from
# 100 columns max
while date_from < @date_to && @periods.length < 100
case @columns
when 'year'
@periods << "#{date_from.year}"
date_from = date_from >> 12
when 'month'
@periods << "#{date_from.year}-#{date_from.month}"
date_from = date_from >> 1
when 'week'
@periods << "#{date_from.year}-#{date_from.cweek}"
date_from = date_from + 7
@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
render :layout => false if request.xhr?
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.csv { send_data(report_to_csv(@criterias, @periods, @hours).read, :type => 'text/csv; header=present', :filename => 'timelog.csv') }
end
end
def details
sort_init 'spent_on', 'desc'
sort_update
cond = ARCondition.new
cond << (@issue.nil? ? @project.project_condition(Setting.display_subprojects_issues?) :
["#{TimeEntry.table_name}.issue_id = ?", @issue.id])
retrieve_date_range
cond << ['spent_on BETWEEN ? AND ?', @from, @to]
TimeEntry.visible_by(User.current) do
respond_to do |format|
format.html {
# Paginate results
@entry_count = TimeEntry.count(:include => :project, :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
@total_hours = TimeEntry.sum(:hours, :include => :project, :conditions => cond.conditions).to_f
render :layout => !request.xhr?
}
format.csv {
# Export all entries
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:conditions => cond.conditions,
:order => sort_clause)
send_data(entries_to_csv(@entries).read, :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
end
end
def edit
render_403 and return if @time_entry && !@time_entry.editable_by?(User.current)
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_to(params[:back_url] || {:action => 'details', :project_id => @time_entry.project})
return
end
@activities = Enumeration::get_values('ACTI')
end
def destroy
render_404 and return unless @time_entry
render_403 and return unless @time_entry.editable_by?(User.current)
@time_entry.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
rescue RedirectBackError
redirect_to :action => 'details', :project_id => @time_entry.project
end
private
def find_project
if params[:id]
@time_entry = TimeEntry.find(params[:id])
@project = @time_entry.project
elsif params[:issue_id]
@issue = Issue.find(params[:issue_id])
@project = @issue.project
elsif params[:project_id]
@project = Project.find(params[:project_id])
else
render_404
return false
end
rescue ActiveRecord::RecordNotFound
render_404
end
# Retrieves the date range based on predefined ranges or specific from/to param dates
def retrieve_date_range
@free_period = false
@from, @to = nil, nil
@@ -157,85 +248,7 @@ class TimelogController < ApplicationController
end
@from, @to = @to, @from if @from && @to && @from > @to
cond = ARCondition.new
cond << (@issue.nil? ? @project.project_condition(Setting.display_subprojects_issues?) :
["#{TimeEntry.table_name}.issue_id = ?", @issue.id])
if @from
if @to
cond << ['spent_on BETWEEN ? AND ?', @from, @to]
else
cond << ['spent_on >= ?', @from]
end
elsif @to
cond << ['spent_on <= ?', @to]
end
TimeEntry.visible_by(User.current) do
respond_to do |format|
format.html {
# Paginate results
@entry_count = TimeEntry.count(:include => :project, :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
@total_hours = TimeEntry.sum(:hours, :include => :project, :conditions => cond.conditions).to_f
render :layout => !request.xhr?
}
format.csv {
# Export all entries
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
:conditions => cond.conditions,
:order => sort_clause)
send_data(entries_to_csv(@entries).read, :type => 'text/csv; header=present', :filename => 'timelog.csv')
}
end
end
end
def edit
render_403 and return if @time_entry && !@time_entry.editable_by?(User.current)
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'details', :project_id => @time_entry.project
return
end
@activities = Enumeration::get_values('ACTI')
end
def destroy
render_404 and return unless @time_entry
render_403 and return unless @time_entry.editable_by?(User.current)
@time_entry.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
rescue RedirectBackError
redirect_to :action => 'details', :project_id => @time_entry.project
end
private
def find_project
if params[:id]
@time_entry = TimeEntry.find(params[:id])
@project = @time_entry.project
elsif params[:issue_id]
@issue = Issue.find(params[:issue_id])
@project = @issue.project
elsif params[:project_id]
@project = Project.find(params[:project_id])
else
render_404
return false
end
rescue ActiveRecord::RecordNotFound
render_404
@from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => @project.project_condition(Setting.display_subprojects_issues?)) || Date.today) - 1
@to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => @project.project_condition(Setting.display_subprojects_issues?)) || Date.today)
end
end
+2 -1
View File
@@ -83,7 +83,8 @@ class UsersController < ApplicationController
end
if @user.update_attributes(params[:user])
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'list'
# Give a string to redirect_to otherwise it would use status param as the response code
redirect_to(url_for(:action => 'list', :status => params[:status], :page => params[:page]))
end
end
@auth_sources = AuthSource.find(:all)
+21 -2
View File
@@ -207,8 +207,10 @@ module ApplicationHelper
rf = Regexp.new(filename, Regexp::IGNORECASE)
# search for the picture in attachments
if found = attachments.detect { |att| att.filename =~ rf }
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found.id
"!#{style}#{image_url}!"
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
alt = desc.blank? ? nil : "(#{desc})"
"!#{style}#{image_url}#{alt}!"
else
"!#{style}#{filename}!"
end
@@ -425,6 +427,10 @@ module ApplicationHelper
form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
end
def back_url_hidden_field_tag
hidden_field_tag 'back_url', (params[:back_url] || request.env['HTTP_REFERER'])
end
def check_all_links(form_name)
link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
" | " +
@@ -463,9 +469,22 @@ module ApplicationHelper
end
def calendar_for(field_id)
include_calendar_headers_tags
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
end
def include_calendar_headers_tags
unless @calendar_headers_tags_included
@calendar_headers_tags_included = true
content_for :header_tags do
javascript_include_tag('calendar/calendar') +
javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
javascript_include_tag('calendar/calendar-setup') +
stylesheet_link_tag('calendar')
end
end
end
def wikitoolbar_for(field_id)
return '' unless Setting.text_formatting == 'textile'
+13
View File
@@ -32,6 +32,19 @@ module IssuesHelper
"<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
end
def sidebar_queries
unless @sidebar_queries
# User can see public queries and his own queries
visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
# Project specific queries and global queries
visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
@sidebar_queries = Query.find(:all,
:order => "name ASC",
:conditions => visible.conditions)
end
@sidebar_queries
end
def show_detail(detail, no_html=false)
case detail.property
+5 -2
View File
@@ -58,8 +58,11 @@ module RepositoriesHelper
end
def with_leading_slash(path)
path ||= ''
path.starts_with?('/') ? path : "/#{path}"
path.to_s.starts_with?('/') ? path : "/#{path}"
end
def without_leading_slash(path)
path.gsub(%r{^/+}, '')
end
def subversion_field_tags(form, repository)
+56
View File
@@ -76,4 +76,60 @@ module TimelogHelper
export.rewind
export
end
def format_criteria_value(criteria, value)
value.blank? ? l(:label_none) : ((k = @available_criterias[criteria][:klass]) ? k.find_by_id(value.to_i) : format_value(value, @available_criterias[criteria][:format]))
end
def report_to_csv(criterias, periods, hours)
export = StringIO.new
CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
# Column headers
headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
headers += periods
headers << l(:label_total)
csv << headers.collect {|c| to_utf8(c) }
# Content
report_criteria_to_csv(csv, criterias, periods, hours)
# Total row
row = [ l(:label_total) ] + [''] * (criterias.size - 1)
total = 0
periods.each do |period|
sum = sum_hours(select_hours(hours, @columns, period.to_s))
total += sum
row << (sum > 0 ? "%.2f" % sum : '')
end
row << "%.2f" %total
csv << row
end
export.rewind
export
end
def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
hours_for_value = select_hours(hours, criterias[level], value)
next if hours_for_value.empty?
row = [''] * level
row << to_utf8(format_criteria_value(criterias[level], value))
row += [''] * (criterias.length - level - 1)
total = 0
periods.each do |period|
sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
total += sum
row << (sum > 0 ? "%.2f" % sum : '')
end
row << "%.2f" %total
csv << row
if criterias.length > level + 1
report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
end
end
end
def to_utf8(s)
@ic ||= Iconv.new(l(:general_csv_encoding), 'UTF-8')
begin; @ic.iconv(s.to_s); rescue; s.to_s; end
end
end
+12
View File
@@ -22,4 +22,16 @@ module UsersHelper
[l(:status_registered), 2],
[l(:status_locked), 3]], selected)
end
def change_status_link(user)
url = {:action => 'edit', :id => user, :page => params[:page], :status => params[:status]}
if user.locked?
link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock'
elsif user.registered?
link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :post, :class => 'icon icon-unlock'
else
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :post, :class => 'icon icon-lock'
end
end
end
+48 -49
View File
@@ -35,48 +35,48 @@ class Attachment < ActiveRecord::Base
errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes
end
def file=(incomming_file)
unless incomming_file.nil?
@temp_file = incomming_file
if @temp_file.size > 0
self.filename = sanitize_filename(@temp_file.original_filename)
self.disk_filename = DateTime.now.strftime("%y%m%d%H%M%S") + "_" + self.filename
self.content_type = @temp_file.content_type.to_s.chomp
self.filesize = @temp_file.size
end
end
end
def file=(incoming_file)
unless incoming_file.nil?
@temp_file = incoming_file
if @temp_file.size > 0
self.filename = sanitize_filename(@temp_file.original_filename)
self.disk_filename = DateTime.now.strftime("%y%m%d%H%M%S") + "_" + self.filename
self.content_type = @temp_file.content_type.to_s.chomp
self.filesize = @temp_file.size
end
end
end
def file
nil
end
# Copy temp file to its final location
def before_save
if @temp_file && (@temp_file.size > 0)
logger.debug("saving '#{self.diskfile}'")
File.open(diskfile, "wb") do |f|
f.write(@temp_file.read)
end
self.digest = Digest::MD5.hexdigest(File.read(diskfile))
end
# Don't save the content type if it's longer than the authorized length
if self.content_type && self.content_type.length > 255
self.content_type = nil
end
end
# Deletes file on the disk
def after_destroy
if self.filename?
File.delete(diskfile) if File.exist?(diskfile)
end
end
# Returns file's location on disk
def diskfile
"#{@@storage_path}/#{self.disk_filename}"
end
def file
nil
end
# Copy temp file to its final location
def before_save
if @temp_file && (@temp_file.size > 0)
logger.debug("saving '#{self.diskfile}'")
File.open(diskfile, "wb") do |f|
f.write(@temp_file.read)
end
self.digest = Digest::MD5.hexdigest(File.read(diskfile))
end
# Don't save the content type if it's longer than the authorized length
if self.content_type && self.content_type.length > 255
self.content_type = nil
end
end
# Deletes file on the disk
def after_destroy
if self.filename?
File.delete(diskfile) if File.exist?(diskfile)
end
end
# Returns file's location on disk
def diskfile
"#{@@storage_path}/#{self.disk_filename}"
end
def increment_download
increment!(:downloads)
@@ -87,18 +87,17 @@ class Attachment < ActiveRecord::Base
end
def image?
self.filename =~ /\.(jpeg|jpg|gif|png)$/i
self.filename =~ /\.(jpe?g|gif|png)$/i
end
private
def sanitize_filename(value)
# get only the filename, not the whole path
just_filename = value.gsub(/^.*(\\|\/)/, '')
# NOTE: File.basename doesn't work right with Windows paths on Unix
# INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
# get only the filename, not the whole path
just_filename = value.gsub(/^.*(\\|\/)/, '')
# NOTE: File.basename doesn't work right with Windows paths on Unix
# INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
# Finally, replace all non alphanumeric, underscore or periods with underscore
@filename = just_filename.gsub(/[^\w\.\-]/,'_')
# Finally, replace all non alphanumeric, hyphens or periods with underscore
@filename = just_filename.gsub(/[^\w\.\-]/,'_')
end
end
+4
View File
@@ -35,6 +35,10 @@ class Changeset < ActiveRecord::Base
validates_uniqueness_of :revision, :scope => :repository_id
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
end
def comments=(comment)
write_attribute(:comments, comment.strip)
end
+11 -1
View File
@@ -93,7 +93,11 @@ class Issue < ActiveRecord::Base
self.priority = nil
write_attribute(:priority_id, pid)
end
def estimated_hours=(h)
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
end
def validate
if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
errors.add :due_date, :activerecord_error_not_a_date
@@ -153,6 +157,8 @@ class Issue < ActiveRecord::Base
# Close duplicates if the issue was closed
if @issue_before_change && !@issue_before_change.closed? && self.closed?
duplicates.each do |duplicate|
# Reload is need in case the duplicate was updated by a previous duplicate
duplicate.reload
# Don't re-close it if it's already closed
next if duplicate.closed?
# Same user and notes
@@ -237,4 +243,8 @@ class Issue < ActiveRecord::Base
yield
end
end
def to_s
"#{tracker} ##{id}: #{subject}"
end
end
+1
View File
@@ -33,6 +33,7 @@ class Journal < ActiveRecord::Base
acts_as_event :title => Proc.new {|o| "#{o.issue.tracker.name} ##{o.issue.id}: #{o.issue.subject}" + ((s = o.new_status) ? " (#{s})" : '') },
:description => :notes,
:author => :user,
:type => Proc.new {|o| (s = o.new_status) && s.is_closed? ? 'issue-closed' : 'issue-edit' },
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
def save
+1
View File
@@ -28,6 +28,7 @@ class Message < ActiveRecord::Base
:date_column => 'created_on'
acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
:description => :content,
:type => Proc.new {|o| o.parent_id.nil? ? 'message' : 'reply'},
:url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id, :id => o.id}}
attr_protected :locked, :sticky
+2
View File
@@ -21,6 +21,8 @@ class MessageObserver < ActiveRecord::Observer
recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author && m.author.active?}
# send notification to the board watchers
recipients += message.board.watcher_recipients
# send notification to project members who want to be notified
recipients += message.board.project.recipients
recipients = recipients.compact.uniq
Mailer.deliver_message_posted(message, recipients) if !recipients.empty? && Setting.notified_events.include?('message_posted')
end
+6 -4
View File
@@ -33,7 +33,7 @@ class Project < ActiveRecord::Base
has_many :documents, :dependent => :destroy
has_many :news, :dependent => :delete_all, :include => :author
has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
has_many :boards, :order => "position ASC"
has_many :boards, :dependent => :destroy, :order => "position ASC"
has_one :repository, :dependent => :destroy
has_many :changesets, :through => :repository
has_one :wiki, :dependent => :destroy
@@ -75,12 +75,14 @@ class Project < ActiveRecord::Base
conditions = nil
if include_subprojects && !active_children.empty?
ids = [id] + active_children.collect {|c| c.id}
conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
conditions = ["#{Project.table_name}.id IN (#{ids.join(',')})"]
end
conditions ||= ["#{Issue.table_name}.project_id = ?", id]
conditions ||= ["#{Project.table_name}.id = ?", id]
# Quick and dirty fix for Rails 2 compatibility
Issue.send(:with_scope, :find => { :conditions => conditions }) do
yield
Version.send(:with_scope, :find => { :conditions => conditions }) do
yield
end
end
end
+13 -6
View File
@@ -116,6 +116,11 @@ class Query < ActiveRecord::Base
set_language_if_valid(User.current.language)
end
def after_initialize
# Store the fact that project is nil (used in #editable_by?)
@is_for_all = project.nil?
end
def validate
filters.each_key do |field|
errors.add label_for(field), :activerecord_error_blank unless
@@ -128,8 +133,10 @@ class Query < ActiveRecord::Base
def editable_by?(user)
return false unless user
return true if !is_public && self.user_id == user.id
is_public && user.allowed_to?(:manage_public_queries, project)
# Admin can edit them all and regular users can edit their private queries
return true if user.admin? || (!is_public && self.user_id == user.id)
# Members can not edit public queries that are for all project (only admin is allowed to)
is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
end
def available_filters
@@ -139,7 +146,7 @@ class Query < ActiveRecord::Base
@available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
"tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
"priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect{|s| [s.name, s.id.to_s] } },
"priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI'], :order => 'position').collect{|s| [s.name, s.id.to_s] } },
"subject" => { :type => :text, :order => 8 },
"created_on" => { :type => :date_past, :order => 9 },
"updated_on" => { :type => :date_past, :order => 10 },
@@ -294,7 +301,7 @@ class Query < ActiveRecord::Base
# custom field
db_table = CustomValue.table_name
db_field = 'value'
sql << "#{Issue.table_name}.id IN (SELECT #{db_table}.customized_id FROM #{db_table} where #{db_table}.customized_type='Issue' AND #{db_table}.customized_id=#{Issue.table_name}.id AND #{db_table}.custom_field_id=#{$1} AND "
sql << "#{Issue.table_name}.id IN (SELECT #{Issue.table_name}.id FROM #{Issue.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='Issue' AND #{db_table}.customized_id=#{Issue.table_name}.id AND #{db_table}.custom_field_id=#{$1} WHERE "
else
# regular field
db_table = Issue.table_name
@@ -313,9 +320,9 @@ class Query < ActiveRecord::Base
when "!"
sql = sql + "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
when "!*"
sql = sql + "#{db_table}.#{db_field} IS NULL"
sql = sql + "#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} = ''"
when "*"
sql = sql + "#{db_table}.#{db_field} IS NOT NULL"
sql = sql + "#{db_table}.#{db_field} IS NOT NULL AND #{db_table}.#{db_field} <> ''"
when ">="
sql = sql + "#{db_table}.#{db_field} >= #{v.first.to_i}"
when "<="
+12 -5
View File
@@ -35,7 +35,8 @@ class Repository::Cvs < Repository
end
def entries(path=nil, identifier=nil)
entries=scm.entries(path, identifier)
rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
if entries
entries.each() do |entry|
unless entry.lastrev.nil? || entry.lastrev.identifier
@@ -137,12 +138,18 @@ class Repository::Cvs < Repository
end
# Renumber new changesets in chronological order
c = changesets.find(:first, :order => 'committed_on DESC, id DESC', :conditions => "revision NOT LIKE '_%'")
next_rev = c.nil? ? 1 : (c.revision.to_i + 1)
changesets.find(:all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE '_%'").each do |changeset|
changeset.update_attribute :revision, next_rev
next_rev += 1
changeset.update_attribute :revision, next_revision_number
end
end # transaction
end
private
# Returns the next revision number to assign to a CVS changeset
def next_revision_number
# Need to retrieve existing revision numbers to sort them as integers
@current_revision_number ||= (connection.select_values("SELECT revision FROM #{Changeset.table_name} WHERE repository_id = #{id} AND revision NOT LIKE '_%'").collect(&:to_i).max || 0)
@current_revision_number += 1
end
end
+2 -1
View File
@@ -29,7 +29,8 @@ class Repository::Darcs < Repository
end
def entries(path=nil, identifier=nil)
entries=scm.entries(path, identifier)
patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
entries = scm.entries(path, patch.nil? ? nil : patch.scmid)
if entries
entries.each do |entry|
# Search the DB for the entry's last change
+8 -1
View File
@@ -34,6 +34,11 @@ class Repository::Mercurial < Repository
if entries
entries.each do |entry|
next unless entry.is_file?
# Set the filesize unless browsing a specific revision
if identifier.nil?
full_path = File.join(root_url, entry.path)
entry.size = File.stat(full_path).size if File.file?(full_path)
end
# Search the DB for the entry's last change
change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
if change
@@ -53,7 +58,9 @@ class Repository::Mercurial < Repository
# latest revision found in database
db_revision = latest_changeset ? latest_changeset.revision.to_i : -1
# latest revision in the repository
scm_revision = scm_info.lastrev.identifier.to_i
latest_revision = scm_info.lastrev
return if latest_revision.nil?
scm_revision = latest_revision.identifier.to_i
if db_revision < scm_revision
logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
identifier_from = db_revision + 1
+5 -1
View File
@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Copyright (C) 2006-2008 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
@@ -39,6 +39,10 @@ class TimeEntry < ActiveRecord::Base
errors.add :issue_id, :activerecord_error_invalid if (issue_id && !issue) || (issue && project!=issue.project)
end
def hours=(h)
write_attribute :hours, (h.is_a?(String) ? h.to_hours : h)
end
# tyear, tmonth, tweek assigned where setting spent_on attributes
# these attributes make time aggregations easier
def spent_on=(date)
+29 -15
View File
@@ -18,6 +18,9 @@
require "digest/sha1"
class User < ActiveRecord::Base
class OnTheFlyCreationFailure < Exception; end
# Account statuses
STATUS_ANONYMOUS = 0
STATUS_ACTIVE = 1
@@ -105,15 +108,17 @@ class User < ActiveRecord::Base
onthefly.language = Setting.default_language
if onthefly.save
user = find(:first, :conditions => ["login=?", login])
logger.info("User '#{user.login}' created on the fly.") if logger
logger.info("User '#{user.login}' created from the LDAP") if logger
else
logger.error("User '#{onthefly.login}' found in LDAP but could not be created (#{onthefly.errors.full_messages.join(', ')})") if logger
raise OnTheFlyCreationFailure.new
end
end
end
user.update_attribute(:last_login_on, Time.now) if user
user
rescue => text
raise text
rescue => text
raise text
end
# Return user's full name for display
@@ -222,17 +227,26 @@ class User < ActiveRecord::Base
# action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
def allowed_to?(action, project)
# No action allowed on archived projects
return false unless project.active?
# No action allowed on disabled modules
return false unless project.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
role = role_for_project(project)
return false unless role
role.allowed_to?(action) && (project.is_public? || role.member?)
def allowed_to?(action, project, options={})
if project
# No action allowed on archived projects
return false unless project.active?
# No action allowed on disabled modules
return false unless project.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
role = role_for_project(project)
return false unless role
role.allowed_to?(action) && (project.is_public? || role.member?)
elsif options[:global]
# authorize if user has at least one role that has this permission
roles = memberships.collect {|m| m.role}.uniq
roles.detect {|r| r.allowed_to?(action)}
else
false
end
end
def self.current=(user)
+1
View File
@@ -32,6 +32,7 @@ class WikiContent < ActiveRecord::Base
acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"},
:description => :comments,
:datetime => :updated_on,
:type => 'wiki-page',
:url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
def text=(plain)
-7
View File
@@ -35,10 +35,3 @@
<%= submit_tag l(:button_submit) %>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
+5 -2
View File
@@ -19,12 +19,15 @@ while day <= calendar.enddt %>
elsif day == i.due_date
image_tag('arrow_to.png')
end %>
<%= h("#{i.project.name} -") unless @project && @project == i.project %>
<%= h("#{i.project} -") unless @project && @project == i.project %>
<%= link_to_issue i %>: <%= h(truncate(i.subject, 30)) %>
<span class="tip"><%= render_issue_tooltip i %></span>
</div>
<% else %>
<%= link_to_version i, :class => "icon icon-package" %>
<span class="icon icon-package">
<%= h("#{i.project} -") unless @project && @project == i.project %>
<%= link_to_version i%>
</span>
<% end %>
<% end %>
</td>
-7
View File
@@ -49,10 +49,3 @@
<% end %>
<%= wikitoolbar_for 'issue_description' %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
+1 -1
View File
@@ -1,5 +1,5 @@
<% for journal in journals %>
<div id="change-<%= journal.id %>">
<div id="change-<%= journal.id %>" class="journal">
<h4><div style="float:right;"><%= link_to "##{journal.indice}", :anchor => "note-#{journal.indice}" %></div>
<%= content_tag('a', '', :name => "note-#{journal.indice}")%>
<%= format_time(journal.created_on) %> - <%= journal.user.name %></h4>
+6 -5
View File
@@ -1,13 +1,14 @@
<h3><%= l(:label_issue_plural) %></h3>
<%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
<% if @project %>
<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
<%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
<% end %>
<% unless sidebar_queries.empty? -%>
<h3><%= l(:label_query_plural) %></h3>
<% queries = @project.queries.find(:all,
:order => "name ASC",
:conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
queries.each do |query| %>
<% sidebar_queries.each do |query| -%>
<%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
<% end %>
<% end -%>
<% end -%>
-7
View File
@@ -47,10 +47,3 @@
<p><%= submit_tag l(:button_submit) %>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
+2 -2
View File
@@ -18,7 +18,7 @@
:update => "content",
}, :class => 'icon icon-reload' %>
<% if current_role && current_role.allowed_to?(:save_queries) %>
<% if User.current.allowed_to?(:save_queries, @project, :global => true) %>
<%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
<% end %>
</p>
@@ -54,7 +54,7 @@
<% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %>
<% end if @project%>
<% end %>
<% 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)) %>
+1 -1
View File
@@ -8,7 +8,7 @@
</div>
<%= submit_tag l(:button_create) %>
<%= link_to_remote l(:label_preview),
{ :url => { :controller => 'issues', :action => 'preview', :project_id => @project, :id => @issue },
{ :url => { :controller => 'issues', :action => 'preview', :project_id => @project },
:method => 'post',
:update => 'preview',
:with => "Form.serialize('issue-form')",
+1 -1
View File
@@ -13,7 +13,7 @@
<h3><%=h @issue.subject %></h3>
<p class="author">
<%= authoring @issue.created_on, @issue.author %>.
<%= l(:label_updated_time, distance_of_time_in_words(Time.now, @issue.updated_on)) if @issue.created_on != @issue.updated_on %>.
<%= l(:label_updated_time, distance_of_time_in_words(Time.now, @issue.updated_on)) + '.' if @issue.created_on != @issue.updated_on %>
</p>
<table width="100%">
+1 -1
View File
@@ -26,7 +26,7 @@
</div>
<div class="message reply">
<h4><%=h message.subject %> - <%= authoring message.created_on, message.author %></h4>
<div class="wiki"><%= textilizable message.content %></div>
<div class="wiki"><%= textilizable message, :content, :attachments => message.attachments %></div>
<%= link_to_attachments message.attachments, :no_author => true %>
</div>
<% end %>
+1 -1
View File
@@ -25,7 +25,7 @@
<div id="preview" class="wiki"></div>
</div>
<p><em><% unless @news.summary.empty? %><%=h @news.summary %><br /><% end %>
<p><em><% unless @news.summary.blank? %><%=h @news.summary %><br /><% end %>
<span class="author"><%= authoring @news.created_on, @news.author %></span></em></p>
<div class="wiki">
<%= textilizable(@news.description) %>
-8
View File
@@ -46,11 +46,3 @@
</fieldset>
<% end %>
<!--[eoform:project]-->
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
+1 -1
View File
@@ -6,7 +6,7 @@
<h3><%= format_activity_day(day) %></h3>
<dl>
<% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
<dt class="<%= e.class.name.downcase %>"><span class="time"><%= format_time(e.event_datetime, false) %></span>
<dt class="<%= e.event_type %>"><span class="time"><%= format_time(e.event_datetime, false) %></span>
<%= content_tag('span', h(e.project), :class => 'project') if @project.nil? || @project != e.project %> <%= link_to h(truncate(e.event_title, 100)), e.event_url %></dt>
<dd><% unless e.event_description.blank? -%>
<span class="description"><%= format_activity_description(e.event_description) %></span><br />
+9 -7
View File
@@ -1,14 +1,16 @@
<h2><%=l(:label_confirmation)%></h2>
<div class="box">
<center>
<p><strong><%=h @project_to_destroy.name %></strong><br />
<%=l(:text_project_destroy_confirmation)%></p>
<div class="warning">
<p><strong><%=h @project_to_destroy %></strong><br />
<%=l(:text_project_destroy_confirmation)%>
<% if @project_to_destroy.children.any? %>
<br /><%= l(:text_subprojects_destroy_warning, content_tag('strong', h(@project_to_destroy.children.sort.collect{|p| p.to_s}.join(', ')))) %>
<% end %>
</p>
<p>
<% form_tag({:controller => 'projects', :action => 'destroy', :id => @project_to_destroy}) do %>
<%= hidden_field_tag "confirm", 1 %>
<label><%= check_box_tag 'confirm', 1 %> <%= l(:general_text_Yes) %></label>
<%= submit_tag l(:button_delete) %>
<% end %>
</p>
</center>
</div>
</div>
+7 -3
View File
@@ -70,10 +70,13 @@ top = headers_height + 8
@events.each do |i| %>
<div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
<% if i.is_a? Issue %>
<%= h("#{i.project.name} -") unless @project && @project == i.project %>
<%= h("#{i.project} -") unless @project && @project == i.project %>
<%= link_to_issue i %>: <%=h i.subject %>
<% else %>
<%= link_to_version i, :class => "icon icon-package" %>
<span class="icon icon-package">
<%= h("#{i.project} -") unless @project && @project == i.project %>
<%= link_to_version i %>
</span>
<% end %>
</small></div>
<% top = top + 20
@@ -197,7 +200,8 @@ top = headers_height + 10
%>
<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><%= i.name %></strong>
<%= h("#{i.project} -") unless @project && @project == i.project %>
<strong><%=h i %></strong>
</div>
<% end %>
<% top = top + 20
+4 -4
View File
@@ -9,10 +9,10 @@
<table class="list">
<thead><tr>
<th><%=l(:field_version)%></th>
<th><%=l(:field_filename)%></th>
<th><%=l(:label_date)%></th>
<th><%=l(:field_filesize)%></th>
<th><%=l(:label_downloads_abbr)%></th>
<%= sort_header_tag("#{Attachment.table_name}.filename", :caption => l(:field_filename)) %>
<%= sort_header_tag("#{Attachment.table_name}.created_on", :caption => l(:label_date), :default_order => 'desc') %>
<%= sort_header_tag("#{Attachment.table_name}.filesize", :caption => l(:field_filesize), :default_order => 'desc') %>
<%= sort_header_tag("#{Attachment.table_name}.downloads", :caption => l(:label_downloads_abbr), :default_order => 'desc') %>
<th>MD5</th>
<% if delete_allowed %><th></th><% end %>
</tr></thead>
+6 -6
View File
@@ -59,19 +59,19 @@ function toggle_multi_select(field) {
<table width="100%">
<tr>
<td>
<table style="padding:0;">
<table>
<% query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.each do |filter| %>
<% field = filter[0]
options = filter[1] %>
<tr <%= 'style="display:none;"' unless query.has_filter?(field) %> id="tr_<%= field %>">
<td valign="top" style="width:200px;">
<tr <%= 'style="display:none;"' unless query.has_filter?(field) %> id="tr_<%= field %>" class="filter">
<td style="width:200px;">
<%= check_box_tag 'fields[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %>
<label for="cb_<%= field %>"><%= filter[1][:name] || l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) %></label>
</td>
<td valign="top" style="width:150px;">
<td style="width:150px;">
<%= select_tag "operators[#{field}]", options_for_select(operators_for_select(options[:type]), query.operator_for(field)), :id => "operators_#{field}", :onchange => "toggle_operator('#{field}');", :class => "select-small", :style => "vertical-align: top;" %>
</td>
<td valign="top">
<td>
<div id="div_values_<%= field %>" style="display:none;">
<% case options[:type]
when :list, :list_optional, :list_status, :list_subprojects %>
@@ -93,7 +93,7 @@ function toggle_multi_select(field) {
<% end %>
</table>
</td>
<td align="right" valign="top">
<td class="add-filter">
<%= l(:label_filter_add) %>:
<%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [ field[1][:name] || l(("field_"+field[0].to_s.gsub(/\_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact), :onchange => "add_filter();", :class => "select-small" %>
</td>
+8 -3
View File
@@ -6,11 +6,16 @@
<p><label for="query_name"><%=l(:field_name)%></label>
<%= text_field 'query', 'name', :size => 80 %></p>
<% if current_role.allowed_to?(:manage_public_queries) %>
<p><label for="query_is_public"><%=l(:field_is_public)%></label>
<%= check_box 'query', 'is_public' %></p>
<% if User.current.admin? || (@project && current_role.allowed_to?(:manage_public_queries)) %>
<p><label for="query_is_public"><%=l(:field_is_public)%></label>
<%= check_box 'query', 'is_public',
:onchange => (User.current.admin? ? nil : 'if (this.checked) {$("query_is_for_all").checked = false; $("query_is_for_all").disabled = true;} else {$("query_is_for_all").disabled = false;}') %></p>
<% end %>
<p><label for="query_is_for_all"><%=l(:field_is_for_all)%></label>
<%= check_box_tag 'query_is_for_all', 1, @query.project.nil?,
:disabled => (!@query.new_record? && (@query.project.nil? || (@query.is_public? && !User.current.admin?))) %></p>
<p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
<%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
:onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
+1 -1
View File
@@ -49,7 +49,7 @@
<td><div class="square action_<%= change.action %>"></div> <%= change.path %> <%= "(#{change.revision})" unless change.revision.blank? %></td>
<td align="right">
<% if change.action == "M" %>
<%= link_to l(:label_view_diff), :action => 'diff', :id => @project, :path => change.path, :rev => @changeset.revision %>
<%= link_to l(:label_view_diff), :action => 'diff', :id => @project, :path => without_leading_slash(change.path), :rev => @changeset.revision %>
<% end %>
</td>
</tr>
+28
View File
@@ -0,0 +1,28 @@
<fieldset id="filters"><legend><%= l(:label_date_range) %></legend>
<p>
<%= radio_button_tag 'period_type', '1', !@free_period %>
<%= select_tag 'period', options_for_period_select(params[:period]),
:onchange => 'this.form.onsubmit();',
:onfocus => '$("period_type_1").checked = true;' %>
</p>
<p>
<%= radio_button_tag 'period_type', '2', @free_period %>
<span onclick="$('period_type_2').checked = true;">
<%= l(:label_date_from) %>
<%= text_field_tag 'from', @from, :size => 10 %> <%= calendar_for('from') %>
<%= l(:label_date_to) %>
<%= text_field_tag 'to', @to, :size => 10 %> <%= calendar_for('to') %>
</span>
<%= submit_tag l(:button_apply), :name => nil %>
</p>
</fieldset>
<div class="tabs">
<% url_params = @free_period ? { :from => @from, :to => @to } : { :period => params[:period] } %>
<ul>
<li><%= link_to(l(:label_details), url_params.merge({:controller => 'timelog', :action => 'details', :project_id => @project }),
:class => (@controller.action_name == 'details' ? 'selected' : nil)) %></li>
<li><%= link_to(l(:label_report), url_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project}),
:class => (@controller.action_name == 'report' ? 'selected' : nil)) %></li>
</ul>
</div>
+3 -1
View File
@@ -1,5 +1,6 @@
<table class="list time-entries">
<thead>
<tr>
<%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %>
<%= sort_header_tag('user_id', :caption => l(:label_member)) %>
<%= sort_header_tag('activity_id', :caption => l(:label_activity)) %>
@@ -8,6 +9,7 @@
<th><%= l(:field_comments) %></th>
<%= sort_header_tag('hours', :caption => l(:field_hours)) %>
<th></th>
</tr>
</thead>
<tbody>
<% entries.each do |entry| -%>
@@ -35,5 +37,5 @@
</td>
</tr>
<% end -%>
</tbdoy>
</tbody>
</table>
+5 -3
View File
@@ -1,14 +1,16 @@
<% @hours.collect {|h| h[criterias[level]]}.uniq.each do |value| %>
<% @hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value| %>
<% hours_for_value = select_hours(hours, criterias[level], value) -%>
<% next if hours_for_value.empty? -%>
<tr class="<%= cycle('odd', 'even') %> <%= 'last-level' unless criterias.length > level+1 %>">
<%= '<td></td>' * level %>
<td><%= value.nil? ? l(:label_none) : @available_criterias[criterias[level]][:klass].find_by_id(value) %></td>
<td><%= format_criteria_value(criterias[level], value) %></td>
<%= '<td></td>' * (criterias.length - level - 1) -%>
<% total = 0 -%>
<% @periods.each do |period| -%>
<% sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s)) %>
<% sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s)); total += sum -%>
<td class="hours"><%= html_hours("%.2f" % sum) if sum > 0 %></td>
<% end -%>
<td class="hours"><%= html_hours("%.2f" % total) if total > 0 %></td>
</tr>
<% if criterias.length > level+1 -%>
<%= render(:partial => 'report_criteria', :locals => {:criterias => criterias, :hours => hours_for_value, :level => (level + 1)}) %>
+2 -24
View File
@@ -1,5 +1,4 @@
<div class="contextual">
<%= link_to(l(:label_report), {:controller => 'timelog', :action => 'report', :project_id => @project}, :class => 'icon icon-report') %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time' %>
</div>
@@ -12,23 +11,7 @@
<% form_remote_tag( :url => {}, :method => :get, :update => 'content' ) do %>
<%= hidden_field_tag 'project_id', params[:project_id] %>
<%= hidden_field_tag 'issue_id', params[:issue_id] if @issue %>
<fieldset><legend><%= l(:label_date_range) %></legend>
<p>
<%= radio_button_tag 'period_type', '1', !@free_period %>
<%= select_tag 'period', options_for_period_select(params[:period]),
:onchange => 'this.form.onsubmit();',
:onfocus => '$("period_type_1").checked = true;' %>
</p>
<p>
<%= radio_button_tag 'period_type', '2', @free_period %>
<%= l(:label_date_from) %>
<%= text_field_tag 'from', @from, :size => 10, :onfocus => '$("period_type_2").checked = true;' %> <%= calendar_for('from') %>
<%= l(:label_date_to) %>
<%= text_field_tag 'to', @to, :size => 10, :onfocus => '$("period_type_2").checked = true;' %> <%= calendar_for('to') %>
<%= submit_tag l(:button_apply), :name => nil, :onclick => '$("period_type_2").checked = true;' %>
</p>
</fieldset>
<%= render :partial => 'date_range' %>
<% end %>
<div class="total-hours">
@@ -45,9 +28,4 @@
</p>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
<% html_title l(:label_spent_time), l(:label_details) %>
+1 -7
View File
@@ -2,6 +2,7 @@
<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :project_id => @time_entry.project} do |f| %>
<%= error_messages_for 'time_entry' %>
<%= back_url_hidden_field_tag %>
<div class="box">
<p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
@@ -14,10 +15,3 @@
<%= submit_tag l(:button_save) %>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
+31 -31
View File
@@ -1,36 +1,32 @@
<div class="contextual">
<%= link_to(l(:label_details), {:controller => 'timelog', :action => 'details', :project_id => @project}, :class => 'icon icon-details') %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time' %>
</div>
<h2><%= l(:label_spent_time) %></h2>
<% form_remote_tag(:url => {:project_id => @project}, :update => 'content') do %>
<% form_remote_tag(:url => {}, :update => 'content') do %>
<% @criterias.each do |criteria| %>
<%= hidden_field_tag 'criterias[]', criteria %>
<%= hidden_field_tag 'criterias[]', criteria, :id => nil %>
<% end %>
<fieldset><legend><%= l(:label_date_range) %></legend>
<p>
<%= l(:label_date_from) %>
<%= text_field_tag 'date_from', @date_from, :size => 10 %><%= calendar_for('date_from') %>
<%= l(:label_date_to) %>
<%= text_field_tag 'date_to', @date_to, :size => 10 %><%= calendar_for('date_to') %>
<%= l(:label_details) %>
<%= select_tag 'period', options_for_select([[l(:label_year), 'year'],
[l(:label_month), 'month'],
[l(:label_week), 'week']], @columns) %>
&nbsp;
<%= submit_tag l(:button_apply) %>
</p>
</fieldset>
<%= hidden_field_tag 'project_id', params[:project_id] %>
<%= render :partial => 'date_range' %>
<p><%= l(:button_add) %>: <%= select_tag('criterias[]', options_for_select([[]] + (@available_criterias.keys - @criterias).collect{|k| [l(@available_criterias[k][:label]), k]}),
<p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'],
[l(:label_month), 'month'],
[l(:label_week), 'week'],
[l(:label_day_plural).titleize, 'day']], @columns),
:onchange => "this.form.onsubmit();" %>
<%= l(:button_add) %>: <%= select_tag('criterias[]', options_for_select([[]] + (@available_criterias.keys - @criterias).collect{|k| [l(@available_criterias[k][:label]), k]}),
:onchange => "this.form.onsubmit();",
:style => 'width: 200px',
:id => nil,
:disabled => (@criterias.length >= 3)) %>
<%= link_to_remote l(:button_clear), {:url => {:project_id => @project, :date_from => @date_from, :date_to => @date_to, :period => @columns}, :update => 'content'},
:class => 'icon icon-reload' %></p>
<%= link_to_remote l(:button_clear), {:url => {:project_id => @project, :period_type => params[:period_type], :period => params[:period], :from => @from, :to => @to, :columns => @columns},
:update => 'content'
}, :class => 'icon icon-reload' %></p>
<% end %>
<% unless @criterias.empty? %>
<div class="total-hours">
<p><%= l(:label_total) %>: <%= html_hours(lwr(:label_f_hour, @total_hours)) %></p>
@@ -41,11 +37,13 @@
<thead>
<tr>
<% @criterias.each do |criteria| %>
<th width="15%"><%= l(@available_criterias[criteria][:label]) %></th>
<th><%= l(@available_criterias[criteria][:label]) %></th>
<% end %>
<% columns_width = (40 / (@periods.length+1)).to_i %>
<% @periods.each do |period| %>
<th width="<%= ((100 - @criterias.length * 15 - 15 ) / @periods.length).to_i %>%"><%= period %></th>
<th class="period" width="<%= columns_width %>%"><%= period %></th>
<% end %>
<th class="total" width="<%= columns_width %>%"><%= l(:label_total) %></th>
</tr>
</thead>
<tbody>
@@ -53,20 +51,22 @@
<tr class="total">
<td><%= l(:label_total) %></td>
<%= '<td></td>' * (@criterias.size - 1) %>
<% total = 0 -%>
<% @periods.each do |period| -%>
<% sum = sum_hours(select_hours(@hours, @columns, period.to_s)) %>
<% sum = sum_hours(select_hours(@hours, @columns, period.to_s)); total += sum -%>
<td class="hours"><%= html_hours("%.2f" % sum) if sum > 0 %></td>
<% end -%>
<td class="hours"><%= html_hours("%.2f" % total) if total > 0 %></td>
</tr>
</tbody>
</table>
<% end %>
<p class="other-formats">
<%= l(:label_export_to) %>
<span><%= link_to 'CSV', params.merge({:format => 'csv'}), :class => 'csv' %></span>
</p>
<% end %>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
<% html_title l(:label_spent_time), l(:label_report) %>
-7
View File
@@ -30,10 +30,3 @@
</div>
</div>
<!--[eoform:user]-->
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>
+1 -11
View File
@@ -33,17 +33,7 @@
<td align="center"><%= image_tag('true.png') if user.admin? %></td>
<td class="created_on" align="center"><%= format_time(user.created_on) %></td>
<td class="last_login_on" align="center"><%= format_time(user.last_login_on) unless user.last_login_on.nil? %></td>
<td>
<small>
<% if user.locked? -%>
<%= link_to l(:button_unlock), {:action => 'edit', :id => user, :user => {:status => User::STATUS_ACTIVE}}, :method => :post, :class => 'icon icon-unlock' %>
<% elsif user.registered? -%>
<%= link_to l(:button_activate), {:action => 'edit', :id => user, :user => {:status => User::STATUS_ACTIVE}}, :method => :post, :class => 'icon icon-unlock' %>
<% else -%>
<%= link_to l(:button_lock), {:action => 'edit', :id => user, :user => {:status => User::STATUS_LOCKED}}, :method => :post, :class => 'icon icon-lock' %>
<% end -%>
</small>
</td>
<td><small><%= change_status_link(user) %></small></td>
</tr>
<% end -%>
</tbody>
-7
View File
@@ -6,10 +6,3 @@
<p><%= f.text_field :wiki_page_title, :label => :label_wiki_page, :size => 60, :disabled => @project.wiki.nil? %></p>
<p><%= f.text_field :effective_date, :size => 10 %><%= calendar_for('version_effective_date') %></p>
</div>
<% content_for :header_tags do %>
<%= javascript_include_tag 'calendar/calendar' %>
<%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
<%= javascript_include_tag 'calendar/calendar-setup' %>
<%= stylesheet_link_tag 'calendar' %>
<% end %>