Merge branch 'master' of git://github.com/edavis10/redmine
@ -169,6 +169,13 @@ class ApplicationController < ActionController::Base
|
||||
render_404
|
||||
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]
|
||||
# TODO: some subclasses override this, see about merging their logic
|
||||
def find_optional_project
|
||||
|
||||
@ -6,9 +6,15 @@ class ContextMenusController < ApplicationController
|
||||
if (@issues.size == 1)
|
||||
@issue = @issues.first
|
||||
@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
|
||||
projects = @issues.collect(&:project).compact.uniq
|
||||
@project = projects.first if projects.size == 1
|
||||
@projects = @issues.collect(&:project).compact.uniq
|
||||
@project = @projects.first if @projects.size == 1
|
||||
|
||||
@can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
|
||||
:log_time => (@project && User.current.allowed_to?(:log_time, @project)),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class FilesController < ApplicationController
|
||||
menu_item :files
|
||||
|
||||
before_filter :find_project
|
||||
before_filter :find_project_by_project_id
|
||||
before_filter :authorize
|
||||
|
||||
helper :sort
|
||||
@ -19,4 +19,18 @@ class FilesController < ApplicationController
|
||||
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? && 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
|
||||
|
||||
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
|
||||
|
||||
helper :gantt
|
||||
helper :issues
|
||||
helper :projects
|
||||
helper :queries
|
||||
@ -14,32 +15,17 @@ class GanttsController < ApplicationController
|
||||
|
||||
def show
|
||||
@gantt = Redmine::Helpers::Gantt.new(params)
|
||||
@gantt.project = @project
|
||||
retrieve_query
|
||||
@query.group_by = nil
|
||||
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
|
||||
@gantt.query = @query if @query.valid?
|
||||
|
||||
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
|
||||
|
||||
respond_to do |format|
|
||||
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.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
|
||||
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, :type => 'application/pdf', :filename => "#{basename}.pdf") }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ class IssuesController < ApplicationController
|
||||
include SortHelper
|
||||
include IssuesHelper
|
||||
helper :timelog
|
||||
helper :gantt
|
||||
include Redmine::Export::PDF
|
||||
|
||||
verify :method => [:post, :delete],
|
||||
@ -133,7 +134,7 @@ class IssuesController < ApplicationController
|
||||
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
|
||||
respond_to do |format|
|
||||
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 })
|
||||
}
|
||||
format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
|
||||
|
||||
26
app/controllers/project_enumerations_controller.rb
Normal file
@ -0,0 +1,26 @@
|
||||
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
|
||||
@ -18,21 +18,23 @@
|
||||
class ProjectsController < ApplicationController
|
||||
menu_item :overview
|
||||
menu_item :roadmap, :only => :roadmap
|
||||
menu_item :files, :only => [:add_file]
|
||||
menu_item :settings, :only => :settings
|
||||
|
||||
before_filter :find_project, :except => [ :index, :list, :add, :copy ]
|
||||
before_filter :authorize, :except => [ :index, :list, :add, :copy, :archive, :unarchive, :destroy]
|
||||
before_filter :authorize_global, :only => :add
|
||||
before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
|
||||
before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
|
||||
before_filter :authorize_global, :only => [:new, :create]
|
||||
before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
|
||||
accept_key_auth :index
|
||||
|
||||
after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller|
|
||||
|
||||
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
|
||||
if controller.request.post?
|
||||
controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# TODO: convert to PUT only
|
||||
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
|
||||
|
||||
helper :sort
|
||||
include SortHelper
|
||||
helper :custom_fields
|
||||
@ -61,40 +63,45 @@ class ProjectsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
# Add a new project
|
||||
def add
|
||||
def new
|
||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||
@trackers = Tracker.all
|
||||
@project = Project.new(params[:project])
|
||||
if request.get?
|
||||
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
|
||||
@project.trackers = Tracker.all
|
||||
@project.is_public = Setting.default_projects_public?
|
||||
@project.enabled_module_names = Setting.default_projects_modules
|
||||
else
|
||||
@project.enabled_module_names = params[:enabled_modules]
|
||||
if validate_parent_id && @project.save
|
||||
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
|
||||
# Add current user as a project member if he is not admin
|
||||
unless User.current.admin?
|
||||
r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
|
||||
m = Member.new(:user => User.current, :roles => [r])
|
||||
@project.members << m
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :controller => 'projects', :action => 'settings', :id => @project
|
||||
}
|
||||
format.xml { head :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
|
||||
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
|
||||
@project.trackers = Tracker.all
|
||||
@project.is_public = Setting.default_projects_public?
|
||||
@project.enabled_module_names = Setting.default_projects_modules
|
||||
end
|
||||
|
||||
def create
|
||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||
@trackers = Tracker.all
|
||||
@project = Project.new(params[:project])
|
||||
|
||||
@project.enabled_module_names = params[:enabled_modules]
|
||||
if validate_parent_id && @project.save
|
||||
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
|
||||
# Add current user as a project member if he is not admin
|
||||
unless User.current.admin?
|
||||
r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
|
||||
m = Member.new(:user => User.current, :roles => [r])
|
||||
@project.members << m
|
||||
end
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :controller => 'projects', :action => 'settings', :id => @project
|
||||
}
|
||||
format.xml { head :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html { render :action => 'new' }
|
||||
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def copy
|
||||
@ -175,28 +182,27 @@ class ProjectsController < ApplicationController
|
||||
@wiki ||= @project.wiki
|
||||
end
|
||||
|
||||
# Edit @project
|
||||
def edit
|
||||
if request.get?
|
||||
end
|
||||
|
||||
def update
|
||||
@project.attributes = params[:project]
|
||||
if validate_parent_id && @project.save
|
||||
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'settings', :id => @project
|
||||
}
|
||||
format.xml { head :ok }
|
||||
end
|
||||
else
|
||||
@project.attributes = params[:project]
|
||||
if validate_parent_id && @project.save
|
||||
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'settings', :id => @project
|
||||
}
|
||||
format.xml { head :ok }
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
settings
|
||||
render :action => 'settings'
|
||||
}
|
||||
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
settings
|
||||
render :action => 'settings'
|
||||
}
|
||||
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -239,42 +245,6 @@ class ProjectsController < ApplicationController
|
||||
@project = nil
|
||||
end
|
||||
|
||||
def add_file
|
||||
if request.post?
|
||||
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? && Setting.notified_events.include?('file_added')
|
||||
Mailer.deliver_attachments_added(attachments[:files])
|
||||
end
|
||||
redirect_to :controller => 'files', :action => 'index', :id => @project
|
||||
return
|
||||
end
|
||||
@versions = @project.versions.sort
|
||||
end
|
||||
|
||||
def save_activities
|
||||
if request.post? && 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 reset_activities
|
||||
@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
|
||||
|
||||
private
|
||||
def find_optional_project
|
||||
return true unless params[:id]
|
||||
|
||||
@ -260,8 +260,8 @@ private
|
||||
end
|
||||
|
||||
@from, @to = @to, @from if @from && @to && @from > @to
|
||||
@from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) - 1
|
||||
@to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today)
|
||||
@from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
|
||||
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
|
||||
end
|
||||
|
||||
def load_available_criterias
|
||||
|
||||
@ -95,7 +95,9 @@ class UsersController < ApplicationController
|
||||
if request.post?
|
||||
@user.admin = params[:user][:admin] if params[:user][:admin]
|
||||
@user.login = params[:user][:login] if params[:user][:login]
|
||||
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
|
||||
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)
|
||||
|
||||
@ -18,9 +18,9 @@
|
||||
class VersionsController < ApplicationController
|
||||
menu_item :roadmap
|
||||
model_object Version
|
||||
before_filter :find_model_object, :except => [:index, :new, :close_completed]
|
||||
before_filter :find_project_from_association, :except => [:index, :new, :close_completed]
|
||||
before_filter :find_project, :only => [:index, :new, :close_completed]
|
||||
before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
|
||||
before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
|
||||
before_filter :find_project, :only => [:index, :new, :create, :close_completed]
|
||||
before_filter :authorize
|
||||
|
||||
helper :custom_fields
|
||||
@ -63,6 +63,17 @@ class VersionsController < ApplicationController
|
||||
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
|
||||
@version.attributes = attributes
|
||||
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 @version.save
|
||||
respond_to do |format|
|
||||
@ -79,7 +90,7 @@ class VersionsController < ApplicationController
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.html { render :action => 'new' }
|
||||
format.js do
|
||||
render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
|
||||
end
|
||||
@ -87,9 +98,12 @@ class VersionsController < ApplicationController
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def edit
|
||||
if request.post? && params[:version]
|
||||
end
|
||||
|
||||
def update
|
||||
if request.put? && params[:version]
|
||||
attributes = params[:version].dup
|
||||
attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
|
||||
if @version.update_attributes(attributes)
|
||||
@ -100,7 +114,7 @@ class VersionsController < ApplicationController
|
||||
end
|
||||
|
||||
def close_completed
|
||||
if request.post?
|
||||
if request.put?
|
||||
@project.close_completed_versions
|
||||
end
|
||||
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
|
||||
|
||||
@ -20,12 +20,4 @@ module AdminHelper
|
||||
options_for_select([[l(:label_all), ''],
|
||||
[l(:status_active), 1]], selected)
|
||||
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
|
||||
|
||||
@ -32,8 +32,27 @@ module ApplicationHelper
|
||||
end
|
||||
|
||||
# Display a link if user is authorized
|
||||
#
|
||||
# @param [String] name Anchor text (passed to link_to)
|
||||
# @param [Hash, String] options Hash params or url for the link target (passed to link_to).
|
||||
# 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)
|
||||
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
|
||||
if options.is_a?(String)
|
||||
begin
|
||||
route = ActionController::Routing::Routes.recognize_path(options.gsub(/\?.*/,''), :method => options[:method] || :get)
|
||||
link_controller = route[:controller]
|
||||
link_action = route[:action]
|
||||
rescue ActionController::RoutingError # Parse failed, not a route
|
||||
link_controller, link_action = nil, nil
|
||||
end
|
||||
else
|
||||
link_controller = options[:controller] || params[:controller]
|
||||
link_action = options[:action]
|
||||
end
|
||||
|
||||
link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(link_controller, link_action)
|
||||
end
|
||||
|
||||
# Display a link to remote if user is authorized
|
||||
@ -102,6 +121,11 @@ module ApplicationHelper
|
||||
|
||||
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
|
||||
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:
|
||||
@ -302,7 +326,7 @@ module ApplicationHelper
|
||||
def time_tag(time)
|
||||
text = distance_of_time_in_words(Time.now, time)
|
||||
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
|
||||
content_tag('acronym', text, :title => format_time(time))
|
||||
end
|
||||
@ -813,6 +837,8 @@ module ApplicationHelper
|
||||
email = $1
|
||||
end
|
||||
return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
24
app/helpers/gantt_helper.rb
Normal file
@ -0,0 +1,24 @@
|
||||
# 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
|
||||
@ -35,8 +35,10 @@ module IssuesHelper
|
||||
@cached_label_due_date ||= l(:field_due_date)
|
||||
@cached_label_assigned_to ||= l(:field_assigned_to)
|
||||
@cached_label_priority ||= l(:field_priority)
|
||||
|
||||
@cached_label_project ||= l(:field_project)
|
||||
|
||||
link_to_issue(issue) + "<br /><br />" +
|
||||
"<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
|
||||
"<strong>#{@cached_label_status}</strong>: #{issue.status.name}<br />" +
|
||||
"<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
|
||||
"<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
|
||||
@ -243,7 +245,7 @@ module IssuesHelper
|
||||
when :in
|
||||
if gantt.zoom < 4
|
||||
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)))})
|
||||
else
|
||||
l(:text_zoom_in) +
|
||||
@ -253,7 +255,7 @@ module IssuesHelper
|
||||
when :out
|
||||
if gantt.zoom > 1
|
||||
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)))})
|
||||
else
|
||||
l(:text_zoom_out) +
|
||||
|
||||
@ -62,10 +62,28 @@ class Issue < ActiveRecord::Base
|
||||
|
||||
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 :on_active_project, :include => [:status, :project, :tracker],
|
||||
: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_save :reschedule_following_issues, :close_duplicates, :update_done_ratio_from_issue_status
|
||||
@ -357,6 +375,13 @@ class Issue < ActiveRecord::Base
|
||||
def overdue?
|
||||
!due_date.nil? && (due_date < Date.today) && !status.is_closed?
|
||||
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
|
||||
|
||||
# Users the issue can be assigned to
|
||||
def assignable_users
|
||||
@ -821,7 +846,7 @@ class Issue < ActiveRecord::Base
|
||||
j.id as #{select_field},
|
||||
count(i.id) as total
|
||||
from
|
||||
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} as j
|
||||
#{Issue.table_name} i, #{IssueStatus.table_name} s, #{joins} j
|
||||
where
|
||||
i.status_id=s.id
|
||||
and #{where}
|
||||
|
||||
@ -65,4 +65,12 @@ class Journal < ActiveRecord::Base
|
||||
def attachments
|
||||
journalized.respond_to?(:attachments) ? journalized.attachments : nil
|
||||
end
|
||||
|
||||
# Returns a string of css classes
|
||||
def css_classes
|
||||
s = 'journal'
|
||||
s << ' has-notes' unless notes.blank?
|
||||
s << ' has-details' unless details.blank?
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
@ -33,7 +33,11 @@ class Principal < ActiveRecord::Base
|
||||
}
|
||||
|
||||
before_create :set_default_empty_values
|
||||
|
||||
|
||||
def name(formatter = nil)
|
||||
to_s
|
||||
end
|
||||
|
||||
def <=>(principal)
|
||||
if self.class.name == principal.class.name
|
||||
self.to_s.downcase <=> principal.to_s.downcase
|
||||
|
||||
@ -412,6 +412,58 @@ class Project < ActiveRecord::Base
|
||||
def short_description(length = 255)
|
||||
description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
|
||||
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.
|
||||
# action can be:
|
||||
@ -441,6 +493,15 @@ class Project < ActiveRecord::Base
|
||||
enabled_modules.clear
|
||||
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
|
||||
def self.next_identifier
|
||||
|
||||
@ -195,6 +195,12 @@ class Query < ActiveRecord::Base
|
||||
end
|
||||
@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?
|
||||
|
||||
group_values = Group.all.collect {|g| [g.name, g.id] }
|
||||
@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] }
|
||||
@available_filters["assigned_to_role"] = { :type => :list_optional, :order => 7, :values => role_values } unless role_values.empty?
|
||||
|
||||
if User.current.logged?
|
||||
@available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
|
||||
@ -432,6 +438,47 @@ class Query < ActiveRecord::Base
|
||||
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 << 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
|
||||
# regular field
|
||||
db_table = Issue.table_name
|
||||
|
||||
@ -81,4 +81,20 @@ class TimeEntry < ActiveRecord::Base
|
||||
yield
|
||||
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
|
||||
|
||||
@ -73,6 +73,18 @@ class Version < ActiveRecord::Base
|
||||
def completed?
|
||||
effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
|
||||
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
|
||||
# and the time spent on the open issues.
|
||||
@ -123,6 +135,10 @@ class Version < ActiveRecord::Base
|
||||
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"
|
||||
# Those with no effective_date are at the end, sorted by "Project Name - Version name"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<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>
|
||||
|
||||
<h2><%=l(:label_project_plural)%></h2>
|
||||
@ -26,7 +26,7 @@
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<% 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><%= textilizable project.short_description, :project => project %></td>
|
||||
<td align="center"><%= checked_image project.is_public? %></td>
|
||||
@ -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_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_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>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@ -4,20 +4,23 @@
|
||||
<% if !@issue.nil? -%>
|
||||
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
|
||||
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
|
||||
<ul>
|
||||
<% @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,
|
||||
:selected => (s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
|
||||
<% end -%>
|
||||
</ul>
|
||||
</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">
|
||||
<a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
|
||||
<ul>
|
||||
<% @statuses.each do |s| -%>
|
||||
<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 => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
|
||||
<% end -%>
|
||||
</ul>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% unless @trackers.nil? %>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_tracker) %></a>
|
||||
@ -29,6 +32,8 @@
|
||||
</ul>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% if @projects.size == 1 %>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_priority) %></a>
|
||||
<ul>
|
||||
@ -38,6 +43,8 @@
|
||||
<% end -%>
|
||||
</ul>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% unless @project.nil? || @project.shared_versions.open.empty? -%>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
|
||||
@ -77,7 +84,8 @@
|
||||
</ul>
|
||||
</li>
|
||||
<% end -%>
|
||||
<% if Issue.use_field_for_done_ratio? %>
|
||||
|
||||
<% if Issue.use_field_for_done_ratio? && @projects.size == 1 %>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
|
||||
<ul>
|
||||
@ -88,6 +96,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% if !@issue.nil? %>
|
||||
<% if @can[:log_time] -%>
|
||||
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<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>
|
||||
|
||||
<h2><%=l(:label_attachment_plural)%></h2>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
<%= error_messages_for 'attachment' %>
|
||||
<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? %>
|
||||
<p><label for="version_id"><%=l(:field_version)%></label>
|
||||
@ -1,3 +1,4 @@
|
||||
<% @gantt.view = self %>
|
||||
<h2><%= l(:label_gantt) %></h2>
|
||||
|
||||
<% form_tag(gantt_path(:month => params[:month], :year => params[:year], :months => params[:months]), :method => :put, :id => 'query_form') do %>
|
||||
@ -55,11 +56,12 @@ if @gantt.zoom >1
|
||||
end
|
||||
end
|
||||
|
||||
# Width of the entire chart
|
||||
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
|
||||
%>
|
||||
|
||||
<table width="100%" style="border:0; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="width:<%= subject_width %>px; padding:0px;">
|
||||
@ -67,26 +69,10 @@ t_height = g_height + headers_height
|
||||
<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:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
|
||||
<%
|
||||
#
|
||||
# Tasks subjects
|
||||
#
|
||||
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 %>
|
||||
<% top = headers_height + 8 %>
|
||||
|
||||
<%= @gantt.subjects(:headers_height => headers_height, :top => top, :g_width => g_width) %>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@ -164,53 +150,9 @@ if show_days
|
||||
end
|
||||
end %>
|
||||
|
||||
<%
|
||||
#
|
||||
# Tasks
|
||||
#
|
||||
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> <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"> </div>
|
||||
<% end %>
|
||||
<% if d_width > 0 %>
|
||||
<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="<%= css %> task_done"> </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"> </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 %>
|
||||
<% top = headers_height + 10 %>
|
||||
|
||||
<%= @gantt.lines(:top => top, :zoom => zoom, :g_width => g_width ) %>
|
||||
|
||||
<%
|
||||
#
|
||||
@ -227,8 +169,8 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
|
||||
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td align="left"><%= link_to_remote ('« ' + l(:label_previous)), {:url => @gantt.params_previous, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %></td>
|
||||
<td align="right"><%= link_to_remote (l(:label_next) + ' »'), {:url => @gantt.params_next, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td>
|
||||
<td align="left"><%= link_to_remote ('« ' + 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) + ' »'), {:url => @gantt.params_next, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
<%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
|
||||
l(:label_version_new),
|
||||
'version[name]',
|
||||
{:controller => 'versions', :action => 'new', :project_id => @project},
|
||||
{:controller => 'versions', :action => 'create', :project_id => @project},
|
||||
:title => l(:label_version_new),
|
||||
:tabindex => 200) if authorize_for('versions', 'new') %>
|
||||
</p>
|
||||
@ -40,6 +40,6 @@
|
||||
</div>
|
||||
|
||||
<div style="clear:both;"> </div>
|
||||
<%= render :partial => 'form_custom_fields' %>
|
||||
<%= render :partial => 'issues/form_custom_fields' %>
|
||||
|
||||
<% end %>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
</div>
|
||||
|
||||
<div id="attributes" class="attributes">
|
||||
<%= render :partial => 'attributes' %>
|
||||
<%= render :partial => 'issues/attributes' %>
|
||||
</div>
|
||||
|
||||
<% if @issue.new_record? %>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<% reply_links = authorize_for('issues', 'edit') -%>
|
||||
<% for journal in journals %>
|
||||
<div id="change-<%= journal.id %>" class="journal">
|
||||
<div id="change-<%= journal.id %>" class="<%= journal.css_classes %>">
|
||||
<h4><div class="journal-link"><%= link_to "##{journal.indice}", :anchor => "note-#{journal.indice}" %></div>
|
||||
<%= avatar(journal.user, :size => "24") %>
|
||||
<%= content_tag('a', '', :name => "note-#{journal.indice}")%>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div class="autoscroll">
|
||||
<table class="list issues">
|
||||
<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)}" %>
|
||||
</th>
|
||||
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
|
||||
@ -25,7 +25,7 @@
|
||||
<% previous_group = group %>
|
||||
<% end %>
|
||||
<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>
|
||||
<% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %>
|
||||
</tr>
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<% remote_form_for(:relation, @relation,
|
||||
:url => {:controller => 'issue_relations', :action => 'new', :issue_id => @issue},
|
||||
:method => :post,
|
||||
:complete => "Form.Element.focus('relation_issue_to_id');",
|
||||
:html => {:id => 'new-relation-form', :style => (@relation ? '' : 'display: none;')}) do |f| %>
|
||||
<%= render :partial => 'issue_relations/form', :locals => {:f => f}%>
|
||||
<% end %>
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
{ :url => { :set_filter => 1 },
|
||||
:before => 'selectAllOptions("selected_columns");',
|
||||
:update => "content",
|
||||
:complete => "apply_filters_observer()",
|
||||
:with => "Form.serialize('query_form')"
|
||||
}, :class => 'icon icon-checked' %>
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<% 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 } %>
|
||||
<%= submit_tag l(:button_save) %>
|
||||
<% end %>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<% end %>
|
||||
</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>
|
||||
<%= submit_tag l(:button_delete) %>
|
||||
<% end %>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<% end %>
|
||||
|
||||
<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_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %>
|
||||
<%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<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 } %>
|
||||
|
||||
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
|
||||
@ -1,4 +1,4 @@
|
||||
<% 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">
|
||||
<thead><tr>
|
||||
@ -32,7 +32,7 @@
|
||||
</table>
|
||||
|
||||
<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,
|
||||
:confirm => l(:text_are_you_sure),
|
||||
:class => 'icon icon-del') %>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<td class="buttons">
|
||||
<% 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_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 %>
|
||||
</td>
|
||||
</tr>
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
<div class="contextual">
|
||||
<% 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 %>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<div class="contextual">
|
||||
<% 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 %>
|
||||
</div>
|
||||
|
||||
|
||||
@ -53,6 +53,18 @@ function toggle_multi_select(field) {
|
||||
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>
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<h2><%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %></h2>
|
||||
|
||||
<!-- 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_to', params[:rev_to]) if params[:rev_to] %>
|
||||
<p><label><%= l(:label_view_diff) %></label>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
select_tag('status_by',
|
||||
status_by_options_for_select(criteria),
|
||||
: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')"))) %>
|
||||
</legend>
|
||||
<% if counts.empty? %>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<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 } %>
|
||||
<%= submit_tag l(:button_save) %>
|
||||
<% end %>
|
||||
|
||||
@ -51,4 +51,4 @@
|
||||
|
||||
<% html_title(l(:label_roadmap)) %>
|
||||
|
||||
<%= context_menu :controller => 'issues', :action => 'context_menu' %>
|
||||
<%= context_menu issues_context_menu_path %>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<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 } %>
|
||||
<%= submit_tag l(:button_create) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@ -16,11 +16,11 @@
|
||||
|
||||
<% unless @pages.empty? %>
|
||||
<% 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) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% 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 %>
|
||||
|
||||
@ -4,9 +4,14 @@
|
||||
# Code is not reloaded between requests
|
||||
config.cache_classes = true
|
||||
|
||||
#####
|
||||
# Customize the default logger (http://ruby-doc.org/core/classes/Logger.html)
|
||||
#
|
||||
# Use a different logger for distributed setups
|
||||
# config.logger = SyslogLogger.new
|
||||
|
||||
#
|
||||
# Rotate logs bigger than 1MB, keeps no more than 7 rotated logs around.
|
||||
# config.logger = Logger.new(config.log_path, 7, 1048576)
|
||||
|
||||
# Full error reports are disabled and caching is turned on
|
||||
config.action_controller.consider_all_requests_local = false
|
||||
|
||||
@ -116,6 +116,7 @@ bg:
|
||||
greater_than_start_date: "трябва да е след началната дата"
|
||||
not_same_project: "не е от същия проект"
|
||||
circular_dependency: "Тази релация ще доведе до безкрайна зависимост"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Изберете
|
||||
|
||||
@ -906,3 +907,5 @@ bg:
|
||||
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
|
||||
|
||||
@ -130,6 +130,7 @@ bs:
|
||||
greater_than_start_date: "mora biti veći nego početni datum"
|
||||
not_same_project: "ne pripada istom projektu"
|
||||
circular_dependency: "Ova relacija stvar cirkularnu zavisnost"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Molimo odaberite
|
||||
|
||||
@ -926,3 +927,5 @@ bs:
|
||||
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
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
# Redmine catalan translation:
|
||||
# by Joan Duran
|
||||
|
||||
ca:
|
||||
# Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
|
||||
direction: ltr
|
||||
date:
|
||||
formats:
|
||||
@ -65,6 +69,7 @@ ca:
|
||||
other: "almost {{count}} years"
|
||||
|
||||
number:
|
||||
# Default format for numbers
|
||||
format:
|
||||
separator: "."
|
||||
delimiter: ""
|
||||
@ -83,6 +88,7 @@ ca:
|
||||
mb: "MB"
|
||||
gb: "GB"
|
||||
tb: "TB"
|
||||
|
||||
|
||||
# Used in array.to_sentence.
|
||||
support:
|
||||
@ -116,6 +122,7 @@ ca:
|
||||
greater_than_start_date: "ha de ser superior que la data inicial"
|
||||
not_same_project: "no pertany al mateix projecte"
|
||||
circular_dependency: "Aquesta relació crearia una dependència circular"
|
||||
cant_link_an_issue_with_a_descendant: "Un assumpte no es pot enllaçar a una de les seves subtasques"
|
||||
|
||||
actionview_instancetag_blank_option: Seleccioneu
|
||||
|
||||
@ -149,18 +156,33 @@ ca:
|
||||
notice_email_sent: "S'ha enviat un correu electrònic a {{value}}"
|
||||
notice_email_error: "S'ha produït un error en enviar el correu ({{value}})"
|
||||
notice_feeds_access_key_reseted: "S'ha reiniciat la clau d'accés del RSS."
|
||||
notice_api_access_key_reseted: "S'ha reiniciat la clau d'accés a l'API."
|
||||
notice_failed_to_save_issues: "No s'han pogut desar %s assumptes de {{count}} seleccionats: {{ids}}."
|
||||
notice_failed_to_save_members: "No s'han pogut desar els membres: {{errors}}."
|
||||
notice_no_issue_selected: "No s'ha seleccionat cap assumpte. Activeu els assumptes que voleu editar."
|
||||
notice_account_pending: "S'ha creat el compte i ara està pendent de l'aprovació de l'administrador."
|
||||
notice_default_data_loaded: "S'ha carregat correctament la configuració predeterminada."
|
||||
notice_unable_delete_version: "No s'ha pogut suprimir la versió."
|
||||
notice_unable_delete_time_entry: "No s'ha pogut suprimir l'entrada del registre de temps."
|
||||
notice_issue_done_ratios_updated: "S'ha actualitzat el tant per cent dels assumptes."
|
||||
|
||||
error_can_t_load_default_data: "No s'ha pogut carregar la configuració predeterminada: {{value}} "
|
||||
error_scm_not_found: "No s'ha trobat l'entrada o la revisió en el dipòsit."
|
||||
error_scm_command_failed: "S'ha produït un error en intentar accedir al dipòsit: {{value}}"
|
||||
error_scm_annotate: "L'entrada no existeix o no s'ha pogut anotar."
|
||||
error_issue_not_found_in_project: "No s'ha trobat l'assumpte o no pertany a aquest projecte"
|
||||
|
||||
error_no_tracker_in_project: "Aquest projecte no té seguidor associat. Comproveu els paràmetres del projecte."
|
||||
error_no_default_issue_status: "No s'ha definit cap estat d'assumpte predeterminat. Comproveu la configuració (aneu a «Administració -> Estats de l'assumpte»)."
|
||||
error_can_not_delete_custom_field: "No s'ha pogut suprimir el camp personalitat"
|
||||
error_can_not_delete_tracker: "Aquest seguidor conté assumptes i no es pot suprimir."
|
||||
error_can_not_remove_role: "Aquest rol s'està utilitzant i no es pot suprimir."
|
||||
error_can_not_reopen_issue_on_closed_version: "Un assumpte assignat a una versió tancada no es pot tornar a obrir"
|
||||
error_can_not_archive_project: "Aquest projecte no es pot arxivar"
|
||||
error_issue_done_ratios_not_updated: "No s'ha actualitza el tant per cent dels assumptes."
|
||||
error_workflow_copy_source: "Seleccioneu un seguidor o rol font"
|
||||
error_workflow_copy_target: "Seleccioneu seguidors i rols objectiu"
|
||||
error_unable_delete_issue_status: "No s'ha pogut suprimir l'estat de l'assumpte"
|
||||
error_unable_to_connect: "No s'ha pogut connectar ({{value}})"
|
||||
warning_attachments_not_saved: "No s'han pogut desar {{count}} fitxers."
|
||||
|
||||
mail_subject_lost_password: "Contrasenya de {{value}}"
|
||||
@ -173,6 +195,10 @@ ca:
|
||||
mail_body_account_activation_request: "S'ha registrat un usuari nou ({{value}}). El seu compte està pendent d'aprovació:"
|
||||
mail_subject_reminder: "{{count}} assumptes venceran els següents {{days}} dies"
|
||||
mail_body_reminder: "{{count}} assumptes que teniu assignades venceran els següents {{days}} dies:"
|
||||
mail_subject_wiki_content_added: "S'ha afegit la pàgina wiki «{{page}}»"
|
||||
mail_body_wiki_content_added: "En {{author}} ha afegit la pàgina wiki «{{page}}»."
|
||||
mail_subject_wiki_content_updated: "S'ha actualitzat la pàgina wiki «{{page}}»"
|
||||
mail_body_wiki_content_updated: "En {{author}} ha actualitzat la pàgina wiki «{{page}}»."
|
||||
|
||||
gui_validation_error: 1 error
|
||||
gui_validation_error_plural: "{{count}} errors"
|
||||
@ -212,6 +238,7 @@ ca:
|
||||
field_priority: Prioritat
|
||||
field_fixed_version: Versió objectiu
|
||||
field_user: Usuari
|
||||
field_principal: Principal
|
||||
field_role: Rol
|
||||
field_homepage: Pàgina web
|
||||
field_is_public: Públic
|
||||
@ -256,6 +283,7 @@ ca:
|
||||
field_redirect_existing_links: Redirigeix els enllaços existents
|
||||
field_estimated_hours: Temps previst
|
||||
field_column_names: Columnes
|
||||
field_time_entries: "Registre de temps"
|
||||
field_time_zone: Zona horària
|
||||
field_searchable: Es pot cercar
|
||||
field_default_value: Valor predeterminat
|
||||
@ -265,6 +293,9 @@ ca:
|
||||
field_watcher: Vigilància
|
||||
field_identity_url: URL OpenID
|
||||
field_content: Contingut
|
||||
field_group_by: "Agrupa els resultats per"
|
||||
field_sharing: Compartició
|
||||
field_parent_issue: "Tasca pare"
|
||||
|
||||
setting_app_title: "Títol de l'aplicació"
|
||||
setting_app_subtitle: "Subtítol de l'aplicació"
|
||||
@ -300,20 +331,35 @@ ca:
|
||||
setting_activity_days_default: "Dies a mostrar l'activitat del projecte"
|
||||
setting_display_subprojects_issues: "Mostra els assumptes d'un subprojecte en el projecte pare per defecte"
|
||||
setting_enabled_scm: "Habilita l'SCM"
|
||||
setting_mail_handler_body_delimiters: "Trunca els correus electrònics després d'una d'aquestes línies"
|
||||
setting_mail_handler_api_enabled: "Habilita el WS per correus electrònics d'entrada"
|
||||
setting_mail_handler_api_key: Clau API
|
||||
setting_sequential_project_identifiers: Genera identificadors de projecte seqüencials
|
||||
setting_gravatar_enabled: "Utilitza les icones d'usuari Gravatar"
|
||||
setting_gravatar_default: "Imatge Gravatar predeterminada"
|
||||
setting_diff_max_lines_displayed: Número màxim de línies amb diferències mostrades
|
||||
setting_file_max_size_displayed: Mida màxima dels fitxers de text mostrats en línia
|
||||
setting_repository_log_display_limit: Número màxim de revisions que es mostren al registre de fitxers
|
||||
setting_openid: "Permet entrar i registrar-se amb l'OpenID"
|
||||
|
||||
setting_password_min_length: "Longitud mínima de la contrasenya"
|
||||
setting_new_project_user_role_id: "Aquest rol es dóna a un usuari no administrador per a crear projectes"
|
||||
setting_default_projects_modules: "Mòduls activats per defecte en els projectes nous"
|
||||
setting_issue_done_ratio: "Calcula tant per cent realitzat de l'assumpte amb"
|
||||
setting_issue_done_ratio_issue_status: "Utilitza l'estat de l'assumpte"
|
||||
setting_issue_done_ratio_issue_field: "Utilitza el camp de l'assumpte"
|
||||
setting_start_of_week: "Inicia les setmanes en"
|
||||
setting_rest_api_enabled: "Habilita el servei web REST"
|
||||
setting_cache_formatted_text: Cache formatted text
|
||||
|
||||
permission_add_project: "Crea projectes"
|
||||
permission_add_subprojects: "Crea subprojectes"
|
||||
permission_edit_project: Edita el projecte
|
||||
permission_select_project_modules: Selecciona els mòduls del projecte
|
||||
permission_manage_members: Gestiona els membres
|
||||
permission_manage_project_activities: "Gestiona les activitats del projecte"
|
||||
permission_manage_versions: Gestiona les versions
|
||||
permission_manage_categories: Gestiona les categories dels assumptes
|
||||
permission_view_issues: "Visualitza els assumptes"
|
||||
permission_add_issues: Afegeix assumptes
|
||||
permission_edit_issues: Edita els assumptes
|
||||
permission_manage_issue_relations: Gestiona les relacions dels assumptes
|
||||
@ -328,6 +374,7 @@ ca:
|
||||
permission_view_calendar: Visualitza el calendari
|
||||
permission_view_issue_watchers: Visualitza la llista de vigilàncies
|
||||
permission_add_issue_watchers: Afegeix vigilàncies
|
||||
permission_delete_issue_watchers: Suprimeix els vigilants
|
||||
permission_log_time: Registra el temps invertit
|
||||
permission_view_time_entries: Visualitza el temps invertit
|
||||
permission_edit_time_entries: Edita els registres de temps
|
||||
@ -357,6 +404,8 @@ ca:
|
||||
permission_edit_own_messages: Edita els missatges propis
|
||||
permission_delete_messages: Suprimeix els missatges
|
||||
permission_delete_own_messages: Suprimeix els missatges propis
|
||||
permission_export_wiki_pages: "Exporta les pàgines wiki"
|
||||
permission_manage_subtasks: "Gestiona subtasques"
|
||||
|
||||
project_module_issue_tracking: "Seguidor d'assumptes"
|
||||
project_module_time_tracking: Seguidor de temps
|
||||
@ -366,10 +415,13 @@ ca:
|
||||
project_module_wiki: Wiki
|
||||
project_module_repository: Dipòsit
|
||||
project_module_boards: Taulers
|
||||
project_module_calendar: Calendari
|
||||
project_module_gantt: Gantt
|
||||
|
||||
label_user: Usuari
|
||||
label_user_plural: Usuaris
|
||||
label_user_new: Usuari nou
|
||||
label_user_anonymous: Anònim
|
||||
label_project: Projecte
|
||||
label_project_new: Projecte nou
|
||||
label_project_plural: Projectes
|
||||
@ -416,12 +468,13 @@ ca:
|
||||
label_information_plural: Informació
|
||||
label_please_login: Entreu
|
||||
label_register: Registre
|
||||
label_login_with_open_id_option: o entra amb l'OpenID
|
||||
label_login_with_open_id_option: "o entra amb l'OpenID"
|
||||
label_password_lost: Contrasenya perduda
|
||||
label_home: Inici
|
||||
label_my_page: La meva pàgina
|
||||
label_my_account: El meu compte
|
||||
label_my_projects: Els meus projectes
|
||||
label_my_page_block: "Els meus blocs de pàgina"
|
||||
label_administration: Administració
|
||||
label_login: Entra
|
||||
label_logout: Surt
|
||||
@ -441,6 +494,7 @@ ca:
|
||||
label_auth_source_new: "Mode d'autenticació nou"
|
||||
label_auth_source_plural: "Modes d'autenticació"
|
||||
label_subproject_plural: Subprojectes
|
||||
label_subproject_new: "Subprojecte nou"
|
||||
label_and_its_subprojects: "{{value}} i els seus subprojectes"
|
||||
label_min_max_length: Longitud mín - max
|
||||
label_list: Llist
|
||||
@ -475,8 +529,9 @@ ca:
|
||||
label_version: Versió
|
||||
label_version_new: Versió nova
|
||||
label_version_plural: Versions
|
||||
label_close_versions: "Tanca les versions completades"
|
||||
label_confirmation: Confirmació
|
||||
label_export_to: 'També disponible a:'
|
||||
label_export_to: "També disponible a:"
|
||||
label_read: Llegeix...
|
||||
label_public_projects: Projectes públics
|
||||
label_open_issues: obert
|
||||
@ -533,6 +588,8 @@ ca:
|
||||
label_not_equals: no és
|
||||
label_in_less_than: en menys de
|
||||
label_in_more_than: en més de
|
||||
label_greater_or_equal: ">="
|
||||
label_less_or_equal: <=
|
||||
label_in: en
|
||||
label_today: avui
|
||||
label_all_time: tot el temps
|
||||
@ -555,17 +612,21 @@ ca:
|
||||
label_browse: Navega
|
||||
label_modification: "{{count}} canvi"
|
||||
label_modification_plural: "{{count}} canvis"
|
||||
label_branch: Branca
|
||||
label_tag: Etiqueta
|
||||
label_revision: Revisió
|
||||
label_revision_plural: Revisions
|
||||
label_revision_id: "Revisió {{value}}"
|
||||
label_associated_revisions: Revisions associades
|
||||
label_added: afegit
|
||||
label_modified: modificat
|
||||
label_renamed: reanomenat
|
||||
label_copied: copiat
|
||||
label_renamed: reanomenat
|
||||
label_deleted: suprimit
|
||||
label_latest_revision: Última revisió
|
||||
label_latest_revision_plural: Últimes revisions
|
||||
label_view_revisions: Visualitza les revisions
|
||||
label_view_all_revisions: "Visualitza totes les revisions"
|
||||
label_max_size: Mida màxima
|
||||
label_sort_highest: Mou a la part superior
|
||||
label_sort_higher: Mou cap amunt
|
||||
@ -591,6 +652,7 @@ ca:
|
||||
label_changes_details: Detalls de tots els canvis
|
||||
label_issue_tracking: "Seguiment d'assumptes"
|
||||
label_spent_time: Temps invertit
|
||||
label_overall_spent_time: "Temps total invertit"
|
||||
label_f_hour: "{{value}} hora"
|
||||
label_f_hour_plural: "{{value}} hores"
|
||||
label_time_tracking: Temps de seguiment
|
||||
@ -628,6 +690,8 @@ ca:
|
||||
label_board: Fòrum
|
||||
label_board_new: Fòrum nou
|
||||
label_board_plural: Fòrums
|
||||
label_board_locked: Bloquejat
|
||||
label_board_sticky: Sticky
|
||||
label_topic_plural: Temes
|
||||
label_message_plural: Missatges
|
||||
label_message_last: Últim missatge
|
||||
@ -643,6 +707,8 @@ ca:
|
||||
label_language_based: "Basat en l'idioma de l'usuari"
|
||||
label_sort_by: "Ordena per {{value}}"
|
||||
label_send_test_email: Envia un correu electrònic de prova
|
||||
label_feeds_access_key: "Clau d'accés del RSS"
|
||||
label_missing_feeds_access_key: "Falta una clau d'accés del RSS"
|
||||
label_feeds_access_key_created_on: "Clau d'accés del RSS creada fa {{value}}"
|
||||
label_module_plural: Mòduls
|
||||
label_added_time_by: "Afegit per {{author}} fa {{age}}"
|
||||
@ -688,6 +754,28 @@ ca:
|
||||
label_ascending: Ascendent
|
||||
label_descending: Descendent
|
||||
label_date_from_to: Des de {{start}} a {{end}}
|
||||
label_wiki_content_added: "S'ha afegit la pàgina wiki"
|
||||
label_wiki_content_updated: "S'ha actualitzat la pàgina wiki"
|
||||
label_group: Grup
|
||||
label_group_plural: Grups
|
||||
label_group_new: Grup nou
|
||||
label_time_entry_plural: Temps invertit
|
||||
label_version_sharing_hierarchy: "Amb la jerarquia del projecte"
|
||||
label_version_sharing_system: "Amb tots els projectes"
|
||||
label_version_sharing_descendants: "Amb tots els subprojectes"
|
||||
label_version_sharing_tree: "Amb l'arbre del projecte"
|
||||
label_version_sharing_none: "Sense compartir"
|
||||
label_update_issue_done_ratios: "Actualitza el tant per cent dels assumptes realitzats"
|
||||
label_copy_source: Font
|
||||
label_copy_target: Objectiu
|
||||
label_copy_same_as_target: "El mateix que l'objectiu"
|
||||
label_display_used_statuses_only: "Mostra només els estats que utilitza aquest seguidor"
|
||||
label_api_access_key: "Clau d'accés a l'API"
|
||||
label_missing_api_access_key: "Falta una clau d'accés de l'API"
|
||||
label_api_access_key_created_on: "Clau d'accés de l'API creada fa {{value}}"
|
||||
label_profile: Perfil
|
||||
label_subtask_plural: Subtasques
|
||||
label_project_copy_notifications: "Envia notificacions de correu electrònic durant la còpia del projecte"
|
||||
|
||||
button_login: Entra
|
||||
button_submit: Tramet
|
||||
@ -709,11 +797,12 @@ ca:
|
||||
button_list: Llista
|
||||
button_view: Visualitza
|
||||
button_move: Mou
|
||||
button_move_and_follow: "Mou i segueix"
|
||||
button_back: Enrere
|
||||
button_cancel: Cancel·la
|
||||
button_activate: Activa
|
||||
button_sort: Ordena
|
||||
button_log_time: "Hora d'entrada"
|
||||
button_log_time: "Registre de temps"
|
||||
button_rollback: Torna a aquesta versió
|
||||
button_watch: Vigila
|
||||
button_unwatch: No vigilis
|
||||
@ -724,15 +813,24 @@ ca:
|
||||
button_rename: Reanomena
|
||||
button_change_password: Canvia la contrasenya
|
||||
button_copy: Copia
|
||||
button_copy_and_follow: "Copia i segueix"
|
||||
button_annotate: Anota
|
||||
button_update: Actualitza
|
||||
button_configure: Configura
|
||||
button_quote: Cita
|
||||
button_duplicate: Duplica
|
||||
button_show: Mostra
|
||||
|
||||
status_active: actiu
|
||||
status_registered: informat
|
||||
status_locked: bloquejat
|
||||
|
||||
version_status_open: oberta
|
||||
version_status_locked: bloquejada
|
||||
version_status_closed: tancada
|
||||
|
||||
field_active: Actiu
|
||||
|
||||
text_select_mail_notifications: "Seleccioneu les accions per les quals s'hauria d'enviar una notificació per correu electrònic."
|
||||
text_regexp_info: ex. ^[A-Z0-9]+$
|
||||
text_min_max_length_info: 0 significa sense restricció
|
||||
@ -740,6 +838,10 @@ ca:
|
||||
text_subprojects_destroy_warning: "També seran suprimits els seus subprojectes: {{value}}."
|
||||
text_workflow_edit: Seleccioneu un rol i un seguidor per a editar el flux de treball
|
||||
text_are_you_sure: Segur?
|
||||
text_journal_changed: "{{label}} ha canviat de {{old}} a {{new}}"
|
||||
text_journal_set_to: "{{label}} s'ha establert a {{value}}"
|
||||
text_journal_deleted: "{{label}} s'ha suprimit ({{old}})"
|
||||
text_journal_added: "S'ha afegit {{label}} {{value}}"
|
||||
text_tip_task_begin_day: "tasca que s'inicia aquest dia"
|
||||
text_tip_task_end_day: tasca que finalitza aquest dia
|
||||
text_tip_task_begin_end_day: "tasca que s'inicia i finalitza aquest dia"
|
||||
@ -750,6 +852,7 @@ ca:
|
||||
text_tracker_no_workflow: "No s'ha definit cap flux de treball per a aquest seguidor"
|
||||
text_unallowed_characters: Caràcters no permesos
|
||||
text_comma_separated: Es permeten valors múltiples (separats per una coma).
|
||||
text_line_separated: "Es permeten diversos valors (una línia per cada valor)."
|
||||
text_issues_ref_in_commit_messages: Referència i soluciona els assumptes en els missatges publicats
|
||||
text_issue_added: "L'assumpte {{id}} ha sigut informat per {{author}}."
|
||||
text_issue_updated: "L'assumpte {{id}} ha sigut actualitzat per {{author}}."
|
||||
@ -770,14 +873,21 @@ ca:
|
||||
text_destroy_time_entries_question: "S'han informat {{hours}} hores en els assumptes que aneu a suprimir. Què voleu fer?"
|
||||
text_destroy_time_entries: Suprimeix les hores informades
|
||||
text_assign_time_entries_to_project: Assigna les hores informades al projecte
|
||||
text_reassign_time_entries: 'Torna a assignar les hores informades a aquest assumpte:'
|
||||
text_reassign_time_entries: "Torna a assignar les hores informades a aquest assumpte:"
|
||||
text_user_wrote: "{{value}} va escriure:"
|
||||
text_enumeration_destroy_question: "{{count}} objectes estan assignats a aquest valor."
|
||||
text_enumeration_category_reassign_to: 'Torna a assignar-los a aquest valor:'
|
||||
text_enumeration_category_reassign_to: "Torna a assignar-los a aquest valor:"
|
||||
text_email_delivery_not_configured: "El lliurament per correu electrònic no està configurat i les notificacions estan inhabilitades.\nConfigureu el servidor SMTP a config/email.yml i reinicieu l'aplicació per habilitar-lo."
|
||||
text_repository_usernames_mapping: "Seleccioneu l'assignació entre els usuaris del Redmine i cada nom d'usuari trobat al dipòsit.\nEls usuaris amb el mateix nom d'usuari o correu del Redmine i del dipòsit s'assignaran automàticament."
|
||||
text_diff_truncated: "... Aquestes diferències s'han trucat perquè excedeixen la mida màxima que es pot mostrar."
|
||||
text_custom_field_possible_values_info: 'Una línia per a cada valor'
|
||||
text_custom_field_possible_values_info: "Una línia per a cada valor"
|
||||
text_wiki_page_destroy_question: "Aquesta pàgina té {{descendants}} pàgines fill i descendents. Què voleu fer?"
|
||||
text_wiki_page_nullify_children: "Deixa les pàgines fill com a pàgines arrel"
|
||||
text_wiki_page_destroy_children: "Suprimeix les pàgines fill i tots els seus descendents"
|
||||
text_wiki_page_reassign_children: "Reasigna les pàgines fill a aquesta pàgina pare"
|
||||
text_own_membership_delete_confirmation: "Esteu a punt de suprimir algun o tots els vostres permisos i potser no podreu editar més aquest projecte.\nSegur que voleu continuar?"
|
||||
text_zoom_in: Redueix
|
||||
text_zoom_out: Amplia
|
||||
|
||||
default_role_manager: Gestor
|
||||
default_role_developer: Desenvolupador
|
||||
@ -804,108 +914,7 @@ ca:
|
||||
enumeration_issue_priorities: Prioritat dels assumptes
|
||||
enumeration_doc_categories: Categories del document
|
||||
enumeration_activities: Activitats (seguidor de temps)
|
||||
label_greater_or_equal: ">="
|
||||
label_less_or_equal: <=
|
||||
text_wiki_page_destroy_question: This page has {{descendants}} child page(s) and descendant(s). What do you want to do?
|
||||
text_wiki_page_reassign_children: Reassign child pages to this parent page
|
||||
text_wiki_page_nullify_children: Keep child pages as root pages
|
||||
text_wiki_page_destroy_children: Delete child pages and all their descendants
|
||||
setting_password_min_length: Minimum password length
|
||||
field_group_by: Group results by
|
||||
mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated"
|
||||
label_wiki_content_added: Wiki page added
|
||||
mail_subject_wiki_content_added: "'{{page}}' wiki page has been added"
|
||||
mail_body_wiki_content_added: The '{{page}}' wiki page has been added by {{author}}.
|
||||
label_wiki_content_updated: Wiki page updated
|
||||
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
||||
permission_add_project: Create project
|
||||
setting_new_project_user_role_id: Role given to a non-admin user who creates a project
|
||||
label_view_all_revisions: View all revisions
|
||||
label_tag: Tag
|
||||
label_branch: Branch
|
||||
error_no_tracker_in_project: No tracker is associated to this project. Please check the Project settings.
|
||||
error_no_default_issue_status: No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").
|
||||
text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
|
||||
text_journal_set_to: "{{label}} set to {{value}}"
|
||||
text_journal_deleted: "{{label}} deleted ({{old}})"
|
||||
label_group_plural: Groups
|
||||
label_group: Group
|
||||
label_group_new: New group
|
||||
label_time_entry_plural: Spent time
|
||||
text_journal_added: "{{label}} {{value}} added"
|
||||
field_active: Active
|
||||
enumeration_system_activity: System Activity
|
||||
permission_delete_issue_watchers: Delete watchers
|
||||
version_status_closed: closed
|
||||
version_status_locked: locked
|
||||
version_status_open: open
|
||||
error_can_not_reopen_issue_on_closed_version: An issue assigned to a closed version can not be reopened
|
||||
label_user_anonymous: Anonymous
|
||||
button_move_and_follow: Move and follow
|
||||
setting_default_projects_modules: Default enabled modules for new projects
|
||||
setting_gravatar_default: Default Gravatar image
|
||||
field_sharing: Sharing
|
||||
label_version_sharing_hierarchy: With project hierarchy
|
||||
label_version_sharing_system: With all projects
|
||||
label_version_sharing_descendants: With subprojects
|
||||
label_version_sharing_tree: With project tree
|
||||
label_version_sharing_none: Not shared
|
||||
error_can_not_archive_project: This project can not be archived
|
||||
button_duplicate: Duplicate
|
||||
button_copy_and_follow: Copy and follow
|
||||
label_copy_source: Source
|
||||
setting_issue_done_ratio: Calculate the issue done ratio with
|
||||
setting_issue_done_ratio_issue_status: Use the issue status
|
||||
error_issue_done_ratios_not_updated: Issue done ratios not updated.
|
||||
error_workflow_copy_target: Please select target tracker(s) and role(s)
|
||||
setting_issue_done_ratio_issue_field: Use the issue field
|
||||
label_copy_same_as_target: Same as target
|
||||
label_copy_target: Target
|
||||
notice_issue_done_ratios_updated: Issue done ratios updated.
|
||||
error_workflow_copy_source: Please select a source tracker or role
|
||||
label_update_issue_done_ratios: Update issue done ratios
|
||||
setting_start_of_week: Start calendars on
|
||||
permission_view_issues: View Issues
|
||||
label_display_used_statuses_only: Only display statuses that are used by this tracker
|
||||
label_revision_id: Revision {{value}}
|
||||
label_api_access_key: API access key
|
||||
label_api_access_key_created_on: API access key created {{value}} ago
|
||||
label_feeds_access_key: RSS access key
|
||||
notice_api_access_key_reseted: Your API access key was reset.
|
||||
setting_rest_api_enabled: Enable REST web service
|
||||
label_missing_api_access_key: Missing an API access key
|
||||
label_missing_feeds_access_key: Missing a RSS access key
|
||||
button_show: Show
|
||||
text_line_separated: Multiple values allowed (one line for each value).
|
||||
setting_mail_handler_body_delimiters: Truncate emails after one of these lines
|
||||
permission_add_subprojects: Create subprojects
|
||||
label_subproject_new: New subproject
|
||||
text_own_membership_delete_confirmation: |-
|
||||
You are about to remove some or all of your permissions and may no longer be able to edit this project after that.
|
||||
Are you sure you want to continue?
|
||||
label_close_versions: Close completed versions
|
||||
label_board_sticky: Sticky
|
||||
label_board_locked: Locked
|
||||
permission_export_wiki_pages: Export wiki pages
|
||||
setting_cache_formatted_text: Cache formatted text
|
||||
permission_manage_project_activities: Manage project activities
|
||||
error_unable_delete_issue_status: Unable to delete issue status
|
||||
label_profile: Profile
|
||||
permission_manage_subtasks: Manage subtasks
|
||||
field_parent_issue: Parent task
|
||||
label_subtask_plural: Subtasks
|
||||
label_project_copy_notifications: Send email notifications during the project copy
|
||||
error_can_not_delete_custom_field: Unable to delete custom field
|
||||
error_unable_to_connect: Unable to connect ({{value}})
|
||||
error_can_not_remove_role: This role is in use and can not be deleted.
|
||||
error_can_not_delete_tracker: This tracker contains issues and can't be deleted.
|
||||
field_principal: Principal
|
||||
label_my_page_block: My page block
|
||||
notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
|
||||
text_zoom_out: Zoom out
|
||||
text_zoom_in: Zoom in
|
||||
notice_unable_delete_time_entry: Unable to delete time log entry.
|
||||
label_overall_spent_time: Overall spent time
|
||||
field_time_entries: Log time
|
||||
project_module_gantt: Gantt
|
||||
project_module_calendar: Calendar
|
||||
enumeration_system_activity: Activitat del sistema
|
||||
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -116,6 +116,7 @@ cs:
|
||||
greater_than_start_date: "musí být větší než počáteční datum"
|
||||
not_same_project: "nepatří stejnému projektu"
|
||||
circular_dependency: "Tento vztah by vytvořil cyklickou závislost"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
# Updated by Josef Liška <jl@chl.cz>
|
||||
# CZ translation by Maxim Krušina | Massimo Filippi, s.r.o. | maxim@mxm.cz
|
||||
@ -912,3 +913,5 @@ cs:
|
||||
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
|
||||
|
||||
@ -130,6 +130,7 @@ da:
|
||||
greater_than_start_date: "skal være senere end startdatoen"
|
||||
not_same_project: "hører ikke til samme projekt"
|
||||
circular_dependency: "Denne relation vil skabe et afhængighedsforhold"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
template:
|
||||
header:
|
||||
@ -928,3 +929,5 @@ da:
|
||||
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
|
||||
|
||||
@ -932,3 +932,5 @@ de:
|
||||
enumeration_activities: Aktivitäten (Zeiterfassung)
|
||||
enumeration_system_activity: System-Aktivität
|
||||
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -119,6 +119,7 @@ el:
|
||||
greater_than_start_date: "πρέπει να είναι αργότερα από την ημερομηνία έναρξης"
|
||||
not_same_project: "δεν ανήκει στο ίδιο έργο"
|
||||
circular_dependency: "Αυτή η σχέση θα δημιουργήσει κυκλικές εξαρτήσεις"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Παρακαλώ επιλέξτε
|
||||
|
||||
@ -912,3 +913,5 @@ el:
|
||||
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
|
||||
|
||||
@ -122,6 +122,7 @@ en-GB:
|
||||
greater_than_start_date: "must be greater than start date"
|
||||
not_same_project: "doesn't belong to the same project"
|
||||
circular_dependency: "This relation would create a circular dependency"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Please select
|
||||
|
||||
@ -916,3 +917,5 @@ en-GB:
|
||||
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
|
||||
|
||||
@ -293,6 +293,8 @@ en:
|
||||
field_group_by: Group results by
|
||||
field_sharing: Sharing
|
||||
field_parent_issue: Parent task
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
setting_app_title: Application title
|
||||
setting_app_subtitle: Application subtitle
|
||||
|
||||
@ -132,6 +132,7 @@ es:
|
||||
greater_than_start_date: "debe ser posterior a la fecha de comienzo"
|
||||
not_same_project: "no pertenece al mismo proyecto"
|
||||
circular_dependency: "Esta relación podría crear una dependencia circular"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
# Append your own errors here or at the model/attributes scope.
|
||||
|
||||
@ -952,3 +953,5 @@ es:
|
||||
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
|
||||
|
||||
@ -121,6 +121,7 @@ eu:
|
||||
greater_than_start_date: "hasiera data baino handiagoa izan behar du"
|
||||
not_same_project: "ez dago proiektu berdinean"
|
||||
circular_dependency: "Erlazio honek mendekotasun zirkular bat sortuko luke"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Hautatu mesedez
|
||||
|
||||
@ -916,3 +917,5 @@ eu:
|
||||
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
|
||||
|
||||
@ -147,7 +147,7 @@ fi:
|
||||
greater_than_start_date: "tulee olla aloituspäivän jälkeinen"
|
||||
not_same_project: "ei kuulu samaan projektiin"
|
||||
circular_dependency: "Tämä suhde loisi kehän."
|
||||
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Valitse, ole hyvä
|
||||
|
||||
@ -938,3 +938,5 @@ fi:
|
||||
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
|
||||
|
||||
@ -931,3 +931,5 @@ fr:
|
||||
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
|
||||
|
||||
@ -150,6 +150,7 @@ gl:
|
||||
greater_than_start_date: "debe ser posterior á data de comezo"
|
||||
not_same_project: "non pertence ao mesmo proxecto"
|
||||
circular_dependency: "Esta relación podería crear unha dependencia circular"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Por favor seleccione
|
||||
|
||||
@ -928,3 +929,5 @@ gl:
|
||||
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
|
||||
|
||||
@ -123,6 +123,7 @@ he:
|
||||
greater_than_start_date: "חייב להיות מאוחר יותר מתאריך ההתחלה"
|
||||
not_same_project: "לא שייך לאותו הפרויקט"
|
||||
circular_dependency: "הקשר הזה יצור תלות מעגלית"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: בחר בבקשה
|
||||
|
||||
@ -917,3 +918,5 @@ he:
|
||||
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
|
||||
|
||||
@ -117,6 +117,7 @@ hr:
|
||||
greater_than_start_date: "mora biti veci nego pocetni datum"
|
||||
not_same_project: "ne pripada istom projektu"
|
||||
circular_dependency: "Ovaj relacija stvara kružnu ovisnost"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Molimo odaberite
|
||||
|
||||
@ -919,3 +920,5 @@ hr:
|
||||
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
|
||||
|
||||
@ -143,6 +143,7 @@
|
||||
greater_than_start_date: "nagyobbnak kell lennie, mint az indítás dátuma"
|
||||
not_same_project: "nem azonos projekthez tartozik"
|
||||
circular_dependency: "Ez a kapcsolat egy körkörös függőséget eredményez"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Kérem válasszon
|
||||
|
||||
@ -935,3 +936,5 @@
|
||||
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
|
||||
|
||||
@ -119,6 +119,7 @@ id:
|
||||
greater_than_start_date: "harus lebih besar dari tanggal mulai"
|
||||
not_same_project: "tidak tergabung dalam proyek yang sama"
|
||||
circular_dependency: "kaitan ini akan menghasilkan circular dependency"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Silakan pilih
|
||||
|
||||
@ -920,3 +921,5 @@ id:
|
||||
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
|
||||
|
||||
@ -126,6 +126,7 @@ it:
|
||||
greater_than_start_date: "deve essere maggiore della data di partenza"
|
||||
not_same_project: "non appartiene allo stesso progetto"
|
||||
circular_dependency: "Questa relazione creerebbe una dipendenza circolare"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Scegli
|
||||
|
||||
@ -916,3 +917,5 @@ it:
|
||||
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
|
||||
|
||||
@ -144,6 +144,7 @@ ja:
|
||||
greater_than_start_date: "を開始日より後にしてください"
|
||||
not_same_project: "同じプロジェクトに属していません"
|
||||
circular_dependency: "この関係では、循環依存になります"
|
||||
cant_link_an_issue_with_a_descendant: "指定したチケットとは親子関係になっているため関連づけられません"
|
||||
|
||||
actionview_instancetag_blank_option: 選んでください
|
||||
|
||||
@ -318,6 +319,8 @@ ja:
|
||||
field_group_by: グループ条件
|
||||
field_sharing: 共有
|
||||
field_parent_issue: 親チケット
|
||||
field_member_of_group: 担当者のグループ
|
||||
field_assigned_to_role: 担当者のロール
|
||||
|
||||
setting_app_title: アプリケーションのタイトル
|
||||
setting_app_subtitle: アプリケーションのサブタイトル
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# by Kihyun Yoon(ddumbugie@gmail.com),http://plenum.textcube.com/
|
||||
# by John Hwang (jhwang@tavon.org),http://github.com/tavon
|
||||
# by Yonghwan SO(please insert your email), last update at 2009-09-11
|
||||
# last update at 2010-01-23 by Kihyun Yoon
|
||||
# last update at 2010-09-06 by Kihyun Yoon
|
||||
ko:
|
||||
direction: ltr
|
||||
date:
|
||||
@ -173,6 +173,7 @@ ko:
|
||||
greater_than_start_date: "는 시작날짜보다 커야 합니다"
|
||||
not_same_project: "는 같은 프로젝트에 속해 있지 않습니다"
|
||||
circular_dependency: "이 관계는 순환 의존관계를 만들 수 있습니다"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: 선택하세요
|
||||
|
||||
@ -958,13 +959,15 @@ ko:
|
||||
error_unable_to_connect: 연결할 수 없습니다(({{value}})
|
||||
error_can_not_remove_role: 이 역할은 현재 사용 중이이서 삭제할 수 없습니다.
|
||||
error_can_not_delete_tracker: 이 유형의 일감들이 있에서 삭제할 수 없습니다.
|
||||
field_principal: Principal
|
||||
label_my_page_block: My page block
|
||||
field_principal: 신원
|
||||
label_my_page_block: 내 페이지 출력화면
|
||||
notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
|
||||
text_zoom_out: Zoom out
|
||||
text_zoom_in: Zoom in
|
||||
notice_unable_delete_time_entry: Unable to delete time log entry.
|
||||
label_overall_spent_time: Overall spent time
|
||||
field_time_entries: Log time
|
||||
project_module_gantt: Gantt
|
||||
project_module_calendar: Calendar
|
||||
text_zoom_out: 더 작게
|
||||
text_zoom_in: 더 크게
|
||||
notice_unable_delete_time_entry: 시간 기록 항목을 삭제할 수 없습니다.
|
||||
label_overall_spent_time: 총 소요시간
|
||||
field_time_entries: 기록된 시간
|
||||
project_module_gantt: Gantt 챠트
|
||||
project_module_calendar: 달력
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -143,7 +143,7 @@ lt:
|
||||
other: "Išsaugant objektą {{model}} rastos {{count}} klaidos"
|
||||
body: "Šiuose laukuose yra klaidų:"
|
||||
|
||||
pranešimus:
|
||||
messages:
|
||||
inclusion: "nenumatyta reikšmė"
|
||||
exclusion: "užimtas"
|
||||
invalid: "neteisingas"
|
||||
@ -179,6 +179,7 @@ lt:
|
||||
greater_than_start_date: "turi būti didesnė negu pradžios data"
|
||||
not_same_project: "nepriklauso tam pačiam projektui"
|
||||
circular_dependency: "Šis ryšys sukurtų ciklinę priklausomybę"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: prašom parinkti
|
||||
|
||||
@ -976,3 +977,5 @@ lt:
|
||||
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
|
||||
|
||||
@ -113,6 +113,7 @@ lv:
|
||||
greater_than_start_date: "jābūt vēlākam par sākuma datumu"
|
||||
not_same_project: "nepieder pie tā paša projekta"
|
||||
circular_dependency: "Šī relācija radītu ciklisku atkarību"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Izvēlieties
|
||||
|
||||
@ -907,3 +908,5 @@ lv:
|
||||
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
|
||||
|
||||
@ -913,3 +913,5 @@ mk:
|
||||
enumeration_activities: Активности (следење на време)
|
||||
enumeration_system_activity: Системска активност
|
||||
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -117,6 +117,7 @@ mn:
|
||||
greater_than_start_date: "must be greater than start date"
|
||||
not_same_project: "нэг ижил төсөлд хамаарахгүй байна"
|
||||
circular_dependency: "Энэ харьцаа нь гинжин(рекурсив) харьцаа үүсгэх юм байна"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Сонгоно уу
|
||||
|
||||
@ -913,3 +914,5 @@ mn:
|
||||
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
|
||||
|
||||
@ -116,6 +116,7 @@ nl:
|
||||
greater_than_start_date: "moet na de startdatum liggen"
|
||||
not_same_project: "hoort niet bij hetzelfde project"
|
||||
circular_dependency: "Deze relatie zou een circulaire afhankelijkheid tot gevolg hebben"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Selecteer
|
||||
|
||||
@ -701,7 +702,7 @@ nl:
|
||||
setting_date_format: Datumformaat
|
||||
setting_default_language: Standaard taal
|
||||
setting_default_projects_public: Nieuwe projecten zijn standaard publiek
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
setting_diff_max_lines_displayed: Max aantal diff regels weer te geven
|
||||
setting_display_subprojects_issues: Standaard issues van subproject tonen
|
||||
setting_emails_footer: E-mails footer
|
||||
setting_enabled_scm: SCM ingeschakeld
|
||||
@ -709,7 +710,7 @@ nl:
|
||||
setting_gravatar_enabled: Gebruik Gravatar gebruikersiconen
|
||||
setting_host_name: Hostnaam
|
||||
setting_issue_list_default_columns: Standaardkolommen getoond op de lijst met issues
|
||||
setting_issues_export_limit: Limiet export issues
|
||||
setting_issues_export_limit: Max aantal te exporteren issues
|
||||
setting_login_required: Authenticatie vereist
|
||||
setting_mail_from: Afzender e-mail adres
|
||||
setting_mail_handler_api_enabled: Schakel WS in voor inkomende mail.
|
||||
@ -727,7 +728,7 @@ nl:
|
||||
setting_welcome_text: Welkomsttekst
|
||||
setting_wiki_compression: Wikigeschiedenis comprimeren
|
||||
status_active: actief
|
||||
status_locked: gelockt
|
||||
status_locked: vergrendeld
|
||||
status_registered: geregistreerd
|
||||
text_are_you_sure: Weet u het zeker?
|
||||
text_assign_time_entries_to_project: Gerapporteerde uren toevoegen aan dit project
|
||||
@ -753,7 +754,7 @@ nl:
|
||||
text_load_default_configuration: Laad de standaardconfiguratie
|
||||
text_min_max_length_info: 0 betekent geen restrictie
|
||||
text_no_configuration_data: "Rollen, trackers, issue statussen en workflows zijn nog niet geconfigureerd.\nHet is ten zeerste aangeraden om de standaard configuratie in te laden. U kunt deze aanpassen nadat deze is ingeladen."
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
text_plugin_assets_writable: Plugin assets directory beschrijfbaar
|
||||
text_project_destroy_confirmation: Weet u zeker dat u dit project en alle gerelateerde gegevens wilt verwijderen?
|
||||
text_project_identifier_info: 'kleine letters (a-z), cijfers en liggende streepjes toegestaan.<br />Eenmaal bewaard kan de identificatiecode niet meer worden gewijzigd.'
|
||||
text_reassign_time_entries: 'Gerapporteerde uren opnieuw toewijzen:'
|
||||
@ -778,7 +779,7 @@ nl:
|
||||
text_custom_field_possible_values_info: 'Per lijn een waarde'
|
||||
label_display: Toon
|
||||
field_editable: Bewerkbaar
|
||||
setting_repository_log_display_limit: Maximum hoeveelheid van revisies zichbaar
|
||||
setting_repository_log_display_limit: Max aantal revisies zichbaar
|
||||
setting_file_max_size_displayed: Max grootte van tekst bestanden inline zichtbaar
|
||||
field_watcher: Watcher
|
||||
setting_openid: Sta OpenID login en registratie toe
|
||||
@ -824,7 +825,7 @@ nl:
|
||||
version_status_closed: gesloten
|
||||
version_status_locked: vergrendeld
|
||||
version_status_open: open
|
||||
error_can_not_reopen_issue_on_closed_version: An issue assigned to a closed version can not be reopened
|
||||
error_can_not_reopen_issue_on_closed_version: Een issue toegewezen aan een gesloten versie kan niet heropend worden
|
||||
label_user_anonymous: Anoniem
|
||||
button_move_and_follow: Verplaats en volg
|
||||
setting_default_projects_modules: Standaard geactiveerde modules voor nieuwe projecten
|
||||
@ -833,36 +834,36 @@ nl:
|
||||
label_version_sharing_hierarchy: Met project hiërarchie
|
||||
label_version_sharing_system: Met alle projecten
|
||||
label_version_sharing_descendants: Met subprojecten
|
||||
label_version_sharing_tree: With project tree
|
||||
label_version_sharing_tree: Met project boom
|
||||
label_version_sharing_none: Niet gedeeld
|
||||
error_can_not_archive_project: Dit project kan niet worden gearchiveerd
|
||||
button_duplicate: Dupliceer
|
||||
button_copy_and_follow: Kopiëer en volg
|
||||
label_copy_source: Bron
|
||||
setting_issue_done_ratio: Bereken issue done ratio met
|
||||
setting_issue_done_ratio: Bereken issue percentage voldaan met
|
||||
setting_issue_done_ratio_issue_status: Gebruik de issue status
|
||||
error_issue_done_ratios_not_updated: Issue done ratios niet geupdate.
|
||||
error_issue_done_ratios_not_updated: Issue percentage voldaan niet geupdate.
|
||||
error_workflow_copy_target: Selecteer tracker(s) en rol(len)
|
||||
setting_issue_done_ratio_issue_field: Gebruik het issue veld
|
||||
label_copy_same_as_target: Zelfde als doel
|
||||
label_copy_target: Doel
|
||||
notice_issue_done_ratios_updated: Issue done ratios updated.
|
||||
notice_issue_done_ratios_updated: Issue percentage voldaan geupdate.
|
||||
error_workflow_copy_source: Selecteer een bron tracker of rol
|
||||
label_update_issue_done_ratios: Update issue done ratios
|
||||
label_update_issue_done_ratios: Update issue percentage voldaan
|
||||
setting_start_of_week: Week begint op
|
||||
permission_view_issues: Bekijk Issues
|
||||
label_display_used_statuses_only: Laat alleen statussen zien die gebruikt worden door deze tracker
|
||||
label_revision_id: Revision {{value}}
|
||||
label_revision_id: Revisie {{value}}
|
||||
label_api_access_key: API access key
|
||||
label_api_access_key_created_on: API access key gemaakt {{value}} geleden
|
||||
label_feeds_access_key: RSS access key
|
||||
notice_api_access_key_reseted: Uw API access key was gereset.
|
||||
setting_rest_api_enabled: Enable REST web service
|
||||
setting_rest_api_enabled: Activeer REST web service
|
||||
label_missing_api_access_key: Geen API access key
|
||||
label_missing_feeds_access_key: Geen RSS access key
|
||||
button_show: Laat zien
|
||||
text_line_separated: Meerdere waarden toegestaan (elke regel is een waarde).
|
||||
setting_mail_handler_body_delimiters: Truncate emails after one of these lines
|
||||
setting_mail_handler_body_delimiters: Breek email verwerking af na een van deze regels
|
||||
permission_add_subprojects: Maak subprojecten
|
||||
label_subproject_new: Nieuw subproject
|
||||
text_own_membership_delete_confirmation: |-
|
||||
@ -873,10 +874,10 @@ nl:
|
||||
label_board_locked: Vergrendeld
|
||||
permission_export_wiki_pages: Exporteer wiki pagina's
|
||||
setting_cache_formatted_text: Cache opgemaakte tekst
|
||||
permission_manage_project_activities: Manage project activities
|
||||
permission_manage_project_activities: Beheer project activiteiten
|
||||
error_unable_delete_issue_status: Verwijderen van issue status niet gelukt
|
||||
label_profile: Profiel
|
||||
permission_manage_subtasks: Manage subtasks
|
||||
permission_manage_subtasks: Beheer subtasks
|
||||
field_parent_issue: Parent task
|
||||
label_subtask_plural: Subtasks
|
||||
label_project_copy_notifications: Stuur email notificaties voor de project kopie
|
||||
@ -891,6 +892,8 @@ nl:
|
||||
text_zoom_in: Zoom in
|
||||
notice_unable_delete_time_entry: Verwijderen niet mogelijk van tijd log invoer.
|
||||
label_overall_spent_time: Totaal bestede tijd
|
||||
field_time_entries: Log time
|
||||
field_time_entries: Log tijd
|
||||
project_module_gantt: Gantt
|
||||
project_module_calendar: Calendar
|
||||
project_module_calendar: Kalender
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
greater_than_start_date: "må være større enn startdato"
|
||||
not_same_project: "hører ikke til samme prosjekt"
|
||||
circular_dependency: "Denne relasjonen ville lagd en sirkulær avhengighet"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
|
||||
actionview_instancetag_blank_option: Vennligst velg
|
||||
@ -903,3 +904,5 @@
|
||||
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
|
||||
|
||||
@ -96,8 +96,8 @@ pl:
|
||||
few: "ponad {{count}} lata"
|
||||
other: "ponad {{count}} lat"
|
||||
almost_x_years:
|
||||
one: "almost 1 year"
|
||||
other: "almost {{count}} years"
|
||||
one: "prawie rok"
|
||||
other: "prawie {{count}} lata"
|
||||
|
||||
activerecord:
|
||||
errors:
|
||||
@ -122,13 +122,14 @@ pl:
|
||||
greater_than: "musi być większe niż {{count}}"
|
||||
greater_than_or_equal_to: "musi być większe lub równe {{count}}"
|
||||
equal_to: "musi być równe {{count}}"
|
||||
less_than: "musie być mniejsze niż {{count}}"
|
||||
less_than: "musi być mniejsze niż {{count}}"
|
||||
less_than_or_equal_to: "musi być mniejsze lub równe {{count}}"
|
||||
odd: "musi być nieparzyste"
|
||||
even: "musi być parzyste"
|
||||
greater_than_start_date: "musi być większe niż początkowa data"
|
||||
not_same_project: "nie należy do tego samego projektu"
|
||||
circular_dependency: "Ta relacja może wytworzyć kołową zależność"
|
||||
cant_link_an_issue_with_a_descendant: "Zagadnienie nie może zostać powiązane z jednym z własnych podzagadnień"
|
||||
|
||||
support:
|
||||
array:
|
||||
@ -159,19 +160,19 @@ pl:
|
||||
button_edit: Edytuj
|
||||
button_list: Lista
|
||||
button_lock: Zablokuj
|
||||
button_log_time: Log czasu
|
||||
button_log_time: Dziennik
|
||||
button_login: Login
|
||||
button_move: Przenieś
|
||||
button_quote: Cytuj
|
||||
button_rename: Zmień nazwę
|
||||
button_reply: Odpowiedz
|
||||
button_reset: Resetuj
|
||||
button_rollback: Przywróc do tej wersji
|
||||
button_rollback: Przywróć do tej wersji
|
||||
button_save: Zapisz
|
||||
button_sort: Sortuj
|
||||
button_submit: Wyślij
|
||||
button_test: Testuj
|
||||
button_unarchive: Przywróc z archiwum
|
||||
button_unarchive: Przywróć z archiwum
|
||||
button_uncheck_all: Odznacz wszystko
|
||||
button_unlock: Odblokuj
|
||||
button_unwatch: Nie obserwuj
|
||||
@ -538,12 +539,12 @@ pl:
|
||||
label_project_latest: Ostatnie projekty
|
||||
label_project_new: Nowy projekt
|
||||
label_project_plural234: Projekty
|
||||
label_project_plural5: Projekty
|
||||
label_project_plural5: Projektów
|
||||
label_project_plural: Projekty
|
||||
label_x_projects:
|
||||
zero: no projects
|
||||
one: 1 project
|
||||
other: "{{count}} projects"
|
||||
zero: brak projektów
|
||||
one: jeden projekt
|
||||
other: "{{count}} projektów"
|
||||
label_public_projects: Projekty publiczne
|
||||
label_query: Kwerenda
|
||||
label_query_new: Nowa kwerenda
|
||||
@ -589,7 +590,7 @@ pl:
|
||||
label_sort_highest: Przesuń na górę
|
||||
label_sort_lower: Do dołu
|
||||
label_sort_lowest: Przesuń na dół
|
||||
label_spent_time: Spędzony czas
|
||||
label_spent_time: Przepracowany czas
|
||||
label_start_to_end: początek do końca
|
||||
label_start_to_start: początek do początku
|
||||
label_statistics: Statystyki
|
||||
@ -601,7 +602,7 @@ pl:
|
||||
label_this_month: ten miesiąc
|
||||
label_this_week: ten tydzień
|
||||
label_this_year: ten rok
|
||||
label_time_tracking: Śledzenie czasu
|
||||
label_time_tracking: Śledzenie czasu pracy
|
||||
label_today: dzisiaj
|
||||
label_topic_plural: Tematy
|
||||
label_total: Ogółem
|
||||
@ -683,11 +684,11 @@ pl:
|
||||
permission_edit_messages: Edycja wiadomości
|
||||
permission_edit_own_issue_notes: Edycja własnych notatek
|
||||
permission_edit_own_messages: Edycja własnych wiadomości
|
||||
permission_edit_own_time_entries: Edycja własnego logu czasu
|
||||
permission_edit_own_time_entries: Edycja własnego dziennika
|
||||
permission_edit_project: Edycja projektów
|
||||
permission_edit_time_entries: Edycja logów czasu
|
||||
permission_edit_time_entries: Edycja wpisów dziennika
|
||||
permission_edit_wiki_pages: Edycja stron wiki
|
||||
permission_log_time: Zapisywanie spędzonego czasu
|
||||
permission_log_time: Zapisywanie przepracowanego czasu
|
||||
permission_manage_boards: Zarządzanie forami
|
||||
permission_manage_categories: Zarządzanie kategoriami zaganień
|
||||
permission_manage_documents: Zarządzanie dokumentami
|
||||
@ -711,7 +712,7 @@ pl:
|
||||
permission_view_gantt: Podgląd diagramu Gantta
|
||||
permission_view_issue_watchers: Podgląd listy obserwatorów
|
||||
permission_view_messages: Podgląd wiadomości
|
||||
permission_view_time_entries: Podgląd spędzonego czasu
|
||||
permission_view_time_entries: Podgląd przepracowanego czasu
|
||||
permission_view_wiki_edits: Podgląd historii wiki
|
||||
permission_view_wiki_pages: Podgląd wiki
|
||||
project_module_boards: Fora
|
||||
@ -720,7 +721,7 @@ pl:
|
||||
project_module_issue_tracking: Śledzenie zagadnień
|
||||
project_module_news: Komunikaty
|
||||
project_module_repository: Repozytorium
|
||||
project_module_time_tracking: Śledzenie czasu
|
||||
project_module_time_tracking: Śledzenie czasu pracy
|
||||
project_module_wiki: Wiki
|
||||
setting_activity_days_default: Dni wyświetlane w aktywności projektu
|
||||
setting_app_subtitle: Podtytuł aplikacji
|
||||
@ -742,7 +743,7 @@ pl:
|
||||
setting_feeds_limit: Limit danych RSS
|
||||
setting_gravatar_enabled: Używaj ikon użytkowników Gravatar
|
||||
setting_host_name: Nazwa hosta i ścieżka
|
||||
setting_issue_list_default_columns: Domyślne kolumny wiświetlane na liście zagadnień
|
||||
setting_issue_list_default_columns: Domyślne kolumny wyświetlane na liście zagadnień
|
||||
setting_issues_export_limit: Limit eksportu zagadnień
|
||||
setting_login_required: Identyfikacja wymagana
|
||||
setting_mail_from: Adres email wysyłki
|
||||
@ -764,20 +765,20 @@ pl:
|
||||
status_locked: zablokowany
|
||||
status_registered: zarejestrowany
|
||||
text_are_you_sure: Jesteś pewien ?
|
||||
text_assign_time_entries_to_project: Przypisz logowany czas do projektu
|
||||
text_assign_time_entries_to_project: Przypisz wpisy dziennika do projektu
|
||||
text_caracters_maximum: "{{count}} znaków maksymalnie."
|
||||
text_caracters_minimum: "Musi być nie krótsze niż {{count}} znaków."
|
||||
text_comma_separated: Wielokrotne wartości dozwolone (rozdzielone przecinkami).
|
||||
text_default_administrator_account_changed: Zmieniono domyślne hasło administratora
|
||||
text_destroy_time_entries: Usuń zalogowany czas
|
||||
text_destroy_time_entries_question: Zalogowano {{hours}} godzin przy zagadnieniu, które chcesz usunąć. Co chcesz zrobić?
|
||||
text_destroy_time_entries: Usuń wpisy dziennika
|
||||
text_destroy_time_entries_question: Przepracowano {{hours}} godzin przy zagadnieniu, które chcesz usunąć. Co chcesz zrobić?
|
||||
text_email_delivery_not_configured: "Dostarczanie poczty elektronicznej nie zostało skonfigurowane, więc powiadamianie jest nieaktywne.\nSkonfiguruj serwer SMTP w config/email.yml a następnie zrestartuj aplikację i uaktywnij to."
|
||||
text_enumeration_category_reassign_to: 'Zmień przypisanie na tą wartość:'
|
||||
text_enumeration_destroy_question: "{{count}} obiektów jest przypisana do tej wartości."
|
||||
text_file_repository_writable: Zapisywalne repozytorium plików
|
||||
text_issue_added: "Zagadnienie {{id}} zostało wprowadzone (by {{author}})."
|
||||
text_issue_category_destroy_assignments: Usuń przydziały kategorii
|
||||
text_issue_category_destroy_question: "Zagadnienia ({{count}}) są przypisane do tej kategorii. Co chcesz uczynić?"
|
||||
text_issue_category_destroy_question: "Zagadnienia ({{count}}) są przypisane do tej kategorii. Co chcesz zrobić?"
|
||||
text_issue_category_reassign_to: Przydziel zagadnienie do tej kategorii
|
||||
text_issue_updated: "Zagadnienie {{id}} zostało zaktualizowane (by {{author}})."
|
||||
text_issues_destroy_confirmation: 'Czy jestes pewien, że chcesz usunąć wskazane zagadnienia?'
|
||||
@ -786,9 +787,9 @@ pl:
|
||||
text_load_default_configuration: Załaduj domyślną konfigurację
|
||||
text_min_max_length_info: 0 oznacza brak restrykcji
|
||||
text_no_configuration_data: "Role użytkowników, typy zagadnień, statusy zagadnień oraz przepływ pracy nie zostały jeszcze skonfigurowane.\nJest wysoce rekomendowane by załadować domyślną konfigurację. Po załadowaniu będzie możliwość edycji tych danych."
|
||||
text_project_destroy_confirmation: Jesteś pewien, że chcesz usunąć ten projekt i wszyskie powiązane dane?
|
||||
text_project_destroy_confirmation: Jesteś pewien, że chcesz usunąć ten projekt i wszystkie powiązane dane?
|
||||
text_project_identifier_info: 'Małe litery (a-z), liczby i myślniki dozwolone.<br />Raz zapisany, identyfikator nie może być zmieniony.'
|
||||
text_reassign_time_entries: 'Przepnij zalogowany czas do tego zagadnienia:'
|
||||
text_reassign_time_entries: 'Przepnij przepracowany czas do tego zagadnienia:'
|
||||
text_regexp_info: np. ^[A-Z0-9]+$
|
||||
text_repository_usernames_mapping: "Wybierz lub uaktualnij przyporządkowanie użytkowników Redmine do użytkowników repozytorium.\nUżytkownicy z taką samą nazwą lub adresem email są przyporządkowani automatycznie."
|
||||
text_rmagick_available: RMagick dostępne (opcjonalnie)
|
||||
@ -799,9 +800,9 @@ pl:
|
||||
text_tip_task_begin_day: zadanie zaczynające się dzisiaj
|
||||
text_tip_task_begin_end_day: zadanie zaczynające i kończące się dzisiaj
|
||||
text_tip_task_end_day: zadanie kończące się dzisiaj
|
||||
text_tracker_no_workflow: Brak przepływu zefiniowanego dla tego typu zagadnienia
|
||||
text_tracker_no_workflow: Brak przepływu zdefiniowanego dla tego typu zagadnienia
|
||||
text_unallowed_characters: Niedozwolone znaki
|
||||
text_user_mail_option: "W przypadku niezaznaczonych projektów, będziesz otrzymywał powiadomienia tylko na temat zagadnien, które obserwujesz, lub w których bierzesz udział (np. jesteś autorem lub adresatem)."
|
||||
text_user_mail_option: "W przypadku niezaznaczonych projektów, będziesz otrzymywał powiadomienia tylko na temat zagadnień, które obserwujesz, lub w których bierzesz udział (np. jesteś autorem lub adresatem)."
|
||||
text_user_wrote: "{{value}} napisał:"
|
||||
text_wiki_destroy_confirmation: Jesteś pewien, że chcesz usunąć to wiki i całą jego zawartość ?
|
||||
text_workflow_edit: Zaznacz rolę i typ zagadnienia do edycji przepływu
|
||||
@ -817,7 +818,7 @@ pl:
|
||||
button_create_and_continue: Stwórz i dodaj kolejne
|
||||
text_custom_field_possible_values_info: 'Każda wartość w osobnej linii'
|
||||
setting_repository_log_display_limit: Maksymalna liczba rewizji pokazywanych w logu pliku
|
||||
setting_file_max_size_displayed: Maksymalny rozmiar plików tekstowych zagnieżdżanych w stronie
|
||||
setting_file_max_size_displayed: Maksymalny rozmiar plików tekstowych osadzanych w stronie
|
||||
field_watcher: Obserwator
|
||||
setting_openid: Logowanie i rejestracja przy użyciu OpenID
|
||||
field_identity_url: Identyfikator OpenID (URL)
|
||||
@ -829,10 +830,10 @@ pl:
|
||||
label_date_from_to: Od {{start}} do {{end}}
|
||||
label_greater_or_equal: ">="
|
||||
label_less_or_equal: <=
|
||||
text_wiki_page_destroy_question: This page has {{descendants}} child page(s) and descendant(s). What do you want to do?
|
||||
text_wiki_page_reassign_children: Reassign child pages to this parent page
|
||||
text_wiki_page_nullify_children: Keep child pages as root pages
|
||||
text_wiki_page_destroy_children: Delete child pages and all their descendants
|
||||
text_wiki_page_destroy_question: Ta strona posiada podstrony ({{descendants}}). Co chcesz zrobić?
|
||||
text_wiki_page_reassign_children: Podepnij je do strony nadrzędnej względem usuwanej
|
||||
text_wiki_page_nullify_children: Przesuń je na szczyt hierarchii
|
||||
text_wiki_page_destroy_children: Usuń wszystkie podstrony
|
||||
setting_password_min_length: Minimalna długość hasła
|
||||
field_group_by: Grupuj wyniki wg
|
||||
mail_subject_wiki_content_updated: "Strona wiki '{{page}}' została uaktualniona"
|
||||
@ -844,7 +845,7 @@ pl:
|
||||
permission_add_project: Tworzenie projektu
|
||||
setting_new_project_user_role_id: Rola nadawana twórcom projektów, którzy nie posiadają uprawnień administatora
|
||||
label_view_all_revisions: Pokaż wszystkie rewizje
|
||||
label_tag: Tag
|
||||
label_tag: Słowo kluczowe
|
||||
label_branch: Gałąź
|
||||
error_no_tracker_in_project: Projekt nie posiada powiązanych typów zagadnień. Sprawdź ustawienia projektu.
|
||||
error_no_default_issue_status: Nie zdefiniowano domyślnego statusu zagadnień. Sprawdź konfigurację (Przejdź do "Administracja -> Statusy zagadnień).
|
||||
@ -854,7 +855,7 @@ pl:
|
||||
label_group_plural: Grupy
|
||||
label_group: Grupa
|
||||
label_group_new: Nowa grupa
|
||||
label_time_entry_plural: Spędzony czas
|
||||
label_time_entry_plural: Przepracowany czas
|
||||
text_journal_added: "Dodano {{label}} {{value}}"
|
||||
field_active: Aktywne
|
||||
enumeration_system_activity: Aktywność Systemowa
|
||||
@ -912,24 +913,26 @@ pl:
|
||||
label_board_locked: Zamknięta
|
||||
permission_export_wiki_pages: Eksport stron wiki
|
||||
permission_manage_project_activities: Zarządzanie aktywnościami projektu
|
||||
setting_cache_formatted_text: Cache formatted text
|
||||
error_unable_delete_issue_status: Unable to delete issue status
|
||||
label_profile: Profile
|
||||
permission_manage_subtasks: Manage subtasks
|
||||
field_parent_issue: Parent task
|
||||
label_subtask_plural: Subtasks
|
||||
label_project_copy_notifications: Send email notifications during the project copy
|
||||
error_can_not_delete_custom_field: Unable to delete custom field
|
||||
error_unable_to_connect: Unable to connect ({{value}})
|
||||
error_can_not_remove_role: This role is in use and can not be deleted.
|
||||
error_can_not_delete_tracker: This tracker contains issues and can't be deleted.
|
||||
field_principal: Principal
|
||||
label_my_page_block: My page block
|
||||
notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
|
||||
text_zoom_out: Zoom out
|
||||
text_zoom_in: Zoom in
|
||||
notice_unable_delete_time_entry: Unable to delete time log entry.
|
||||
label_overall_spent_time: Overall spent time
|
||||
field_time_entries: Log time
|
||||
project_module_gantt: Gantt
|
||||
project_module_calendar: Calendar
|
||||
setting_cache_formatted_text: Buforuj sformatowany tekst
|
||||
error_unable_delete_issue_status: Nie można usunąć statusu zagadnienia
|
||||
label_profile: Profil
|
||||
permission_manage_subtasks: Zarządzanie podzagadnieniami
|
||||
field_parent_issue: Zagadnienie nadrzędne
|
||||
label_subtask_plural: Podzagadnienia
|
||||
label_project_copy_notifications: Wyślij powiadomienia mailowe przy kopiowaniu projektu
|
||||
error_can_not_delete_custom_field: Nie można usunąć tego pola
|
||||
error_unable_to_connect: Nie można połączyć ({{value}})
|
||||
error_can_not_remove_role: Ta rola przypisana jest niektórym użytkownikom i nie może zostać usunięta.
|
||||
error_can_not_delete_tracker: Ten typ przypisany jest do części zagadnień i nie może zostać usunięty.
|
||||
field_principal: Przełożony
|
||||
label_my_page_block: Elementy
|
||||
notice_failed_to_save_members: "Nie można zapisać uczestników: {{errors}}."
|
||||
text_zoom_out: Zmniejsz czcionkę
|
||||
text_zoom_in: Powiększ czcionkę
|
||||
notice_unable_delete_time_entry: Nie można usunąć wpisu z dziennika.
|
||||
label_overall_spent_time: Przepracowany czas
|
||||
field_time_entries: Dziennik
|
||||
project_module_gantt: Diagram Gantta
|
||||
project_module_calendar: Kalendarz
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -143,6 +143,7 @@ pt-BR:
|
||||
greater_than_start_date: "deve ser maior que a data inicial"
|
||||
not_same_project: "não pertence ao mesmo projeto"
|
||||
circular_dependency: "Esta relação geraria uma dependência circular"
|
||||
cant_link_an_issue_with_a_descendant: "Uma tarefa não pode ser relaciona a uma de suas subtarefas"
|
||||
|
||||
actionview_instancetag_blank_option: Selecione
|
||||
|
||||
@ -936,3 +937,5 @@ pt-BR:
|
||||
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
|
||||
|
||||
@ -128,6 +128,7 @@ pt:
|
||||
greater_than_start_date: "deve ser maior que a data inicial"
|
||||
not_same_project: "não pertence ao mesmo projecto"
|
||||
circular_dependency: "Esta relação iria criar uma dependência circular"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
## Translated by: Pedro Araújo <phcrva19@hotmail.com>
|
||||
actionview_instancetag_blank_option: Seleccione
|
||||
@ -920,3 +921,5 @@ pt:
|
||||
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
|
||||
|
||||
@ -110,6 +110,7 @@ ro:
|
||||
greater_than_start_date: "trebuie să fie după data de început"
|
||||
not_same_project: "trebuie să aparțină aceluiași proiect"
|
||||
circular_dependency: "Această relație ar crea o dependență circulară"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Selectați
|
||||
|
||||
@ -905,3 +906,5 @@ ro:
|
||||
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
|
||||
|
||||
@ -192,11 +192,12 @@ ru:
|
||||
equal_to: "может иметь лишь значение, равное {{count}}"
|
||||
less_than: "может иметь значение меньшее чем {{count}}"
|
||||
less_than_or_equal_to: "может иметь значение меньшее или равное {{count}}"
|
||||
odd: "может иметь лишь четное значение"
|
||||
even: "может иметь лишь нечетное значение"
|
||||
odd: "может иметь лишь нечетное значение"
|
||||
even: "может иметь лишь четное значение"
|
||||
greater_than_start_date: "должна быть позднее даты начала"
|
||||
not_same_project: "не относятся к одному проекту"
|
||||
not_same_project: "не относится к одному проекту"
|
||||
circular_dependency: "Такая связь приведет к циклической зависимости"
|
||||
cant_link_an_issue_with_a_descendant: "Задача не может быть связана со своей подзадачей"
|
||||
|
||||
support:
|
||||
array:
|
||||
@ -293,6 +294,7 @@ ru:
|
||||
field_admin: Администратор
|
||||
field_assignable: Задача может быть назначена этой роли
|
||||
field_assigned_to: Назначена
|
||||
field_assigned_to_role: Роль участника
|
||||
field_attr_firstname: Имя
|
||||
field_attr_lastname: Фамилия
|
||||
field_attr_login: Атрибут Регистрация
|
||||
@ -342,6 +344,7 @@ ru:
|
||||
field_mail: Email
|
||||
field_mail_notification: Уведомления по email
|
||||
field_max_length: Максимальная длина
|
||||
field_member_of_group: Группа участника
|
||||
field_min_length: Минимальная длина
|
||||
field_name: Имя
|
||||
field_new_password: Новый пароль
|
||||
|
||||
@ -112,6 +112,7 @@ sk:
|
||||
greater_than_start_date: "musí byť neskôr ako počiatočný dátum"
|
||||
not_same_project: "nepatrí rovnakému projektu"
|
||||
circular_dependency: "Tento vzťah by vytvoril cyklickú závislosť"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
# SK translation by Stanislav Pach | stano.pach@seznam.cz
|
||||
|
||||
@ -907,3 +908,5 @@ sk:
|
||||
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
|
||||
|
||||
@ -116,6 +116,7 @@ sl:
|
||||
greater_than_start_date: "mora biti kasnejši kot začeten datum"
|
||||
not_same_project: "ne pripada istemu projektu"
|
||||
circular_dependency: "Ta odnos bi povzročil krožno odvisnost"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Prosimo izberite
|
||||
|
||||
@ -908,3 +909,5 @@ sl:
|
||||
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
|
||||
|
||||
@ -68,7 +68,7 @@ sr-YU:
|
||||
|
||||
number:
|
||||
format:
|
||||
separator: "."
|
||||
separator: ","
|
||||
delimiter: ""
|
||||
precision: 3
|
||||
human:
|
||||
@ -910,6 +910,8 @@ sr-YU:
|
||||
enumeration_activities: Aktivnosti (praćenje vremena)
|
||||
enumeration_system_activity: Sistemska aktivnost
|
||||
|
||||
field_time_entries: Log time
|
||||
project_module_gantt: Gantt
|
||||
project_module_calendar: Calendar
|
||||
field_time_entries: Vreme evidencije
|
||||
project_module_gantt: Gantov dijagram
|
||||
project_module_calendar: Kalendar
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -68,7 +68,7 @@ sr:
|
||||
|
||||
number:
|
||||
format:
|
||||
separator: "."
|
||||
separator: ","
|
||||
delimiter: ""
|
||||
precision: 3
|
||||
human:
|
||||
@ -910,6 +910,9 @@ sr:
|
||||
enumeration_activities: Активности (праћење времена)
|
||||
enumeration_system_activity: Системска активност
|
||||
|
||||
field_time_entries: Log time
|
||||
project_module_gantt: Gantt
|
||||
project_module_calendar: Calendar
|
||||
field_time_entries: Време евиденције
|
||||
project_module_gantt: Гантов дијаграм
|
||||
project_module_calendar: Календар
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
|
||||
@ -130,6 +130,7 @@ sv:
|
||||
greater_than_start_date: "måste vara senare än startdatumet"
|
||||
not_same_project: "tillhör inte samma projekt"
|
||||
circular_dependency: "Denna relation skulle skapa ett cirkulärt beroende"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
direction: ltr
|
||||
date:
|
||||
@ -957,3 +958,5 @@ sv:
|
||||
enumeration_doc_categories: Dokumentkategorier
|
||||
enumeration_activities: Aktiviteter (tidsuppföljning)
|
||||
enumeration_system_activity: Systemaktivitet
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -116,6 +116,7 @@ th:
|
||||
greater_than_start_date: "ต้องมากกว่าวันเริ่ม"
|
||||
not_same_project: "ไม่ได้อยู่ในโครงการเดียวกัน"
|
||||
circular_dependency: "ความสัมพันธ์อ้างอิงเป็นวงกลม"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: กรุณาเลือก
|
||||
|
||||
@ -909,3 +910,5 @@ th:
|
||||
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
|
||||
|
||||
@ -143,6 +143,7 @@ tr:
|
||||
greater_than_start_date: "başlangıç tarihinden büyük olmalı"
|
||||
not_same_project: "aynı projeye ait değil"
|
||||
circular_dependency: "Bu ilişki döngüsel bağımlılık meydana getirecektir"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
models:
|
||||
|
||||
actionview_instancetag_blank_option: Lütfen Seçin
|
||||
@ -935,3 +936,5 @@ tr:
|
||||
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
|
||||
|
||||
@ -116,6 +116,7 @@ uk:
|
||||
greater_than_start_date: "повинна бути пізніша за дату початку"
|
||||
not_same_project: "не відносяться до одного проекту"
|
||||
circular_dependency: "Такий зв'язок приведе до циклічної залежності"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: Оберіть
|
||||
|
||||
@ -908,3 +909,5 @@ uk:
|
||||
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
|
||||
|
||||
@ -139,6 +139,7 @@ vi:
|
||||
greater_than_start_date: "phải đi sau ngày bắt đầu"
|
||||
not_same_project: "không thuộc cùng dự án"
|
||||
circular_dependency: "quan hệ có thể gây ra lặp vô tận"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
direction: ltr
|
||||
date:
|
||||
@ -967,3 +968,5 @@ vi:
|
||||
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
|
||||
|
||||
@ -177,6 +177,7 @@
|
||||
greater_than_start_date: "必須在起始日期之後"
|
||||
not_same_project: "不屬於同一個專案"
|
||||
circular_dependency: "這個關聯會導致環狀相依"
|
||||
cant_link_an_issue_with_a_descendant: "項目無法被連結至自己的子項目"
|
||||
|
||||
# You can define own errors for models or model attributes.
|
||||
# The values :model, :attribute and :value are always available for interpolation.
|
||||
@ -997,3 +998,5 @@
|
||||
enumeration_doc_categories: 文件分類
|
||||
enumeration_activities: 活動 (時間追蹤)
|
||||
enumeration_system_activity: 系統活動
|
||||
field_member_of_group: Member of Group
|
||||
field_assigned_to_role: Member of Role
|
||||
|
||||
@ -137,6 +137,7 @@ zh:
|
||||
greater_than_start_date: "必须在起始日期之后"
|
||||
not_same_project: "不属于同一个项目"
|
||||
circular_dependency: "此关联将导致循环依赖"
|
||||
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
|
||||
|
||||
actionview_instancetag_blank_option: 请选择
|
||||
|
||||
@ -930,3 +931,5 @@ zh:
|
||||
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
|
||||
|
||||
@ -172,55 +172,41 @@ ActionController::Routing::Routes.draw do |map|
|
||||
user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
|
||||
end
|
||||
end
|
||||
|
||||
map.with_options :controller => 'projects' do |projects|
|
||||
projects.with_options :conditions => {:method => :get} do |project_views|
|
||||
project_views.connect 'projects', :action => 'index'
|
||||
project_views.connect 'projects.:format', :action => 'index'
|
||||
project_views.connect 'projects/new', :action => 'add'
|
||||
project_views.connect 'projects/:id', :action => 'show'
|
||||
project_views.connect 'projects/:id.:format', :action => 'show'
|
||||
project_views.connect 'projects/:id/:action', :action => /destroy|settings/
|
||||
project_views.connect 'projects/:id/files', :controller => 'files', :action => 'index'
|
||||
project_views.connect 'projects/:id/files/new', :action => 'add_file'
|
||||
project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
|
||||
|
||||
# For nice "roadmap" in the url for the index action
|
||||
map.connect 'projects/:project_id/roadmap', :controller => 'versions', :action => 'index'
|
||||
|
||||
map.resources :projects, :member => {
|
||||
:copy => [:get, :post],
|
||||
:settings => :get,
|
||||
:modules => :post,
|
||||
:archive => :post,
|
||||
:unarchive => :post
|
||||
} do |project|
|
||||
project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy]
|
||||
project.resources :files, :only => [:index, :new, :create]
|
||||
project.resources :versions, :collection => {:close_completed => :put}, :member => {:status_by => :post}
|
||||
end
|
||||
|
||||
# Destroy uses a get request to prompt the user before the actual DELETE request
|
||||
map.project_destroy_confirm 'projects/:id/destroy', :controller => 'projects', :action => 'destroy', :conditions => {:method => :get}
|
||||
|
||||
# TODO: port to be part of the resources route(s)
|
||||
map.with_options :controller => 'projects' do |project_mapper|
|
||||
project_mapper.with_options :conditions => {:method => :get} do |project_views|
|
||||
project_views.connect 'projects/:id/settings/:tab', :controller => 'projects', :action => 'settings'
|
||||
project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new'
|
||||
end
|
||||
end
|
||||
|
||||
map.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity|
|
||||
activity.connect 'projects/:id/activity'
|
||||
activity.connect 'projects/:id/activity.:format'
|
||||
activity.connect 'activity', :id => nil
|
||||
activity.connect 'activity.:format', :id => nil
|
||||
end
|
||||
|
||||
projects.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity|
|
||||
activity.connect 'projects/:id/activity'
|
||||
activity.connect 'projects/:id/activity.:format'
|
||||
activity.connect 'activity', :id => nil
|
||||
activity.connect 'activity.:format', :id => nil
|
||||
end
|
||||
|
||||
projects.with_options :conditions => {:method => :post} do |project_actions|
|
||||
project_actions.connect 'projects/new', :action => 'add'
|
||||
project_actions.connect 'projects', :action => 'add'
|
||||
project_actions.connect 'projects.:format', :action => 'add', :format => /xml/
|
||||
project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/
|
||||
project_actions.connect 'projects/:id/files/new', :action => 'add_file'
|
||||
project_actions.connect 'projects/:id/activities/save', :action => 'save_activities'
|
||||
end
|
||||
|
||||
projects.with_options :conditions => {:method => :put} do |project_actions|
|
||||
project_actions.conditions 'projects/:id.:format', :action => 'edit', :format => /xml/
|
||||
end
|
||||
|
||||
projects.with_options :conditions => {:method => :delete} do |project_actions|
|
||||
project_actions.conditions 'projects/:id.:format', :action => 'destroy', :format => /xml/
|
||||
project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities'
|
||||
end
|
||||
end
|
||||
|
||||
map.with_options :controller => 'versions' do |versions|
|
||||
versions.connect 'projects/:project_id/versions/new', :action => 'new'
|
||||
versions.connect 'projects/:project_id/roadmap', :action => 'index'
|
||||
versions.with_options :conditions => {:method => :post} do |version_actions|
|
||||
version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed'
|
||||
end
|
||||
end
|
||||
|
||||
map.with_options :controller => 'issue_categories' do |categories|
|
||||
categories.connect 'projects/:project_id/issue_categories/new', :action => 'new'
|
||||
end
|
||||
|
||||
@ -46,12 +46,12 @@ end
|
||||
Redmine::AccessControl.map do |map|
|
||||
map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true
|
||||
map.permission :search_project, {:search => :index}, :public => true
|
||||
map.permission :add_project, {:projects => :add}, :require => :loggedin
|
||||
map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
|
||||
map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
|
||||
map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
|
||||
map.permission :select_project_modules, {:projects => :modules}, :require => :member
|
||||
map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member
|
||||
map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :close_completed, :destroy]}, :require => :member
|
||||
map.permission :add_subprojects, {:projects => :add}, :require => :member
|
||||
map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
|
||||
map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
|
||||
|
||||
map.project_module :issue_tracking do |map|
|
||||
# Issue categories
|
||||
@ -87,7 +87,7 @@ Redmine::AccessControl.map do |map|
|
||||
map.permission :view_time_entries, :timelog => [:details, :report]
|
||||
map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member
|
||||
map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin
|
||||
map.permission :manage_project_activities, {:projects => [:save_activities, :reset_activities]}, :require => :member
|
||||
map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
|
||||
end
|
||||
|
||||
map.project_module :news do |map|
|
||||
@ -102,7 +102,7 @@ Redmine::AccessControl.map do |map|
|
||||
end
|
||||
|
||||
map.project_module :files do |map|
|
||||
map.permission :manage_files, {:projects => :add_file}, :require => :loggedin
|
||||
map.permission :manage_files, {:files => [:new, :create]}, :require => :loggedin
|
||||
map.permission :view_files, :files => :index, :versions => :download
|
||||
end
|
||||
|
||||
@ -198,7 +198,7 @@ Redmine::MenuManager.map :project_menu do |menu|
|
||||
:if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
|
||||
menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
|
||||
:if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
|
||||
menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural
|
||||
menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural, :param => :project_id
|
||||
menu.push :repository, { :controller => 'repositories', :action => 'show' },
|
||||
:if => Proc.new { |p| p.repository && !p.repository.new_record? }
|
||||
menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
|
||||
|
||||
@ -184,7 +184,7 @@ module Redmine
|
||||
end
|
||||
pdf.Output
|
||||
end
|
||||
|
||||
|
||||
# Returns a PDF string of a single issue
|
||||
def issue_to_pdf(issue)
|
||||
pdf = IFPDF.new(current_language)
|
||||
@ -208,7 +208,7 @@ module Redmine
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, issue.priority.to_s,"RT")
|
||||
pdf.Ln
|
||||
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_author) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
@ -238,14 +238,14 @@ module Redmine
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, format_date(issue.due_date),"RB")
|
||||
pdf.Ln
|
||||
|
||||
|
||||
for custom_value in issue.custom_field_values
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.MultiCell(155,5, (show_value custom_value),"R")
|
||||
end
|
||||
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_subject) + ":","LTB")
|
||||
pdf.SetFontStyle('',9)
|
||||
@ -311,187 +311,7 @@ module Redmine
|
||||
end
|
||||
pdf.Output
|
||||
end
|
||||
|
||||
# Returns a PDF string of a gantt chart
|
||||
def gantt_to_pdf(gantt, project)
|
||||
pdf = IFPDF.new(current_language)
|
||||
pdf.SetTitle("#{l(:label_gantt)} #{project}")
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage("L")
|
||||
pdf.SetFontStyle('B',12)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(70, 20, project.to_s)
|
||||
pdf.Ln
|
||||
pdf.SetFontStyle('B',9)
|
||||
|
||||
subject_width = 100
|
||||
header_heigth = 5
|
||||
|
||||
headers_heigth = header_heigth
|
||||
show_weeks = false
|
||||
show_days = false
|
||||
|
||||
if gantt.months < 7
|
||||
show_weeks = true
|
||||
headers_heigth = 2*header_heigth
|
||||
if gantt.months < 3
|
||||
show_days = true
|
||||
headers_heigth = 3*header_heigth
|
||||
end
|
||||
end
|
||||
|
||||
g_width = 280 - subject_width
|
||||
zoom = (g_width) / (gantt.date_to - gantt.date_from + 1)
|
||||
g_height = 120
|
||||
t_height = g_height + headers_heigth
|
||||
|
||||
y_start = pdf.GetY
|
||||
|
||||
# Months headers
|
||||
month_f = gantt.date_from
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
gantt.months.times do
|
||||
width = ((month_f >> 1) - month_f) * zoom
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
|
||||
left = left + width
|
||||
month_f = month_f >> 1
|
||||
end
|
||||
|
||||
# Weeks headers
|
||||
if show_weeks
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
if gantt.date_from.cwday == 1
|
||||
# gantt.date_from is monday
|
||||
week_f = gantt.date_from
|
||||
else
|
||||
# find next monday after gantt.date_from
|
||||
week_f = gantt.date_from + (7 - gantt.date_from.cwday + 1)
|
||||
width = (7 - gantt.date_from.cwday + 1) * zoom-1
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width + 1, height, "", "LTR")
|
||||
left = left + width+1
|
||||
end
|
||||
while week_f <= gantt.date_to
|
||||
width = (week_f + 6 <= gantt.date_to) ? 7 * zoom : (gantt.date_to - week_f + 1) * zoom
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
|
||||
left = left + width
|
||||
week_f = week_f+7
|
||||
end
|
||||
end
|
||||
|
||||
# Days headers
|
||||
if show_days
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
wday = gantt.date_from.cwday
|
||||
pdf.SetFontStyle('B',7)
|
||||
(gantt.date_to - gantt.date_from + 1).to_i.times do
|
||||
width = zoom
|
||||
pdf.SetY(y_start + 2 * header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
|
||||
left = left + width
|
||||
wday = wday + 1
|
||||
wday = 1 if wday > 7
|
||||
end
|
||||
end
|
||||
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
|
||||
|
||||
# Tasks
|
||||
top = headers_heigth + y_start
|
||||
pdf.SetFontStyle('B',7)
|
||||
gantt.events.each do |i|
|
||||
pdf.SetY(top)
|
||||
pdf.SetX(15)
|
||||
|
||||
text = ""
|
||||
if i.is_a? Issue
|
||||
text = "#{i.tracker} #{i.id}: #{i.subject}"
|
||||
else
|
||||
text = i.name
|
||||
end
|
||||
text = "#{i.project} - #{text}" unless project && project == i.project
|
||||
pdf.Cell(subject_width-15, 5, text, "LR")
|
||||
|
||||
pdf.SetY(top + 0.2)
|
||||
pdf.SetX(subject_width)
|
||||
pdf.SetFillColor(255, 255, 255)
|
||||
pdf.Cell(g_width, 4.6, "", "LR", 0, "", 1)
|
||||
pdf.SetY(top+1.5)
|
||||
|
||||
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)
|
||||
i_width = ((i_end_date - i_start_date + 1)*zoom)
|
||||
d_width = ((i_done_date - i_start_date)*zoom)
|
||||
l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
|
||||
l_width ||= 0
|
||||
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(200,200,200)
|
||||
pdf.Cell(i_width, 2, "", 0, 0, "", 1)
|
||||
|
||||
if l_width > 0
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(255,100,100)
|
||||
pdf.Cell(l_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
if d_width > 0
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(100,100,255)
|
||||
pdf.Cell(d_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left + i_width)
|
||||
pdf.Cell(30, 2, "#{i.status} #{i.done_ratio}%")
|
||||
else
|
||||
i_left = ((i.start_date - gantt.date_from)*zoom)
|
||||
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(50,200,50)
|
||||
pdf.Cell(2, 2, "", 0, 0, "", 1)
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left + 3)
|
||||
pdf.Cell(30, 2, "#{i.name}")
|
||||
end
|
||||
|
||||
top = top + 5
|
||||
pdf.SetDrawColor(200, 200, 200)
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
if pdf.GetY() > 180
|
||||
pdf.AddPage("L")
|
||||
top = 20
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
end
|
||||
pdf.SetDrawColor(0, 0, 0)
|
||||
end
|
||||
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
pdf.Output
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -19,11 +19,28 @@ module Redmine
|
||||
module Helpers
|
||||
# Simple class to handle gantt chart data
|
||||
class Gantt
|
||||
attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :events
|
||||
|
||||
include ERB::Util
|
||||
include Redmine::I18n
|
||||
|
||||
# :nodoc:
|
||||
# Some utility methods for the PDF export
|
||||
class PDF
|
||||
MaxCharactorsForSubject = 45
|
||||
TotalWidth = 280
|
||||
LeftPaneWidth = 100
|
||||
|
||||
def self.right_pane_width
|
||||
TotalWidth - LeftPaneWidth
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months
|
||||
attr_accessor :query
|
||||
attr_accessor :project
|
||||
attr_accessor :view
|
||||
|
||||
def initialize(options={})
|
||||
options = options.dup
|
||||
@events = []
|
||||
|
||||
if options[:year] && options[:year].to_i >0
|
||||
@year_from = options[:year].to_i
|
||||
@ -51,47 +68,668 @@ module Redmine
|
||||
@date_from = Date.civil(@year_from, @month_from, 1)
|
||||
@date_to = (@date_from >> @months) - 1
|
||||
end
|
||||
|
||||
|
||||
def events=(e)
|
||||
@events = e
|
||||
# Adds all ancestors
|
||||
root_ids = e.select {|i| i.is_a?(Issue) && i.parent_id? }.collect(&:root_id).uniq
|
||||
if root_ids.any?
|
||||
# Retrieves all nodes
|
||||
parents = Issue.find_all_by_root_id(root_ids, :conditions => ["rgt - lft > 1"])
|
||||
# Only add ancestors
|
||||
@events += parents.select {|p| @events.detect {|i| i.is_a?(Issue) && p.is_ancestor_of?(i)}}
|
||||
end
|
||||
@events.uniq!
|
||||
# Sort issues by hierarchy and start dates
|
||||
@events.sort! {|x,y|
|
||||
if x.is_a?(Issue) && y.is_a?(Issue)
|
||||
gantt_issue_compare(x, y, @events)
|
||||
else
|
||||
gantt_start_compare(x, y)
|
||||
end
|
||||
}
|
||||
# Removes issues that have no start or end date
|
||||
@events.reject! {|i| i.is_a?(Issue) && (i.start_date.nil? || i.due_before.nil?) }
|
||||
@events
|
||||
|
||||
def common_params
|
||||
{ :controller => 'gantts', :action => 'show', :project_id => @project }
|
||||
end
|
||||
|
||||
def params
|
||||
{ :zoom => zoom, :year => year_from, :month => month_from, :months => months }
|
||||
common_params.merge({ :zoom => zoom, :year => year_from, :month => month_from, :months => months })
|
||||
end
|
||||
|
||||
def params_previous
|
||||
{ :year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months }
|
||||
common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months })
|
||||
end
|
||||
|
||||
def params_next
|
||||
{ :year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months }
|
||||
common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months })
|
||||
end
|
||||
|
||||
|
||||
### Extracted from the HTML view/helpers
|
||||
# Returns the number of rows that will be rendered on the Gantt chart
|
||||
def number_of_rows
|
||||
if @project
|
||||
return number_of_rows_on_project(@project)
|
||||
else
|
||||
Project.roots.inject(0) do |total, project|
|
||||
total += number_of_rows_on_project(project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the number of rows that will be used to list a project on
|
||||
# the Gantt chart. This will recurse for each subproject.
|
||||
def number_of_rows_on_project(project)
|
||||
# Remove the project requirement for Versions because it will
|
||||
# restrict issues to only be on the current project. This
|
||||
# ends up missing issues which are assigned to shared versions.
|
||||
@query.project = nil if @query.project
|
||||
|
||||
# One Root project
|
||||
count = 1
|
||||
# Issues without a Version
|
||||
count += project.issues.for_gantt.without_version.with_query(@query).count
|
||||
|
||||
# Versions
|
||||
count += project.versions.count
|
||||
|
||||
# Issues on the Versions
|
||||
project.versions.each do |version|
|
||||
count += version.fixed_issues.for_gantt.with_query(@query).count
|
||||
end
|
||||
|
||||
# Subprojects
|
||||
project.children.each do |subproject|
|
||||
count += number_of_rows_on_project(subproject)
|
||||
end
|
||||
|
||||
count
|
||||
end
|
||||
|
||||
# Renders the subjects of the Gantt chart, the left side.
|
||||
def subjects(options={})
|
||||
options = {:indent => 4, :render => :subject, :format => :html}.merge(options)
|
||||
|
||||
output = ''
|
||||
if @project
|
||||
output << render_project(@project, options)
|
||||
else
|
||||
Project.roots.each do |project|
|
||||
output << render_project(project, options)
|
||||
end
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
# Renders the lines of the Gantt chart, the right side
|
||||
def lines(options={})
|
||||
options = {:indent => 4, :render => :line, :format => :html}.merge(options)
|
||||
output = ''
|
||||
|
||||
if @project
|
||||
output << render_project(@project, options)
|
||||
else
|
||||
Project.roots.each do |project|
|
||||
output << render_project(project, options)
|
||||
end
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def render_project(project, options={})
|
||||
options[:top] = 0 unless options.key? :top
|
||||
options[:indent_increment] = 20 unless options.key? :indent_increment
|
||||
options[:top_increment] = 20 unless options.key? :top_increment
|
||||
|
||||
output = ''
|
||||
# Project Header
|
||||
project_header = if options[:render] == :subject
|
||||
subject_for_project(project, options)
|
||||
else
|
||||
# :line
|
||||
line_for_project(project, options)
|
||||
end
|
||||
output << project_header if options[:format] == :html
|
||||
|
||||
options[:top] += options[:top_increment]
|
||||
options[:indent] += options[:indent_increment]
|
||||
|
||||
# Second, Issues without a version
|
||||
issues = project.issues.for_gantt.without_version.with_query(@query)
|
||||
if issues
|
||||
issue_rendering = render_issues(issues, options)
|
||||
output << issue_rendering if options[:format] == :html
|
||||
end
|
||||
|
||||
# Third, Versions
|
||||
project.versions.sort.each do |version|
|
||||
version_rendering = render_version(version, options)
|
||||
output << version_rendering if options[:format] == :html
|
||||
end
|
||||
|
||||
# Fourth, subprojects
|
||||
project.children.each do |project|
|
||||
subproject_rendering = render_project(project, options)
|
||||
output << subproject_rendering if options[:format] == :html
|
||||
end
|
||||
|
||||
# Remove indent to hit the next sibling
|
||||
options[:indent] -= options[:indent_increment]
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def render_issues(issues, options={})
|
||||
output = ''
|
||||
issues.each do |i|
|
||||
issue_rendering = if options[:render] == :subject
|
||||
subject_for_issue(i, options)
|
||||
else
|
||||
# :line
|
||||
line_for_issue(i, options)
|
||||
end
|
||||
output << issue_rendering if options[:format] == :html
|
||||
options[:top] += options[:top_increment]
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
def render_version(version, options={})
|
||||
output = ''
|
||||
# Version header
|
||||
version_rendering = if options[:render] == :subject
|
||||
subject_for_version(version, options)
|
||||
else
|
||||
# :line
|
||||
line_for_version(version, options)
|
||||
end
|
||||
|
||||
output << version_rendering if options[:format] == :html
|
||||
|
||||
options[:top] += options[:top_increment]
|
||||
|
||||
# Remove the project requirement for Versions because it will
|
||||
# restrict issues to only be on the current project. This
|
||||
# ends up missing issues which are assigned to shared versions.
|
||||
@query.project = nil if @query.project
|
||||
|
||||
issues = version.fixed_issues.for_gantt.with_query(@query)
|
||||
if issues
|
||||
# Indent issues
|
||||
options[:indent] += options[:indent_increment]
|
||||
output << render_issues(issues, options)
|
||||
options[:indent] -= options[:indent_increment]
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def subject_for_project(project, options)
|
||||
case options[:format]
|
||||
when :html
|
||||
output = ''
|
||||
|
||||
output << "<div class='project-name' style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:#{options[:indent]}px;overflow:hidden;'><small> "
|
||||
if project.is_a? Project
|
||||
output << "<span class='icon icon-projects #{project.overdue? ? 'project-overdue' : ''}'>"
|
||||
output << view.link_to_project(project)
|
||||
output << '</span>'
|
||||
else
|
||||
ActiveRecord::Base.logger.debug "Gantt#subject_for_project was not given a project"
|
||||
''
|
||||
end
|
||||
output << "</small></div>"
|
||||
|
||||
output
|
||||
when :image
|
||||
|
||||
options[:image].fill('black')
|
||||
options[:image].stroke('transparent')
|
||||
options[:image].stroke_width(1)
|
||||
options[:image].text(options[:indent], options[:top] + 2, project.name)
|
||||
when :pdf
|
||||
options[:pdf].SetY(options[:top])
|
||||
options[:pdf].SetX(15)
|
||||
|
||||
char_limit = PDF::MaxCharactorsForSubject - options[:indent]
|
||||
options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{project.name}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR")
|
||||
|
||||
options[:pdf].SetY(options[:top])
|
||||
options[:pdf].SetX(options[:subject_width])
|
||||
options[:pdf].Cell(options[:g_width], 5, "", "LR")
|
||||
end
|
||||
end
|
||||
|
||||
def line_for_project(project, options)
|
||||
# Skip versions that don't have a start_date
|
||||
if project.is_a?(Project) && project.start_date
|
||||
options[:zoom] ||= 1
|
||||
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
|
||||
|
||||
|
||||
case options[:format]
|
||||
when :html
|
||||
output = ''
|
||||
i_left = ((project.start_date - self.date_from)*options[:zoom]).floor
|
||||
|
||||
start_date = project.start_date
|
||||
start_date ||= self.date_from
|
||||
start_left = ((start_date - self.date_from)*options[:zoom]).floor
|
||||
|
||||
i_end_date = ((project.due_date <= self.date_to) ? project.due_date : self.date_to )
|
||||
i_done_date = start_date + ((project.due_date - start_date+1)* project.completed_percent(:include_subprojects => true)/100).floor
|
||||
i_done_date = (i_done_date <= self.date_from ? self.date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= self.date_to ? self.date_to : i_done_date )
|
||||
|
||||
i_late_date = [i_end_date, Date.today].min if start_date < Date.today
|
||||
i_end = ((i_end_date - self.date_from) * options[:zoom]).floor
|
||||
|
||||
i_width = (i_end - i_left + 1).floor - 2 # total width of the issue (- 2 for left and right borders)
|
||||
d_width = ((i_done_date - start_date)*options[:zoom]).floor - 2 # done width
|
||||
l_width = i_late_date ? ((i_late_date - start_date+1)*options[:zoom]).floor - 2 : 0 # delay width
|
||||
|
||||
# Bar graphic
|
||||
|
||||
# Make sure that negative i_left and i_width don't
|
||||
# overflow the subject
|
||||
if i_end > 0 && i_left <= options[:g_width]
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:#{ i_width }px;' class='task project_todo'> </div>"
|
||||
end
|
||||
|
||||
if l_width > 0 && i_left <= options[:g_width]
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:#{ l_width }px;' class='task project_late'> </div>"
|
||||
end
|
||||
if d_width > 0 && i_left <= options[:g_width]
|
||||
output<< "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:#{ d_width }px;' class='task project_done'> </div>"
|
||||
end
|
||||
|
||||
|
||||
# Starting diamond
|
||||
if start_left <= options[:g_width] && start_left > 0
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:15px;' class='task project-line starting'> </div>"
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left + 12 }px;' class='task label'>"
|
||||
output << "</div>"
|
||||
end
|
||||
|
||||
# Ending diamond
|
||||
# Don't show items too far ahead
|
||||
if i_end <= options[:g_width] && i_end > 0
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ i_end }px;width:15px;' class='task project-line ending'> </div>"
|
||||
end
|
||||
|
||||
# DIsplay the Project name and %
|
||||
if i_end <= options[:g_width]
|
||||
# Display the status even if it's floated off to the left
|
||||
status_px = i_end + 12 # 12px for the diamond
|
||||
status_px = 0 if status_px <= 0
|
||||
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ status_px }px;' class='task label project-name'>"
|
||||
output << "<strong>#{h project } #{h project.completed_percent(:include_subprojects => true).to_i.to_s}%</strong>"
|
||||
output << "</div>"
|
||||
end
|
||||
|
||||
output
|
||||
when :image
|
||||
options[:image].stroke('transparent')
|
||||
i_left = options[:subject_width] + ((project.due_date - self.date_from)*options[:zoom]).floor
|
||||
|
||||
# Make sure negative i_left doesn't overflow the subject
|
||||
if i_left > options[:subject_width]
|
||||
options[:image].fill('blue')
|
||||
options[:image].rectangle(i_left, options[:top], i_left + 6, options[:top] - 6)
|
||||
options[:image].fill('black')
|
||||
options[:image].text(i_left + 11, options[:top] + 1, project.name)
|
||||
end
|
||||
when :pdf
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
i_left = ((project.due_date - @date_from)*options[:zoom])
|
||||
|
||||
# Make sure negative i_left doesn't overflow the subject
|
||||
if i_left > 0
|
||||
options[:pdf].SetX(options[:subject_width] + i_left)
|
||||
options[:pdf].SetFillColor(50,50,200)
|
||||
options[:pdf].Cell(2, 2, "", 0, 0, "", 1)
|
||||
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
options[:pdf].SetX(options[:subject_width] + i_left + 3)
|
||||
options[:pdf].Cell(30, 2, "#{project.name}")
|
||||
end
|
||||
end
|
||||
else
|
||||
ActiveRecord::Base.logger.debug "Gantt#line_for_project was not given a project with a start_date"
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
def subject_for_version(version, options)
|
||||
case options[:format]
|
||||
when :html
|
||||
output = ''
|
||||
output << "<div class='version-name' style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:#{options[:indent]}px;overflow:hidden;'><small> "
|
||||
if version.is_a? Version
|
||||
output << "<span class='icon icon-package #{version.behind_schedule? ? 'version-behind-schedule' : ''} #{version.overdue? ? 'version-overdue' : ''}'>"
|
||||
output << view.link_to_version(version)
|
||||
output << '</span>'
|
||||
else
|
||||
ActiveRecord::Base.logger.debug "Gantt#subject_for_version was not given a version"
|
||||
''
|
||||
end
|
||||
output << "</small></div>"
|
||||
|
||||
output
|
||||
when :image
|
||||
options[:image].fill('black')
|
||||
options[:image].stroke('transparent')
|
||||
options[:image].stroke_width(1)
|
||||
options[:image].text(options[:indent], options[:top] + 2, version.to_s_with_project)
|
||||
when :pdf
|
||||
options[:pdf].SetY(options[:top])
|
||||
options[:pdf].SetX(15)
|
||||
|
||||
char_limit = PDF::MaxCharactorsForSubject - options[:indent]
|
||||
options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{version.to_s_with_project}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR")
|
||||
|
||||
options[:pdf].SetY(options[:top])
|
||||
options[:pdf].SetX(options[:subject_width])
|
||||
options[:pdf].Cell(options[:g_width], 5, "", "LR")
|
||||
end
|
||||
end
|
||||
|
||||
def line_for_version(version, options)
|
||||
# Skip versions that don't have a start_date
|
||||
if version.is_a?(Version) && version.start_date
|
||||
options[:zoom] ||= 1
|
||||
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
|
||||
|
||||
case options[:format]
|
||||
when :html
|
||||
output = ''
|
||||
i_left = ((version.start_date - self.date_from)*options[:zoom]).floor
|
||||
# TODO: or version.fixed_issues.collect(&:start_date).min
|
||||
start_date = version.fixed_issues.minimum('start_date') if version.fixed_issues.present?
|
||||
start_date ||= self.date_from
|
||||
start_left = ((start_date - self.date_from)*options[:zoom]).floor
|
||||
|
||||
i_end_date = ((version.due_date <= self.date_to) ? version.due_date : self.date_to )
|
||||
i_done_date = start_date + ((version.due_date - start_date+1)* version.completed_pourcent/100).floor
|
||||
i_done_date = (i_done_date <= self.date_from ? self.date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= self.date_to ? self.date_to : i_done_date )
|
||||
|
||||
i_late_date = [i_end_date, Date.today].min if start_date < Date.today
|
||||
|
||||
i_width = (i_left - start_left + 1).floor - 2 # total width of the issue (- 2 for left and right borders)
|
||||
d_width = ((i_done_date - start_date)*options[:zoom]).floor - 2 # done width
|
||||
l_width = i_late_date ? ((i_late_date - start_date+1)*options[:zoom]).floor - 2 : 0 # delay width
|
||||
|
||||
i_end = ((i_end_date - self.date_from) * options[:zoom]).floor # Ending pixel
|
||||
|
||||
# Bar graphic
|
||||
|
||||
# Make sure that negative i_left and i_width don't
|
||||
# overflow the subject
|
||||
if i_width > 0 && i_left <= options[:g_width]
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:#{ i_width }px;' class='task milestone_todo'> </div>"
|
||||
end
|
||||
if l_width > 0 && i_left <= options[:g_width]
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:#{ l_width }px;' class='task milestone_late'> </div>"
|
||||
end
|
||||
if d_width > 0 && i_left <= options[:g_width]
|
||||
output<< "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:#{ d_width }px;' class='task milestone_done'> </div>"
|
||||
end
|
||||
|
||||
|
||||
# Starting diamond
|
||||
if start_left <= options[:g_width] && start_left > 0
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left }px;width:15px;' class='task milestone starting'> </div>"
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ start_left + 12 }px;background:#fff;' class='task'>"
|
||||
output << "</div>"
|
||||
end
|
||||
|
||||
# Ending diamond
|
||||
# Don't show items too far ahead
|
||||
if i_left <= options[:g_width] && i_end > 0
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ i_end }px;width:15px;' class='task milestone ending'> </div>"
|
||||
end
|
||||
|
||||
# Display the Version name and %
|
||||
if i_end <= options[:g_width]
|
||||
# Display the status even if it's floated off to the left
|
||||
status_px = i_end + 12 # 12px for the diamond
|
||||
status_px = 0 if status_px <= 0
|
||||
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ status_px }px;' class='task label version-name'>"
|
||||
output << h("#{version.project} -") unless @project && @project == version.project
|
||||
output << "<strong>#{h version } #{h version.completed_pourcent.to_i.to_s}%</strong>"
|
||||
output << "</div>"
|
||||
end
|
||||
|
||||
output
|
||||
when :image
|
||||
options[:image].stroke('transparent')
|
||||
i_left = options[:subject_width] + ((version.start_date - @date_from)*options[:zoom]).floor
|
||||
|
||||
# Make sure negative i_left doesn't overflow the subject
|
||||
if i_left > options[:subject_width]
|
||||
options[:image].fill('green')
|
||||
options[:image].rectangle(i_left, options[:top], i_left + 6, options[:top] - 6)
|
||||
options[:image].fill('black')
|
||||
options[:image].text(i_left + 11, options[:top] + 1, version.name)
|
||||
end
|
||||
when :pdf
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
i_left = ((version.start_date - @date_from)*options[:zoom])
|
||||
|
||||
# Make sure negative i_left doesn't overflow the subject
|
||||
if i_left > 0
|
||||
options[:pdf].SetX(options[:subject_width] + i_left)
|
||||
options[:pdf].SetFillColor(50,200,50)
|
||||
options[:pdf].Cell(2, 2, "", 0, 0, "", 1)
|
||||
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
options[:pdf].SetX(options[:subject_width] + i_left + 3)
|
||||
options[:pdf].Cell(30, 2, "#{version.name}")
|
||||
end
|
||||
end
|
||||
else
|
||||
ActiveRecord::Base.logger.debug "Gantt#line_for_version was not given a version with a start_date"
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
def subject_for_issue(issue, options)
|
||||
case options[:format]
|
||||
when :html
|
||||
output = ''
|
||||
output << "<div class='tooltip'>"
|
||||
output << "<div class='issue-subject' style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:#{options[:indent]}px;overflow:hidden;'><small> "
|
||||
if issue.is_a? Issue
|
||||
css_classes = []
|
||||
css_classes << 'issue-overdue' if issue.overdue?
|
||||
css_classes << 'issue-behind-schedule' if issue.behind_schedule?
|
||||
css_classes << 'icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
||||
|
||||
if issue.assigned_to.present?
|
||||
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
||||
output << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string)
|
||||
end
|
||||
output << "<span class='#{css_classes.join(' ')}'>"
|
||||
output << view.link_to_issue(issue)
|
||||
output << ":"
|
||||
output << h(issue.subject)
|
||||
output << '</span>'
|
||||
else
|
||||
ActiveRecord::Base.logger.debug "Gantt#subject_for_issue was not given an issue"
|
||||
''
|
||||
end
|
||||
output << "</small></div>"
|
||||
|
||||
# Tooltip
|
||||
if issue.is_a? Issue
|
||||
output << "<span class='tip' style='position: absolute;top:#{ options[:top].to_i + 16 }px;left:#{ options[:indent].to_i + 20 }px;'>"
|
||||
output << view.render_issue_tooltip(issue)
|
||||
output << "</span>"
|
||||
end
|
||||
|
||||
output << "</div>"
|
||||
output
|
||||
when :image
|
||||
options[:image].fill('black')
|
||||
options[:image].stroke('transparent')
|
||||
options[:image].stroke_width(1)
|
||||
options[:image].text(options[:indent], options[:top] + 2, issue.subject)
|
||||
when :pdf
|
||||
options[:pdf].SetY(options[:top])
|
||||
options[:pdf].SetX(15)
|
||||
|
||||
char_limit = PDF::MaxCharactorsForSubject - options[:indent]
|
||||
options[:pdf].Cell(options[:subject_width]-15, 5, (" " * options[:indent]) +"#{issue.tracker} #{issue.id}: #{issue.subject}".sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR")
|
||||
|
||||
options[:pdf].SetY(options[:top])
|
||||
options[:pdf].SetX(options[:subject_width])
|
||||
options[:pdf].Cell(options[:g_width], 5, "", "LR")
|
||||
end
|
||||
end
|
||||
|
||||
def line_for_issue(issue, options)
|
||||
# Skip issues that don't have a due_before (due_date or version's due_date)
|
||||
if issue.is_a?(Issue) && issue.due_before
|
||||
case options[:format]
|
||||
when :html
|
||||
output = ''
|
||||
# Handle nil start_dates, rare but can happen.
|
||||
i_start_date = if issue.start_date && issue.start_date >= self.date_from
|
||||
issue.start_date
|
||||
else
|
||||
self.date_from
|
||||
end
|
||||
|
||||
i_end_date = ((issue.due_before && issue.due_before <= self.date_to) ? issue.due_before : self.date_to )
|
||||
i_done_date = i_start_date + ((issue.due_before - i_start_date+1)*issue.done_ratio/100).floor
|
||||
i_done_date = (i_done_date <= self.date_from ? self.date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= self.date_to ? self.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 - self.date_from)*options[:zoom]).floor
|
||||
i_width = ((i_end_date - i_start_date + 1)*options[:zoom]).floor - 2 # total width of the issue (- 2 for left and right borders)
|
||||
d_width = ((i_done_date - i_start_date)*options[:zoom]).floor - 2 # done width
|
||||
l_width = i_late_date ? ((i_late_date - i_start_date+1)*options[:zoom]).floor - 2 : 0 # delay width
|
||||
css = "task " + (issue.leaf? ? 'leaf' : 'parent')
|
||||
|
||||
# Make sure that negative i_left and i_width don't
|
||||
# overflow the subject
|
||||
if i_width > 0
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ i_left }px;width:#{ i_width }px;' class='#{css} task_todo'> </div>"
|
||||
end
|
||||
if l_width > 0
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ i_left }px;width:#{ l_width }px;' class='#{css} task_late'> </div>"
|
||||
end
|
||||
if d_width > 0
|
||||
output<< "<div style='top:#{ options[:top] }px;left:#{ i_left }px;width:#{ d_width }px;' class='#{css} task_done'> </div>"
|
||||
end
|
||||
|
||||
# Display the status even if it's floated off to the left
|
||||
status_px = i_left + i_width + 5
|
||||
status_px = 5 if status_px <= 0
|
||||
|
||||
output << "<div style='top:#{ options[:top] }px;left:#{ status_px }px;' class='#{css} label issue-name'>"
|
||||
output << issue.status.name
|
||||
output << ' '
|
||||
output << (issue.done_ratio).to_i.to_s
|
||||
output << "%"
|
||||
output << "</div>"
|
||||
|
||||
output << "<div class='tooltip' style='position: absolute;top:#{ options[:top] }px;left:#{ i_left }px;width:#{ i_width }px;height:12px;'>"
|
||||
output << '<span class="tip">'
|
||||
output << view.render_issue_tooltip(issue)
|
||||
output << "</span></div>"
|
||||
output
|
||||
|
||||
when :image
|
||||
# Handle nil start_dates, rare but can happen.
|
||||
i_start_date = if issue.start_date && issue.start_date >= @date_from
|
||||
issue.start_date
|
||||
else
|
||||
@date_from
|
||||
end
|
||||
|
||||
i_end_date = (issue.due_before <= date_to ? issue.due_before : date_to )
|
||||
i_done_date = i_start_date + ((issue.due_before - i_start_date+1)*issue.done_ratio/100).floor
|
||||
i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= date_to ? date_to : i_done_date )
|
||||
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
|
||||
|
||||
i_left = options[:subject_width] + ((i_start_date - @date_from)*options[:zoom]).floor
|
||||
i_width = ((i_end_date - i_start_date + 1)*options[:zoom]).floor # total width of the issue
|
||||
d_width = ((i_done_date - i_start_date)*options[:zoom]).floor # done width
|
||||
l_width = i_late_date ? ((i_late_date - i_start_date+1)*options[:zoom]).floor : 0 # delay width
|
||||
|
||||
|
||||
# Make sure that negative i_left and i_width don't
|
||||
# overflow the subject
|
||||
if i_width > 0
|
||||
options[:image].fill('grey')
|
||||
options[:image].rectangle(i_left, options[:top], i_left + i_width, options[:top] - 6)
|
||||
options[:image].fill('red')
|
||||
options[:image].rectangle(i_left, options[:top], i_left + l_width, options[:top] - 6) if l_width > 0
|
||||
options[:image].fill('blue')
|
||||
options[:image].rectangle(i_left, options[:top], i_left + d_width, options[:top] - 6) if d_width > 0
|
||||
end
|
||||
|
||||
# Show the status and % done next to the subject if it overflows
|
||||
options[:image].fill('black')
|
||||
if i_width > 0
|
||||
options[:image].text(i_left + i_width + 5,options[:top] + 1, "#{issue.status.name} #{issue.done_ratio}%")
|
||||
else
|
||||
options[:image].text(options[:subject_width] + 5,options[:top] + 1, "#{issue.status.name} #{issue.done_ratio}%")
|
||||
end
|
||||
|
||||
when :pdf
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
# Handle nil start_dates, rare but can happen.
|
||||
i_start_date = if issue.start_date && issue.start_date >= @date_from
|
||||
issue.start_date
|
||||
else
|
||||
@date_from
|
||||
end
|
||||
|
||||
i_end_date = (issue.due_before <= @date_to ? issue.due_before : @date_to )
|
||||
|
||||
i_done_date = i_start_date + ((issue.due_before - i_start_date+1)*issue.done_ratio/100).floor
|
||||
i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= @date_to ? @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 - @date_from)*options[:zoom])
|
||||
i_width = ((i_end_date - i_start_date + 1)*options[:zoom])
|
||||
d_width = ((i_done_date - i_start_date)*options[:zoom])
|
||||
l_width = ((i_late_date - i_start_date+1)*options[:zoom]) if i_late_date
|
||||
l_width ||= 0
|
||||
|
||||
# Make sure that negative i_left and i_width don't
|
||||
# overflow the subject
|
||||
if i_width > 0
|
||||
options[:pdf].SetX(options[:subject_width] + i_left)
|
||||
options[:pdf].SetFillColor(200,200,200)
|
||||
options[:pdf].Cell(i_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
|
||||
if l_width > 0
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
options[:pdf].SetX(options[:subject_width] + i_left)
|
||||
options[:pdf].SetFillColor(255,100,100)
|
||||
options[:pdf].Cell(l_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
if d_width > 0
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
options[:pdf].SetX(options[:subject_width] + i_left)
|
||||
options[:pdf].SetFillColor(100,100,255)
|
||||
options[:pdf].Cell(d_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
|
||||
options[:pdf].SetY(options[:top]+1.5)
|
||||
|
||||
# Make sure that negative i_left and i_width don't
|
||||
# overflow the subject
|
||||
if (i_left + i_width) >= 0
|
||||
options[:pdf].SetX(options[:subject_width] + i_left + i_width)
|
||||
else
|
||||
options[:pdf].SetX(options[:subject_width])
|
||||
end
|
||||
options[:pdf].Cell(30, 2, "#{issue.status} #{issue.done_ratio}%")
|
||||
end
|
||||
else
|
||||
ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before"
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
# Generates a gantt image
|
||||
# Only defined if RMagick is avalaible
|
||||
def to_image(project, format='PNG')
|
||||
def to_image(format='PNG')
|
||||
date_to = (@date_from >> @months)-1
|
||||
show_weeks = @zoom > 1
|
||||
show_days = @zoom > 2
|
||||
@ -101,7 +739,7 @@ module Redmine
|
||||
# width of one day in pixels
|
||||
zoom = @zoom*2
|
||||
g_width = (@date_to - @date_from + 1)*zoom
|
||||
g_height = 20 * events.length + 20
|
||||
g_height = 20 * number_of_rows + 30
|
||||
headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
|
||||
height = g_height + headers_heigth
|
||||
|
||||
@ -110,21 +748,7 @@ module Redmine
|
||||
gc = Magick::Draw.new
|
||||
|
||||
# Subjects
|
||||
top = headers_heigth + 20
|
||||
gc.fill('black')
|
||||
gc.stroke('transparent')
|
||||
gc.stroke_width(1)
|
||||
events.each do |i|
|
||||
text = ""
|
||||
if i.is_a? Issue
|
||||
text = "#{i.tracker} #{i.id}: #{i.subject}"
|
||||
else
|
||||
text = i.name
|
||||
end
|
||||
text = "#{i.project} - #{text}" unless project && project == i.project
|
||||
gc.text(4, top + 2, text)
|
||||
top = top + 20
|
||||
end
|
||||
subjects(:image => gc, :top => (headers_heigth + 20), :indent => 4, :format => :image)
|
||||
|
||||
# Months headers
|
||||
month_f = @date_from
|
||||
@ -202,38 +826,8 @@ module Redmine
|
||||
|
||||
# content
|
||||
top = headers_heigth + 20
|
||||
gc.stroke('transparent')
|
||||
events.each do |i|
|
||||
if i.is_a?(Issue)
|
||||
i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
|
||||
i_end_date = (i.due_before <= date_to ? i.due_before : 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 <= @date_from ? @date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= date_to ? date_to : i_done_date )
|
||||
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
|
||||
|
||||
i_left = subject_width + ((i_start_date - @date_from)*zoom).floor
|
||||
i_width = ((i_end_date - i_start_date + 1)*zoom).floor # total width of the issue
|
||||
d_width = ((i_done_date - i_start_date)*zoom).floor # done width
|
||||
l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor : 0 # delay width
|
||||
|
||||
gc.fill('grey')
|
||||
gc.rectangle(i_left, top, i_left + i_width, top - 6)
|
||||
gc.fill('red')
|
||||
gc.rectangle(i_left, top, i_left + l_width, top - 6) if l_width > 0
|
||||
gc.fill('blue')
|
||||
gc.rectangle(i_left, top, i_left + d_width, top - 6) if d_width > 0
|
||||
gc.fill('black')
|
||||
gc.text(i_left + i_width + 5,top + 1, "#{i.status.name} #{i.done_ratio}%")
|
||||
else
|
||||
i_left = subject_width + ((i.start_date - @date_from)*zoom).floor
|
||||
gc.fill('green')
|
||||
gc.rectangle(i_left, top, i_left + 6, top - 6)
|
||||
gc.fill('black')
|
||||
gc.text(i_left + 11, top + 1, i.name)
|
||||
end
|
||||
top = top + 20
|
||||
end
|
||||
|
||||
lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image)
|
||||
|
||||
# today red line
|
||||
if Date.today >= @date_from and Date.today <= date_to
|
||||
@ -246,36 +840,137 @@ module Redmine
|
||||
imgl.format = format
|
||||
imgl.to_blob
|
||||
end if Object.const_defined?(:Magick)
|
||||
|
||||
def to_pdf
|
||||
pdf = ::Redmine::Export::PDF::IFPDF.new(current_language)
|
||||
pdf.SetTitle("#{l(:label_gantt)} #{project}")
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage("L")
|
||||
pdf.SetFontStyle('B',12)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(PDF::LeftPaneWidth, 20, project.to_s)
|
||||
pdf.Ln
|
||||
pdf.SetFontStyle('B',9)
|
||||
|
||||
subject_width = PDF::LeftPaneWidth
|
||||
header_heigth = 5
|
||||
|
||||
headers_heigth = header_heigth
|
||||
show_weeks = false
|
||||
show_days = false
|
||||
|
||||
if self.months < 7
|
||||
show_weeks = true
|
||||
headers_heigth = 2*header_heigth
|
||||
if self.months < 3
|
||||
show_days = true
|
||||
headers_heigth = 3*header_heigth
|
||||
end
|
||||
end
|
||||
|
||||
g_width = PDF.right_pane_width
|
||||
zoom = (g_width) / (self.date_to - self.date_from + 1)
|
||||
g_height = 120
|
||||
t_height = g_height + headers_heigth
|
||||
|
||||
y_start = pdf.GetY
|
||||
|
||||
# Months headers
|
||||
month_f = self.date_from
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
self.months.times do
|
||||
width = ((month_f >> 1) - month_f) * zoom
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
|
||||
left = left + width
|
||||
month_f = month_f >> 1
|
||||
end
|
||||
|
||||
# Weeks headers
|
||||
if show_weeks
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
if self.date_from.cwday == 1
|
||||
# self.date_from is monday
|
||||
week_f = self.date_from
|
||||
else
|
||||
# find next monday after self.date_from
|
||||
week_f = self.date_from + (7 - self.date_from.cwday + 1)
|
||||
width = (7 - self.date_from.cwday + 1) * zoom-1
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width + 1, height, "", "LTR")
|
||||
left = left + width+1
|
||||
end
|
||||
while week_f <= self.date_to
|
||||
width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
|
||||
left = left + width
|
||||
week_f = week_f+7
|
||||
end
|
||||
end
|
||||
|
||||
# Days headers
|
||||
if show_days
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
wday = self.date_from.cwday
|
||||
pdf.SetFontStyle('B',7)
|
||||
(self.date_to - self.date_from + 1).to_i.times do
|
||||
width = zoom
|
||||
pdf.SetY(y_start + 2 * header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
|
||||
left = left + width
|
||||
wday = wday + 1
|
||||
wday = 1 if wday > 7
|
||||
end
|
||||
end
|
||||
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
|
||||
|
||||
# Tasks
|
||||
top = headers_heigth + y_start
|
||||
pdf_subjects_and_lines(pdf, {
|
||||
:top => top,
|
||||
:zoom => zoom,
|
||||
:subject_width => subject_width,
|
||||
:g_width => g_width
|
||||
})
|
||||
|
||||
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
pdf.Output
|
||||
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gantt_issue_compare(x, y, issues)
|
||||
if x.parent_id == y.parent_id
|
||||
gantt_start_compare(x, y)
|
||||
elsif x.is_ancestor_of?(y)
|
||||
-1
|
||||
elsif y.is_ancestor_of?(x)
|
||||
1
|
||||
|
||||
# Renders both the subjects and lines of the Gantt chart for the
|
||||
# PDF format
|
||||
def pdf_subjects_and_lines(pdf, options = {})
|
||||
subject_options = {:indent => 0, :indent_increment => 5, :top_increment => 3, :render => :subject, :format => :pdf, :pdf => pdf}.merge(options)
|
||||
line_options = {:indent => 0, :indent_increment => 5, :top_increment => 3, :render => :line, :format => :pdf, :pdf => pdf}.merge(options)
|
||||
|
||||
if @project
|
||||
render_project(@project, subject_options)
|
||||
render_project(@project, line_options)
|
||||
else
|
||||
ax = issues.select {|i| i.is_a?(Issue) && i.is_ancestor_of?(x) && !i.is_ancestor_of?(y) }.sort_by(&:lft).first
|
||||
ay = issues.select {|i| i.is_a?(Issue) && i.is_ancestor_of?(y) && !i.is_ancestor_of?(x) }.sort_by(&:lft).first
|
||||
if ax.nil? && ay.nil?
|
||||
gantt_start_compare(x, y)
|
||||
else
|
||||
gantt_issue_compare(ax || x, ay || y, issues)
|
||||
Project.roots.each do |project|
|
||||
render_project(project, subject_options)
|
||||
render_project(project, line_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def gantt_start_compare(x, y)
|
||||
if x.start_date.nil?
|
||||
-1
|
||||
elsif y.start_date.nil?
|
||||
1
|
||||
else
|
||||
x.start_date <=> y.start_date
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
Before Width: | Height: | Size: 122 B |
BIN
public/images/milestone_done.png
Normal file
|
After Width: | Height: | Size: 137 B |
BIN
public/images/milestone_late.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
public/images/milestone_todo.png
Normal file
|
After Width: | Height: | Size: 155 B |
BIN
public/images/project_marker.png
Normal file
|
After Width: | Height: | Size: 204 B |
|
Before Width: | Height: | Size: 855 B After Width: | Height: | Size: 137 B |