Merge branch 'ticket/master/30-upstream-code-review' into upstream-master

This commit is contained in:
Eric Davis 2011-02-09 17:00:37 -08:00
commit 98f85b3332
443 changed files with 25144 additions and 11309 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
/config/email.yml
/config/initializers/session_store.rb

23
.hgignore Normal file
View File

@ -0,0 +1,23 @@
syntax: glob
config/additional_environment.rb
config/configuration.yml
config/database.yml
config/email.yml
config/initializers/session_store.rb
coverage
db/*.db
db/*.sqlite3
db/schema.rb
files/*
log/*.log*
log/mongrel_debug
public/dispatch.*
public/plugin_assets
tmp/*
tmp/cache/*
tmp/sessions/*
tmp/sockets/*
tmp/test/*
vendor/rails
*.rbc

View File

@ -203,12 +203,24 @@ class AccountController < ApplicationController
self.logged_user = user
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
token = Token.create(:user => user, :action => 'autologin')
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
set_autologin_cookie(user)
end
call_hook(:controller_account_success_authentication_after, {:user => user })
redirect_back_or_default :controller => 'my', :action => 'page'
end
def set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin'
cookie_options = {
:value => token.value,
:expires => 1.year.from_now,
:path => (Redmine::Configuration['autologin_cookie_path'] || '/'),
:secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false),
:httponly => true
}
cookies[cookie_name] = cookie_options
end
# Onthefly creation failed, display the registration form to fill/fix attributes
def onthefly_creation_failed(user, auth_source_options = { })

View File

@ -23,7 +23,7 @@ class ActivitiesController < ApplicationController
events = @activity.events(@date_from, @date_to)
if events.empty? || stale?(:etag => [events.first, User.current])
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
respond_to do |format|
format.html {
@events_by_day = events.group_by(&:event_date)

View File

@ -22,7 +22,7 @@ class ApplicationController < ActionController::Base
include Redmine::I18n
layout 'base'
exempt_from_layout 'builder'
exempt_from_layout 'builder', 'rsb'
# Remove broken cookie after upgrade from 0.8.x (#4292)
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
@ -71,10 +71,10 @@ class ApplicationController < ActionController::Base
elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key])
elsif Setting.rest_api_enabled? && ['xml', 'json'].include?(params[:format])
if params[:key].present? && accept_key_auth_actions.include?(params[:action])
elsif Setting.rest_api_enabled? && api_request?
if (key = api_key_from_request) && accept_key_auth_actions.include?(params[:action])
# Use API key
User.find_by_api_key(params[:key])
User.find_by_api_key(key)
else
# HTTP Basic, either username/password or API key/random
authenticate_with_http_basic do |username, password|
@ -349,6 +349,30 @@ class ApplicationController < ActionController::Base
per_page
end
# Returns offset and limit used to retrieve objects
# for an API response based on offset, limit and page parameters
def api_offset_and_limit(options=params)
if options[:offset].present?
offset = options[:offset].to_i
if offset < 0
offset = 0
end
end
limit = options[:limit].to_i
if limit < 1
limit = 25
elsif limit > 100
limit = 100
end
if offset.nil? && options[:page].present?
offset = (options[:page].to_i - 1) * limit
offset = 0 if offset < 0
end
offset ||= 0
[offset, limit]
end
# qvalues http header parser
# code taken from webrick
def parse_qvalues(value)
@ -378,6 +402,15 @@ class ApplicationController < ActionController::Base
def api_request?
%w(xml json).include? params[:format]
end
# Returns the API key present in the request
def api_key_from_request
if params[:key].present?
params[:key]
elsif request.headers["X-Redmine-API-Key"].present?
request.headers["X-Redmine-API-Key"]
end
end
# Renders a warning flash if obj has unsaved attachments
def render_attachment_warning_if_needed(obj)
@ -413,5 +446,37 @@ class ApplicationController < ActionController::Base
{ attribute => error }
end.to_json
end
# Renders API response on validation failure
def render_validation_errors(object)
options = { :status => :unprocessable_entity, :layout => false }
options.merge!(case params[:format]
when 'xml'; { :xml => object.errors }
when 'json'; { :json => {'errors' => object.errors} } # ActiveResource client compliance
else
raise "Unknown format #{params[:format]} in #render_validation_errors"
end
)
render options
end
# Overrides #default_template so that the api template
# is used automatically if it exists
def default_template(action_name = self.action_name)
if api_request?
begin
return self.view_paths.find_template(default_template_name(action_name), 'api')
rescue ::ActionView::MissingTemplate
# the api template was not found
# fallback to the default behaviour
end
end
super
end
# Overrides #pick_layout so that #render with no arguments
# doesn't use the layout for api requests
def pick_layout(*args)
api_request? ? nil : super
end
end

View File

@ -4,12 +4,14 @@ class AutoCompletesController < ApplicationController
def issues
@issues = []
q = params[:q].to_s
query = (params[:scope] == "all" && Setting.cross_project_issue_relations?) ? Issue : @project.issues
if q.match(/^\d+$/)
@issues << @project.issues.visible.find_by_id(q.to_i)
@issues << query.visible.find_by_id(q.to_i)
end
unless q.blank?
@issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
@issues += query.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
end
@issues.compact!
render :layout => false
end

View File

@ -2,7 +2,8 @@ class ContextMenusController < ApplicationController
helper :watchers
def issues
@issues = Issue.find_all_by_id(params[:ids], :include => :project)
@issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project)
if (@issues.size == 1)
@issue = @issues.first
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)

View File

@ -28,6 +28,7 @@ class IssueRelationsController < ApplicationController
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js do
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render :update do |page|
page.replace_html "relations", :partial => 'issues/relations'
if @relation.errors.empty?
@ -47,7 +48,10 @@ class IssueRelationsController < ApplicationController
end
respond_to do |format|
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
format.js { render(:update) {|page| page.replace_html "relations", :partial => 'issues/relations'} }
format.js {
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
render(:update) {|page| page.replace_html "relations", :partial => 'issues/relations'}
}
end
end

View File

@ -44,6 +44,8 @@ class IssuesController < ApplicationController
include AttachmentsHelper
helper :queries
include QueriesHelper
helper :repositories
include RepositoriesHelper
helper :sort
include SortHelper
include IssuesHelper
@ -65,27 +67,29 @@ class IssuesController < ApplicationController
sort_update(@query.sortable_columns)
if @query.valid?
limit = case params[:format]
case params[:format]
when 'csv', 'pdf'
Setting.issues_export_limit.to_i
@limit = Setting.issues_export_limit.to_i
when 'atom'
Setting.feeds_limit.to_i
@limit = Setting.feeds_limit.to_i
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
per_page_option
@limit = per_page_option
end
@issue_count = @query.issue_count
@issue_pages = Paginator.new self, @issue_count, limit, params['page']
@issue_pages = Paginator.new self, @issue_count, @limit, params['page']
@offset ||= @issue_pages.current.offset
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
:order => sort_clause,
:offset => @issue_pages.current.offset,
:limit => limit)
:offset => @offset,
:limit => @limit)
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.xml { render :layout => false }
format.json { render :text => @issues.to_json, :layout => false }
format.api
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
@ -104,14 +108,14 @@ class IssuesController < ApplicationController
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = IssuePriority.all
@time_entry = TimeEntry.new
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.xml { render :layout => false }
format.json { render :text => @issue.to_json, :layout => false }
format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end
@ -138,15 +142,13 @@ class IssuesController < ApplicationController
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) }
format.json { render :text => @issue.to_json, :status => :created, :location => url_for(:controller => 'issues', :action => 'show'), :layout => false }
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
end
return
else
respond_to do |format|
format.html { render :action => 'new' }
format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
format.api { render_validation_errors(@issue) }
end
end
end
@ -171,8 +173,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.xml { head :ok }
format.json { head :ok }
format.api { head :ok }
end
else
render_attachment_warning_if_needed(@issue)
@ -181,8 +182,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { render :action => 'edit' }
format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
format.api { render_validation_errors(@issue) }
end
end
end
@ -232,17 +232,14 @@ class IssuesController < ApplicationController
TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
end
else
unless params[:format] == 'xml' || params[:format] == 'json'
# display the destroy form if it's a user request
return
end
# display the destroy form if it's a user request
return unless api_request?
end
end
@issues.each(&:destroy)
respond_to do |format|
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.xml { head :ok }
format.json { head :ok }
format.api { head :ok }
end
end

View File

@ -19,6 +19,7 @@ class JournalsController < ApplicationController
before_filter :find_journal, :only => [:edit]
before_filter :find_issue, :only => [:new]
before_filter :find_optional_project, :only => [:index]
before_filter :authorize, :only => [:new, :edit]
accept_key_auth :index
helper :issues

View File

@ -19,6 +19,7 @@ class MyController < ApplicationController
before_filter :require_login
helper :issues
helper :users
helper :custom_fields
BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
@ -53,21 +54,18 @@ class MyController < ApplicationController
@user = User.current
@pref = @user.pref
if request.post?
@user.attributes = params[:user]
@user.mail_notification = params[:notification_option] || 'only_my_events'
@user.safe_attributes = params[:user]
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
set_language_if_valid @user.language
flash[:notice] = l(:notice_account_updated)
redirect_to :action => 'account'
return
end
end
@notification_options = @user.valid_notification_options
@notification_option = @user.mail_notification #? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
end
# Manage user's password

View File

@ -26,15 +26,26 @@ class NewsController < ApplicationController
accept_key_auth :index
def index
@news_pages, @newss = paginate :news,
:per_page => 10,
:conditions => Project.allowed_to_condition(User.current, :view_news, :project => @project),
:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC"
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = 10
end
scope = @project ? @project.news.visible : News.visible
@news_count = scope.count
@news_pages = Paginator.new self, @news_count, @limit, params['page']
@offset ||= @news_pages.current.offset
@newss = scope.all(:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC",
:offset => @offset,
:limit => @limit)
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.xml { render :xml => @newss.to_xml }
format.json { render :json => @newss.to_json }
format.api
format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
end
end

View File

@ -32,9 +32,6 @@ class ProjectsController < ApplicationController
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
@ -52,8 +49,10 @@ class ProjectsController < ApplicationController
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
}
format.xml {
@projects = Project.visible.find(:all, :order => 'lft')
format.api {
@offset, @limit = api_offset_and_limit
@project_count = Project.visible.count
@projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => 'lft')
}
format.atom {
projects = Project.visible.find(:all, :order => 'created_on DESC',
@ -67,19 +66,15 @@ class ProjectsController < ApplicationController
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new(params[:project])
@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
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new(params[:project])
@project = Project.new
@project.safe_attributes = 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
@ -93,12 +88,12 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
}
format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
format.api { render :action => 'show', :status => :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 }
format.api { render_validation_errors(@project) }
end
end
@ -120,18 +115,19 @@ class ProjectsController < ApplicationController
end
else
Mailer.with_deliveries(params[:notifications] == '1') do
@project = Project.new(params[:project])
@project = Project.new
@project.safe_attributes = params[:project]
@project.enabled_module_names = params[:enabled_modules]
if validate_parent_id && @project.copy(@source_project, :only => params[:only])
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
elsif !@project.new_record?
# Project was created
# But some objects were not copied due to validation failures
# (eg. issues from disabled trackers)
# TODO: inform about that
redirect_to :controller => 'projects', :action => 'settings'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
end
@ -169,7 +165,7 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html
format.xml
format.api
end
end
@ -185,8 +181,10 @@ class ProjectsController < ApplicationController
def edit
end
# TODO: convert to PUT only
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@project.attributes = params[:project]
@project.safe_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|
@ -194,7 +192,7 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
format.xml { head :ok }
format.api { head :ok }
end
else
respond_to do |format|
@ -202,13 +200,14 @@ class ProjectsController < ApplicationController
settings
render :action => 'settings'
}
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
format.api { render_validation_errors(@project) }
end
end
end
verify :method => :post, :only => :modules, :render => {:nothing => true, :status => :method_not_allowed }
def modules
@project.enabled_module_names = params[:enabled_modules]
@project.enabled_module_names = params[:enabled_module_names]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project, :tab => 'modules'
end
@ -233,11 +232,11 @@ class ProjectsController < ApplicationController
if request.get?
# display confirmation view
else
if params[:format] == 'xml' || params[:confirm]
if api_request? || params[:confirm]
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => 'admin', :action => 'projects' }
format.xml { head :ok }
format.api { head :ok }
end
end
end

View File

@ -94,6 +94,7 @@ class RepositoriesController < ApplicationController
(show_error_not_found; return) unless @entry
@changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
@properties = @repository.properties(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
end
def revisions
@ -127,18 +128,21 @@ class RepositoriesController < ApplicationController
else
# Prevent empty lines when displaying a file with Windows style eol
@content.gsub!("\r\n", "\n")
@changeset = @repository.find_changeset_by_name(@rev)
end
end
def annotate
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@annotate = @repository.scm.annotate(@path, @rev)
(render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
@changeset = @repository.find_changeset_by_name(@rev)
end
def revision
raise ChangesetNotFound if @rev.blank?
@changeset = @repository.find_changeset_by_name(@rev)
raise ChangesetNotFound unless @changeset
@ -174,9 +178,13 @@ class RepositoriesController < ApplicationController
@diff = @repository.diff(@path, @rev, @rev_to)
show_error_not_found unless @diff
end
@changeset = @repository.find_changeset_by_name(@rev)
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
@diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
end
end
def stats
end
@ -196,7 +204,10 @@ class RepositoriesController < ApplicationController
end
end
private
private
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
def find_repository
@project = Project.find(params[:id])
@repository = @project.repository
@ -205,6 +216,12 @@ private
@path ||= ''
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
@rev_to = params[:rev_to]
unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
if @repository.branches.blank?
raise InvalidRevisionParam
end
end
rescue ActiveRecord::RecordNotFound
render_404
rescue InvalidRevisionParam
@ -212,7 +229,7 @@ private
end
def show_error_not_found
render_error l(:error_scm_not_found)
render_error :message => l(:error_scm_not_found), :status => 404
end
# Handler for Redmine::Scm::Adapters::CommandFailed exception

View File

@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2010 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
@ -18,10 +18,11 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :find_project, :only => [:new, :create]
before_filter :find_time_entry, :only => [:edit, :update, :destroy]
before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
accept_key_auth :index, :show, :create, :update, :destroy
helper :sort
include SortHelper
helper :issues
@ -66,6 +67,16 @@ class TimelogController < ApplicationController
render :layout => !request.xhr?
}
format.api {
@entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
}
format.atom {
entries = TimeEntry.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
@ -85,6 +96,14 @@ class TimelogController < ApplicationController
end
end
end
def show
respond_to do |format|
# TODO: Implement html response
format.html { render :nothing => true, :status => 406 }
format.api
end
end
def new
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@ -102,10 +121,18 @@ class TimelogController < ApplicationController
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
end
else
render :action => 'edit'
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
@ -122,21 +149,40 @@ class TimelogController < ApplicationController
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { head :ok }
end
else
render :action => 'edit'
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
if @time_entry.destroy && @time_entry.destroyed?
flash[:notice] = l(:notice_successful_delete)
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
}
format.api { head :ok }
end
else
flash[:error] = l(:notice_unable_delete_time_entry)
respond_to do |format|
format.html {
flash[:error] = l(:notice_unable_delete_time_entry)
redirect_to :back
}
format.api { render_validation_errors(@time_entry) }
end
end
redirect_to :back
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @time_entry.project
end
@ -154,11 +200,11 @@ private
end
def find_project
if params[:issue_id]
@issue = Issue.find(params[:issue_id])
if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
@issue = Issue.find(issue_id)
@project = @issue.project
elsif params[:project_id]
@project = Project.find(params[:project_id])
elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
@project = Project.find(project_id)
else
render_404
return false

View File

@ -1,5 +1,5 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
# Copyright (C) 2006-2010 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
@ -19,6 +19,8 @@ class UsersController < ApplicationController
layout 'admin'
before_filter :require_admin, :except => :show
before_filter :find_user, :only => [:show, :edit, :update, :edit_membership, :destroy_membership]
accept_key_auth :index, :show, :create, :update
helper :sort
include SortHelper
@ -29,6 +31,13 @@ class UsersController < ApplicationController
sort_init 'login', 'asc'
sort_update %w(login firstname lastname mail admin created_on last_login_on)
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
@ -38,20 +47,21 @@ class UsersController < ApplicationController
end
@user_count = User.count(:conditions => c.conditions)
@user_pages = Paginator.new self, @user_count,
per_page_option,
params['page']
@users = User.find :all,:order => sort_clause,
@user_pages = Paginator.new self, @user_count, @limit, params['page']
@offset ||= @user_pages.current.offset
@users = User.find :all,
:order => sort_clause,
:conditions => c.conditions,
:limit => @user_pages.items_per_page,
:offset => @user_pages.current.offset
:limit => @limit,
:offset => @offset
render :layout => !request.xhr?
respond_to do |format|
format.html { render :layout => !request.xhr? }
format.api
end
end
def show
@user = User.find(params[:id])
# show projects based on current user visibility
@memberships = @user.memberships.all(:conditions => Project.visible_by(User.current))
@ -64,104 +74,110 @@ class UsersController < ApplicationController
return
end
end
render :layout => 'base'
rescue ActiveRecord::RecordNotFound
render_404
respond_to do |format|
format.html { render :layout => 'base' }
format.api
end
end
def new
@notification_options = User::MAIL_NOTIFICATION_OPTIONS
@notification_option = Setting.default_notification_option
@user = User.new(:language => Setting.default_language)
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@auth_sources = AuthSource.find(:all)
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@notification_options = User::MAIL_NOTIFICATION_OPTIONS
@notification_option = Setting.default_notification_option
@user = User.new(params[:user])
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@user.safe_attributes = params[:user]
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
# TODO: Similar to My#account
@user.mail_notification = params[:notification_option] || 'only_my_events'
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ? {:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user})
return
Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_create)
redirect_to(params[:continue] ?
{:controller => 'users', :action => 'new'} :
{:controller => 'users', :action => 'edit', :id => @user}
)
}
format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
end
else
@auth_sources = AuthSource.find(:all)
@notification_option = @user.mail_notification
# Clear password input
@user.password = @user.password_confirmation = nil
render :action => 'new'
respond_to do |format|
format.html { render :action => 'new' }
format.api { render_validation_errors(@user) }
end
end
end
def edit
@user = User.find(params[:id])
@notification_options = @user.valid_notification_options
@notification_option = @user.mail_notification
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@user = User.find(params[:id])
@notification_options = @user.valid_notification_options
@notification_option = @user.mail_notification
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
if params[:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
end
@user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
@user.attributes = params[:user]
@user.safe_attributes = params[:user]
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
# TODO: Similar to My#account
@user.mail_notification = params[:notification_option] || 'only_my_events'
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
if @user.save
@user.pref.save
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
if was_activated
Mailer.deliver_account_activated(@user)
elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
Mailer.deliver_account_information(@user, params[:password])
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
Mailer.deliver_account_information(@user, params[:user][:password])
end
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :back
}
format.api { head :ok }
end
flash[:notice] = l(:notice_successful_update)
redirect_to :back
else
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
# Clear password input
@user.password = @user.password_confirmation = nil
render :action => :edit
respond_to do |format|
format.html { render :action => :edit }
format.api { render_validation_errors(@user) }
end
end
rescue ::ActionController::RedirectBackError
redirect_to :controller => 'users', :action => 'edit', :id => @user
end
def edit_membership
@user = User.find(params[:id])
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
respond_to do |format|
@ -184,7 +200,6 @@ class UsersController < ApplicationController
end
def destroy_membership
@user = User.find(params[:id])
@membership = Member.find(params[:membership_id])
if request.post? && @membership.deletable?
@membership.destroy
@ -194,4 +209,17 @@ class UsersController < ApplicationController
format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
end
end
private
def find_user
if params[:id] == 'current'
require_login || return
@user = User.current
else
@user = User.find(params[:id])
end
rescue ActiveRecord::RecordNotFound
render_404
end
end

View File

@ -104,8 +104,24 @@ module ApplicationHelper
# * :text - Link text (default to the formatted revision)
def link_to_revision(revision, project, options={})
text = options.delete(:text) || format_revision(revision)
rev = revision.respond_to?(:identifier) ? revision.identifier : revision
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
:title => l(:label_revision_id, format_revision(revision)))
end
# Generates a link to a message
def link_to_message(message, options={}, html_options = nil)
link_to(
h(truncate(message.subject, :length => 60)),
{ :controller => 'messages', :action => 'show',
:board_id => message.board_id,
:id => message.root,
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
}.merge(options),
html_options
)
end
# Generates a link to a project if active
@ -449,12 +465,19 @@ module ApplicationHelper
only_path = options.delete(:only_path) == false ? false : true
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
parse_non_pre_blocks(text) do |text|
@parsed_headings = []
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
if @parsed_headings.any?
replace_toc(text, @parsed_headings)
end
text
end
def parse_non_pre_blocks(text)
@ -579,16 +602,26 @@ module ApplicationHelper
# source:some/file#L120 -> Link to line 120 of the file
# source:some/file@52#L120 -> Link to line 120 of the file's revision 52
# export:some/file -> Force the download of the file
# Forum messages:
# Forum messages:
# message#1218 -> Link to message with id 1218
#
# Links can refer other objects from other projects, using project identifier:
# identifier:r52
# identifier:document:"Some document"
# identifier:version:1.0.0
# identifier:source:some/file
def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
leading, esc, prefix, sep, identifier = $1, $2, $3, $5 || $7, $6 || $8
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
link = nil
if project_identifier
project = Project.visible.find_by_identifier(project_identifier)
end
if esc.nil?
if prefix.nil? && sep == 'r'
if project && (changeset = project.changesets.find_by_revision(identifier))
link = link_to("r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
# project.changesets.visible raises an SQL error because of a double join on repositories
if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
link = link_to("#{project_prefix}r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100))
end
@ -602,24 +635,18 @@ module ApplicationHelper
:title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
end
when 'document'
if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
if document = Document.visible.find_by_id(oid)
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
:class => 'document'
end
when 'version'
if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
if version = Version.visible.find_by_id(oid)
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'message'
if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
:controller => 'messages',
:action => 'show',
:board_id => message.board,
:id => message.root,
:anchor => (message.parent ? "message-#{message.id}" : nil)},
:class => 'message'
if message = Message.visible.find_by_id(oid, :include => :parent)
link = link_to_message(message, {:only_path => only_path}, :class => 'message')
end
when 'project'
if p = Project.visible.find_by_id(oid)
@ -631,26 +658,26 @@ module ApplicationHelper
name = identifier.gsub(%r{^"(.*)"$}, "\\1")
case prefix
when 'document'
if project && document = project.documents.find_by_title(name)
if project && document = project.documents.visible.find_by_title(name)
link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
:class => 'document'
end
when 'version'
if project && version = project.versions.find_by_name(name)
if project && version = project.versions.visible.find_by_name(name)
link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
:class => 'version'
end
when 'commit'
if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
:class => 'changeset',
:title => truncate_single_line(changeset.comments, :length => 100)
end
when 'source', 'export'
if project && project.repository
if project && project.repository && User.current.allowed_to?(:browse_repository, project)
name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
path, rev, anchor = $1, $3, $5
link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
:path => to_path_param(path),
:rev => rev,
:anchor => anchor,
@ -670,25 +697,30 @@ module ApplicationHelper
end
end
end
leading + (link || "#{prefix}#{sep}#{identifier}")
leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
# Headings and TOC
# Adds ids and links to headings and renders the TOC if needed unless options[:headings] is set to false
# Adds ids and links to headings unless options[:headings] is set to false
def parse_headings(text, project, obj, attr, only_path, options)
headings = []
return if options[:headings] == false
text.gsub!(HEADING_RE) do
level, attrs, content = $1.to_i, $2, $3
item = strip_tags(content).strip
anchor = item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
headings << [level, anchor, item]
@parsed_headings << [level, anchor, item]
"<h#{level} #{attrs} id=\"#{anchor}\">#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
end unless options[:headings] == false
end
end
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
# Renders the TOC with given headings
def replace_toc(text, headings)
text.gsub!(TOC_RE) do
if headings.empty?
''
@ -866,7 +898,29 @@ module ApplicationHelper
def favicon
"<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
end
# Returns true if arg is expected in the API response
def include_in_api_response?(arg)
unless @included_in_api_response
param = params[:include]
@included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
@included_in_api_response.collect!(&:strip)
end
@included_in_api_response.include?(arg.to_s)
end
# Returns options or nil if nometa param or X-Redmine-Nometa header
# was set in the request
def api_meta(options)
if params[:nometa].present? || request.headers['X-Redmine-Nometa']
# compatibility mode for activeresource clients that raise
# an error when unserializing an array with attributes
nil
else
options
end
end
private
def wiki_helper

View File

@ -29,6 +29,18 @@ module AttachmentsHelper
end
def to_utf8(str)
str
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
return str if str.valid_encoding?
else
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
end
begin
Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + ' ')[0..-3]
rescue Iconv::InvalidEncoding
# "UTF-8//IGNORE" is not supported on some OS
str
end
end
end

View File

@ -104,4 +104,15 @@ module CustomFieldsHelper
def custom_field_formats_for_select
Redmine::CustomFieldFormat.as_select
end
# Renders the custom_values in api views
def render_api_custom_values(custom_values, api)
api.array :custom_fields do
custom_values.each do |custom_value|
api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
api.value custom_value.value
end
end
end unless custom_values.empty?
end
end

View File

@ -189,6 +189,20 @@ module IssuesHelper
end
end
# Renders issue children recursively
def render_api_issue_children(issue, api)
return if issue.leaf?
api.array :children do
issue.children.each do |child|
api.issue(:id => child.id) do
api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
api.subject child.subject
render_api_issue_children(child, api)
end
end
end
end
def issues_to_csv(issues, project = nil)
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
decimal_separator = l(:general_csv_decimal_separator)

View File

@ -16,14 +16,4 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module MessagesHelper
def link_to_message(message)
return '' unless message
link_to h(truncate(message.subject, :length => 60)), :controller => 'messages',
:action => 'show',
:board_id => message.board_id,
:id => message.root,
:r => (message.parent_id && message.id),
:anchor => (message.parent_id ? "message-#{message.id}" : nil)
end
end

View File

@ -18,8 +18,12 @@
require 'iconv'
module RepositoriesHelper
def format_revision(txt)
txt.to_s[0,8]
def format_revision(revision)
if revision.respond_to? :format_identifier
revision.format_identifier
else
revision.to_s
end
end
def truncate_at_line_break(text, length = 255)
@ -87,7 +91,7 @@ module RepositoriesHelper
:action => 'show',
:id => @project,
:path => path_param,
:rev => @changeset.revision)
:rev => @changeset.identifier)
output << "<li class='#{style}'>#{text}</li>"
output << render_changes_tree(s)
elsif c = tree[file][:c]
@ -97,13 +101,13 @@ module RepositoriesHelper
:action => 'entry',
:id => @project,
:path => path_param,
:rev => @changeset.revision) unless c.action == 'D'
:rev => @changeset.identifier) unless c.action == 'D'
text << " - #{c.revision}" unless c.revision.blank?
text << ' (' + link_to('diff', :controller => 'repositories',
:action => 'diff',
:id => @project,
:path => path_param,
:rev => @changeset.revision) + ') ' if c.action == 'M'
:rev => @changeset.identifier) + ') ' if c.action == 'M'
text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank?
output << "<li class='#{style}'>#{text}</li>"
end
@ -113,7 +117,13 @@ module RepositoriesHelper
end
def to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
return str if str.valid_encoding?
else
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
end
@encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
@encodings.each do |encoding|
begin

View File

@ -33,6 +33,10 @@ module UsersHelper
options
end
def user_mail_notification_options(user)
user.valid_notification_options.collect {|o| [l(o.last), o.first]}
end
def change_status_link(user)
url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}

View File

@ -43,7 +43,7 @@ class Attachment < ActiveRecord::Base
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
cattr_accessor :storage_path
@@storage_path = "#{RAILS_ROOT}/files"
@@storage_path = Redmine::Configuration['attachments_storage_path'] || "#{RAILS_ROOT}/files"
def validate
if self.filesize > Setting.attachment_max_size.to_i.kilobytes

View File

@ -23,10 +23,10 @@ class Changeset < ActiveRecord::Base
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
:description => :long_comments,
:datetime => :committed_on,
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.revision}}
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
acts_as_searchable :columns => 'comments',
:include => {:repository => :project},
@ -47,6 +47,15 @@ class Changeset < ActiveRecord::Base
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
end
# Returns the identifier of this changeset; depending on repository backends
def identifier
if repository.class.respond_to? :changeset_identifier
repository.class.changeset_identifier self
else
revision.to_s
end
end
def comments=(comment)
write_attribute(:comments, Changeset.normalize_comments(comment))
@ -56,6 +65,15 @@ class Changeset < ActiveRecord::Base
self.commit_date = date
super
end
# Returns the readable identifier
def format_identifier
if repository.class.respond_to? :format_changeset_identifier
repository.class.format_changeset_identifier self
else
identifier
end
end
def committer=(arg)
write_attribute(:committer, self.class.to_utf8(arg.to_s))
@ -77,52 +95,40 @@ class Changeset < ActiveRecord::Base
scan_comment_for_issue_ids
end
TIMELOG_RE = /
(
(\d+([.,]\d+)?)h?
|
(\d+):(\d+)
|
((\d+)(h|hours?))?((\d+)(m|min)?)?
)
/x
def scan_comment_for_issue_ids
return if comments.blank?
# keywords used to reference issues
ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
ref_keywords_any = ref_keywords.delete('*')
# keywords used to fix issues
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
return if kw_regexp.blank?
referenced_issues = []
if ref_keywords.delete('*')
# find any issue ID in the comments
target_issue_ids = []
comments.scan(%r{([\s\(\[,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
referenced_issues += find_referenced_issues_by_id(target_issue_ids)
end
comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
action = match[0]
target_issue_ids = match[1].scan(/\d+/)
target_issues = find_referenced_issues_by_id(target_issue_ids)
if fix_keywords.include?(action.downcase) && fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
# update status of issues
logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
target_issues.each do |issue|
# the issue may have been updated by the closure of another one (eg. duplicate)
issue.reload
# don't change the status is the issue is closed
next if issue.status.is_closed?
csettext = "r#{self.revision}"
if self.scmid && (! (csettext =~ /^r[0-9]+$/))
csettext = "commit:\"#{self.scmid}\""
end
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, csettext))
issue.status = fix_status
unless Setting.commit_fix_done_ratio.blank?
issue.done_ratio = Setting.commit_fix_done_ratio.to_i
end
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue })
issue.save
comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
action, refs = match[2], match[3]
next unless action.present? || ref_keywords_any
refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
if issue
referenced_issues << issue
fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
end
end
referenced_issues += target_issues
end
referenced_issues.uniq!
@ -136,6 +142,14 @@ class Changeset < ActiveRecord::Base
def long_comments
@long_comments || split_comments.last
end
def text_tag
if scmid?
"commit:#{scmid}"
else
"r#{revision}"
end
end
# Returns the previous changeset
def previous
@ -163,13 +177,64 @@ class Changeset < ActiveRecord::Base
private
# Finds issues that can be referenced by the commit message
# i.e. issues that belong to the repository project, a subproject or a parent project
def find_referenced_issues_by_id(ids)
return [] if ids.compact.empty?
Issue.find_all_by_id(ids, :include => :project).select {|issue|
project == issue.project || project.is_ancestor_of?(issue.project) || project.is_descendant_of?(issue.project)
}
# Finds an issue that can be referenced by the commit message
# i.e. an issue that belong to the repository project, a subproject or a parent project
def find_referenced_issue_by_id(id)
return nil if id.blank?
issue = Issue.find_by_id(id.to_i, :include => :project)
if issue
unless project == issue.project || project.is_ancestor_of?(issue.project) || project.is_descendant_of?(issue.project)
issue = nil
end
end
issue
end
def fix_issue(issue)
status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
if status.nil?
logger.warn("No status macthes commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
return issue
end
# the issue may have been updated by the closure of another one (eg. duplicate)
issue.reload
# don't change the status is the issue is closed
return if issue.status && issue.status.is_closed?
journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag))
issue.status = status
unless Setting.commit_fix_done_ratio.blank?
issue.done_ratio = Setting.commit_fix_done_ratio.to_i
end
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue })
unless issue.save
logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
end
issue
end
def log_time(issue, hours)
time_entry = TimeEntry.new(
:user => user,
:hours => hours,
:issue => issue,
:spent_on => commit_date,
:comments => l(:text_time_logged_by_changeset, :value => text_tag, :locale => Setting.default_language)
)
time_entry.activity = log_time_activity unless log_time_activity.nil?
unless time_entry.save
logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
end
time_entry
end
def log_time_activity
if Setting.commit_logtime_activity_id.to_i > 0
TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
end
end
def split_comments
@ -180,7 +245,13 @@ class Changeset < ActiveRecord::Base
end
def self.to_utf8(str)
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
if str.respond_to?(:force_encoding)
str.force_encoding('UTF-8')
return str if str.valid_encoding?
else
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
end
encoding = Setting.commit_logs_encoding.to_s.strip
unless encoding.blank? || encoding == 'UTF-8'
begin

View File

@ -23,7 +23,6 @@ class CustomField < ActiveRecord::Base
validates_presence_of :name, :field_format
validates_uniqueness_of :name, :scope => :type
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\.\'\-]*$/i
validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
def initialize(attributes = nil)

View File

@ -29,6 +29,9 @@ class Document < ActiveRecord::Base
validates_presence_of :project, :title, :category
validates_length_of :title, :maximum => 60
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_documents) } }
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_documents, project)
end

View File

@ -16,6 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Issue < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
belongs_to :tracker
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
@ -32,7 +34,7 @@ class Issue < ActiveRecord::Base
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
acts_as_nested_set :scope => 'root_id'
acts_as_nested_set :scope => 'root_id', :dependent => :destroy
acts_as_attachable :after_remove => :attachment_removed
acts_as_customizable
acts_as_watchable
@ -87,7 +89,6 @@ class Issue < ActiveRecord::Base
before_create :default_assign
before_save :close_duplicates, :update_done_ratio_from_issue_status
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
after_destroy :destroy_children
after_destroy :update_parent_attributes
# Returns true if usr or current user is allowed to view the issue
@ -214,30 +215,29 @@ class Issue < ActiveRecord::Base
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
end
SAFE_ATTRIBUTES = %w(
tracker_id
status_id
parent_issue_id
category_id
assigned_to_id
priority_id
fixed_version_id
subject
description
start_date
due_date
done_ratio
estimated_hours
custom_field_values
lock_version
) unless const_defined?(:SAFE_ATTRIBUTES)
safe_attributes 'tracker_id',
'status_id',
'parent_issue_id',
'category_id',
'assigned_to_id',
'priority_id',
'fixed_version_id',
'subject',
'description',
'start_date',
'due_date',
'done_ratio',
'estimated_hours',
'custom_field_values',
'custom_fields',
'lock_version',
:if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
SAFE_ATTRIBUTES_ON_TRANSITION = %w(
status_id
assigned_to_id
fixed_version_id
done_ratio
) unless const_defined?(:SAFE_ATTRIBUTES_ON_TRANSITION)
safe_attributes 'status_id',
'assigned_to_id',
'fixed_version_id',
'done_ratio',
:if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
# Safely sets attributes
# Should be called from controllers instead of #attributes=
@ -248,13 +248,8 @@ class Issue < ActiveRecord::Base
return unless attrs.is_a?(Hash)
# User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
if new_record? || user.allowed_to?(:edit_issues, project)
attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES.include?(k)}
elsif new_statuses_allowed_to(user).any?
attrs = attrs.reject {|k,v| !SAFE_ATTRIBUTES_ON_TRANSITION.include?(k)}
else
return
end
attrs = delete_unsafe_attributes(attrs, user)
return if attrs.empty?
# Tracker must be set before since new_statuses_allowed_to depends on it.
if t = attrs.delete('tracker_id')
@ -460,11 +455,14 @@ class Issue < ActiveRecord::Base
(relations_from + relations_to).sort
end
def all_dependent_issues
def all_dependent_issues(except=nil)
except ||= self
dependencies = []
relations_from.each do |relation|
dependencies << relation.issue_to
dependencies += relation.issue_to.all_dependent_issues
if relation.issue_to && relation.issue_to != except
dependencies << relation.issue_to
dependencies += relation.issue_to.all_dependent_issues(except)
end
end
dependencies
end
@ -759,14 +757,6 @@ class Issue < ActiveRecord::Base
end
end
def destroy_children
unless leaf?
children.each do |child|
child.destroy
end
end
end
# Update issues so their versions are not pointing to a
# fixed_version that is not shared with the issue's project
def self.update_versions(conditions=nil)

View File

@ -84,14 +84,15 @@ class IssueRelation < ActiveRecord::Base
def set_issue_to_dates
soonest_start = self.successor_soonest_start
if soonest_start
if soonest_start && issue_to
issue_to.reschedule_after(soonest_start)
end
end
def successor_soonest_start
return nil unless (TYPE_PRECEDES == self.relation_type) && (issue_from.start_date || issue_from.due_date)
(issue_from.due_date || issue_from.start_date) + 1 + delay
if (TYPE_PRECEDES == self.relation_type) && delay && issue_from && (issue_from.start_date || issue_from.due_date)
(issue_from.due_date || issue_from.start_date) + 1 + delay
end
end
def <=>(relation)

View File

@ -25,7 +25,6 @@ class IssueStatus < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
def after_save

View File

@ -148,6 +148,9 @@ class MailHandler < ActionMailer::Base
raise UnauthorizedAction unless user.allowed_to?(:add_issue_notes, issue.project) || user.allowed_to?(:edit_issues, issue.project)
end
# ignore CLI-supplied defaults for new issues
@@handler_options[:issue].clear
journal = issue.init_journal(user, cleaned_up_text_body)
issue.safe_attributes = issue_attributes_from_keywords(issue)
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
@ -232,8 +235,8 @@ class MailHandler < ActionMailer::Base
def extract_keyword!(text, attr, format=nil)
keys = [attr.to_s.humanize]
if attr.is_a?(Symbol)
keys << l("field_#{attr}", :default => '', :locale => user.language) if user
keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
keys << l("field_#{attr}", :default => '', :locale => user.language) if user && user.language.present?
keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) if Setting.default_language.present?
end
keys.reject! {|k| k.blank?}
keys.collect! {|k| Regexp.escape(k)}
@ -256,8 +259,8 @@ class MailHandler < ActionMailer::Base
assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_user_from_keyword(k)
assigned_to = nil if assigned_to && !issue.assignable_users.include?(assigned_to)
{
'tracker_id' => ((k = get_keyword(:tracker)) && issue.project.trackers.find_by_name(k).try(:id)) || issue.project.trackers.find(:first).try(:id),
attrs = {
'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.find_by_name(k).try(:id),
'status_id' => (k = get_keyword(:status)) && IssueStatus.find_by_name(k).try(:id),
'priority_id' => (k = get_keyword(:priority)) && IssuePriority.find_by_name(k).try(:id),
'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.find_by_name(k).try(:id),
@ -268,6 +271,12 @@ class MailHandler < ActionMailer::Base
'estimated_hours' => get_keyword(:estimated_hours, :override => true),
'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
}.delete_if {|k, v| v.blank? }
if issue.new_record? && attrs['tracker_id'].nil?
attrs['tracker_id'] = issue.project.trackers.find(:first).try(:id)
end
attrs
end
# Returns a Hash of issue custom field values extracted from keywords in the email body

View File

@ -296,7 +296,7 @@ class Mailer < ActionMailer::Base
if raise_errors
raise e
elsif mylogger
mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/email.yml."
mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/configuration.yml."
end
ensure
self.class.raise_delivery_errors = raise_errors

View File

@ -42,6 +42,9 @@ class Message < ActiveRecord::Base
after_create :add_author_as_watcher
named_scope :visible, lambda {|*args| { :include => {:board => :project},
:conditions => Project.allowed_to_condition(args.first || User.current, :view_messages) } }
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_messages, project)
end

View File

@ -29,6 +29,11 @@ class News < ActiveRecord::Base
acts_as_activity_provider :find_options => {:include => [:project, :author]},
:author_key => :author_id
named_scope :visible, lambda {|*args| {
:include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_news)
}}
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_news, project)
end

View File

@ -16,6 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Project < ActiveRecord::Base
include Redmine::SafeAttributes
# Project statuses
STATUS_ACTIVE = 1
STATUS_ARCHIVED = 9
@ -64,7 +66,7 @@ class Project < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o}},
:author => nil
attr_protected :status, :enabled_module_names
attr_protected :status
validates_presence_of :name, :identifier
validates_uniqueness_of :identifier
@ -84,6 +86,24 @@ class Project < ActiveRecord::Base
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
def initialize(attributes = nil)
super
initialized = (attributes || {}).stringify_keys
if !initialized.key?('identifier') && Setting.sequential_project_identifiers?
self.identifier = Project.next_identifier
end
if !initialized.key?('is_public')
self.is_public = Setting.default_projects_public?
end
if !initialized.key?('enabled_module_names')
self.enabled_module_names = Setting.default_projects_modules
end
if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
self.trackers = Tracker.all
end
end
def identifier=(identifier)
super unless identifier_frozen?
end
@ -431,24 +451,20 @@ class Project < ActiveRecord::Base
# 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
[
issues.minimum('start_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect(&:start_date)
].flatten.compact.min
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
[
issues.maximum('due_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.maximum('due_date')}
].flatten.compact.max
end
def overdue?
@ -492,7 +508,7 @@ class Project < ActiveRecord::Base
def enabled_module_names=(module_names)
if module_names && module_names.is_a?(Array)
module_names = module_names.collect(&:to_s)
module_names = module_names.collect(&:to_s).reject(&:blank?)
# remove disabled modules
enabled_modules.each {|mod| mod.destroy unless module_names.include?(mod.name)}
# add new modules
@ -501,7 +517,25 @@ class Project < ActiveRecord::Base
enabled_modules.clear
end
end
# Returns an array of the enabled modules names
def enabled_module_names
enabled_modules.collect(&:name)
end
safe_attributes 'name',
'description',
'homepage',
'is_public',
'identifier',
'custom_field_values',
'custom_fields',
'tracker_ids',
'issue_custom_field_ids'
safe_attributes 'enabled_module_names',
:if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) }
# Returns an array of projects that are in this project's hierarchy
#
# Example: parents, children, siblings
@ -669,12 +703,20 @@ class Project < ActiveRecord::Base
end
self.issues << new_issue
issues_map[issue.id] = new_issue
if new_issue.new_record?
logger.info "Project#copy_issues: issue ##{issue.id} could not be copied: #{new_issue.errors.full_messages}" if logger && logger.info
else
issues_map[issue.id] = new_issue unless new_issue.new_record?
end
end
# Relations after in case issues related each other
project.issues.each do |issue|
new_issue = issues_map[issue.id]
unless new_issue
# Issue was not copied
next
end
# Relations
issue.relations_from.each do |source_relation|
@ -701,7 +743,12 @@ class Project < ActiveRecord::Base
# Copies members from +project+
def copy_members(project)
project.memberships.each do |member|
# Copy users first, then groups to handle members with inherited and given roles
members_to_copy = []
members_to_copy += project.memberships.select {|m| m.principal.is_a?(User)}
members_to_copy += project.memberships.select {|m| !m.principal.is_a?(User)}
members_to_copy.each do |member|
new_member = Member.new
new_member.attributes = member.attributes.dup.except("id", "project_id", "created_on")
# only copy non inherited roles

View File

@ -187,10 +187,18 @@ class Query < ActiveRecord::Base
if project
user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] }
else
project_ids = Project.all(:conditions => Project.visible_by(User.current)).collect(&:id)
if project_ids.any?
# members of the user's projects
user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", project_ids]).sort.collect{|s| [s.name, s.id.to_s] }
all_projects = Project.visible.all
if all_projects.any?
# members of visible projects
user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", all_projects.collect(&:id)]).sort.collect{|s| [s.name, s.id.to_s] }
# project filter
project_values = []
Project.project_tree(all_projects) do |p, level|
prefix = (level > 0 ? ('--' * level + ' ') : '')
project_values << ["#{prefix}#{p.name}", p.id.to_s]
end
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values} unless project_values.empty?
end
end
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
@ -225,12 +233,6 @@ class Query < ActiveRecord::Base
@available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => system_shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
end
add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
# project filter
project_values = Project.all(:conditions => Project.visible_by(User.current), :order => 'lft').map do |p|
pre = (p.level > 0 ? ('--' * p.level + ' ') : '')
["#{pre}#{p.name}",p.id.to_s]
end
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values}
end
@available_filters
end
@ -376,15 +378,15 @@ class Query < ActiveRecord::Base
# Returns true if the query is a grouped query
def grouped?
!group_by.blank?
!group_by_column.nil?
end
def group_by_column
groupable_columns.detect {|c| c.name.to_s == group_by}
groupable_columns.detect {|c| c.groupable && c.name.to_s == group_by}
end
def group_by_statement
group_by_column.groupable
group_by_column.try(:groupable)
end
def project_statement

View File

@ -86,17 +86,25 @@ class Repository < ActiveRecord::Base
def diff(path, rev, rev_to)
scm.diff(path, rev, rev_to)
end
def diff_format_revisions(cs, cs_to, sep=':')
text = ""
text << cs_to.format_identifier + sep if cs_to
text << cs.format_identifier if cs
text
end
# Returns a path relative to the url of the repository
def relative_path(path)
path
end
# Finds and returns a revision with a number or the beginning of a hash
def find_changeset_by_name(name)
return nil if name.blank?
changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
end
def latest_changeset
@latest_changeset ||= changesets.find(:first)
end

View File

@ -104,10 +104,11 @@ class Repository::Cvs < Repository
scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
# only add the change to the database, if it doen't exists. the cvs log
# is not exclusive at all.
unless changes.find_by_path_and_revision(scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
revision
tmp_time = revision.time.clone
unless changes.find_by_path_and_revision(
scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
cs = changesets.find(:first, :conditions=>{
:committed_on=>revision.time-time_delta..revision.time+time_delta,
:committed_on=>tmp_time - time_delta .. tmp_time + time_delta,
:committer=>revision.author,
:comments=>Changeset.normalize_comments(revision.message)
})
@ -116,11 +117,14 @@ class Repository::Cvs < Repository
unless cs
# we use a temporaray revision number here (just for inserting)
# later on, we calculate a continous positive number
latest = changesets.find(:first, :order => 'id DESC')
tmp_time2 = tmp_time.clone.gmtime
branch = revision.paths[0][:branch]
scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
cs = Changeset.create(:repository => self,
:revision => "_#{tmp_rev_num}",
:revision => "tmp#{tmp_rev_num}",
:scmid => scmid,
:committer => revision.author,
:committed_on => revision.time,
:committed_on => tmp_time,
:comments => revision.message)
tmp_rev_num += 1
end
@ -144,10 +148,13 @@ class Repository::Cvs < Repository
end
# Renumber new changesets in chronological order
changesets.find(:all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE '_%'").each do |changeset|
changesets.find(
:all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE 'tmp%'"
).each do |changeset|
changeset.update_attribute :revision, next_revision_number
end
end # transaction
@current_revision_number = nil
end
private
@ -155,7 +162,9 @@ class Repository::Cvs < Repository
# Returns the next revision number to assign to a CVS changeset
def next_revision_number
# Need to retrieve existing revision numbers to sort them as integers
@current_revision_number ||= (connection.select_values("SELECT revision FROM #{Changeset.table_name} WHERE repository_id = #{id} AND revision NOT LIKE '_%'").collect(&:to_i).max || 0)
sql = "SELECT revision FROM #{Changeset.table_name} "
sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
@current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
@current_revision_number += 1
end
end

View File

@ -29,6 +29,16 @@ class Repository::Git < Repository
'Git'
end
# Returns the identifier for the given git changeset
def self.changeset_identifier(changeset)
changeset.scmid
end
# Returns the readable identifier for the given git changeset
def self.format_changeset_identifier(changeset)
changeset.revision[0, 8]
end
def branches
scm.branches
end

View File

@ -18,17 +18,34 @@
require 'redmine/scm/adapters/mercurial_adapter'
class Repository::Mercurial < Repository
# sort changesets by revision number
has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id'
attr_protected :root_url
validates_presence_of :url
def scm_adapter
Redmine::Scm::Adapters::MercurialAdapter
end
def self.scm_name
'Mercurial'
end
# Returns the readable identifier for the given mercurial changeset
def self.format_changeset_identifier(changeset)
"#{changeset.revision}:#{changeset.scmid}"
end
# Returns the identifier for the given Mercurial changeset
def self.changeset_identifier(changeset)
changeset.scmid
end
def diff_format_revisions(cs, cs_to, sep=':')
super(cs, cs_to, ' ')
end
def entries(path=nil, identifier=nil)
entries=scm.entries(path, identifier)
if entries
@ -52,6 +69,30 @@ class Repository::Mercurial < Repository
entries
end
# Finds and returns a revision with a number or the beginning of a hash
def find_changeset_by_name(name)
return nil if name.nil? || name.empty?
if /[^\d]/ =~ name or name.to_s.size > 8
e = changesets.find(:first, :conditions => ['scmid = ?', name.to_s])
else
e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
end
return e if e
changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"]) # last ditch
end
# Returns the latest changesets for +path+; sorted by revision number
def latest_changesets(path, rev, limit=10)
if path.blank?
changesets.find(:all, :include => :user, :limit => limit)
else
changes.find(:all, :include => {:changeset => :user},
:conditions => ["path = ?", path.with_leading_slash],
:order => "#{Changeset.table_name}.id DESC",
:limit => limit).collect(&:changeset)
end
end
def fetch_changesets
scm_info = scm.info
if scm_info

View File

@ -43,7 +43,6 @@ class Role < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
def permissions
read_attribute(:permissions) || []

View File

@ -66,6 +66,9 @@ class TimeEntry < ActiveRecord::Base
# these attributes make time aggregations easier
def spent_on=(date)
super
if spent_on.is_a?(Time)
self.spent_on = spent_on.to_date
end
self.tyear = spent_on ? spent_on.year : nil
self.tmonth = spent_on ? spent_on.month : nil
self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil

View File

@ -31,7 +31,6 @@ class Tracker < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :maximum => 30
validates_format_of :name, :with => /^[\w\s\'\-]*$/i
def to_s; name end

View File

@ -18,7 +18,8 @@
require "digest/sha1"
class User < Principal
include Redmine::SafeAttributes
# Account statuses
STATUS_ANONYMOUS = 0
STATUS_ACTIVE = 1
@ -34,13 +35,13 @@ class User < Principal
}
MAIL_NOTIFICATION_OPTIONS = [
[:all, :label_user_mail_option_all],
[:selected, :label_user_mail_option_selected],
[:none, :label_user_mail_option_none],
[:only_my_events, :label_user_mail_option_only_my_events],
[:only_assigned, :label_user_mail_option_only_assigned],
[:only_owner, :label_user_mail_option_only_owner]
]
['all', :label_user_mail_option_all],
['selected', :label_user_mail_option_selected],
['only_my_events', :label_user_mail_option_only_my_events],
['only_assigned', :label_user_mail_option_only_assigned],
['only_owner', :label_user_mail_option_only_owner],
['none', :label_user_mail_option_none]
]
has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
:after_remove => Proc.new {|user, group| group.user_removed(user)}
@ -59,7 +60,7 @@ class User < Principal
attr_accessor :password, :password_confirmation
attr_accessor :last_before_login_on
# Prevents unauthorized assignments
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
@ -67,11 +68,11 @@ class User < Principal
# Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :maximum => 30
validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
validates_length_of :firstname, :lastname, :maximum => 30
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
validates_length_of :mail, :maximum => 60, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
def before_create
self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
@ -259,12 +260,16 @@ class User < Principal
notified_projects_ids
end
# Only users that belong to more than 1 project can select projects for which they are notified
def valid_notification_options
self.class.valid_notification_options(self)
end
# Only users that belong to more than 1 project can select projects for which they are notified
def self.valid_notification_options(user=nil)
# Note that @user.membership.size would fail since AR ignores
# :include association option when doing a count
if memberships.length < 1
MAIL_NOTIFICATION_OPTIONS.delete_if {|option| option.first == :selected}
if user.nil? || user.memberships.length < 1
MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
else
MAIL_NOTIFICATION_OPTIONS
end
@ -390,32 +395,54 @@ class User < Principal
def allowed_to_globally?(action, options)
allowed_to?(action, nil, options.reverse_merge(:global => true))
end
safe_attributes 'login',
'firstname',
'lastname',
'mail',
'mail_notification',
'language',
'custom_field_values',
'custom_fields',
'identity_url'
safe_attributes 'status',
'auth_source_id',
:if => lambda {|user, current_user| current_user.admin?}
safe_attributes 'group_ids',
:if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
# Utility method to help check if a user should be notified about an
# event.
#
# TODO: only supports Issue events currently
def notify_about?(object)
case mail_notification.to_sym
when :all
case mail_notification
when 'all'
true
when :selected
# Handled by the Project
when :none
false
when :only_my_events
when 'selected'
# user receives notifications for created/assigned issues on unselected projects
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
else
false
end
when :only_assigned
when 'none'
false
when 'only_my_events'
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
true
else
false
end
when 'only_assigned'
if object.is_a?(Issue) && object.assigned_to == self
true
else
false
end
when :only_owner
when 'only_owner'
if object.is_a?(Issue) && object.author == self
true
else

View File

@ -43,7 +43,7 @@ class Version < ActiveRecord::Base
end
def start_date
effective_date
@start_date ||= fixed_issues.minimum('start_date')
end
def due_date
@ -77,8 +77,7 @@ class Version < ActiveRecord::Base
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')
elsif due_date && start_date
done_date = start_date + ((due_date - start_date+1)* completed_pourcent/100).floor
return done_date <= Date.today
else

View File

@ -45,11 +45,11 @@ class Wiki < ActiveRecord::Base
# find the page with the given title
def find_page(title, options = {})
title = start_page if title.blank?
title = Wiki.titleize(title).downcase
page = pages.first(:conditions => ["LOWER(title) LIKE ?", title])
title = Wiki.titleize(title)
page = pages.first(:conditions => ["LOWER(title) LIKE LOWER(?)", title])
if !page && !(options[:with_redirect] == false)
# search for a redirect
redirect = redirects.first(:conditions => ["LOWER(title) LIKE ?", title])
redirect = redirects.first(:conditions => ["LOWER(title) LIKE LOWER(?)", title])
page = find_page(redirect.redirects_to, :with_redirect => false) if redirect
end
page

View File

@ -4,7 +4,7 @@
<% if diff_type == 'sbs' -%>
<table class="filecontent">
<thead>
<tr><th colspan="4" class="filename"><%= table_file.file_name %></th></tr>
<tr><th colspan="4" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
</thead>
<tbody>
<% prev_line_left, prev_line_right = nil, nil -%>
@ -31,7 +31,7 @@
<% else -%>
<table class="filecontent syntaxhl">
<thead>
<tr><th colspan="3" class="filename"><%= table_file.file_name %></th></tr>
<tr><th colspan="3" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
</thead>
<tbody>
<% prev_line_left, prev_line_right = nil, nil -%>

View File

@ -59,10 +59,19 @@ end
# Width of the entire chart
g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom
# Collect the number of issues on Versions
@gantt.render(:top => headers_height + 8, :zoom => zoom, :g_width => g_width)
g_height = [(20 * (@gantt.number_of_rows + 6))+150, 206].max
t_height = g_height + headers_height
%>
<% if @gantt.truncated %>
<p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
<% end %>
<table width="100%" style="border:0; border-collapse: collapse;">
<tr>
<td style="width:<%= subject_width %>px; padding:0px;">
@ -70,9 +79,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>
<% top = headers_height + 8 %>
<%= @gantt.subjects(:headers_height => headers_height, :top => top, :g_width => g_width) %>
<div class="gantt_subjects">
<%= @gantt.subjects %>
</div>
</div>
</td>
@ -151,9 +161,7 @@ if show_days
end
end %>
<% top = headers_height + 10 %>
<%= @gantt.lines(:top => top, :zoom => zoom, :g_width => g_width ) %>
<%= @gantt.lines %>
<%
#

View File

@ -10,6 +10,10 @@
<%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
<div class="box tabular">
<fieldset class="attributes">
<legend><%= l(:label_change_properties) %></legend>
<div class="splitcontentleft">
<p><label for="new_project_id"><%=l(:field_project)%>:</label>
<%= select_tag "new_project_id",
project_tree_options_for_select(@allowed_projects, :selected => @target_project),
@ -21,13 +25,6 @@
<p><label for="new_tracker_id"><%=l(:field_tracker)%>:</label>
<%= select_tag "new_tracker_id", "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, "id", "name") %></p>
<p>
<label><%= l(:field_assigned_to) %></label>
<%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %>
</p>
<p>
<label><%= l(:field_status) %></label>
<%= select_tag('status_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %>
@ -38,6 +35,15 @@
<%= select_tag('priority_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %>
</p>
<p>
<label><%= l(:field_assigned_to) %></label>
<%= select_tag('assigned_to_id', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %>
</p>
</div>
<div class="splitcontentright">
<p>
<label><%= l(:field_start_date) %></label>
<%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %>
@ -47,6 +53,9 @@
<label><%= l(:field_due_date) %></label>
<%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %>
</p>
</div>
</fieldset>
<fieldset><legend><%= l(:field_notes) %></legend>
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>

View File

@ -1,7 +1,9 @@
<%= error_messages_for 'relation' %>
<p><%= f.select :relation_type, collection_for_relation_type_select, {}, :onchange => "setPredecessorFieldsVisibility();" %>
<%= l(:label_issue) %> #<%= f.text_field :issue_to_id, :size => 6 %>
<%= l(:label_issue) %> #<%= f.text_field :issue_to_id, :size => 10 %>
<div id="related_issue_candidates" class="autocomplete"></div>
<%= javascript_tag "observeRelatedIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %>
<span id="predecessor_fields" style="display:none;">
<%= l(:field_delay) %>: <%= f.text_field :delay, :size => 3 %> <%= l(:label_day_plural) %>
</span>

View File

@ -1,7 +1,7 @@
<% changesets.each do |changeset| %>
<div class="changeset <%= cycle('odd', 'even') %>">
<p><%= link_to("#{l(:label_revision)} #{changeset.revision}",
:controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => changeset.revision) %><br />
<p><%= link_to_revision(changeset, changeset.project,
:text => "#{l(:label_revision)} #{changeset.format_identifier}") %><br />
<span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p>
<div class="changeset-changes">
<%= textilizable(changeset, :comments) %>

View File

@ -6,9 +6,9 @@
<p><strong><%=l(:label_related_issues)%></strong></p>
<% if @issue.relations.any? %>
<% if @relations.present? %>
<table style="width:100%">
<% @issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation| %>
<% @relations.each do |relation| %>
<tr>
<td><%= l(relation.label_for(@issue)) %> <%= "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)})" if relation.delay && relation.delay != 0 %>
<%= h(relation.other_issue(@issue).project) + ' - ' if Setting.cross_project_issue_relations? %>

View File

@ -43,7 +43,7 @@
<label><%= l(:field_fixed_version) %></label>
<%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
version_options_for_select(@project.shared_versions.open)) %>
version_options_for_select(@project.shared_versions.open.sort)) %>
</p>
<% end %>

View File

@ -0,0 +1,28 @@
api.array :issues, api_meta(:total_count => @issue_count, :offset => @offset, :limit => @limit) do
@issues.each do |issue|
api.issue do
api.id issue.id
api.project(:id => issue.project_id, :name => issue.project.name) unless issue.project.nil?
api.tracker(:id => issue.tracker_id, :name => issue.tracker.name) unless issue.tracker.nil?
api.status(:id => issue.status_id, :name => issue.status.name) unless issue.status.nil?
api.priority(:id => issue.priority_id, :name => issue.priority.name) unless issue.priority.nil?
api.author(:id => issue.author_id, :name => issue.author.name) unless issue.author.nil?
api.assigned_to(:id => issue.assigned_to_id, :name => issue.assigned_to.name) unless issue.assigned_to.nil?
api.category(:id => issue.category_id, :name => issue.category.name) unless issue.category.nil?
api.fixed_version(:id => issue.fixed_version_id, :name => issue.fixed_version.name) unless issue.fixed_version.nil?
api.parent(:id => issue.parent_id) unless issue.parent.nil?
api.subject issue.subject
api.description issue.description
api.start_date issue.start_date
api.due_date issue.due_date
api.done_ratio issue.done_ratio
api.estimated_hours issue.estimated_hours
render_api_custom_values issue.custom_field_values, api
api.created_on issue.created_on
api.updated_on issue.updated_on
end
end
end

View File

@ -1,33 +0,0 @@
xml.instruct!
xml.issues :type => 'array' do
@issues.each do |issue|
xml.issue do
xml.id issue.id
xml.project(:id => issue.project_id, :name => issue.project.name) unless issue.project.nil?
xml.tracker(:id => issue.tracker_id, :name => issue.tracker.name) unless issue.tracker.nil?
xml.status(:id => issue.status_id, :name => issue.status.name) unless issue.status.nil?
xml.priority(:id => issue.priority_id, :name => issue.priority.name) unless issue.priority.nil?
xml.author(:id => issue.author_id, :name => issue.author.name) unless issue.author.nil?
xml.assigned_to(:id => issue.assigned_to_id, :name => issue.assigned_to.name) unless issue.assigned_to.nil?
xml.category(:id => issue.category_id, :name => issue.category.name) unless issue.category.nil?
xml.fixed_version(:id => issue.fixed_version_id, :name => issue.fixed_version.name) unless issue.fixed_version.nil?
xml.parent(:id => issue.parent_id) unless issue.parent.nil?
xml.subject issue.subject
xml.description issue.description
xml.start_date issue.start_date
xml.due_date issue.due_date
xml.done_ratio issue.done_ratio
xml.estimated_hours issue.estimated_hours
xml.custom_fields do
issue.custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end
xml.created_on issue.created_on
xml.updated_on issue.updated_on
end
end
end

View File

@ -0,0 +1,61 @@
api.issue do
api.id @issue.id
api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
api.subject @issue.subject
api.description @issue.description
api.start_date @issue.start_date
api.due_date @issue.due_date
api.done_ratio @issue.done_ratio
api.estimated_hours @issue.estimated_hours
api.spent_hours(@issue.spent_hours) if User.current.allowed_to?(:view_time_entries, @project)
render_api_custom_values @issue.custom_field_values, api
api.created_on @issue.created_on
api.updated_on @issue.updated_on
render_api_issue_children(@issue, api) if include_in_api_response?('children')
api.array :relations do
@relations.each do |relation|
api.relation(:id => relation.id, :issue_id => relation.other_issue(@issue).id, :relation_type => relation.relation_type_for(@issue), :delay => relation.delay)
end
end if include_in_api_response?('relations') && @relations.present?
api.array :changesets do
@issue.changesets.each do |changeset|
api.changeset :revision => changeset.revision do
api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
api.comments changeset.comments
api.committed_on changeset.committed_on
end
end
end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project)
api.array :journals do
@issue.journals.each do |journal|
api.journal :id => journal.id do
api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
api.notes journal.notes
api.created_on journal.created_on
api.array :details do
journal.details.each do |detail|
api.detail :property => detail.property, :name => detail.prop_key do
api.old_value detail.old_value
api.new_value detail.value
end
end
end
end
end
end if include_in_api_response?('journals')
end

View File

@ -47,14 +47,16 @@
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') if @issue.description? %>
</div>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
<% if @issue.description? %>
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %>
</div>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
<% end %>
<%= link_to_attachments @issue %>
<% end -%>

View File

@ -1,62 +0,0 @@
xml.instruct!
xml.issue do
xml.id @issue.id
xml.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
xml.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
xml.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
xml.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
xml.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
xml.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
xml.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
xml.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
xml.parent(:id => @issue.parent_id) unless @issue.parent.nil?
xml.subject @issue.subject
xml.description @issue.description
xml.start_date @issue.start_date
xml.due_date @issue.due_date
xml.done_ratio @issue.done_ratio
xml.estimated_hours @issue.estimated_hours
if User.current.allowed_to?(:view_time_entries, @project)
xml.spent_hours @issue.spent_hours
end
xml.custom_fields do
@issue.custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end unless @issue.custom_field_values.empty?
xml.created_on @issue.created_on
xml.updated_on @issue.updated_on
xml.relations do
@issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation|
xml.relation(:id => relation.id, :issue_id => relation.other_issue(@issue).id, :relation_type => relation.relation_type_for(@issue), :delay => relation.delay)
end
end
xml.changesets do
@issue.changesets.each do |changeset|
xml.changeset :revision => changeset.revision do
xml.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
xml.comments changeset.comments
xml.committed_on changeset.committed_on
end
end
end if User.current.allowed_to?(:view_changesets, @project) && @issue.changesets.any?
xml.journals do
@issue.journals.each do |journal|
xml.journal :id => journal.id do
xml.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
xml.notes journal.notes
xml.details do
journal.details.each do |detail|
xml.detail :property => detail.property, :name => detail.prop_key, :old => detail.old_value, :new => detail.value
end
end
end
end
end unless @issue.journals.empty?
end

View File

@ -9,8 +9,9 @@
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
<%= javascript_include_tag :defaults %>
<%= heads_for_theme %>
<%= heads_for_wiki_formatter %>
<!--[if IE]>
<!--[if IE 6]>
<style type="text/css">
* html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); }
body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
@ -68,7 +69,7 @@
<div id="footer">
<div class="bgl"><div class="bgr">
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2010 Jean-Philippe Lang
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2011 Jean-Philippe Lang
</div></div>
</div>
</div>

View File

@ -36,7 +36,7 @@ function removeBlock(block) {
<div class="contextual">
<% form_tag({:action => "add_block"}, :id => "block-form") do %>
<%= label_tag('block-select', l(:label_my_page_block)) %>
<%= label_tag('block-select', l(:label_my_page_block)) %>:
<%= select_tag 'block', "<option></option>" + options_for_select(@block_options), :id => "block-select" %>
<%= link_to_remote l(:button_add),
{:url => { :action => "add_block" },

View File

@ -0,0 +1,14 @@
api.array :news, api_meta(:total_count => @news_count, :offset => @offset, :limit => @limit) do
@newss.each do |news|
api.news do
api.id news.id
api.project(:id => news.project_id, :name => news.project.name) unless news.project.nil?
api.author(:id => news.author_id, :name => news.author.name) unless news.author.nil?
api.title news.title
api.summary news.summary
api.description news.description
api.created_on news.created_on
end
end
end

View File

@ -23,8 +23,22 @@
<%= call_hook(:view_projects_form, :project => @project, :form => f) %>
</div>
<% if @project.new_record? %>
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<label class="floating">
<%= check_box_tag 'project[enabled_module_names][]', m, @project.module_enabled?(m), :id => "project_enabled_module_names_#{m}" %>
<%= l_or_humanize(m, :prefix => "project_module_") %>
</label>
<% end %>
<%= hidden_field_tag 'project[enabled_module_names][]', '' %>
<%= javascript_tag 'observeProjectModules()' %>
</fieldset>
<% end %>
<% if @project.new_record? || @project.module_enabled?('issue_tracking') %>
<% unless @trackers.empty? %>
<fieldset class="box"><legend><%=l(:label_tracker_plural)%></legend>
<fieldset class="box" id="project_trackers"><legend><%=l(:label_tracker_plural)%></legend>
<% @trackers.each do |tracker| %>
<label class="floating">
<%= check_box_tag 'project[tracker_ids][]', tracker.id, @project.trackers.include?(tracker) %>
@ -36,7 +50,7 @@
<% end %>
<% unless @issue_custom_fields.empty? %>
<fieldset class="box"><legend><%=l(:label_custom_field_plural)%></legend>
<fieldset class="box" id="project_issue_custom_fields"><legend><%=l(:label_custom_field_plural)%></legend>
<% @issue_custom_fields.each do |custom_field| %>
<label class="floating">
<%= check_box_tag 'project[issue_custom_field_ids][]', custom_field.id, (@project.all_issue_custom_fields.include? custom_field), (custom_field.is_for_all? ? {:disabled => "disabled"} : {}) %>
@ -46,4 +60,5 @@
<%= hidden_field_tag 'project[issue_custom_field_ids][]', '' %>
</fieldset>
<% end %>
<% end %>
<!--[eoform:project]-->

View File

@ -0,0 +1,16 @@
api.array :projects, api_meta(:total_count => @project_count, :offset => @offset, :limit => @limit) do
@projects.each do |project|
api.project do
api.id project.id
api.name project.name
api.identifier project.identifier
api.description project.description
api.parent(:id => project.parent_id, :name => project.parent.name) unless project.parent.nil?
render_api_custom_values project.visible_custom_field_values, api
api.created_on project.created_on
api.updated_on project.updated_on
end
end
end

View File

@ -1,19 +0,0 @@
xml.instruct!
xml.projects :type => 'array' do
@projects.each do |project|
xml.project do
xml.id project.id
xml.name project.name
xml.identifier project.identifier
xml.description project.description
xml.parent(:id => project.parent_id, :name => project.parent.name) unless project.parent.nil?
xml.custom_fields do
project.visible_custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end unless project.custom_field_values.empty?
xml.created_on project.created_on
xml.updated_on project.updated_on
end
end
end

View File

@ -2,16 +2,6 @@
<% 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>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<label class="floating">
<%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %>
<%= l_or_humanize(m, :prefix => "project_module_") %>
</label>
<% end %>
</fieldset>
<%= submit_tag l(:button_save) %>
<%= javascript_tag "Form.Element.focus('project_name');" %>
<% end %>

View File

@ -56,8 +56,8 @@
<div class="splitcontentright">
<% if roles.any? && principals.any? %>
<% remote_form_for(:member, @member, :url => {:controller => 'members', :action => 'new', :id => @project}, :method => :post,
:loading => "$('member-add-submit').disable()",
:complete => "$('member-add-submit').enable()") do |f| %>
:loading => '$(\'member-add-submit\').disable();',
:complete => 'if($(\'member-add-submit\')) $(\'member-add-submit\').enable();') do |f| %>
<fieldset><legend><%=l(:label_member_new)%></legend>
<p><%= label_tag "principal_search", l(:label_principal_search) %><%= text_field_tag 'principal_search', nil %></p>

View File

@ -7,7 +7,7 @@
<legend><%= l(:text_select_project_modules) %></legend>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<p><label><%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) -%>
<p><label><%= check_box_tag 'enabled_module_names[]', m, @project.module_enabled?(m) -%>
<%= l_or_humanize(m, :prefix => "project_module_") %></label></p>
<% end %>
</fieldset>

View File

@ -0,0 +1,18 @@
api.project do
api.id @project.id
api.name @project.name
api.identifier @project.identifier
api.description @project.description
api.homepage @project.homepage
render_api_custom_values @project.visible_custom_field_values, api
api.created_on @project.created_on
api.updated_on @project.updated_on
api.array :trackers do
@project.trackers.each do |tracker|
api.tracker(:id => tracker.id, :name => tracker.name)
end
end if include_in_api_response?('trackers')
end

View File

@ -1,23 +0,0 @@
xml.instruct!
xml.project do
xml.id @project.id
xml.name @project.name
xml.identifier @project.identifier
xml.description @project.description
xml.homepage @project.homepage
xml.custom_fields do
@project.visible_custom_field_values.each do |custom_value|
xml.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
end
end unless @project.custom_field_values.empty?
xml.created_on @project.created_on
xml.updated_on @project.updated_on
xml.trackers do
@project.trackers.each do |tracker|
xml.tracker(:id => tracker.id, :name => tracker.name)
end
end
end

View File

@ -16,6 +16,6 @@ dirs.each do |dir|
/ <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %>
<% end %>
<%= "@ #{h revision}" if revision %>
<%= "@ #{h format_revision(@changeset)}" if @changeset %>
<% html_title(with_leading_slash(path)) -%>

View File

@ -17,7 +17,7 @@
</td>
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
<td class="revision"><%= link_to_revision(changeset.revision, @project) if changeset %></td>
<td class="revision"><%= link_to_revision(changeset, @project) if changeset %></td>
<td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
<td class="author"><%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %></td>
<td class="comments"><%=h truncate(changeset.comments, :length => 50) unless changeset.nil? %></td>

View File

@ -13,9 +13,9 @@
<% line_num = 1 %>
<% revisions.each do |changeset| %>
<tr class="changeset <%= cycle 'odd', 'even' %>">
<td class="id"><%= link_to_revision(changeset.revision, project) %></td>
<td class="checkbox"><%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
<td class="id"><%= link_to_revision(changeset, project) %></td>
<td class="checkbox"><%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
<td class="committed_on"><%= format_time(changeset.committed_on) %></td>
<td class="author"><%=h changeset.author %></td>
<td class="comments"><%= textilizable(truncate_at_line_break(changeset.comments)) %></td>

View File

@ -19,7 +19,7 @@
<tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>">
<th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th>
<td class="revision">
<%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %></td>
<%= (revision.identifier ? link_to_revision(revision, @project) : format_revision(revision)) if revision %></td>
<td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td>
<td class="line-code"><pre><%= line %></pre></td>
</tr>

View File

@ -1,4 +1,4 @@
<h2><%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %></h2>
<h2><%= l(:label_revision) %> <%= @diff_format_revisions %> <%=h @path %></h2>
<!-- Choose view type -->
<% form_tag({:path => to_path_param(@path)}, :method => 'get') do %>

View File

@ -1,25 +1,25 @@
<div class="contextual">
&#171;
<% unless @changeset.previous.nil? -%>
<%= link_to_revision(@changeset.previous.revision, @project, :text => l(:label_previous)) %>
<%= link_to_revision(@changeset.previous, @project, :text => l(:label_previous)) %>
<% else -%>
<%= l(:label_previous) %>
<% end -%>
|
<% unless @changeset.next.nil? -%>
<%= link_to_revision(@changeset.next.revision, @project, :text => l(:label_next)) %>
<%= link_to_revision(@changeset.next, @project, :text => l(:label_next)) %>
<% else -%>
<%= l(:label_next) %>
<% end -%>
&#187;&nbsp;
<% form_tag({:controller => 'repositories', :action => 'revision', :id => @project, :rev => nil}, :method => :get) do %>
<%= text_field_tag 'rev', @rev[0,8], :size => 8 %>
<%= text_field_tag 'rev', @rev, :size => 8 %>
<%= submit_tag 'OK', :name => nil %>
<% end %>
</div>
<h2><%= l(:label_revision) %> <%= format_revision(@changeset.revision) %></h2>
<h2><%= l(:label_revision) %> <%= format_revision(@changeset) %></h2>
<p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %>
<span class="author"><%= authoring(@changeset.committed_on, @changeset.author) %></span></p>
@ -45,7 +45,7 @@
<li class="change change-D"><%= l(:label_deleted) %></li>
</ul>
<p><%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %></p>
<p><%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.identifier) if @changeset.changes.any? %></p>
<div class="changeset-changes">
<%= render_changeset_changes %>
@ -56,4 +56,4 @@
<%= stylesheet_link_tag "scm" %>
<% end %>
<% html_title("#{l(:label_revision)} #{@changeset.revision}") -%>
<% html_title("#{l(:label_revision)} #{format_revision(@changeset)}") -%>

View File

@ -8,6 +8,8 @@
<p><%= setting_select :issue_done_ratio, Issue::DONE_RATIO_OPTIONS.collect {|i| [l("setting_issue_done_ratio_#{i}"), i]} %></p>
<p><%= setting_text_field :issues_export_limit, :size => 6 %></p>
<p><%= setting_text_field :gantt_items_limit, :size => 6 %></p>
</div>
<fieldset class="box settings"><legend><%= l(:setting_issue_list_default_columns) %></legend>

View File

@ -8,7 +8,7 @@
<p><%= setting_check_box :plain_text_mail %></p>
<p><%= setting_select(:default_notification_option, User::MAIL_NOTIFICATION_OPTIONS.collect {|o| [l(o.last), o.first.to_s]}) %></p>
<p><%= setting_select(:default_notification_option, User.valid_notification_options.collect {|o| [l(o.last), o.first.to_s]}) %></p>
</div>

View File

@ -31,6 +31,13 @@
&nbsp;<%= l(:label_applied_status) %>: <%= setting_select :commit_fix_status_id, [["", 0]] + IssueStatus.find(:all).collect{|status| [status.name, status.id.to_s]}, :label => false %>
&nbsp;<%= l(:field_done_ratio) %>: <%= setting_select :commit_fix_done_ratio, (0..10).to_a.collect {|r| ["#{r*10} %", "#{r*10}"] }, :blank => :label_no_change_option, :label => false %>
<br /><em><%= l(:text_comma_separated) %></em></p>
<p><%= setting_check_box :commit_logtime_enabled,
:onclick => "if (this.checked) { Form.Element.enable('settings_commit_logtime_activity_id'); } else { Form.Element.disable('settings_commit_logtime_activity_id'); }"%></p>
<p><%= setting_select :commit_logtime_activity_id,
[[l(:label_default), 0]] + TimeEntryActivity.shared.all.collect{|activity| [activity.name, activity.id.to_s]},
:disabled => !Setting.commit_logtime_enabled?%></p>
</fieldset>
<%= submit_tag l(:button_save) %>

View File

@ -0,0 +1,16 @@
api.array :time_entries do
@entries.each do |time_entry|
api.time_entry do
api.id time_entry.id
api.project(:id => time_entry.project_id, :name => time_entry.project.name) unless time_entry.project.nil?
api.issue(:id => time_entry.issue_id) unless time_entry.issue.nil?
api.user(:id => time_entry.user_id, :name => time_entry.user.name) unless time_entry.user.nil?
api.activity(:id => time_entry.activity_id, :name => time_entry.activity.name) unless time_entry.activity.nil?
api.hours time_entry.hours
api.comments time_entry.comments
api.spent_on time_entry.spent_on
api.created_on time_entry.created_on
api.updated_on time_entry.updated_on
end
end
end

View File

@ -0,0 +1,12 @@
api.time_entry do
api.id @time_entry.id
api.project(:id => @time_entry.project_id, :name => @time_entry.project.name) unless @time_entry.project.nil?
api.issue(:id => @time_entry.issue_id) unless @time_entry.issue.nil?
api.user(:id => @time_entry.user_id, :name => @time_entry.user.name) unless @time_entry.user.nil?
api.activity(:id => @time_entry.activity_id, :name => @time_entry.activity.name) unless @time_entry.activity.nil?
api.hours @time_entry.hours
api.comments @time_entry.comments
api.spent_on @time_entry.spent_on
api.created_on @time_entry.created_on
api.updated_on @time_entry.updated_on
end

View File

@ -25,11 +25,9 @@
<p><%= f.select :auth_source_id, ([[l(:label_internal), ""]] + @auth_sources.collect { |a| [a.name, a.id] }), {}, :onchange => "if (this.value=='') {Element.show('password_fields');} else {Element.hide('password_fields');}" %></p>
<% end %>
<div id="password_fields" style="<%= 'display:none;' if @user.auth_source %>">
<p><label for="password"><%=l(:field_password)%><span class="required"> *</span></label>
<%= password_field_tag 'password', nil, :size => 25 %><br />
<p><%= f.password_field :password, :required => true, :size => 25 %><br />
<em><%= l(:text_caracters_minimum, :count => Setting.password_min_length) %></em></p>
<p><label for="password_confirmation"><%=l(:field_password_confirmation)%><span class="required"> *</span></label>
<%= password_field_tag 'password_confirmation', nil, :size => 25 %></p>
<p><%= f.password_field :password_confirmation, :required => true, :size => 25 %></p>
</div>
</div>

View File

@ -1,8 +1,8 @@
<p>
<%= select_tag 'notification_option', options_for_select(@notification_options.collect {|o| [l(o.last), o.first]}, @notification_option.to_sym),
:onchange => 'if ($("notification_option").value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
<%= select_tag 'user[mail_notification]', options_for_select(user_mail_notification_options(@user), @user.mail_notification),
:onchange => 'if (this.value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
</p>
<% content_tag 'div', :id => 'notified-projects', :style => (@notification_option == 'selected' ? '' : 'display:none;') do %>
<% content_tag 'div', :id => 'notified-projects', :style => (@user.mail_notification == 'selected' ? '' : 'display:none;') do %>
<p><% @user.projects.each do |project| %>
<label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%=h project.name %></label><br />
<% end %></p>

View File

@ -38,8 +38,8 @@
</td>
<%= call_hook(:view_users_memberships_table_row, :user => @user, :membership => membership, :roles => roles, :projects => projects )%>
</tr>
<% end; reset_cycle %>
</tbody>
<% end; reset_cycle %>
</table>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>

View File

@ -0,0 +1,15 @@
api.array :users, api_meta(:total_count => @user_count, :offset => @offset, :limit => @limit) do
@users.each do |user|
api.user do
api.id user.id
api.login user.login
api.firstname user.firstname
api.lastname user.lastname
api.mail user.mail
api.created_on user.created_on
api.last_login_on user.last_login_on
render_api_custom_values user.visible_custom_field_values, api
end
end
end

View File

@ -0,0 +1,24 @@
api.user do
api.id @user.id
api.login @user.login if User.current.admin?
api.firstname @user.firstname
api.lastname @user.lastname
api.mail @user.mail if User.current.admin? || !@user.pref.hide_mail
api.created_on @user.created_on
api.last_login_on @user.last_login_on
render_api_custom_values @user.visible_custom_field_values, api
api.array :memberships do
@memberships.each do |membership|
api.membership do
api.project :id => membership.project.id, :name => membership.project.name
api.array :roles do
membership.roles.each do |role|
api.role :id => role.id, :name => role.name
end
end
end if membership.project
end
end if include_in_api_response?('memberships') && @memberships
end

View File

@ -5,10 +5,10 @@
<h2><%= @page.pretty_title %></h2>
<p>
<%= l(:label_version) %> <%= link_to @diff.content_from.version, :action => 'show', :id => @page.title, :version => @diff.content_from.version %>
<%= l(:label_version) %> <%= link_to @diff.content_from.version, :action => 'show', :id => @page.title, :project_id => @page.project, :version => @diff.content_from.version %>
<em>(<%= @diff.content_from.author ? @diff.content_from.author.name : "anonyme" %>, <%= format_time(@diff.content_from.updated_on) %>)</em>
&#8594;
<%= l(:label_version) %> <%= link_to @diff.content_to.version, :action => 'show', :id => @page.title, :version => @diff.content_to.version %>/<%= @page.content.version %>
<%= l(:label_version) %> <%= link_to @diff.content_to.version, :action => 'show', :id => @page.title, :project_id => @page.project, :version => @diff.content_to.version %>/<%= @page.content.version %>
<em>(<%= @diff.content_to.author ? @diff.content_to.author.name : "anonyme" %>, <%= format_time(@diff.content_to.updated_on) %>)</em>
</p>

View File

@ -3,8 +3,7 @@
<h3><%= l(:label_history) %></h3>
<% form_tag({:action => "diff"}, :method => :get) do %>
<%= hidden_field_tag('project_id', h(@project.to_param)) %>
<table class="list">
<table class="list wiki-page-versions">
<thead><tr>
<th>#</th>
<th></th>
@ -18,14 +17,14 @@
<% show_diff = @versions.size > 1 %>
<% line_num = 1 %>
<% @versions.each do |ver| %>
<tr class="<%= cycle("odd", "even") %>">
<tr class="wiki-page-version <%= cycle("odd", "even") %>">
<td class="id"><%= link_to ver.version, :action => 'show', :id => @page.title, :project_id => @page.project, :version => ver.version %></td>
<td class="checkbox"><%= radio_button_tag('version', ver.version, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < @versions.size) %></td>
<td class="checkbox"><%= radio_button_tag('version_from', ver.version, (line_num==2), :id => "cbto-#{line_num}") if show_diff && (line_num > 1) %></td>
<td align="center"><%= format_time(ver.updated_on) %></td>
<td><%= link_to_user ver.author %></td>
<td><%=h ver.comments %></td>
<td align="center"><%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %></td>
<td class="updated_on"><%= format_time(ver.updated_on) %></td>
<td class="author"><%= link_to_user ver.author %></td>
<td class="comments"><%=h ver.comments %></td>
<td class="buttons"><%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %></td>
</tr>
<% line_num += 1 %>
<% end %>

View File

@ -106,5 +106,17 @@ module Rails
end
end
# TODO: Workaround for #7013 to be removed for 1.2.0
# Loads i18n 0.4.2 before Rails loads any more recent gem
# 0.5.0 is not compatible with the old interpolation syntax
# Plugins will have to migrate to the new syntax for 1.2.0
require 'rubygems'
begin
gem 'i18n', '0.4.2'
rescue Gem::LoadError => load_error
$stderr.puts %(Missing the i18n 0.4.2 gem. Please `gem install -v=0.4.2 i18n`)
exit 1
end
# All that for this:
Rails.boot!

View File

@ -0,0 +1,126 @@
# = Redmine configuration file
#
# Each environment has it's own configuration options. If you are only
# running in production, only the production block needs to be configured.
# Environment specific configuration options override the default ones.
#
# Note that this file needs to be a valid YAML file.
#
# == Outgoing email settings (email_delivery setting)
#
# === Common configurations
#
# ==== Sendmail command
#
# production:
# email_delivery:
# delivery_method: :sendmail
#
# ==== Simple SMTP server at localhost
#
# production:
# email_delivery:
# delivery_method: :smtp
# smtp_settings:
# address: "localhost"
# port: 25
#
# ==== SMTP server at example.com using LOGIN authentication and checking HELO for foo.com
#
# production:
# email_delivery:
# delivery_method: :smtp
# smtp_settings:
# address: "example.com"
# port: 25
# authentication: :login
# domain: 'foo.com'
# user_name: 'myaccount'
# password: 'password'
#
# ==== SMTP server at example.com using PLAIN authentication
#
# production:
# email_delivery:
# delivery_method: :smtp
# smtp_settings:
# address: "example.com"
# port: 25
# authentication: :plain
# domain: 'example.com'
# user_name: 'myaccount'
# password: 'password'
#
# ==== SMTP server at using TLS (GMail)
#
# This requires some additional configuration. See the article at:
# http://redmineblog.com/articles/setup-redmine-to-send-email-using-gmail/
#
# production:
# email_delivery:
# delivery_method: :smtp
# smtp_settings:
# tls: true
# address: "smtp.gmail.com"
# port: 587
# domain: "smtp.gmail.com" # 'your.domain.com' for GoogleApps
# authentication: :plain
# user_name: "your_email@gmail.com"
# password: "your_password"
#
#
# === More configuration options
#
# See the "Configuration options" at the following website for a list of the
# full options allowed:
#
# http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer
# default configuration options for all environments
default:
# Outgoing emails configuration (see examples above)
email_delivery:
delivery_method: :smtp
smtp_settings:
address: smtp.example.net
port: 25
domain: example.net
authentication: :login
user_name: "redmine@example.net"
password: "redmine"
# Absolute path to the directory where attachments are stored.
# The default is the 'files' directory in your Redmine instance.
# Your Redmine instance needs to have write permission on this
# directory.
# Examples:
# attachments_storage_path: /var/redmine/files
# attachments_storage_path: D:/redmine/files
attachments_storage_path:
# Configuration of the autologin cookie.
# autologin_cookie_name: the name of the cookie (default: autologin)
# autologin_cookie_path: the cookie path (default: /)
# autologin_cookie_secure: true sets the cookie secure flag (default: false)
autologin_cookie_name:
autologin_cookie_path:
autologin_cookie_secure:
# Configuration of SCM executable command.
# Absolute path (e.g. /usr/local/bin/hg) or command name (e.g. hg.exe, bzr.exe)
# On Windows, *.cmd, *.bat (e.g. hg.cmd, bzr.bat) does not work.
scm_subversion_command: svn # (default: svn)
scm_mercurial_command: "\"C:\Program Files\TortoiseHg\hg.exe\"" # (default: hg)
scm_git_command: /usr/local/bin/git # (default: git)
scm_cvs_command: cvs # (default: cvs)
scm_bazaar_command: bzr.exe # (default: bzr)
scm_darcs_command: darcs-1.0.9-i386-linux # (default: darcs)
# specific configuration options for production environment
# that overrides the default ones
production:
# specific configuration options for development environment
# that overrides the default ones
development:

View File

@ -36,4 +36,4 @@ test_pgsql:
test_sqlite3:
adapter: sqlite3
database: db/test.db
database: db/test.sqlite3

View File

@ -1,87 +0,0 @@
# = Outgoing email settings
#
# Each environment has it's own configuration options. If you are only
# running in production, only the production block needs to be configured.
#
# == Common configurations
#
# === Sendmail command
#
# production:
# delivery_method: :sendmail
#
# === Simple SMTP server at localhost
#
# production:
# delivery_method: :smtp
# smtp_settings:
# address: "localhost"
# port: 25
#
# === SMTP server at example.com using LOGIN authentication and checking HELO for foo.com
#
# production:
# delivery_method: :smtp
# smtp_settings:
# address: "example.com"
# port: 25
# authentication: :login
# domain: 'foo.com'
# user_name: 'myaccount'
# password: 'password'
#
# === SMTP server at example.com using PLAIN authentication
#
# production:
# delivery_method: :smtp
# smtp_settings:
# address: "example.com"
# port: 25
# authentication: :plain
# domain: 'example.com'
# user_name: 'myaccount'
# password: 'password'
#
# === SMTP server at using TLS (GMail)
#
# This requires some additional configuration. See the article at:
# http://redmineblog.com/articles/setup-redmine-to-send-email-using-gmail/
#
# production:
# delivery_method: :smtp
# smtp_settings:
# tls: true
# address: "smtp.gmail.com"
# port: 587
# domain: "smtp.gmail.com" # 'your.domain.com' for GoogleApps
# authentication: :plain
# user_name: "your_email@gmail.com"
# password: "your_password"
#
#
# == More configuration options
#
# See the "Configuration options" at the following website for a list of the
# full options allowed:
#
# http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer
production:
delivery_method: :smtp
smtp_settings:
address: smtp.example.net
port: 25
domain: example.net
authentication: :login
user_name: "redmine@example.net"
password: "redmine"
development:
delivery_method: :smtp
smtp_settings:
address: 127.0.0.1
port: 25
domain: example.net
authentication: :login
user_name: "redmine@example.net"
password: "redmine"

View File

@ -46,11 +46,12 @@ Rails::Initializer.run do |config|
# config.active_record.schema_format = :ruby
# Deliveries are disabled by default. Do NOT modify this section.
# Define your email configuration in email.yml instead.
# Define your email configuration in configuration.yml instead.
# It will automatically turn deliveries on
config.action_mailer.perform_deliveries = false
config.gem 'rubytree', :lib => 'tree'
config.gem 'coderay', :version => '~>0.9.7'
# Load any local configuration that is kept out of source control
# (e.g. gems, patches).

View File

@ -86,9 +86,19 @@ module I18n
module Base
def warn_syntax_deprecation!(*args)
return if @skip_syntax_deprecation
warn "The {{key}} interpolation syntax in I18n messages is deprecated. Please use %{key} instead.\nDowngrade your i18n gem to 0.3.7 (everything above must be deinstalled) to remove this warning, see http://www.redmine.org/issues/5608 for more information."
warn "The {{key}} interpolation syntax in I18n messages is deprecated and will be removed in Redmine 1.2. Please use %{key} instead, see http://www.redmine.org/issues/7013 for more information."
@skip_syntax_deprecation = true
end
end
end
end
module ActionController
module MimeResponds
class Responder
def api(&block)
any(:xml, :json, &block)
end
end
end
end

View File

@ -1,3 +1,5 @@
I18n.default_locale = 'en'
# Adds fallback to default locale for untranslated strings
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
require 'redmine'

View File

@ -1,17 +0,0 @@
# Loads action_mailer settings from email.yml
# and turns deliveries on if configuration file is found
filename = File.join(File.dirname(__FILE__), '..', 'email.yml')
if File.file?(filename)
mailconfig = YAML::load_file(filename)
if mailconfig.is_a?(Hash) && mailconfig.has_key?(Rails.env)
# Enable deliveries
ActionMailer::Base.perform_deliveries = true
mailconfig[Rails.env].each do |k, v|
v.symbolize_keys! if v.respond_to?(:symbolize_keys!)
ActionMailer::Base.send("#{k}=", v)
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -32,37 +32,37 @@ bs:
half_a_minute: "pola minute"
less_than_x_seconds:
one: "manje od 1 sekunde"
other: "manje od {{count}} sekudni"
other: "manje od %{count} sekudni"
x_seconds:
one: "1 sekunda"
other: "{{count}} sekundi"
other: "%{count} sekundi"
less_than_x_minutes:
one: "manje od 1 minute"
other: "manje od {{count}} minuta"
other: "manje od %{count} minuta"
x_minutes:
one: "1 minuta"
other: "{{count}} minuta"
other: "%{count} minuta"
about_x_hours:
one: "oko 1 sahat"
other: "oko {{count}} sahata"
other: "oko %{count} sahata"
x_days:
one: "1 dan"
other: "{{count}} dana"
other: "%{count} dana"
about_x_months:
one: "oko 1 mjesec"
other: "oko {{count}} mjeseci"
other: "oko %{count} mjeseci"
x_months:
one: "1 mjesec"
other: "{{count}} mjeseci"
other: "%{count} mjeseci"
about_x_years:
one: "oko 1 godine"
other: "oko {{count}} godina"
other: "oko %{count} godina"
over_x_years:
one: "preko 1 godine"
other: "preko {{count}} godina"
other: "preko %{count} godina"
almost_x_years:
one: "almost 1 year"
other: "almost {{count}} years"
other: "almost %{count} years"
number:
@ -106,6 +106,10 @@ bs:
activerecord:
errors:
template:
header:
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
messages:
inclusion: "nije uključeno u listu"
exclusion: "je rezervisano"
@ -120,11 +124,11 @@ bs:
taken: "već je zauzeto"
not_a_number: "nije broj"
not_a_date: "nije ispravan datum"
greater_than: "mora bit veći od {{count}}"
greater_than_or_equal_to: "mora bit veći ili jednak {{count}}"
equal_to: "mora biti jednak {{count}}"
less_than: "mora biti manji od {{count}}"
less_than_or_equal_to: "mora bit manji ili jednak {{count}}"
greater_than: "mora bit veći od %{count}"
greater_than_or_equal_to: "mora bit veći ili jednak %{count}"
equal_to: "mora biti jednak %{count}"
less_than: "mora biti manji od %{count}"
less_than_or_equal_to: "mora bit manji ili jednak %{count}"
odd: "mora biti neparan"
even: "mora biti paran"
greater_than_start_date: "mora biti veći nego početni datum"
@ -156,9 +160,9 @@ bs:
notice_account_wrong_password: Pogrešna lozinka
notice_can_t_change_password: Ovaj nalog koristi eksterni izvor prijavljivanja. Ne mogu da promjenim šifru.
notice_default_data_loaded: Podrazumjevana konfiguracija uspječno učitana.
notice_email_error: Došlo je do greške pri slanju emaila ({{value}})
notice_email_sent: "Email je poslan {{value}}"
notice_failed_to_save_issues: "Neuspješno snimanje {{count}} aktivnosti na {{total}} izabrano: {{ids}}."
notice_email_error: Došlo je do greške pri slanju emaila (%{value})
notice_email_sent: "Email je poslan %{value}"
notice_failed_to_save_issues: "Neuspješno snimanje %{count} aktivnosti na %{total} izabrano: %{ids}."
notice_feeds_access_key_reseted: Vaš RSS pristup je resetovan.
notice_file_not_found: Stranica kojoj pokušavate da pristupite ne postoji ili je uklonjena.
notice_locking_conflict: "Konflikt: podaci su izmjenjeni od strane drugog korisnika."
@ -169,28 +173,28 @@ bs:
notice_successful_delete: Brisanje izvršeno.
notice_successful_update: Promjene uspješno izvršene.
error_can_t_load_default_data: "Podrazumjevane postavke se ne mogu učitati {{value}}"
error_scm_command_failed: "Desila se greška pri pristupu repozitoriju: {{value}}"
error_can_t_load_default_data: "Podrazumjevane postavke se ne mogu učitati %{value}"
error_scm_command_failed: "Desila se greška pri pristupu repozitoriju: %{value}"
error_scm_not_found: "Unos i/ili revizija ne postoji u repozitoriju."
error_scm_annotate: "Ova stavka ne postoji ili nije označena."
error_issue_not_found_in_project: 'Aktivnost nije nađena ili ne pripada ovom projektu'
warning_attachments_not_saved: "{{count}} fajl(ovi) ne mogu biti snimljen(i)."
warning_attachments_not_saved: "%{count} fajl(ovi) ne mogu biti snimljen(i)."
mail_subject_lost_password: "Vaša {{value}} lozinka"
mail_subject_lost_password: "Vaša %{value} lozinka"
mail_body_lost_password: 'Za promjenu lozinke, kliknite na sljedeći link:'
mail_subject_register: "Aktivirajte {{value}} vaš korisnički račun"
mail_subject_register: "Aktivirajte %{value} vaš korisnički račun"
mail_body_register: 'Za aktivaciju vašeg korisničkog računa, kliknite na sljedeći link:'
mail_body_account_information_external: "Možete koristiti vaš {{value}} korisnički račun za prijavu na sistem."
mail_body_account_information_external: "Možete koristiti vaš %{value} korisnički račun za prijavu na sistem."
mail_body_account_information: Informacija o vašem korisničkom računu
mail_subject_account_activation_request: "{{value}} zahtjev za aktivaciju korisničkog računa"
mail_body_account_activation_request: "Novi korisnik ({{value}}) se registrovao. Korisnički račun čeka vaše odobrenje za aktivaciju:"
mail_subject_reminder: "{{count}} aktivnost(i) u kašnjenju u narednim {{days}} danima"
mail_body_reminder: "{{count}} aktivnost(i) koje su dodjeljenje vama u narednim {{days}} danima:"
mail_subject_account_activation_request: "%{value} zahtjev za aktivaciju korisničkog računa"
mail_body_account_activation_request: "Novi korisnik (%{value}) se registrovao. Korisnički račun čeka vaše odobrenje za aktivaciju:"
mail_subject_reminder: "%{count} aktivnost(i) u kašnjenju u narednim %{days} danima"
mail_body_reminder: "%{count} aktivnost(i) koje su dodjeljenje vama u narednim %{days} danima:"
gui_validation_error: 1 greška
gui_validation_error_plural: "{{count}} grešaka"
gui_validation_error_plural: "%{count} grešaka"
field_name: Ime
field_description: Opis
@ -252,6 +256,7 @@ bs:
field_attr_lastname: Atribut za prezime
field_attr_mail: Atribut za email
field_onthefly: 'Kreiranje korisnika "On-the-fly"'
field_start_date: Početak
field_done_ratio: % Realizovano
field_auth_source: Mod za authentifikaciju
field_hide_mail: Sakrij moju email adresu
@ -391,14 +396,14 @@ bs:
label_x_projects:
zero: 0 projekata
one: 1 projekat
other: "{{count}} projekata"
other: "%{count} projekata"
label_project_all: Svi projekti
label_project_latest: Posljednji projekti
label_issue: Aktivnost
label_issue_new: Nova aktivnost
label_issue_plural: Aktivnosti
label_issue_view_all: Vidi sve aktivnosti
label_issues_by: "Aktivnosti po {{value}}"
label_issues_by: "Aktivnosti po %{value}"
label_issue_added: Aktivnost je dodana
label_issue_updated: Aktivnost je izmjenjena
label_document: Dokument
@ -448,7 +453,7 @@ bs:
label_activity_plural: Promjene
label_activity: Operacija
label_overall_activity: Pregled svih promjena
label_user_activity: "Promjene izvršene od: {{value}}"
label_user_activity: "Promjene izvršene od: %{value}"
label_new: Novi
label_logged_as: Prijavljen kao
label_environment: Sistemsko okruženje
@ -457,7 +462,7 @@ bs:
label_auth_source_new: Novi mod authentifikacije
label_auth_source_plural: Modovi authentifikacije
label_subproject_plural: Podprojekti
label_and_its_subprojects: "{{value}} i njegovi podprojekti"
label_and_its_subprojects: "%{value} i njegovi podprojekti"
label_min_max_length: Min - Maks dužina
label_list: Lista
label_date: Datum
@ -468,8 +473,8 @@ bs:
label_text: Dugi tekst
label_attribute: Atribut
label_attribute_plural: Atributi
label_download: "{{count}} download"
label_download_plural: "{{count}} download-i"
label_download: "%{count} download"
label_download_plural: "%{count} download-i"
label_no_data: Nema podataka za prikaz
label_change_status: Promjeni status
label_history: Istorija
@ -500,17 +505,17 @@ bs:
label_closed_issues: zatvoren
label_closed_issues_plural: zatvoreni
label_x_open_issues_abbr_on_total:
zero: 0 otvoreno / {{total}}
one: 1 otvorena / {{total}}
other: "{{count}} otvorene / {{total}}"
zero: 0 otvoreno / %{total}
one: 1 otvorena / %{total}
other: "%{count} otvorene / %{total}"
label_x_open_issues_abbr:
zero: 0 otvoreno
one: 1 otvorena
other: "{{count}} otvorene"
other: "%{count} otvorene"
label_x_closed_issues_abbr:
zero: 0 zatvoreno
one: 1 zatvorena
other: "{{count}} zatvorene"
other: "%{count} zatvorene"
label_total: Ukupno
label_permissions: Dozvole
label_current_status: Tekući status
@ -528,7 +533,7 @@ bs:
label_months_from: mjeseci od
label_gantt: Gantt
label_internal: Interno
label_last_changes: "posljednjih {{count}} promjena"
label_last_changes: "posljednjih %{count} promjena"
label_change_view_all: Vidi sve promjene
label_personalize_page: Personaliziraj ovu stranicu
label_comment: Komentar
@ -536,7 +541,7 @@ bs:
label_x_comments:
zero: bez komentara
one: 1 komentar
other: "{{count}} komentari"
other: "%{count} komentari"
label_comment_add: Dodaj komentar
label_comment_added: Komentar je dodan
label_comment_delete: Izbriši komentar
@ -555,7 +560,7 @@ bs:
label_yesterday: juče
label_this_week: ova hefta
label_last_week: zadnja hefta
label_last_n_days: "posljednjih {{count}} dana"
label_last_n_days: "posljednjih %{count} dana"
label_this_month: ovaj mjesec
label_last_month: posljednji mjesec
label_this_year: ova godina
@ -569,8 +574,8 @@ bs:
label_repository: Repozitorij
label_repository_plural: Repozitoriji
label_browse: Listaj
label_modification: "{{count}} promjena"
label_modification_plural: "{{count}} promjene"
label_modification: "%{count} promjena"
label_modification_plural: "%{count} promjene"
label_revision: Revizija
label_revision_plural: Revizije
label_associated_revisions: Doddjeljene revizije
@ -588,8 +593,8 @@ bs:
label_sort_lower: Pomjeri dole
label_sort_lowest: Pomjeri na dno
label_roadmap: Plan realizacije
label_roadmap_due_in: "Obavezan do {{value}}"
label_roadmap_overdue: "{{value}} kasni"
label_roadmap_due_in: "Obavezan do %{value}"
label_roadmap_overdue: "%{value} kasni"
label_roadmap_no_issues: Nema aktivnosti za ovu verziju
label_search: Traži
label_result_plural: Rezultati
@ -607,8 +612,8 @@ bs:
label_changes_details: Detalji svih promjena
label_issue_tracking: Evidencija aktivnosti
label_spent_time: Utrošak vremena
label_f_hour: "{{value}} sahat"
label_f_hour_plural: "{{value}} sahata"
label_f_hour: "%{value} sahat"
label_f_hour_plural: "%{value} sahata"
label_time_tracking: Evidencija vremena
label_change_plural: Promjene
label_statistics: Statistika
@ -657,13 +662,13 @@ bs:
label_date_from: Od
label_date_to: Do
label_language_based: Bazirano na korisnikovom jeziku
label_sort_by: "Sortiraj po {{value}}"
label_sort_by: "Sortiraj po %{value}"
label_send_test_email: Pošalji testni email
label_feeds_access_key_created_on: "RSS pristupni ključ kreiran prije {{value}} dana"
label_feeds_access_key_created_on: "RSS pristupni ključ kreiran prije %{value} dana"
label_module_plural: Moduli
label_added_time_by: "Dodano od {{author}} prije {{age}}"
label_updated_time_by: "Izmjenjeno od {{author}} prije {{age}}"
label_updated_time: "Izmjenjeno prije {{value}}"
label_added_time_by: "Dodano od %{author} prije %{age}"
label_updated_time_by: "Izmjenjeno od %{author} prije %{age}"
label_updated_time: "Izmjenjeno prije %{value}"
label_jump_to_a_project: Skoči na projekat...
label_file_plural: Fajlovi
label_changeset_plural: Setovi promjena
@ -679,7 +684,7 @@ bs:
label_registration_activation_by_email: aktivacija korisničkog računa email-om
label_registration_manual_activation: ručna aktivacija korisničkog računa
label_registration_automatic_activation: automatska kreacija korisničkog računa
label_display_per_page: "Po stranici: {{value}}"
label_display_per_page: "Po stranici: %{value}"
label_age: Starost
label_change_properties: Promjena osobina
label_general: Generalno
@ -745,44 +750,44 @@ bs:
text_regexp_info: npr. ^[A-Z0-9]+$
text_min_max_length_info: 0 znači bez restrikcije
text_project_destroy_confirmation: Sigurno želite izbrisati ovaj projekat i njegove podatke ?
text_subprojects_destroy_warning: "Podprojekt(i): {{value}} će takođe biti izbrisani."
text_subprojects_destroy_warning: "Podprojekt(i): %{value} će takođe biti izbrisani."
text_workflow_edit: Odaberite ulogu i područje aktivnosti za ispravku toka promjena na aktivnosti
text_are_you_sure: Da li ste sigurni ?
text_tip_issue_begin_day: zadatak počinje danas
text_tip_issue_end_day: zadatak završava danas
text_tip_issue_begin_end_day: zadatak započinje i završava danas
text_project_identifier_info: 'Samo mala slova (a-z), brojevi i crtice su dozvoljeni.<br />Nakon snimanja, identifikator se ne može mijenjati.'
text_caracters_maximum: "maksimum {{count}} karaktera."
text_caracters_minimum: "Dužina mora biti najmanje {{count}} znakova."
text_length_between: "Broj znakova između {{min}} i {{max}}."
text_caracters_maximum: "maksimum %{count} karaktera."
text_caracters_minimum: "Dužina mora biti najmanje %{count} znakova."
text_length_between: "Broj znakova između %{min} i %{max}."
text_tracker_no_workflow: Tok statusa nije definisan za ovo područje aktivnosti
text_unallowed_characters: Nedozvoljeni znakovi
text_comma_separated: Višestruke vrijednosti dozvoljene (odvojiti zarezom).
text_issues_ref_in_commit_messages: 'Referenciranje i zatvaranje aktivnosti putem "commit" poruka'
text_issue_added: "Aktivnost {{id}} je prijavljena od {{author}}."
text_issue_updated: "Aktivnost {{id}} je izmjenjena od {{author}}."
text_issue_added: "Aktivnost %{id} je prijavljena od %{author}."
text_issue_updated: "Aktivnost %{id} je izmjenjena od %{author}."
text_wiki_destroy_confirmation: Sigurno želite izbrisati ovaj wiki i čitav njegov sadržaj ?
text_issue_category_destroy_question: "Neke aktivnosti ({{count}}) pripadaju ovoj kategoriji. Sigurno to želite uraditi ?"
text_issue_category_destroy_question: "Neke aktivnosti (%{count}) pripadaju ovoj kategoriji. Sigurno to želite uraditi ?"
text_issue_category_destroy_assignments: Ukloni kategoriju
text_issue_category_reassign_to: Ponovo dodijeli ovu kategoriju
text_user_mail_option: "Za projekte koje niste odabrali, primićete samo notifikacije o stavkama koje pratite ili ste u njih uključeni (npr. vi ste autor ili su vama dodjeljenje)."
text_no_configuration_data: "Uloge, područja aktivnosti, statusi aktivnosti i tok promjena statusa nisu konfigurisane.\nKrajnje je preporučeno da učitate tekuđe postavke. Kasnije ćete ih moći mjenjati po svojim potrebama."
text_load_default_configuration: Učitaj tekuću konfiguraciju
text_status_changed_by_changeset: "Primjenjeno u setu promjena {{value}}."
text_status_changed_by_changeset: "Primjenjeno u setu promjena %{value}."
text_issues_destroy_confirmation: 'Sigurno želite izbrisati odabranu/e aktivnost/i ?'
text_select_project_modules: 'Odaberi module koje želite u ovom projektu:'
text_default_administrator_account_changed: Tekući administratorski račun je promjenjen
text_file_repository_writable: U direktorij sa fajlovima koji su prilozi se može pisati
text_plugin_assets_writable: U direktorij plugin-ova se može pisati
text_rmagick_available: RMagick je dostupan (opciono)
text_destroy_time_entries_question: "{{hours}} sahata je prijavljeno na aktivnostima koje želite brisati. Želite li to učiniti ?"
text_destroy_time_entries_question: "%{hours} sahata je prijavljeno na aktivnostima koje želite brisati. Želite li to učiniti ?"
text_destroy_time_entries: Izbriši prijavljeno vrijeme
text_assign_time_entries_to_project: Dodaj prijavljenoo vrijeme projektu
text_reassign_time_entries: 'Preraspodjeli prijavljeno vrijeme na ovu aktivnost:'
text_user_wrote: "{{value}} je napisao/la:"
text_enumeration_destroy_question: "Za {{count}} objekata je dodjeljenja ova vrijednost."
text_user_wrote: "%{value} je napisao/la:"
text_enumeration_destroy_question: "Za %{count} objekata je dodjeljenja ova vrijednost."
text_enumeration_category_reassign_to: 'Ponovo im dodjeli ovu vrijednost:'
text_email_delivery_not_configured: "Email dostava nije konfiguraisana, notifikacija je onemogućena.\nKonfiguriši SMTP server u config/email.yml i restartuj aplikaciju nakon toga."
text_email_delivery_not_configured: "Email dostava nije konfiguraisana, notifikacija je onemogućena.\nKonfiguriši SMTP server u config/configuration.yml i restartuj aplikaciju nakon toga."
text_repository_usernames_mapping: "Odaberi ili ispravi redmine korisnika mapiranog za svako korisničko ima nađeno u logu repozitorija.\nKorisnici sa istim imenom u redmineu i u repozitoruju se automatski mapiraju."
text_diff_truncated: '... Ovaj prikaz razlike je odsječen pošto premašuje maksimalnu veličinu za prikaz'
text_custom_field_possible_values_info: 'Jedna linija za svaku vrijednost'
@ -817,23 +822,23 @@ bs:
button_annotate: Zabilježi
button_activate: Aktiviraj
label_sort: Sortiranje
label_date_from_to: Od {{start}} do {{end}}
label_date_from_to: Od %{start} do %{end}
label_ascending: Rastuće
label_descending: Opadajuće
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_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: "'{{id}}' wiki page has been updated"
mail_subject_wiki_content_updated: "'%{id}' wiki page has been updated"
label_wiki_content_added: Wiki page added
mail_subject_wiki_content_added: "'{{id}}' wiki page has been added"
mail_body_wiki_content_added: The '{{id}}' wiki page has been added by {{author}}.
mail_subject_wiki_content_added: "'%{id}' wiki page has been added"
mail_body_wiki_content_added: The '%{id}' wiki page has been added by %{author}.
label_wiki_content_updated: Wiki page updated
mail_body_wiki_content_updated: The '{{id}}' wiki page has been updated by {{author}}.
mail_body_wiki_content_updated: The '%{id}' 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
@ -841,14 +846,14 @@ bs:
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}})"
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"
text_journal_added: "%{label} %{value} added"
field_active: Active
enumeration_system_activity: System Activity
permission_delete_issue_watchers: Delete watchers
@ -883,9 +888,9 @@ bs:
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_revision_id: Revision %{value}
label_api_access_key: API access key
label_api_access_key_created_on: API access key created {{value}} ago
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
@ -912,12 +917,12 @@ bs:
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_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}}."
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.
@ -925,7 +930,7 @@ bs:
field_time_entries: Log time
project_module_gantt: Gantt
project_module_calendar: Calendar
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of
@ -936,8 +941,12 @@ bs:
field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role
notice_not_authorized_archived_project: The project you're trying to access has been archived.
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
field_visible: Visible
setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart

View File

@ -36,37 +36,37 @@ ca:
half_a_minute: "mig minut"
less_than_x_seconds:
one: "menys d'un segon"
other: "menys de {{count}} segons"
other: "menys de %{count} segons"
x_seconds:
one: "1 segons"
other: "{{count}} segons"
other: "%{count} segons"
less_than_x_minutes:
one: "menys d'un minut"
other: "menys de {{count}} minuts"
other: "menys de %{count} minuts"
x_minutes:
one: "1 minut"
other: "{{count}} minuts"
other: "%{count} minuts"
about_x_hours:
one: "aproximadament 1 hora"
other: "aproximadament {{count}} hores"
other: "aproximadament %{count} hores"
x_days:
one: "1 dia"
other: "{{count}} dies"
other: "%{count} dies"
about_x_months:
one: "aproximadament 1 mes"
other: "aproximadament {{count}} mesos"
other: "aproximadament %{count} mesos"
x_months:
one: "1 mes"
other: "{{count}} mesos"
other: "%{count} mesos"
about_x_years:
one: "aproximadament 1 any"
other: "aproximadament {{count}} anys"
other: "aproximadament %{count} anys"
over_x_years:
one: "més d'un any"
other: "més de {{count}} anys"
other: "més de %{count} anys"
almost_x_years:
one: "almost 1 year"
other: "almost {{count}} years"
other: "almost %{count} years"
number:
# Default format for numbers
@ -98,6 +98,10 @@ ca:
activerecord:
errors:
template:
header:
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
messages:
inclusion: "no està inclòs a la llista"
exclusion: "està reservat"
@ -112,11 +116,11 @@ ca:
taken: "ja s'està utilitzant"
not_a_number: "no és un número"
not_a_date: "no és una data vàlida"
greater_than: "ha de ser més gran que {{count}}"
greater_than_or_equal_to: "ha de ser més gran o igual a {{count}}"
equal_to: "ha de ser igual a {{count}}"
less_than: "ha de ser menys que {{count}}"
less_than_or_equal_to: "ha de ser menys o igual a {{count}}"
greater_than: "ha de ser més gran que %{count}"
greater_than_or_equal_to: "ha de ser més gran o igual a %{count}"
equal_to: "ha de ser igual a %{count}"
less_than: "ha de ser menys que %{count}"
less_than_or_equal_to: "ha de ser menys o igual a %{count}"
odd: "ha de ser senar"
even: "ha de ser parell"
greater_than_start_date: "ha de ser superior que la data inicial"
@ -153,12 +157,12 @@ ca:
notice_file_not_found: "La pàgina a la que intenteu accedir no existeix o s'ha suprimit."
notice_locking_conflict: Un altre usuari ha actualitzat les dades.
notice_not_authorized: No teniu permís per a accedir a aquesta pàgina.
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_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_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."
@ -166,9 +170,9 @@ ca:
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_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_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."
@ -182,26 +186,26 @@ ca:
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."
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}}"
mail_subject_lost_password: "Contrasenya de %{value}"
mail_body_lost_password: "Per a canviar la contrasenya, feu clic en l'enllaç següent:"
mail_subject_register: "Activació del compte de {{value}}"
mail_subject_register: "Activació del compte de %{value}"
mail_body_register: "Per a activar el compte, feu clic en l'enllaç següent:"
mail_body_account_information_external: "Podeu utilitzar el compte «{{value}}» per a entrar."
mail_body_account_information_external: "Podeu utilitzar el compte «%{value}» per a entrar."
mail_body_account_information: Informació del compte
mail_subject_account_activation_request: "Sol·licitud d'activació del compte de {{value}}"
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 «{{id}}»"
mail_body_wiki_content_added: "En {{author}} ha afegit la pàgina wiki «{{id}}»."
mail_subject_wiki_content_updated: "S'ha actualitzat la pàgina wiki «{{id}}»"
mail_body_wiki_content_updated: "En {{author}} ha actualitzat la pàgina wiki «{{id}}»."
mail_subject_account_activation_request: "Sol·licitud d'activació del compte de %{value}"
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 «%{id}»"
mail_body_wiki_content_added: "En %{author} ha afegit la pàgina wiki «%{id}»."
mail_subject_wiki_content_updated: "S'ha actualitzat la pàgina wiki «%{id}»"
mail_body_wiki_content_updated: "En %{author} ha actualitzat la pàgina wiki «%{id}»."
gui_validation_error: 1 error
gui_validation_error_plural: "{{count}} errors"
gui_validation_error_plural: "%{count} errors"
field_name: Nom
field_description: Descripció
@ -264,6 +268,7 @@ ca:
field_attr_lastname: Atribut del cognom
field_attr_mail: Atribut del correu electrònic
field_onthefly: "Creació de l'usuari «al vol»"
field_start_date: Inici
field_done_ratio: % realitzat
field_auth_source: "Mode d'autenticació"
field_hide_mail: "Oculta l'adreça de correu electrònic"
@ -427,14 +432,14 @@ ca:
label_x_projects:
zero: cap projecte
one: 1 projecte
other: "{{count}} projectes"
other: "%{count} projectes"
label_project_all: Tots els projectes
label_project_latest: Els últims projectes
label_issue: Assumpte
label_issue_new: Assumpte nou
label_issue_plural: Assumptes
label_issue_view_all: Visualitza tots els assumptes
label_issues_by: "Assumptes per {{value}}"
label_issues_by: "Assumptes per %{value}"
label_issue_added: Assumpte afegit
label_issue_updated: Assumpte actualitzat
label_document: Document
@ -484,7 +489,7 @@ ca:
label_registered_on: Informat el
label_activity: Activitat
label_overall_activity: Activitat global
label_user_activity: "Activitat de {{value}}"
label_user_activity: "Activitat de %{value}"
label_new: Nou
label_logged_as: Heu entrat com a
label_environment: Entorn
@ -494,7 +499,7 @@ ca:
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_and_its_subprojects: "%{value} i els seus subprojectes"
label_min_max_length: Longitud mín - max
label_list: Llist
label_date: Data
@ -505,8 +510,8 @@ ca:
label_text: Text llarg
label_attribute: Atribut
label_attribute_plural: Atributs
label_download: "{{count}} baixada"
label_download_plural: "{{count}} baixades"
label_download: "%{count} baixada"
label_download_plural: "%{count} baixades"
label_no_data: Sense dades a mostrar
label_change_status: "Canvia l'estat"
label_history: Historial
@ -538,17 +543,17 @@ ca:
label_closed_issues: tancat
label_closed_issues_plural: tancats
label_x_open_issues_abbr_on_total:
zero: 0 oberts / {{total}}
one: 1 obert / {{total}}
other: "{{count}} oberts / {{total}}"
zero: 0 oberts / %{total}
one: 1 obert / %{total}
other: "%{count} oberts / %{total}"
label_x_open_issues_abbr:
zero: 0 oberts
one: 1 obert
other: "{{count}} oberts"
other: "%{count} oberts"
label_x_closed_issues_abbr:
zero: 0 tancats
one: 1 tancat
other: "{{count}} tancats"
other: "%{count} tancats"
label_total: Total
label_permissions: Permisos
label_current_status: Estat actual
@ -566,7 +571,7 @@ ca:
label_months_from: mesos des de
label_gantt: Gantt
label_internal: Intern
label_last_changes: "últims {{count}} canvis"
label_last_changes: "últims %{count} canvis"
label_change_view_all: Visualitza tots els canvis
label_personalize_page: Personalitza aquesta pàgina
label_comment: Comentari
@ -574,7 +579,7 @@ ca:
label_x_comments:
zero: sense comentaris
one: 1 comentari
other: "{{count}} comentaris"
other: "%{count} comentaris"
label_comment_add: Afegeix un comentari
label_comment_added: Comentari afegit
label_comment_delete: Suprimeix comentaris
@ -595,7 +600,7 @@ ca:
label_yesterday: ahir
label_this_week: aquesta setmana
label_last_week: "l'última setmana"
label_last_n_days: "els últims {{count}} dies"
label_last_n_days: "els últims %{count} dies"
label_this_month: aquest més
label_last_month: "l'últim més"
label_this_year: aquest any
@ -609,13 +614,13 @@ ca:
label_repository: Dipòsit
label_repository_plural: Dipòsits
label_browse: Navega
label_modification: "{{count}} canvi"
label_modification_plural: "{{count}} canvis"
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_revision_id: "Revisió %{value}"
label_associated_revisions: Revisions associades
label_added: afegit
label_modified: modificat
@ -632,8 +637,8 @@ ca:
label_sort_lower: Mou cap avall
label_sort_lowest: Mou a la part inferior
label_roadmap: Planificació
label_roadmap_due_in: "Venç en {{value}}"
label_roadmap_overdue: "{{value}} tard"
label_roadmap_due_in: "Venç en %{value}"
label_roadmap_overdue: "%{value} tard"
label_roadmap_no_issues: No hi ha assumptes per a aquesta versió
label_search: Cerca
label_result_plural: Resultats
@ -652,8 +657,8 @@ ca:
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_f_hour: "%{value} hora"
label_f_hour_plural: "%{value} hores"
label_time_tracking: Temps de seguiment
label_change_plural: Canvis
label_statistics: Estadístiques
@ -704,15 +709,15 @@ ca:
label_date_from: Des de
label_date_to: A
label_language_based: "Basat en l'idioma de l'usuari"
label_sort_by: "Ordena per {{value}}"
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_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}}"
label_updated_time_by: "Actualitzat per {{author}} fa {{age}}"
label_updated_time: "Actualitzat fa {{value}}"
label_added_time_by: "Afegit per %{author} fa %{age}"
label_updated_time_by: "Actualitzat per %{author} fa %{age}"
label_updated_time: "Actualitzat fa %{value}"
label_jump_to_a_project: Salta al projecte...
label_file_plural: Fitxers
label_changeset_plural: Conjunt de canvis
@ -728,7 +733,7 @@ ca:
label_registration_activation_by_email: activació del compte per correu electrònic
label_registration_manual_activation: activació del compte manual
label_registration_automatic_activation: activació del compte automàtica
label_display_per_page: "Per pàgina: {{value}}"
label_display_per_page: "Per pàgina: %{value}"
label_age: Edat
label_change_properties: Canvia les propietats
label_general: General
@ -751,7 +756,7 @@ ca:
label_sort: Ordena
label_ascending: Ascendent
label_descending: Descendent
label_date_from_to: Des de {{start}} a {{end}}
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
@ -770,7 +775,7 @@ ca:
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_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"
@ -833,53 +838,53 @@ ca:
text_regexp_info: ex. ^[A-Z0-9]+$
text_min_max_length_info: 0 significa sense restricció
text_project_destroy_confirmation: Segur que voleu suprimir aquest projecte i les dades relacionades?
text_subprojects_destroy_warning: "També seran suprimits els seus subprojectes: {{value}}."
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_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_issue_begin_day: "tasca que s'inicia aquest dia"
text_tip_issue_end_day: tasca que finalitza aquest dia
text_tip_issue_begin_end_day: "tasca que s'inicia i finalitza aquest dia"
text_project_identifier_info: "Es permeten lletres en minúscules (a-z), números i guions.<br />Un cop desat, l'identificador no es pot modificar."
text_caracters_maximum: "{{count}} caràcters com a màxim."
text_caracters_minimum: "Com a mínim ha de tenir {{count}} caràcters."
text_length_between: "Longitud entre {{min}} i {{max}} caràcters."
text_caracters_maximum: "%{count} caràcters com a màxim."
text_caracters_minimum: "Com a mínim ha de tenir %{count} caràcters."
text_length_between: "Longitud entre %{min} i %{max} caràcters."
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}}."
text_issue_added: "L'assumpte %{id} ha sigut informat per %{author}."
text_issue_updated: "L'assumpte %{id} ha sigut actualitzat per %{author}."
text_wiki_destroy_confirmation: Segur que voleu suprimir aquest wiki i tots els seus continguts?
text_issue_category_destroy_question: "Alguns assumptes ({{count}}) estan assignats a aquesta categoria. Què voleu fer?"
text_issue_category_destroy_question: "Alguns assumptes (%{count}) estan assignats a aquesta categoria. Què voleu fer?"
text_issue_category_destroy_assignments: Suprimeix les assignacions de la categoria
text_issue_category_reassign_to: Torna a assignar els assumptes a aquesta categoria
text_user_mail_option: "Per als projectes no seleccionats, només rebreu notificacions sobre les coses que vigileu o que hi esteu implicat (ex. assumptes que en sou l'autor o hi esteu assignat)."
text_no_configuration_data: "Encara no s'han configurat els rols, seguidors, estats de l'assumpte i flux de treball.\nÉs altament recomanable que carregueu la configuració predeterminada. Podreu modificar-la un cop carregada."
text_load_default_configuration: Carrega la configuració predeterminada
text_status_changed_by_changeset: "Aplicat en el conjunt de canvis {{value}}."
text_status_changed_by_changeset: "Aplicat en el conjunt de canvis %{value}."
text_issues_destroy_confirmation: "Segur que voleu suprimir els assumptes seleccionats?"
text_select_project_modules: "Seleccioneu els mòduls a habilitar per a aquest projecte:"
text_default_administrator_account_changed: "S'ha canviat el compte d'administrador predeterminat"
text_file_repository_writable: Es pot escriure en el dipòsit de fitxers
text_plugin_assets_writable: Es pot escriure als connectors actius
text_rmagick_available: RMagick disponible (opcional)
text_destroy_time_entries_question: "S'han informat {{hours}} hores en els assumptes que aneu a suprimir. Què voleu fer?"
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_user_wrote: "{{value}} va escriure:"
text_enumeration_destroy_question: "{{count}} objectes estan assignats a aquest valor."
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_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_email_delivery_not_configured: "El lliurament per correu electrònic no està configurat i les notificacions estan inhabilitades.\nConfigureu el servidor SMTP a config/configuration.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_wiki_page_destroy_question: "Aquesta pàgina té {{descendants}} pàgines fill i descendents. Què voleu fer?"
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"
@ -914,7 +919,7 @@ ca:
enumeration_activities: Activitats (seguidor de temps)
enumeration_system_activity: Activitat del sistema
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of
@ -925,8 +930,12 @@ ca:
field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role
notice_not_authorized_archived_project: The project you're trying to access has been archived.
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
field_visible: Visible
setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart

View File

@ -32,37 +32,37 @@ cs:
half_a_minute: "půl minuty"
less_than_x_seconds:
one: "méně než sekunda"
other: "méně než {{count}} sekund"
other: "méně než %{count} sekund"
x_seconds:
one: "1 sekunda"
other: "{{count}} sekund"
other: "%{count} sekund"
less_than_x_minutes:
one: "méně než minuta"
other: "méně než {{count}} minut"
other: "méně než %{count} minut"
x_minutes:
one: "1 minuta"
other: "{{count}} minut"
other: "%{count} minut"
about_x_hours:
one: "asi 1 hodina"
other: "asi {{count}} hodin"
other: "asi %{count} hodin"
x_days:
one: "1 den"
other: "{{count}} dnů"
other: "%{count} dnů"
about_x_months:
one: "asi 1 měsíc"
other: "asi {{count}} měsíců"
other: "asi %{count} měsíců"
x_months:
one: "1 měsíc"
other: "{{count}} měsíců"
other: "%{count} měsíců"
about_x_years:
one: "asi 1 rok"
other: "asi {{count}} let"
other: "asi %{count} let"
over_x_years:
one: "více než 1 rok"
other: "více než {{count}} roky"
other: "více než %{count} roky"
almost_x_years:
one: "témeř 1 rok"
other: "téměř {{count}} roky"
other: "téměř %{count} roky"
number:
format:
@ -92,6 +92,10 @@ cs:
activerecord:
errors:
template:
header:
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
messages:
inclusion: "není zahrnuto v seznamu"
exclusion: "je rezervováno"
@ -106,11 +110,11 @@ cs:
taken: "je již použito"
not_a_number: "není číslo"
not_a_date: "není platné datum"
greater_than: "musí být větší než {{count}}"
greater_than_or_equal_to: "musí být větší nebo rovno {{count}}"
equal_to: "musí být přesně {{count}}"
less_than: "musí být méně než {{count}}"
less_than_or_equal_to: "musí být méně nebo rovno {{count}}"
greater_than: "musí být větší než %{count}"
greater_than_or_equal_to: "musí být větší nebo rovno %{count}"
equal_to: "musí být přesně %{count}"
less_than: "musí být méně než %{count}"
less_than_or_equal_to: "musí být méně nebo rovno %{count}"
odd: "musí být liché"
even: "musí být sudé"
greater_than_start_date: "musí být větší než počáteční datum"
@ -152,30 +156,30 @@ cs:
notice_locking_conflict: Údaje byly změněny jiným uživatelem.
notice_scm_error: Záznam a/nebo revize neexistuje v repozitáři.
notice_not_authorized: Nemáte dostatečná práva pro zobrazení této stránky.
notice_email_sent: "Na adresu {{value}} byl odeslán email"
notice_email_error: "Při odesílání emailu nastala chyba ({{value}})"
notice_email_sent: "Na adresu %{value} byl odeslán email"
notice_email_error: "Při odesílání emailu nastala chyba (%{value})"
notice_feeds_access_key_reseted: Váš klíč pro přístup k RSS byl resetován.
notice_failed_to_save_issues: "Chyba při uložení {{count}} úkolu(ů) z {{total}} vybraných: {{ids}}."
notice_failed_to_save_issues: "Chyba při uložení %{count} úkolu(ů) z %{total} vybraných: %{ids}."
notice_no_issue_selected: "Nebyl zvolen žádný úkol. Prosím, zvolte úkoly, které chcete editovat"
notice_account_pending: "Váš účet byl vytvořen, nyní čeká na schválení administrátorem."
notice_default_data_loaded: Výchozí konfigurace úspěšně nahrána.
error_can_t_load_default_data: "Výchozí konfigurace nebyla nahrána: {{value}}"
error_can_t_load_default_data: "Výchozí konfigurace nebyla nahrána: %{value}"
error_scm_not_found: "Položka a/nebo revize neexistují v repozitáři."
error_scm_command_failed: "Při pokusu o přístup k repozitáři došlo k chybě: {{value}}"
error_scm_command_failed: "Při pokusu o přístup k repozitáři došlo k chybě: %{value}"
error_issue_not_found_in_project: 'Úkol nebyl nalezen nebo nepatří k tomuto projektu'
mail_subject_lost_password: "Vaše heslo ({{value}})"
mail_subject_lost_password: "Vaše heslo (%{value})"
mail_body_lost_password: 'Pro změnu vašeho hesla klikněte na následující odkaz:'
mail_subject_register: "Aktivace účtu ({{value}})"
mail_subject_register: "Aktivace účtu (%{value})"
mail_body_register: 'Pro aktivaci vašeho účtu klikněte na následující odkaz:'
mail_body_account_information_external: "Pomocí vašeho účtu {{value}} se můžete přihlásit."
mail_body_account_information_external: "Pomocí vašeho účtu %{value} se můžete přihlásit."
mail_body_account_information: Informace o vašem účtu
mail_subject_account_activation_request: "Aktivace {{value}} účtu"
mail_body_account_activation_request: "Byl zaregistrován nový uživatel {{value}}. Aktivace jeho účtu závisí na vašem potvrzení."
mail_subject_account_activation_request: "Aktivace %{value} účtu"
mail_body_account_activation_request: "Byl zaregistrován nový uživatel %{value}. Aktivace jeho účtu závisí na vašem potvrzení."
gui_validation_error: 1 chyba
gui_validation_error_plural: "{{count}} chyb(y)"
gui_validation_error_plural: "%{count} chyb(y)"
field_name: Název
field_description: Popis
@ -237,6 +241,7 @@ cs:
field_attr_lastname: Příjemní (atribut)
field_attr_mail: Email (atribut)
field_onthefly: Automatické vytváření uživatelů
field_start_date: Začátek
field_done_ratio: % Hotovo
field_auth_source: Autentifikační mód
field_hide_mail: Nezobrazovat můj email
@ -310,14 +315,14 @@ cs:
label_x_projects:
zero: žádné projekty
one: 1 projekt
other: "{{count}} projekty(ů)"
other: "%{count} projekty(ů)"
label_project_all: Všechny projekty
label_project_latest: Poslední projekty
label_issue: Úkol
label_issue_new: Nový úkol
label_issue_plural: Úkoly
label_issue_view_all: Všechny úkoly
label_issues_by: "Úkoly podle {{value}}"
label_issues_by: "Úkoly podle %{value}"
label_issue_added: Úkol přidán
label_issue_updated: Úkol aktualizován
label_document: Dokument
@ -383,8 +388,8 @@ cs:
label_text: Dlouhý text
label_attribute: Atribut
label_attribute_plural: Atributy
label_download: "{{count}} stažení"
label_download_plural: "{{count}} stažení"
label_download: "%{count} stažení"
label_download_plural: "%{count} stažení"
label_no_data: Žádné položky
label_change_status: Změnit stav
label_history: Historie
@ -415,17 +420,17 @@ cs:
label_closed_issues: uzavřený
label_closed_issues_plural: uzavřené
label_x_open_issues_abbr_on_total:
zero: 0 otevřených / {{total}}
one: 1 otevřený / {{total}}
other: "{{count}} otevřených / {{total}}"
zero: 0 otevřených / %{total}
one: 1 otevřený / %{total}
other: "%{count} otevřených / %{total}"
label_x_open_issues_abbr:
zero: 0 otevřených
one: 1 otevřený
other: "{{count}} otevřených"
other: "%{count} otevřených"
label_x_closed_issues_abbr:
zero: 0 uzavřených
one: 1 uzavřený
other: "{{count}} uzavřených"
other: "%{count} uzavřených"
label_total: Celkem
label_permissions: Práva
label_current_status: Aktuální stav
@ -443,7 +448,7 @@ cs:
label_months_from: měsíců od
label_gantt: Ganttův graf
label_internal: Interní
label_last_changes: "posledních {{count}} změn"
label_last_changes: "posledních %{count} změn"
label_change_view_all: Zobrazit všechny změny
label_personalize_page: Přizpůsobit tuto stránku
label_comment: Komentář
@ -451,7 +456,7 @@ cs:
label_x_comments:
zero: žádné komentáře
one: 1 komentář
other: "{{count}} komentářů"
other: "%{count} komentářů"
label_comment_add: Přidat komentáře
label_comment_added: Komentář přidán
label_comment_delete: Odstranit komentář
@ -470,7 +475,7 @@ cs:
label_yesterday: včera
label_this_week: tento týden
label_last_week: minulý týden
label_last_n_days: "posledních {{count}} dnů"
label_last_n_days: "posledních %{count} dnů"
label_this_month: tento měsíc
label_last_month: minulý měsíc
label_this_year: tento rok
@ -484,8 +489,8 @@ cs:
label_repository: Repozitář
label_repository_plural: Repozitáře
label_browse: Procházet
label_modification: "{{count}} změna"
label_modification_plural: "{{count}} změn"
label_modification: "%{count} změna"
label_modification_plural: "%{count} změn"
label_revision: Revize
label_revision_plural: Revizí
label_associated_revisions: Související verze
@ -501,8 +506,8 @@ cs:
label_sort_lower: Přesunout dolů
label_sort_lowest: Přesunout na konec
label_roadmap: Plán
label_roadmap_due_in: "Zbývá {{value}}"
label_roadmap_overdue: "{{value}} pozdě"
label_roadmap_due_in: "Zbývá %{value}"
label_roadmap_overdue: "%{value} pozdě"
label_roadmap_no_issues: Pro tuto verzi nejsou žádné úkoly
label_search: Hledat
label_result_plural: Výsledky
@ -520,8 +525,8 @@ cs:
label_changes_details: Detail všech změn
label_issue_tracking: Sledování úkolů
label_spent_time: Strávený čas
label_f_hour: "{{value}} hodina"
label_f_hour_plural: "{{value}} hodin"
label_f_hour: "%{value} hodina"
label_f_hour_plural: "%{value} hodin"
label_time_tracking: Sledování času
label_change_plural: Změny
label_statistics: Statistiky
@ -541,7 +546,7 @@ cs:
label_relation_delete: Odstranit souvislost
label_relates_to: související s
label_duplicates: duplicity
label_blocks: bloků
label_blocks: blokuje
label_blocked_by: zablokován
label_precedes: předchází
label_follows: následuje
@ -569,12 +574,12 @@ cs:
label_date_from: Od
label_date_to: Do
label_language_based: Podle výchozího jazyku
label_sort_by: "Seřadit podle {{value}}"
label_sort_by: "Seřadit podle %{value}"
label_send_test_email: Poslat testovací email
label_feeds_access_key_created_on: "Přístupový klíč pro RSS byl vytvořen před {{value}}"
label_feeds_access_key_created_on: "Přístupový klíč pro RSS byl vytvořen před %{value}"
label_module_plural: Moduly
label_added_time_by: "Přidáno uživatelem {{author}} před {{age}}"
label_updated_time: "Aktualizováno před {{value}}"
label_added_time_by: "Přidáno uživatelem %{author} před %{age}"
label_updated_time: "Aktualizováno před %{value}"
label_jump_to_a_project: Vyberte projekt...
label_file_plural: Soubory
label_changeset_plural: Changesety
@ -590,7 +595,7 @@ cs:
label_registration_activation_by_email: aktivace účtu emailem
label_registration_manual_activation: manuální aktivace účtu
label_registration_automatic_activation: automatická aktivace účtu
label_display_per_page: "{{value}} na stránku"
label_display_per_page: "%{value} na stránku"
label_age: Věk
label_change_properties: Změnit vlastnosti
label_general: Obecné
@ -657,29 +662,29 @@ cs:
text_tip_issue_end_day: úkol končí v tento den
text_tip_issue_begin_end_day: úkol začíná a končí v tento den
text_project_identifier_info: 'Jsou povolena malá písmena (a-z), čísla a pomlčky.<br />Po uložení již není možné identifikátor změnit.'
text_caracters_maximum: "{{count}} znaků maximálně."
text_caracters_minimum: "Musí být alespoň {{count}} znaků dlouhé."
text_length_between: "Délka mezi {{min}} a {{max}} znaky."
text_caracters_maximum: "%{count} znaků maximálně."
text_caracters_minimum: "Musí být alespoň %{count} znaků dlouhé."
text_length_between: "Délka mezi %{min} a %{max} znaky."
text_tracker_no_workflow: Pro tuto frontu není definován žádný průběh práce
text_unallowed_characters: Nepovolené znaky
text_comma_separated: Povoleno více hodnot (oddělěné čárkou).
text_issues_ref_in_commit_messages: Odkazování a opravování úkolů ve zprávách commitů
text_issue_added: "Úkol {{id}} byl vytvořen uživatelem {{author}}."
text_issue_updated: "Úkol {{id}} byl aktualizován uživatelem {{author}}."
text_issue_added: "Úkol %{id} byl vytvořen uživatelem %{author}."
text_issue_updated: "Úkol %{id} byl aktualizován uživatelem %{author}."
text_wiki_destroy_confirmation: Opravdu si přejete odstranit tuto Wiki a celý její obsah?
text_issue_category_destroy_question: "Některé úkoly ({{count}}) jsou přiřazeny k této kategorii. Co s nimi chtete udělat?"
text_issue_category_destroy_question: "Některé úkoly (%{count}) jsou přiřazeny k této kategorii. Co s nimi chtete udělat?"
text_issue_category_destroy_assignments: Zrušit přiřazení ke kategorii
text_issue_category_reassign_to: Přiřadit úkoly do této kategorie
text_user_mail_option: "U projektů, které nebyly vybrány, budete dostávat oznámení pouze o vašich či o sledovaných položkách (např. o položkách jejichž jste autor nebo ke kterým jste přiřazen(a))."
text_no_configuration_data: "Role, fronty, stavy úkolů ani průběh práce nebyly zatím nakonfigurovány.\nVelice doporučujeme nahrát výchozí konfiguraci. Po té si můžete vše upravit"
text_load_default_configuration: Nahrát výchozí konfiguraci
text_status_changed_by_changeset: "Použito v changesetu {{value}}."
text_status_changed_by_changeset: "Použito v changesetu %{value}."
text_issues_destroy_confirmation: 'Opravdu si přejete odstranit všechny zvolené úkoly?'
text_select_project_modules: 'Aktivní moduly v tomto projektu:'
text_default_administrator_account_changed: Výchozí nastavení administrátorského účtu změněno
text_file_repository_writable: Povolen zápis do adresáře ukládání souborů
text_rmagick_available: RMagick k dispozici (volitelné)
text_destroy_time_entries_question: "U úkolů, které chcete odstranit je evidováno {{hours}} práce. Co chete udělat?"
text_destroy_time_entries_question: "U úkolů, které chcete odstranit je evidováno %{hours} práce. Co chete udělat?"
text_destroy_time_entries: Odstranit evidované hodiny.
text_assign_time_entries_to_project: Přiřadit evidované hodiny projektu
text_reassign_time_entries: 'Přeřadit evidované hodiny k tomuto úkolu:'
@ -711,20 +716,20 @@ cs:
enumeration_activities: Aktivity (sledování času)
error_scm_annotate: "Položka neexistuje nebo nemůže být komentována."
label_planning: Plánování
text_subprojects_destroy_warning: "Jeho podprojek(y): {{value}} budou také smazány."
label_and_its_subprojects: "{{value}} a jeho podprojekty"
mail_body_reminder: "{{count}} úkol(ů), které máte přiřazeny má termín během několik dní ({{days}}):"
mail_subject_reminder: "{{count}} úkol(ů) má termín během několik dní ({{days}})"
text_user_wrote: "{{value}} napsal:"
text_subprojects_destroy_warning: "Jeho podprojek(y): %{value} budou také smazány."
label_and_its_subprojects: "%{value} a jeho podprojekty"
mail_body_reminder: "%{count} úkol(ů), které máte přiřazeny má termín během několik dní (%{days}):"
mail_subject_reminder: "%{count} úkol(ů) má termín během několik dní (%{days})"
text_user_wrote: "%{value} napsal:"
label_duplicated_by: duplikováno od
setting_enabled_scm: Povolené SCM
text_enumeration_category_reassign_to: 'Přeřadit je do této:'
text_enumeration_destroy_question: "Několik ({{count}}) objektů je přiřazeno k této hodnotě."
text_enumeration_destroy_question: "Několik (%{count}) objektů je přiřazeno k této hodnotě."
label_incoming_emails: Příchozí e-maily
label_generate_key: Generovat klíč
setting_mail_handler_api_enabled: Povolit WS pro příchozí e-maily
setting_mail_handler_api_key: API klíč
text_email_delivery_not_configured: "Doručování e-mailů není nastaveno a odesílání notifikací je zakázáno.\nNastavte Váš SMTP server v souboru config/email.yml a restartujte aplikaci."
text_email_delivery_not_configured: "Doručování e-mailů není nastaveno a odesílání notifikací je zakázáno.\nNastavte Váš SMTP server v souboru config/configuration.yml a restartujte aplikaci."
field_parent_title: Rodičovská stránka
label_issue_watchers: Sledování
setting_commit_logs_encoding: Kódování zpráv při commitu
@ -785,12 +790,12 @@ cs:
text_repository_usernames_mapping: "Vybrat nebo upravit mapování mezi Redmine uživateli a uživatelskými jmény nalezenými v logu repozitáře.\nUživatelé se shodným Redmine uživatelským jménem a uživatelským jménem v repozitáři jsou mapovaní automaticky."
permission_edit_own_messages: Upravit vlastní zprávy
permission_delete_own_messages: Smazat vlastní zprávy
label_user_activity: "Aktivita uživatele: {{value}}"
label_updated_time_by: "Akutualizováno: {{author}} před: {{age}}"
label_user_activity: "Aktivita uživatele: %{value}"
label_updated_time_by: "Akutualizováno: %{author} před: %{age}"
text_diff_truncated: '... Rozdílový soubor je zkrácen, protože jeho délka přesahuje max. limit.'
setting_diff_max_lines_displayed: Maximální počet zobrazenách řádků rozdílů
text_plugin_assets_writable: Možnost zápisu do adresáře plugin assets
warning_attachments_not_saved: "{{count}} soubor(ů) nebylo možné uložit."
warning_attachments_not_saved: "%{count} soubor(ů) nebylo možné uložit."
button_create_and_continue: Vytvořit a pokračovat
text_custom_field_possible_values_info: 'Každá hodnota na novém řádku'
label_display: Zobrazit
@ -805,21 +810,21 @@ cs:
label_descending: Sestupně
label_sort: Řazení
label_ascending: Vzestupně
label_date_from_to: Od {{start}} do {{end}}
label_date_from_to: Od %{start} do %{end}
label_greater_or_equal: ">="
label_less_or_equal: <=
text_wiki_page_destroy_question: Tato stránka má {{descendants}} podstránek a potomků. Co chcete udělat?
text_wiki_page_destroy_question: Tato stránka má %{descendants} podstránek a potomků. Co chcete udělat?
text_wiki_page_reassign_children: Přiřadit podstránky k tomuto rodiči
text_wiki_page_nullify_children: Ponechat podstránky jako kořenové stránky
text_wiki_page_destroy_children: Smazat podstránky a všechny jejich potomky
setting_password_min_length: Minimální délka hesla
field_group_by: Seskupovat výsledky podle
mail_subject_wiki_content_updated: "'{{id}}' Wiki stránka byla aktualizována"
mail_subject_wiki_content_updated: "'%{id}' Wiki stránka byla aktualizována"
label_wiki_content_added: Wiki stránka přidána
mail_subject_wiki_content_added: "'{{id}}' Wiki stránka byla přidána"
mail_body_wiki_content_added: "'{{id}}' Wiki stránka byla přidána od {{author}}."
mail_subject_wiki_content_added: "'%{id}' Wiki stránka byla přidána"
mail_body_wiki_content_added: "'%{id}' Wiki stránka byla přidána od %{author}."
label_wiki_content_updated: Wiki stránka aktualizována
mail_body_wiki_content_updated: "'{{id}}' Wiki stránka byla aktualizována od {{author}}."
mail_body_wiki_content_updated: "'%{id}' Wiki stránka byla aktualizována od %{author}."
permission_add_project: Vytvořit projekt
setting_new_project_user_role_id: Role přiřazená uživateli bez práv administrátora, který projekt vytvořil
label_view_all_revisions: Zobrazit všechny revize
@ -827,14 +832,14 @@ cs:
label_branch: Branch
error_no_tracker_in_project: Žádná fronta nebyla přiřazena tomuto projektu. Prosím zkontroluje nastavení projektu.
error_no_default_issue_status: Není nastaven výchozí stav úkolu. Prosím zkontrolujte nastavení ("Administrace -> Stavy úkolů").
text_journal_changed: "{{label}} změněn z {{old}} na {{new}}"
text_journal_set_to: "{{label}} nastaven na {{value}}"
text_journal_deleted: "{{label}} smazán ({{old}})"
text_journal_changed: "%{label} změněn z %{old} na %{new}"
text_journal_set_to: "%{label} nastaven na %{value}"
text_journal_deleted: "%{label} smazán (%{old})"
label_group_plural: Skupiny
label_group: Skupina
label_group_new: Nová skupina
label_time_entry_plural: Strávený čas
text_journal_added: "{{label}} {{value}} přidán"
text_journal_added: "%{label} %{value} přidán"
field_active: Aktivní
enumeration_system_activity: Systémová aktivita
permission_delete_issue_watchers: Smazat přihlížející
@ -869,9 +874,9 @@ cs:
setting_start_of_week: Začínat kalendáře
permission_view_issues: Zobrazit úkoly
label_display_used_statuses_only: Zobrazit pouze stavy které jsou použité touto frontou
label_revision_id: Revize {{value}}
label_revision_id: Revize %{value}
label_api_access_key: API přístupový klíč
label_api_access_key_created_on: API přístupový klíč vytvořen {{value}}
label_api_access_key_created_on: API přístupový klíč vytvořen %{value}
label_feeds_access_key: RSS přístupový klíč
notice_api_access_key_reseted: Váš API přístupový klíč byl resetován.
setting_rest_api_enabled: Zapnout službu REST
@ -898,12 +903,12 @@ cs:
label_subtask_plural: Podúkol
label_project_copy_notifications: Odeslat email oznámení v průběhu kopie projektu
error_can_not_delete_custom_field: Nelze smazat volitelné pole
error_unable_to_connect: Nelze se připojit ({{value}})
error_unable_to_connect: Nelze se připojit (%{value})
error_can_not_remove_role: Tato role je právě používaná a nelze ji smazat.
error_can_not_delete_tracker: Tato fronta obsahuje úkoly a nemůže být smazán.
field_principal: Hlavní
label_my_page_block: Bloky na mé stránce
notice_failed_to_save_members: "Nepodařilo se uložit člena(y): {{errors}}."
notice_failed_to_save_members: "Nepodařilo se uložit člena(y): %{errors}."
text_zoom_out: Oddálit
text_zoom_in: Přiblížit
notice_unable_delete_time_entry: Nelze smazat čas ze záznamu.
@ -911,7 +916,7 @@ cs:
field_time_entries: Zaznamenaný čas
project_module_gantt: Gantt
project_module_calendar: Kalendář
button_edit_associated_wikipage: "Upravit přiřazenou Wiki stránku: {{page_title}}"
button_edit_associated_wikipage: "Upravit přiřazenou Wiki stránku: %{page_title}"
text_are_you_sure_with_children: Smazat úkol včetně všech podúkolů?
field_text: Textové pole
label_user_mail_option_only_owner: Only for things I am the owner of
@ -922,8 +927,12 @@ cs:
field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role
notice_not_authorized_archived_project: The project you're trying to access has been archived.
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
field_visible: Visible
setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart

View File

@ -35,37 +35,37 @@ da:
half_a_minute: "et halvt minut"
less_than_x_seconds:
one: "mindre end et sekund"
other: "mindre end {{count}} sekunder"
other: "mindre end %{count} sekunder"
x_seconds:
one: "et sekund"
other: "{{count}} sekunder"
other: "%{count} sekunder"
less_than_x_minutes:
one: "mindre end et minut"
other: "mindre end {{count}} minutter"
other: "mindre end %{count} minutter"
x_minutes:
one: "et minut"
other: "{{count}} minutter"
other: "%{count} minutter"
about_x_hours:
one: "cirka en time"
other: "cirka {{count}} timer"
other: "cirka %{count} timer"
x_days:
one: "en dag"
other: "{{count}} dage"
other: "%{count} dage"
about_x_months:
one: "cirka en måned"
other: "cirka {{count}} måneder"
other: "cirka %{count} måneder"
x_months:
one: "en måned"
other: "{{count}} måneder"
other: "%{count} måneder"
about_x_years:
one: "cirka et år"
other: "cirka {{count}} år"
other: "cirka %{count} år"
over_x_years:
one: "mere end et år"
other: "mere end {{count}} år"
other: "mere end %{count} år"
almost_x_years:
one: "næsten 1 år"
other: "næsten {{count}} år"
other: "næsten %{count} år"
number:
format:
@ -107,6 +107,10 @@ da:
activerecord:
errors:
template:
header:
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
messages:
inclusion: "er ikke i listen"
exclusion: "er reserveret"
@ -115,16 +119,16 @@ da:
accepted: "skal accepteres"
empty: "må ikke udelades"
blank: "skal udfyldes"
too_long: "er for lang (højst {{count}} tegn)"
too_short: "er for kort (mindst {{count}} tegn)"
wrong_length: "har forkert længde (skulle være {{count}} tegn)"
too_long: "er for lang (højst %{count} tegn)"
too_short: "er for kort (mindst %{count} tegn)"
wrong_length: "har forkert længde (skulle være %{count} tegn)"
taken: "er allerede anvendt"
not_a_number: "er ikke et tal"
greater_than: "skal være større end {{count}}"
greater_than_or_equal_to: "skal være større end eller lig med {{count}}"
equal_to: "skal være lig med {{count}}"
less_than: "skal være mindre end {{count}}"
less_than_or_equal_to: "skal være mindre end eller lig med {{count}}"
greater_than: "skal være større end %{count}"
greater_than_or_equal_to: "skal være større end eller lig med %{count}"
equal_to: "skal være lig med %{count}"
less_than: "skal være mindre end %{count}"
less_than_or_equal_to: "skal være mindre end eller lig med %{count}"
odd: "skal være ulige"
even: "skal være lige"
greater_than_start_date: "skal være senere end startdatoen"
@ -134,8 +138,8 @@ da:
template:
header:
one: "En fejl forhindrede {{model}} i at blive gemt"
other: "{{count}} fejl forhindrede denne {{model}} i at blive gemt"
one: "En fejl forhindrede %{model} i at blive gemt"
other: "%{count} fejl forhindrede denne %{model} i at blive gemt"
body: "Der var problemer med følgende felter:"
actionview_instancetag_blank_option: Vælg venligst
@ -166,29 +170,29 @@ da:
notice_file_not_found: Siden du forsøger at tilgå eksisterer ikke eller er blevet fjernet.
notice_locking_conflict: Data er opdateret af en anden bruger.
notice_not_authorized: Du har ikke adgang til denne side.
notice_email_sent: "En email er sendt til {{value}}"
notice_email_error: "En fejl opstod under afsendelse af email ({{value}})"
notice_email_sent: "En email er sendt til %{value}"
notice_email_error: "En fejl opstod under afsendelse af email (%{value})"
notice_feeds_access_key_reseted: Din adgangsnøgle til RSS er nulstillet.
notice_failed_to_save_issues: "Det mislykkedes at gemme {{count}} sage(r) på {{total}} valgt: {{ids}}."
notice_failed_to_save_issues: "Det mislykkedes at gemme %{count} sage(r) på %{total} valgt: %{ids}."
notice_no_issue_selected: "Ingen sag er valgt! Vælg venligst hvilke emner du vil rette."
notice_account_pending: "Din konto er oprettet, og afventer administrators godkendelse."
notice_default_data_loaded: Standardopsætningen er indlæst.
error_can_t_load_default_data: "Standardopsætning kunne ikke indlæses: {{value}}"
error_can_t_load_default_data: "Standardopsætning kunne ikke indlæses: %{value}"
error_scm_not_found: "Adgang nægtet og/eller revision blev ikke fundet i det valgte repository."
error_scm_command_failed: "En fejl opstod under forbindelsen til det valgte repository: {{value}}"
error_scm_command_failed: "En fejl opstod under forbindelsen til det valgte repository: %{value}"
mail_subject_lost_password: "Dit {{value}} kodeord"
mail_subject_lost_password: "Dit %{value} kodeord"
mail_body_lost_password: 'For at ændre dit kodeord, klik på dette link:'
mail_subject_register: "{{value}} kontoaktivering"
mail_subject_register: "%{value} kontoaktivering"
mail_body_register: 'Klik på dette link for at aktivere din konto:'
mail_body_account_information_external: "Du kan bruge din {{value}} konto til at logge ind."
mail_body_account_information_external: "Du kan bruge din %{value} konto til at logge ind."
mail_body_account_information: Din kontoinformation
mail_subject_account_activation_request: "{{value}} kontoaktivering"
mail_body_account_activation_request: "En ny bruger ({{value}}) er registreret. Godkend venligst kontoen:"
mail_subject_account_activation_request: "%{value} kontoaktivering"
mail_body_account_activation_request: "En ny bruger (%{value}) er registreret. Godkend venligst kontoen:"
gui_validation_error: 1 fejl
gui_validation_error_plural: "{{count}} fejl"
gui_validation_error_plural: "%{count} fejl"
field_name: Navn
field_description: Beskrivelse
@ -250,6 +254,7 @@ da:
field_attr_lastname: Efternavn attribut
field_attr_mail: Email attribut
field_onthefly: løbende brugeroprettelse
field_start_date: Start date
field_done_ratio: % Færdig
field_auth_source: Sikkerhedsmetode
field_hide_mail: Skjul min email
@ -318,14 +323,14 @@ da:
label_x_projects:
zero: Ingen projekter
one: 1 projekt
other: "{{count}} projekter"
other: "%{count} projekter"
label_project_all: Alle projekter
label_project_latest: Seneste projekter
label_issue: Sag
label_issue_new: Opret sag
label_issue_plural: Sager
label_issue_view_all: Vis alle sager
label_issues_by: "Sager fra {{value}}"
label_issues_by: "Sager fra %{value}"
label_issue_added: Sagen er oprettet
label_issue_updated: Sagen er opdateret
label_document: Dokument
@ -390,8 +395,8 @@ da:
label_text: Lang tekst
label_attribute: Attribut
label_attribute_plural: Attributter
label_download: "{{count}} Download"
label_download_plural: "{{count}} Downloads"
label_download: "%{count} Download"
label_download_plural: "%{count} Downloads"
label_no_data: Ingen data at vise
label_change_status: Ændringsstatus
label_history: Historik
@ -422,17 +427,17 @@ da:
label_closed_issues: lukket
label_closed_issues_plural: lukkede
label_x_open_issues_abbr_on_total:
zero: 0 åbne / {{total}}
one: 1 åben / {{total}}
other: "{{count}} åbne / {{total}}"
zero: 0 åbne / %{total}
one: 1 åben / %{total}
other: "%{count} åbne / %{total}"
label_x_open_issues_abbr:
zero: 0 åbne
one: 1 åben
other: "{{count}} åbne"
other: "%{count} åbne"
label_x_closed_issues_abbr:
zero: 0 lukkede
one: 1 lukket
other: "{{count}} lukkede"
other: "%{count} lukkede"
label_total: Total
label_permissions: Rettigheder
label_current_status: Nuværende status
@ -450,7 +455,7 @@ da:
label_months_from: måneder frem
label_gantt: Gantt
label_internal: Intern
label_last_changes: "sidste {{count}} ændringer"
label_last_changes: "sidste %{count} ændringer"
label_change_view_all: Vis alle ændringer
label_personalize_page: Tilret denne side
label_comment: Kommentar
@ -458,7 +463,7 @@ da:
label_x_comments:
zero: ingen kommentarer
one: 1 kommentar
other: "{{count}} kommentarer"
other: "%{count} kommentarer"
label_comment_add: Tilføj en kommentar
label_comment_added: Kommentaren er tilføjet
label_comment_delete: Slet kommentar
@ -477,7 +482,7 @@ da:
label_yesterday: i går
label_this_week: denne uge
label_last_week: sidste uge
label_last_n_days: "sidste {{count}} dage"
label_last_n_days: "sidste %{count} dage"
label_this_month: denne måned
label_last_month: sidste måned
label_this_year: dette år
@ -491,8 +496,8 @@ da:
label_repository: Repository
label_repository_plural: Repositories
label_browse: Gennemse
label_modification: "{{count}} ændring"
label_modification_plural: "{{count}} ændringer"
label_modification: "%{count} ændring"
label_modification_plural: "%{count} ændringer"
label_revision: Revision
label_revision_plural: Revisioner
label_associated_revisions: Tilnyttede revisioner
@ -509,7 +514,7 @@ da:
label_sort_lowest: Flyt til bunden
label_roadmap: Roadmap
label_roadmap_due_in: Deadline
label_roadmap_overdue: "{{value}} forsinket"
label_roadmap_overdue: "%{value} forsinket"
label_roadmap_no_issues: Ingen sager i denne version
label_search: Søg
label_result_plural: Resultater
@ -527,8 +532,8 @@ da:
label_changes_details: Detaljer for alle ændringer
label_issue_tracking: Sags søgning
label_spent_time: Anvendt tid
label_f_hour: "{{value}} time"
label_f_hour_plural: "{{value}} timer"
label_f_hour: "%{value} time"
label_f_hour_plural: "%{value} timer"
label_time_tracking: Tidsstyring
label_change_plural: Ændringer
label_statistics: Statistik
@ -576,12 +581,12 @@ da:
label_date_from: Fra
label_date_to: Til
label_language_based: Baseret på brugerens sprog
label_sort_by: "Sortér efter {{value}}"
label_sort_by: "Sortér efter %{value}"
label_send_test_email: Send en test email
label_feeds_access_key_created_on: "RSS adgangsnøgle dannet for {{value}} siden"
label_feeds_access_key_created_on: "RSS adgangsnøgle dannet for %{value} siden"
label_module_plural: Moduler
label_added_time_by: "Tilføjet af {{author}} for {{age}} siden"
label_updated_time: "Opdateret for {{value}} siden"
label_added_time_by: "Tilføjet af %{author} for %{age} siden"
label_updated_time: "Opdateret for %{value} siden"
label_jump_to_a_project: Skift til projekt...
label_file_plural: Filer
label_changeset_plural: Ændringer
@ -597,7 +602,7 @@ da:
label_registration_activation_by_email: kontoaktivering på email
label_registration_manual_activation: manuel kontoaktivering
label_registration_automatic_activation: automatisk kontoaktivering
label_display_per_page: "Per side: {{value}}"
label_display_per_page: "Per side: %{value}"
label_age: Alder
label_change_properties: Ændre indstillinger
label_general: Generelt
@ -659,23 +664,23 @@ da:
text_tip_issue_end_day: opaven slutter denne dag
text_tip_issue_begin_end_day: opgaven begynder og slutter denne dag
text_project_identifier_info: 'Små bogstaver (a-z), numre og bindestreg er tilladt.<br />Denne er en unik identifikation for projektet, og kan defor ikke rettes senere.'
text_caracters_maximum: "max {{count}} karakterer."
text_caracters_minimum: "Skal være mindst {{count}} karakterer lang."
text_length_between: "Længde skal være mellem {{min}} og {{max}} karakterer."
text_caracters_maximum: "max %{count} karakterer."
text_caracters_minimum: "Skal være mindst %{count} karakterer lang."
text_length_between: "Længde skal være mellem %{min} og %{max} karakterer."
text_tracker_no_workflow: Ingen arbejdsgang defineret for denne type
text_unallowed_characters: Ikke-tilladte karakterer
text_comma_separated: Adskillige værdier tilladt (adskilt med komma).
text_issues_ref_in_commit_messages: Referer og løser sager i commit-beskeder
text_issue_added: "Sag {{id}} er rapporteret af {{author}}."
text_issue_updated: "Sag {{id}} er blevet opdateret af {{author}}."
text_issue_added: "Sag %{id} er rapporteret af %{author}."
text_issue_updated: "Sag %{id} er blevet opdateret af %{author}."
text_wiki_destroy_confirmation: Er du sikker på at du vil slette denne wiki og dens indhold?
text_issue_category_destroy_question: "Nogle sager ({{count}}) er tildelt denne kategori. Hvad ønsker du at gøre?"
text_issue_category_destroy_question: "Nogle sager (%{count}) er tildelt denne kategori. Hvad ønsker du at gøre?"
text_issue_category_destroy_assignments: Slet tildelinger af kategori
text_issue_category_reassign_to: Tildel sager til denne kategori
text_user_mail_option: "For ikke-valgte projekter vil du kun modtage beskeder omhandlende ting du er involveret i eller overvåger (f.eks. sager du har indberettet eller ejer)."
text_no_configuration_data: "Roller, typer, sagsstatuser og arbejdsgange er endnu ikke konfigureret.\nDet er anbefalet at indlæse standardopsætningen. Du vil kunne ændre denne når den er indlæst."
text_load_default_configuration: Indlæs standardopsætningen
text_status_changed_by_changeset: "Anvendt i ændring {{value}}."
text_status_changed_by_changeset: "Anvendt i ændring %{value}."
text_issues_destroy_confirmation: 'Er du sikker på du ønsker at slette den/de valgte sag(er)?'
text_select_project_modules: 'Vælg moduler er skal være aktiveret for dette projekt:'
text_default_administrator_account_changed: Standard administratorkonto ændret
@ -711,7 +716,7 @@ da:
label_add_another_file: Tilføj endnu en fil
label_chronological_order: I kronologisk rækkefølge
setting_activity_days_default: Antal dage der vises under projektaktivitet
text_destroy_time_entries_question: "{{hours}} timer er registreret på denne sag som du er ved at slette. Hvad vil du gøre?"
text_destroy_time_entries_question: "%{hours} timer er registreret på denne sag som du er ved at slette. Hvad vil du gøre?"
error_issue_not_found_in_project: 'Sagen blev ikke fundet eller tilhører ikke dette projekt'
text_assign_time_entries_to_project: Tildel raporterede timer til projektet
setting_display_subprojects_issues: Vis sager for underprojekter på hovedprojektet som standard
@ -725,7 +730,7 @@ da:
setting_default_projects_public: Nye projekter er offentlige som standard
error_scm_annotate: "Filen findes ikke, eller kunne ikke annoteres."
label_planning: Planlægning
text_subprojects_destroy_warning: "Dets underprojekter(er): {{value}} vil også blive slettet."
text_subprojects_destroy_warning: "Dets underprojekter(er): %{value} vil også blive slettet."
permission_edit_issues: Redigér sager
setting_diff_max_lines_displayed: Højeste antal forskelle der vises
permission_edit_own_issue_notes: Redigér egne noter
@ -748,11 +753,11 @@ da:
setting_sequential_project_identifiers: Generér sekventielle projekt-identifikatorer
setting_plain_text_mail: Emails som almindelig tekst (ingen HTML)
field_parent_title: Siden over
text_email_delivery_not_configured: "Email-afsendelse er ikke indstillet og notifikationer er defor slået fra.\nKonfigurér din SMTP server i config/email.yml og genstart applikationen for at aktivere email-afsendelse."
text_email_delivery_not_configured: "Email-afsendelse er ikke indstillet og notifikationer er defor slået fra.\nKonfigurér din SMTP server i config/configuration.yml og genstart applikationen for at aktivere email-afsendelse."
permission_protect_wiki_pages: Beskyt wiki sider
permission_manage_documents: Administrér dokumenter
permission_add_issue_watchers: Tilføj overvågere
warning_attachments_not_saved: "der var {{count}} fil(er), som ikke kunne gemmes."
warning_attachments_not_saved: "der var %{count} fil(er), som ikke kunne gemmes."
permission_comment_news: Kommentér nyheder
text_enumeration_category_reassign_to: 'FLyt dem til denne værdi:'
permission_select_project_modules: Vælg projektmoduler
@ -760,7 +765,7 @@ da:
permission_delete_messages: Slet beskeder
permission_move_issues: Flyt sager
permission_edit_wiki_pages: Redigér wiki sider
label_user_activity: "{{value}}'s aktivitet"
label_user_activity: "%{value}'s aktivitet"
permission_manage_issue_relations: Administrér sags-relationer
label_issue_watchers: Overvågere
permission_delete_wiki_pages: Slet wiki sider
@ -771,27 +776,27 @@ da:
permission_manage_boards: Administrér fora
permission_delete_wiki_pages_attachments: Slet filer vedhæftet wiki sider
permission_view_messages: Se beskeder
text_enumeration_destroy_question: "{{count}} objekter er tildelt denne værdi."
text_enumeration_destroy_question: "%{count} objekter er tildelt denne værdi."
permission_manage_files: Administrér filer
permission_add_messages: Opret beskeder
permission_edit_issue_notes: Redigér noter
permission_manage_news: Administrér nyheder
text_plugin_assets_writable: Der er skriverettigheder til plugin assets folderen
label_display: Vis
label_and_its_subprojects: "{{value}} og dets underprojekter"
label_and_its_subprojects: "%{value} og dets underprojekter"
permission_view_calendar: Se kalender
button_create_and_continue: Opret og fortsæt
setting_gravatar_enabled: Anvend Gravatar bruger ikoner
label_updated_time_by: "Opdateret af {{author}} for {{age}} siden"
label_updated_time_by: "Opdateret af %{author} for %{age} siden"
text_diff_truncated: '... Listen over forskelle er bleve afkortet da den overstiger den maksimale størrelse der kan vises.'
text_user_wrote: "{{value}} skrev:"
text_user_wrote: "%{value} skrev:"
setting_mail_handler_api_enabled: Aktiver webservice for indkomne emails
permission_delete_issues: Slet sager
permission_view_documents: Se dokumenter
permission_browse_repository: Gennemse repository
permission_manage_repository: Administrér repository
permission_manage_members: Administrér medlemmer
mail_subject_reminder: "{{count}} sag(er) har deadline i de kommende dage ({{days}})"
mail_subject_reminder: "%{count} sag(er) har deadline i de kommende dage (%{days})"
permission_add_issue_notes: Tilføj noter
permission_edit_messages: Redigér beskeder
permission_view_issue_watchers: Se liste over overvågere
@ -816,26 +821,26 @@ da:
field_identity_url: OpenID URL
label_login_with_open_id_option: eller login med OpenID
setting_per_page_options: Enheder per side muligheder
mail_body_reminder: "{{count}} sage(er) som er tildelt dig har deadline indenfor de næste {{days}} dage:"
mail_body_reminder: "%{count} sage(er) som er tildelt dig har deadline indenfor de næste %{days} dage:"
field_content: Indhold
label_descending: Aftagende
label_sort: Sortér
label_ascending: Tiltagende
label_date_from_to: Fra {{start}} til {{end}}
label_date_from_to: Fra %{start} til %{end}
label_greater_or_equal: ">="
label_less_or_equal: <=
text_wiki_page_destroy_question: Denne side har {{descendants}} underside(r) og afledte. Hvad vil du gøre?
text_wiki_page_destroy_question: Denne side har %{descendants} underside(r) og afledte. Hvad vil du gøre?
text_wiki_page_reassign_children: Flyt undersider til denne side
text_wiki_page_nullify_children: Behold undersider som rod-sider
text_wiki_page_destroy_children: Slet undersider ogalle deres afledte sider.
setting_password_min_length: Mindste længde på kodeord
field_group_by: Gruppér resultater efter
mail_subject_wiki_content_updated: "'{{id}}' wikisiden er blevet opdateret"
mail_subject_wiki_content_updated: "'%{id}' wikisiden er blevet opdateret"
label_wiki_content_added: Wiki side tilføjet
mail_subject_wiki_content_added: "'{{id}}' wikisiden er blevet tilføjet"
mail_body_wiki_content_added: The '{{id}}' wikiside er blevet tilføjet af {{author}}.
mail_subject_wiki_content_added: "'%{id}' wikisiden er blevet tilføjet"
mail_body_wiki_content_added: The '%{id}' wikiside er blevet tilføjet af %{author}.
label_wiki_content_updated: Wikiside opdateret
mail_body_wiki_content_updated: Wikisiden '{{id}}' er blevet opdateret af {{author}}.
mail_body_wiki_content_updated: Wikisiden '%{id}' er blevet opdateret af %{author}.
permission_add_project: Opret projekt
setting_new_project_user_role_id: Denne rolle gives til en bruger, som ikke er administrator, og som opretter et projekt
label_view_all_revisions: Se alle revisioner
@ -843,14 +848,14 @@ da:
label_branch: Branch
error_no_tracker_in_project: Der er ingen sagshåndtering for dette projekt. Kontrollér venligst projektindstillingerne.
error_no_default_issue_status: Der er ikek defineret en standardstatus. Kontrollér venligst indstillingernen (Gå til "Administration -> Sagsstatuser").
text_journal_changed: "{{label}} ændret fra {{old}} til {{new}}"
text_journal_set_to: "{{label}} sat til {{value}}"
text_journal_deleted: "{{label}} slettet ({{old}})"
text_journal_changed: "%{label} ændret fra %{old} til %{new}"
text_journal_set_to: "%{label} sat til %{value}"
text_journal_deleted: "%{label} slettet (%{old})"
label_group_plural: Grupper
label_group: Grupper
label_group_new: Ny gruppe
label_time_entry_plural: Anvendt tid
text_journal_added: "{{label}} {{value}} tilføjet"
text_journal_added: "%{label} %{value} tilføjet"
field_active: Aktiv
enumeration_system_activity: System Aktivitet
permission_delete_issue_watchers: Slet overvågere
@ -885,9 +890,9 @@ da:
setting_start_of_week: Start kalendre på
permission_view_issues: Vis sager
label_display_used_statuses_only: Vis kun statuser der er benyttet af denne tracker
label_revision_id: Revision {{value}}
label_revision_id: Revision %{value}
label_api_access_key: API nøgle
label_api_access_key_created_on: API nøgle genereret {{value}} siden
label_api_access_key_created_on: API nøgle genereret %{value} siden
label_feeds_access_key: RSS nøgle
notice_api_access_key_reseted: Din API nøgle er nulstillet.
setting_rest_api_enabled: Aktiver REST web service
@ -914,12 +919,12 @@ da:
label_subtask_plural: Under opgaver
label_project_copy_notifications: Send email notifikationer, mens projektet kopieres
error_can_not_delete_custom_field: Kan ikke slette brugerdefineret felt
error_unable_to_connect: Kan ikke forbinde ({{value}})
error_unable_to_connect: Kan ikke forbinde (%{value})
error_can_not_remove_role: Denne rolle er i brug og kan ikke slettes.
error_can_not_delete_tracker: Denne type indeholder sager og kan ikke slettes.
field_principal: Principal
label_my_page_block: blok
notice_failed_to_save_members: "Fejl under lagring af medlem(mer): {{errors}}."
notice_failed_to_save_members: "Fejl under lagring af medlem(mer): %{errors}."
text_zoom_out: Zoom ud
text_zoom_in: Zoom in
notice_unable_delete_time_entry: Kan ikke slette tidsregistrering.
@ -927,7 +932,7 @@ da:
field_time_entries: Log tid
project_module_gantt: Gantt
project_module_calendar: Kalender
button_edit_associated_wikipage: "Redigér tilknyttet Wiki side: {{page_title}}"
button_edit_associated_wikipage: "Redigér tilknyttet Wiki side: %{page_title}"
text_are_you_sure_with_children: Slet sag og alle undersager?
field_text: Tekstfelt
label_user_mail_option_only_owner: Only for things I am the owner of
@ -938,8 +943,12 @@ da:
field_member_of_group: Medlem af Gruppe
field_assigned_to_role: Medlem af Rolle
notice_not_authorized_archived_project: The project you're trying to access has been archived.
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
field_visible: Visible
setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart

Some files were not shown because too many files have changed in this diff Show More