Compare commits

...

36 Commits
1.0.3 ... 1.0.5

Author SHA1 Message Date
Jean-Philippe Lang
7f41fbccb1 tagged version 1.0.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/1.0.5@4570 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:25:21 +00:00
Jean-Philippe Lang
aba0dc0371 Updates for 1.0.5 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4569 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:04:53 +00:00
Jean-Philippe Lang
902d765ab7 Merged r4553 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4565 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 10:00:35 +00:00
Jean-Philippe Lang
c6852b7af2 Merged r4469 from trunk
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4564 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 09:59:36 +00:00
Jean-Philippe Lang
0bff40c1e1 Merged r4467 from trunk
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4563 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 09:58:18 +00:00
Jean-Philippe Lang
40ca445071 Merged r4561 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4562 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-23 09:57:02 +00:00
Jean-Philippe Lang
f9bd713e3e Removed 1.1 specific translations that were unsafely merged in 1.0 (#7162).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4557 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-22 21:05:13 +00:00
Jean-Philippe Lang
ba99936295 Restores non english field_start_date translations (#6629, #7016).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4555 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-22 20:48:23 +00:00
Jean-Philippe Lang
63c4fb5b97 Merged r4541 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4543 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 18:42:00 +00:00
Jean-Philippe Lang
c02ecdd096 Merged r4539 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4540 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 18:14:30 +00:00
Jean-Philippe Lang
cb30fa49fc Merged r4468 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4538 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 17:22:43 +00:00
Jean-Philippe Lang
f4ada319d1 Merged r4535 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4536 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 17:13:13 +00:00
Jean-Philippe Lang
fdaf587652 Merged r4516 from trunk (i18n 0.4.2 gem is now required: 'gem install -v=0.4.2 i18n', see #7013).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4532 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 13:53:34 +00:00
Jean-Philippe Lang
142b837271 Backported r4530 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4531 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-18 13:42:50 +00:00
Jean-Philippe Lang
bd8e8bc71b Fixed: error when sending notification on wiki edit for bg, es, zh-TW locales (#7024).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4478 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-07 22:05:20 +00:00
Jean-Philippe Lang
a67f06910b Merged r4449 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4450 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-29 19:34:56 +00:00
Jean-Philippe Lang
ed52753a12 Updates for 1.0.4 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4447 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-28 12:47:36 +00:00
Jean-Philippe Lang
bac64c9ab4 Backported r4441 to r4444 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4445 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 16:45:07 +00:00
Jean-Philippe Lang
b58382ef2e Merged r4386 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4440 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 15:48:30 +00:00
Jean-Philippe Lang
541a371b41 Backported r4357, r4358, r4360 and r4363 to r4367 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4439 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 15:16:26 +00:00
Jean-Philippe Lang
bdb888476d Merged r4437 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4438 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 14:47:31 +00:00
Jean-Philippe Lang
f5f55359a8 Translations updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4436 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 14:40:51 +00:00
Jean-Philippe Lang
eb590ce1eb Merged r4428 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4435 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 14:31:29 +00:00
Jean-Philippe Lang
db64675310 Merged r4431 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4434 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 14:30:41 +00:00
Jean-Philippe Lang
92b37c0bea Merged r4426 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4433 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-27 14:28:51 +00:00
Jean-Philippe Lang
faa461c66a Merged r4422 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4423 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-21 14:26:01 +00:00
Jean-Philippe Lang
b5a409791c Merged r4419 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4420 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 14:12:35 +00:00
Jean-Philippe Lang
6c362a5a76 Merged r4417 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4418 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 14:06:32 +00:00
Jean-Philippe Lang
dbb26b08f8 Merged r4414 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4415 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-20 10:23:13 +00:00
Jean-Philippe Lang
2a7a95d746 Backported r4397 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4401 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 11:30:53 +00:00
Jean-Philippe Lang
c70ae6c735 Merged r4380 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4400 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 11:18:23 +00:00
Jean-Philippe Lang
e174c10d6d Merged r4385 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4399 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 11:17:20 +00:00
Jean-Philippe Lang
2b9a5bf6e2 Merged r4378 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4398 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-14 11:15:53 +00:00
Jean-Philippe Lang
d3b536e9a6 Merged r4389 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4390 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-09 19:56:29 +00:00
Jean-Philippe Lang
9b1eda1b22 Fixed a test broken by r4355 (#6786).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4356 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 14:47:23 +00:00
Jean-Philippe Lang
f1f0704ef8 Merged r4354 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.0-stable@4355 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-11-01 13:14:14 +00:00
93 changed files with 4797 additions and 4553 deletions

View File

@@ -26,7 +26,7 @@ class IssuesController < ApplicationController
before_filter :find_optional_project, :only => [:index]
before_filter :check_for_default_issue_status, :only => [:new, :create]
before_filter :build_new_issue_from_params, :only => [:new, :create]
accept_key_auth :index, :show
accept_key_auth :index, :show, :create, :update, :destroy
rescue_from Query::StatementInvalid, :with => :query_statement_invalid
@@ -300,6 +300,7 @@ private
render_error l(:error_no_tracker_in_project)
return false
end
@issue.start_date ||= Date.today
if params[:issue].is_a?(Hash)
@issue.safe_attributes = params[:issue]
if User.current.allowed_to?(:add_issue_watchers, @project) && @issue.new_record?
@@ -307,7 +308,6 @@ private
end
end
@issue.author = User.current
@issue.start_date ||= Date.today
@priorities = IssuePriority.all
@allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
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

@@ -24,7 +24,7 @@ class ProjectsController < ApplicationController
before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
before_filter :authorize_global, :only => [:new, :create]
before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
accept_key_auth :index
accept_key_auth :index, :show, :create, :update, :destroy
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
if controller.request.post?
@@ -93,7 +93,7 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings', :id => @project
}
format.xml { head :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
end
else
respond_to do |format|

View File

@@ -109,6 +109,10 @@ class VersionsController < ApplicationController
if @version.update_attributes(attributes)
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
respond_to do |format|
format.html { render :action => 'edit' }
end
end
end
end

View File

@@ -18,7 +18,7 @@
class Board < ActiveRecord::Base
belongs_to :project
has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :delete_all, :order => "#{Message.table_name}.created_on DESC"
has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC"
belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
acts_as_list :scope => :project_id
acts_as_watchable

View File

@@ -31,6 +31,7 @@ class Group < Principal
def user_added(user)
members.each do |member|
next if member.project.nil?
user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id)
member.member_roles.each do |member_role|
user_member.member_roles << MemberRole.new(:role => member_role.role, :inherited_from => member_role.id)

View File

@@ -237,7 +237,7 @@ class Issue < ActiveRecord::Base
if !user.allowed_to?(:manage_subtasks, project)
attrs.delete('parent_issue_id')
elsif !attrs['parent_issue_id'].blank?
attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'])
attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
end
end

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

@@ -368,15 +368,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

@@ -2,5 +2,5 @@
<div class="box">
<p><%= f.text_field :name, :size => 30, :required => true %></p>
<p><%= f.select :assigned_to_id, @project.users.collect{|u| [u.name, u.id]}, :include_blank => true %></p>
<p><%= f.select :assigned_to_id, @project.users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %></p>
</div>

View File

@@ -47,7 +47,7 @@
<hr />
<div class="contextual">
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') unless @issue.description.blank? %>
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') unless @issue.description.blank? %>
</div>
<p><strong><%=l(:field_description)%></strong></p>

View File

@@ -5,9 +5,9 @@ function recreateSortables() {
Sortable.destroy('list-left');
Sortable.destroy('list-right');
Sortable.create("list-top", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=top', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-top")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-left", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=left', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-left")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-right", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=right', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-right")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-top", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('<%= url_for(:controller => 'my', :action => 'order_blocks', :group => 'top') %>', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-top")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-left", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('<%= url_for(:controller => 'my', :action => 'order_blocks', :group => 'left') %>', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-left")})}, only:'mypage-box', tag:'div'})
Sortable.create("list-right", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('<%= url_for(:controller => 'my', :action => 'order_blocks', :group => 'right') %>', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-right")})}, only:'mypage-box', tag:'div'})
}
function updateSelect() {

View File

@@ -6,7 +6,7 @@
<p><%= setting_text_area :welcome_text, :cols => 60, :rows => 5, :class => 'wiki-edit' %></p>
<%= wikitoolbar_for 'settings_welcome_text' %>
<p><%= setting_text_field :attachment_max_size, :size => 6 %> KB</p>
<p><%= setting_text_field :attachment_max_size, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p>
<p><%= setting_text_field :per_page_options, :size => 20 %><br />
<em><%= l(:text_comma_separated) %></em></p>
@@ -26,7 +26,7 @@
<p><%= setting_text_field :feeds_limit, :size => 6 %></p>
<p><%= setting_text_field :file_max_size_displayed, :size => 6 %> KB</p>
<p><%= setting_text_field :file_max_size_displayed, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p>
<p><%= setting_text_field :diff_max_lines_displayed, :size => 6 %></p>

View File

@@ -5,6 +5,7 @@
<% if @workflow_counts.empty? %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% else %>
<div class="autoscroll">
<table class="list">
<thead>
<tr>
@@ -30,4 +31,5 @@
<% end -%>
</tbody>
</table>
</div>
<% 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

@@ -84,9 +84,9 @@ ActionMailer::Base.send :include, AsynchronousMailer
module I18n
module Backend
module Base
def warn_syntax_deprecation!
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

View File

@@ -221,6 +221,7 @@ bg:
field_attr_lastname: Атрибут Фамилия (Lastname)
field_attr_mail: Атрибут Email
field_onthefly: Динамично създаване на потребител
field_start_date: Начална дата
field_done_ratio: % Прогрес
field_auth_source: Начин на оторизация
field_hide_mail: Скрий e-mail адреса ми
@@ -321,7 +322,7 @@ bg:
label_registered_on: Регистрация
label_activity: Дейност
label_new: Нов
label_logged_as: Влязъл като
label_logged_as: Здравейте,
label_environment: Среда
label_authentication: Оторизация
label_auth_source: Начин на оторозация
@@ -808,12 +809,12 @@ bg:
text_wiki_page_destroy_children: Изтриване на страниците деца и всички техни descendants
setting_password_min_length: Минимална дължина на парола
field_group_by: Групиране на резултатите по
mail_subject_wiki_content_updated: "Wiki страницата '{{id}}' не беше обновена"
mail_subject_wiki_content_updated: "Wiki страницата '{{page}}' не беше обновена"
label_wiki_content_added: Wiki страница беше добавена
mail_subject_wiki_content_added: "Wiki страницата '{{id}}' беше добавена"
mail_body_wiki_content_added: Wiki страницата '{{id}}' беше добавена от {{author}}.
mail_subject_wiki_content_added: "Wiki страницата '{{page}}' беше добавена"
mail_body_wiki_content_added: Wiki страницата '{{page}}' беше добавена от {{author}}.
label_wiki_content_updated: Wiki страница беше обновена
mail_body_wiki_content_updated: Wiki страницата '{{id}}' беше обновена от {{author}}.
mail_body_wiki_content_updated: Wiki страницата '{{page}}' беше обновена от {{author}}.
permission_add_project: Създаване на проект
setting_new_project_user_role_id: Роля, давана на потребител, създаващ проекти, който не е администратор
label_view_all_revisions: Разглеждане на всички ревизии
@@ -850,8 +851,8 @@ bg:
button_duplicate: Дублиране
button_copy_and_follow: Копиране и продължаване
label_copy_source: Източник
setting_issue_done_ratio: Изчисление на процента на готово задачи с
setting_issue_done_ratio_issue_status: Използване на състояниетона задачите
setting_issue_done_ratio: Изчисление на процента на готови задачи с
setting_issue_done_ratio_issue_status: Използване на състоянието на задачите
error_issue_done_ratios_not_updated: Процентът на завършените задачи не е обновен.
error_workflow_copy_target: Моля изберете тракер(и) и роля (роли).
setting_issue_done_ratio_issue_field: Използване на поле 'задача'
@@ -896,28 +897,26 @@ bg:
error_can_not_remove_role: Тази роля се използва и не може да бъде изтрита.
error_can_not_delete_tracker: Този тракер съдържа задачи и не може да бъде изтрит.
field_principal: Principal
label_my_page_block: My page block
label_my_page_block: Блокове в личната страница
notice_failed_to_save_members: "Невъзможност за запис на член(ове): {{errors}}."
text_zoom_out: Намаляване
text_zoom_in: Увеличаване
notice_unable_delete_time_entry: Невъзможност за изтриване на запис на time log.
label_overall_spent_time: Общо употребено време
field_time_entries: Log time
notice_not_authorized_archived_project: The project you're trying to access has been archived.
text_tip_issue_end_day: issue ending this day
field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of
field_member_of_group: Assignee's group
project_module_gantt: Gantt
text_are_you_sure_with_children: Delete issue and all child issues?
text_tip_issue_begin_end_day: issue beginning and ending this day
setting_default_notification_option: Default notification option
project_module_calendar: Calendar
label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
text_tip_issue_begin_day: issue beginning this day
label_user_mail_option_only_assigned: Only for things I am assigned to
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
notice_not_authorized_archived_project: Проектът, който се опитвате да видите е архивиран.
text_tip_issue_end_day: задача, завършваща този ден
field_text: Текстово поле
field_member_of_group: Член на група
project_module_gantt: Мрежов график
text_are_you_sure_with_children: Изтриване на задачата и нейните подзадачи?
text_tip_issue_begin_end_day: задача, започваща и завършваща този ден
setting_default_notification_option: Подразбиращ се начин за известяване
project_module_calendar: Календар
text_tip_issue_begin_day: задача, започваща този ден
button_edit_associated_wikipage: "Редактиране на асоциираната Wiki страница: {{page_title}}"
field_assigned_to_role: Assignee's role
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
label_principal_search: "Търсене на потребител или група:"
label_user_search: "Търсене на потребител:"
field_visible: Видим
setting_emails_header: Emails header

View File

@@ -252,6 +252,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
@@ -931,7 +932,5 @@ bs:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -264,6 +264,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"
@@ -919,7 +920,5 @@ ca:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -237,6 +237,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
@@ -917,6 +918,5 @@ cs:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -250,6 +250,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
@@ -370,7 +371,7 @@ da:
label_reported_issues: Rapporterede sager
label_assigned_to_me_issues: Sager tildelt mig
label_last_login: Sidste login tidspunkt
label_registered_on: Registeret den
label_registered_on: Registreret den
label_activity: Aktivitet
label_new: Ny
label_logged_as: Registreret som
@@ -441,7 +442,7 @@ da:
label_none: intet
label_nobody: ingen
label_next: Næste
label_previous: Forrig
label_previous: Forrige
label_used_by: Brugt af
label_details: Detaljer
label_add_note: Tilføj note
@@ -537,7 +538,7 @@ da:
label_view_diff: Vis forskelle
label_diff_inline: inline
label_diff_side_by_side: side om side
label_options: Optioner
label_options: Formatering
label_copy_workflow_from: Kopier arbejdsgang fra
label_permissions_report: Godkendelsesrapport
label_watched_issues: Overvågede sager
@@ -547,7 +548,7 @@ da:
label_relation_new: Ny relation
label_relation_delete: Slet relation
label_relates_to: relaterer til
label_duplicates: kopierer
label_duplicates: duplikater
label_blocks: blokerer
label_blocked_by: blokeret af
label_precedes: kommer før
@@ -870,8 +871,8 @@ da:
label_version_sharing_tree: Med projekt træ
label_version_sharing_none: Ikke delt
error_can_not_archive_project: Dette projekt kan ikke arkiveres
button_duplicate: Kopier
button_copy_and_follow: Kopier og overvåg
button_duplicate: Duplikér
button_copy_and_follow: Kopiér og overvåg
label_copy_source: Kilde
setting_issue_done_ratio: Beregn sagsløsning ratio
setting_issue_done_ratio_issue_status: Benyt sags status
@@ -896,14 +897,14 @@ da:
label_missing_feeds_access_key: Mangler en RSS nøgle
button_show: Vis
text_line_separated: Flere væredier tilladt (en linje for hver værdi).
setting_mail_handler_body_delimiters: Trunker emails efter en af disse linjer
setting_mail_handler_body_delimiters: Trunkér emails efter en af disse linjer
permission_add_subprojects: Lav underprojekter
label_subproject_new: Nyt underprojekt
text_own_membership_delete_confirmation: |-
Du er ved at fjerne en eller flere af dine rettigheder, og kan muligvis ikke redigere projektet bagefter.
Er du sikker på du ønsker at fortsætte?
label_close_versions: Luk færdige versioner
label_board_sticky: Sticky
label_board_sticky: Klistret
label_board_locked: Låst
permission_export_wiki_pages: Eksporter wiki sider
setting_cache_formatted_text: Cache formatteret tekst
@@ -914,26 +915,24 @@ da:
field_parent_issue: Hoved opgave
label_subtask_plural: Under opgaver
label_project_copy_notifications: Send email notifikationer, mens projektet kopieres
error_can_not_delete_custom_field: Unable to delete custom field
error_unable_to_connect: Unable to connect ({{value}})
error_can_not_remove_role: This role is in use and can not be deleted.
error_can_not_delete_tracker: This tracker contains issues and can't be deleted.
error_can_not_delete_custom_field: Kan ikke slette brugerdefineret felt
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: My page block
notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
text_zoom_out: Zoom out
label_my_page_block: blok
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: Unable to delete time log entry.
label_overall_spent_time: Overall spent time
field_time_entries: Log time
notice_unable_delete_time_entry: Kan ikke slette tidsregistrering.
label_overall_spent_time: Overordnet forbrug af tid
field_time_entries: Log tid
project_module_gantt: Gantt
project_module_calendar: Calendar
field_member_of_group: Member of Group
field_assigned_to_role: Member of Role
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field
field_start_date: Start date
project_module_calendar: Kalender
field_member_of_group: Medlem af Gruppe
field_assigned_to_role: Medlem af Rolle
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_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -280,6 +280,7 @@ de:
field_attr_lastname: Name-Attribut
field_attr_mail: E-Mail-Attribut
field_onthefly: On-the-fly-Benutzererstellung
field_start_date: Beginn
field_done_ratio: % erledigt
field_auth_source: Authentifizierungs-Modus
field_hide_mail: E-Mail-Adresse nicht anzeigen
@@ -939,9 +940,6 @@ de:
field_text: Text field
setting_default_notification_option: Default notification option
notice_not_authorized_archived_project: The project you're trying to access has been archived.
label_user_mail_option_none: "Only for things I watch or I'm involved in"
field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role
field_start_date: Start date
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"

View File

@@ -247,6 +247,7 @@ el:
field_attr_lastname: Ιδιότητα επωνύμου
field_attr_mail: Ιδιότητα email
field_onthefly: Άμεση δημιουργία χρήστη
field_start_date: Εκκίνηση
field_done_ratio: % επιτεύχθη
field_auth_source: Τρόπος πιστοποίησης
field_hide_mail: Απόκρυψη διεύθυνσης email
@@ -917,7 +918,5 @@ el:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -313,6 +313,7 @@ es:
field_role: Perfil
field_searchable: Incluir en las búsquedas
field_spent_on: Fecha
field_start_date: Fecha de inicio
field_start_page: Página principal
field_status: Estado
field_subject: Tema
@@ -635,8 +636,8 @@ es:
label_user_activity: "Actividad de {{value}}"
label_user_mail_no_self_notified: "No quiero ser avisado de cambios hechos por mí"
label_user_mail_option_all: "Para cualquier evento en todos mis proyectos"
label_user_mail_option_none: "Sólo para elementos monitorizados o relacionados conmigo"
label_user_mail_option_selected: "Para cualquier evento de los proyectos seleccionados..."
label_user_mail_option_none: "Sólo para elementos monitorizados o relacionados conmigo"
label_user_new: Nuevo usuario
label_user_plural: Usuarios
label_version: Versión
@@ -951,13 +952,15 @@ es:
label_overall_spent_time: Tiempo total dedicado
field_time_entries: Log time
project_module_gantt: Gantt
project_module_calendar: Calendar
field_member_of_group: Member of Group
field_assigned_to_role: Member of Role
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
project_module_calendar: Calendario
button_edit_associated_wikipage: "Editar paginas Wiki asociadas: {{page_title}}"
text_are_you_sure_with_children: ¿Borrar peticiones y todas sus peticiones hijas?
field_text: Campo de texto
setting_default_notification_option: Opcion de notificacion por defecto
field_member_of_group: Asignado al grupo
field_assigned_to_role: Asignado al perfil
notice_not_authorized_archived_project: El proyecto al que intenta acceder ha sido archivado.
label_principal_search: "Buscar por usuario o grupo:"
label_user_search: "Buscar por usuario:"
field_visible: Visible
setting_emails_header: Encabezado de Correos

File diff suppressed because it is too large Load Diff

View File

@@ -262,6 +262,7 @@ fi:
field_attr_lastname: Sukunimenmääre
field_attr_mail: Sähköpostinmääre
field_onthefly: Automaattinen käyttäjien luonti
field_start_date: Alku
field_done_ratio: % Tehty
field_auth_source: Varmennusmuoto
field_hide_mail: Piiloita sähköpostiosoitteeni
@@ -942,6 +943,5 @@ fi:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -271,6 +271,7 @@ fr:
field_attr_lastname: Attribut Nom
field_attr_mail: Attribut Email
field_onthefly: Création des utilisateurs à la volée
field_start_date: Début
field_done_ratio: % réalisé
field_auth_source: Mode d'authentification
field_hide_mail: Cacher mon adresse mail
@@ -937,12 +938,6 @@ fr:
button_edit_associated_wikipage: "Modifier la page wiki associée: {{page_title}}"
text_are_you_sure_with_children: Supprimer la demande et toutes ses sous-demandes ?
field_text: Champ texte
label_user_mail_option_only_owner: Seulement pour ce que j'ai créé
setting_default_notification_option: Option de notification par défaut
label_user_mail_option_only_my_events: Seulement pour ce que je surveille
label_user_mail_option_only_assigned: Seulement pour ce qui m'est assigné
label_user_mail_option_none: Aucune notification
field_member_of_group: Groupe de l'assigné
field_assigned_to_role: Rôle de l'assigné
field_start_date: Start date

View File

@@ -290,6 +290,7 @@ gl:
field_role: Perfil
field_searchable: Incluír nas búsquedas
field_spent_on: Data
field_start_date: Data de inicio
field_start_page: Páxina principal
field_status: Estado
field_subject: Tema
@@ -933,7 +934,5 @@ gl:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -266,6 +266,7 @@ he:
field_attr_lastname: תכונת שם משפחה
field_attr_mail: תכונת דוא"ל
field_onthefly: יצירת משתמשים זריזה
field_start_date: תאריך התחלה
field_done_ratio: % גמור
field_auth_source: מקור הזדהות
field_hide_mail: החבא את כתובת הדוא"ל שלי
@@ -731,9 +732,6 @@ he:
label_user_mail_option_all: "לכל אירוע בכל הפרויקטים שלי"
label_user_mail_option_selected: "לכל אירוע בפרויקטים שבחרתי בלבד..."
label_user_mail_option_none: "רק לנושאים שאני צופה או קשור אליהם"
label_user_mail_option_only_my_events: עבור דברים שאני צופה או מעורב בהם בלבד
label_user_mail_option_only_assigned: עבור דברים שאני אחראי עליהם בלבד
label_user_mail_option_only_owner: עבור דברים שאני הבעלים שלהם בלבד
label_user_mail_no_self_notified: "אני לא רוצה שיודיעו לי על שינויים שאני מבצע"
label_registration_activation_by_email: הפעל חשבון באמצעות דוא"ל
label_registration_manual_activation: הפעלת חשבון ידנית
@@ -927,9 +925,7 @@ he:
enumeration_doc_categories: קטגוריות מסמכים
enumeration_activities: פעילויות (מעקב אחר זמנים)
enumeration_system_activity: פעילות מערכת
label_user_mail_option_none: No events
field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -253,6 +253,7 @@ hr:
field_attr_lastname: Atribut prezimena
field_attr_mail: Atribut e-pošte
field_onthefly: "Izrada korisnika \"u hodu\""
field_start_date: Pocetak
field_done_ratio: % Učinjeno
field_auth_source: Vrsta prijavljivanja
field_hide_mail: Sakrij moju adresu e-pošte
@@ -924,6 +925,5 @@ hr:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -260,6 +260,7 @@
field_attr_lastname: Vezetéknév
field_attr_mail: E-mail
field_onthefly: On-the-fly felhasználó létrehozás
field_start_date: Kezdés dátuma
field_done_ratio: Elkészült (%)
field_auth_source: Azonosítási mód
field_hide_mail: Rejtse el az e-mail címem
@@ -940,6 +941,5 @@
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -251,6 +251,7 @@ id:
field_attr_lastname: Atribut nama belakang
field_attr_mail: Atribut email
field_onthefly: Pembuatan pengguna seketika
field_start_date: Mulai
field_done_ratio: % Selesai
field_auth_source: Mode otentikasi
field_hide_mail: Sembunyikan email saya
@@ -925,6 +926,5 @@ id:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -232,6 +232,7 @@ it:
field_attr_lastname: Attributo cognome
field_attr_mail: Attributo email
field_onthefly: Creazione utente "al volo"
field_start_date: Inizio
field_done_ratio: % completato
field_auth_source: Modalità di autenticazione
field_hide_mail: Nascondi il mio indirizzo email
@@ -921,6 +922,5 @@ it:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -288,6 +288,7 @@ ja:
field_attr_lastname: 苗字属性
field_attr_mail: メール属性
field_onthefly: あわせてユーザを作成
field_start_date: 開始日
field_done_ratio: 進捗 %
field_auth_source: 認証方式
field_hide_mail: メールアドレスを隠す
@@ -322,6 +323,7 @@ ja:
field_member_of_group: 担当者のグループ
field_assigned_to_role: 担当者のロール
field_text: テキスト
field_visible: 表示
setting_app_title: アプリケーションのタイトル
setting_app_subtitle: アプリケーションのサブタイトル
@@ -351,6 +353,7 @@ ja:
setting_issue_list_default_columns: チケットの一覧で表示する項目
setting_repositories_encodings: リポジトリのエンコーディング
setting_commit_logs_encoding: コミットメッセージのエンコーディング
setting_emails_header: メールのヘッダ
setting_emails_footer: メールのフッタ
setting_protocol: プロトコル
setting_per_page_options: ページ毎の表示件数
@@ -754,9 +757,6 @@ ja:
label_user_mail_option_all: "参加しているプロジェクトの全ての通知"
label_user_mail_option_selected: "選択したプロジェクトの全ての通知..."
label_user_mail_option_none: "通知しない"
label_user_mail_option_only_my_events: "ウォッチまたは関係している事柄のみ"
label_user_mail_option_only_assigned: "自分が担当している事柄のみ"
label_user_mail_option_only_owner: "自分が作成した事柄のみ"
label_user_mail_no_self_notified: 自分自身による変更の通知は不要
label_registration_activation_by_email: メールでアカウントを有効化
label_registration_manual_activation: 手動でアカウントを有効化
@@ -806,6 +806,8 @@ ja:
label_api_access_key_created_on: "APIアクセスキーは{{value}}前に作成されました"
label_subtask_plural: 子チケット
label_project_copy_notifications: コピーしたチケットのメール通知を送信する
label_principal_search: "ユーザまたはグループの検索:"
label_user_search: "ユーザの検索:"
button_login: ログイン
button_submit: 変更
@@ -947,6 +949,5 @@ ja:
enumeration_doc_categories: 文書カテゴリ
enumeration_activities: 作業分類 (時間トラッキング)
enumeration_system_activity: システム作業分類
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -299,6 +299,7 @@ ko:
field_attr_lastname: 성 속성
field_attr_mail: 메일 속성
field_onthefly: 동적 사용자 생성
field_start_date: 시작시간
field_done_ratio: 진척도
field_auth_source: 인증 공급자
field_hide_mail: 메일 주소 숨기기
@@ -973,6 +974,5 @@ ko:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -310,6 +310,7 @@ lt:
field_attr_lastname: Pavardės priskiria
field_attr_mail: Elektroninio pašto požymis
field_onthefly: Automatinis vartotojų registravimas
field_start_date: Pradėti
field_done_ratio: % atlikta
field_auth_source: Autentiškumo nustatymo būdas
field_hide_mail: Paslėpkite mano elektroninio pašto adresą
@@ -981,6 +982,5 @@ lt:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@ nl:
field_role: Rol
field_searchable: Doorzoekbaar
field_spent_on: Datum
field_start_date: Startdatum
field_start_page: Startpagina
field_status: Status
field_subject: Onderwerp
@@ -899,6 +900,5 @@ nl:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -231,6 +231,7 @@
field_attr_lastname: Etternavnsattributt
field_attr_mail: E-post-attributt
field_onthefly: On-the-fly brukeropprettelse
field_start_date: Start
field_done_ratio: % Ferdig
field_auth_source: Autentifikasjonsmodus
field_hide_mail: Skjul min e-post-adresse
@@ -908,6 +909,5 @@
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -277,6 +277,7 @@ pl:
field_role: Rola
field_searchable: Przeszukiwalne
field_spent_on: Data
field_start_date: Start
field_start_page: Strona startowa
field_status: Status
field_subject: Temat
@@ -938,6 +939,5 @@ pl:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -264,6 +264,7 @@ pt-BR:
field_attr_lastname: Atributo para sobrenome
field_attr_mail: Atributo para e-mail
field_onthefly: Criar usuários dinamicamente ("on-the-fly")
field_start_date: Início
field_done_ratio: % Terminado
field_auth_source: Modo de autenticação
field_hide_mail: Ocultar meu e-mail
@@ -941,10 +942,6 @@ pt-BR:
button_edit_associated_wikipage: "Editar página wiki relacionada: {{page_title}}"
text_are_you_sure_with_children: Excluir a tarefa e suas subtarefas?
field_text: Campo de texto
label_user_mail_option_only_owner: Somente para as coisas que eu criei
setting_default_notification_option: Opção padrão de notificação
label_user_mail_option_only_my_events: Somente para as coisas que eu esteja observando ou esteja envolvido
label_user_mail_option_only_assigned: Somente para as coisas que estejam atribuídas a mim
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -249,6 +249,7 @@ pt:
field_attr_lastname: Atributo último nome
field_attr_mail: Atributo e-mail
field_onthefly: Criação de utilizadores na hora
field_start_date: Início
field_done_ratio: % Completo
field_auth_source: Modo de autenticação
field_hide_mail: Esconder endereço de e-mail
@@ -925,6 +926,5 @@ pt:
button_edit_associated_wikipage: "Editar página Wiki associada: {{page_title}}"
text_are_you_sure_with_children: Apagar tarefa e todas as sub-tarefas?
field_text: Text field
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -232,6 +232,7 @@ ro:
field_attr_lastname: Atribut nume
field_attr_mail: Atribut email
field_onthefly: Creare utilizator pe loc
field_start_date: Data începerii
field_done_ratio: Realizat (%)
field_auth_source: Mod autentificare
field_hide_mail: Nu se afișează adresa de email
@@ -910,6 +911,5 @@ ro:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -363,6 +363,7 @@ ru:
field_role: Роль
field_searchable: Доступно для поиска
field_spent_on: Дата
field_start_date: Начата
field_start_page: Стартовая страница
field_status: Статус
field_subject: Тема
@@ -381,9 +382,9 @@ ru:
field_version: Версия
field_watcher: Наблюдатель
general_csv_decimal_separator: '.'
general_csv_decimal_separator: ','
general_csv_encoding: UTF-8
general_csv_separator: ','
general_csv_separator: ';'
general_first_day_of_week: '1'
general_lang_name: 'Russian (Русский)'
general_pdf_encoding: UTF-8
@@ -702,9 +703,6 @@ ru:
label_user_mail_option_all: "О всех событиях во всех моих проектах"
label_user_mail_option_none: "Только о тех событиях, которые я отслеживаю или в которых я участвую"
label_user_mail_option_selected: "О всех событиях только в выбранном проекте..."
label_user_mail_option_only_owner: Только для объектов, для которых я являюсь владельцем
label_user_mail_option_only_my_events: Только для объектов, которые я отслеживаю или в которых участвую
label_user_mail_option_only_assigned: Только для объектов, которые назначены мне
label_user_new: Новый пользователь
label_user_plural: Пользователи
label_version: Версия
@@ -1035,11 +1033,9 @@ ru:
text_zoom_in: Приблизить
notice_unable_delete_time_entry: Невозможно удалить запись журнала.
label_overall_spent_time: Всего затрачено времени
label_user_mail_option_none: Нет событий
field_member_of_group: Группа назначенного
field_assigned_to_role: Роль назначенного
notice_not_authorized_archived_project: Запрашиваемый проект был архивирован.
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
field_start_date: Start Date

View File

@@ -231,6 +231,7 @@ sk:
field_attr_lastname: Priezvisko (atribut)
field_attr_mail: Email (atribut)
field_onthefly: Automatické vytváranie užívateľov
field_start_date: Začiatok
field_done_ratio: % hotovo
field_auth_source: Autentifikačný mód
field_hide_mail: Nezobrazovať môj email
@@ -912,6 +913,5 @@ sk:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -236,6 +236,7 @@ sl:
field_attr_lastname: Oznaka za priimek
field_attr_mail: Oznaka za e-naslov
field_onthefly: Sprotna izdelava uporabnikov
field_start_date: Začetek
field_done_ratio: % Narejeno
field_auth_source: Način overovljanja
field_hide_mail: Skrij moj e-naslov
@@ -913,6 +914,5 @@ sl:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -261,6 +261,7 @@ sr-YU:
field_attr_lastname: Atribut prezimena
field_attr_mail: Atribut e-adrese
field_onthefly: Kreiranje korisnika u toku rada
field_start_date: Početak
field_done_ratio: % urađeno
field_auth_source: Režim potvrde identiteta
field_hide_mail: Sakrij moju e-adresu
@@ -917,6 +918,5 @@ sr-YU:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -261,6 +261,7 @@ sr:
field_attr_lastname: Атрибут презимена
field_attr_mail: Атрибут е-адресе
field_onthefly: Креирање корисника у току рада
field_start_date: Почетак
field_done_ratio: % урађено
field_auth_source: Режим потврде идентитета
field_hide_mail: Сакриј моју е-адресу
@@ -918,6 +919,5 @@ sr:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -308,6 +308,7 @@ sv:
field_attr_lastname: Efternamnsattribut
field_attr_mail: Mailattribut
field_onthefly: Skapa användare on-the-fly
field_start_date: Startdatum
field_done_ratio: % Klart
field_auth_source: Autentiseringsläge
field_hide_mail: Dölj min mailadress
@@ -823,6 +824,8 @@ sv:
label_profile: Profil
label_subtask_plural: Underaktiviteter
label_project_copy_notifications: Skicka mailnotifieringar när projektet kopieras
label_principal_search: "Sök efter användare eller grupp:"
label_user_search: "Sök efter användare:"
button_login: Logga in
button_submit: Skicka
@@ -891,9 +894,9 @@ sv:
text_journal_set_to: "{{label}} satt till {{value}}"
text_journal_deleted: "{{label}} borttagen ({{old}})"
text_journal_added: "{{label}} {{value}} tillagd"
text_tip_issue_begin_day: arbetsuppgift som börjar denna dag
text_tip_issue_end_day: arbetsuppgift som slutar denna dag
text_tip_issue_begin_end_day: arbetsuppgift börjar och slutar denna dag
text_tip_issue_begin_day: ärende som börjar denna dag
text_tip_issue_end_day: ärende som slutar denna dag
text_tip_issue_begin_end_day: ärende som börjar och slutar denna dag
text_project_identifier_info: 'Små bokstäver (a-z), siffror och streck tillåtna.<br />När den är sparad kan identifieraren inte ändras.'
text_caracters_maximum: "max {{count}} tecken."
text_caracters_minimum: "Måste vara minst {{count}} tecken lång."
@@ -969,6 +972,5 @@ sv:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -233,6 +233,7 @@ th:
field_attr_lastname: นามสกุล attribute
field_attr_mail: อีเมล์ attribute
field_onthefly: สร้างผู้ใช้ทันที
field_start_date: เริ่ม
field_done_ratio: % สำเร็จ
field_auth_source: วิธีการยืนยันตัวตน
field_hide_mail: ซ่อนอีเมล์ของฉัน
@@ -914,6 +915,5 @@ th:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -260,6 +260,7 @@ tr:
field_attr_lastname: Soyad Niteliği
field_attr_mail: E-Posta Niteliği
field_onthefly: Anında kullanıcı oluşturma
field_start_date: Başlangıç
field_done_ratio: % tamamlandı
field_auth_source: Kimlik Denetim Modu
field_hide_mail: E-posta adresimi gizle
@@ -940,6 +941,5 @@ tr:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -227,6 +227,7 @@ uk:
field_attr_lastname: Атрибут Прізвище
field_attr_mail: Атрибут Email
field_onthefly: Створення користувача на льоту
field_start_date: Початок
field_done_ratio: % зроблено
field_auth_source: Режим аутентифікації
field_hide_mail: Приховувати мій email
@@ -913,6 +914,5 @@ uk:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -294,6 +294,7 @@ vi:
field_attr_lastname: Lastname attribute
field_attr_mail: Email attribute
field_onthefly: On-the-fly user creation
field_start_date: Bắt đầu
field_done_ratio: Tiến độ
field_auth_source: Authentication mode
field_hide_mail: Không làm lộ email của bạn
@@ -972,6 +973,5 @@ vi:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -177,7 +177,7 @@
greater_than_start_date: "必須在開始日期之後"
not_same_project: "不屬於同一個專案"
circular_dependency: "這個關聯會導致環狀相依"
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
cant_link_an_issue_with_a_descendant: "項目無法被連結至自己的子項目"
# You can define own errors for models or model attributes.
# The values :model, :attribute and :value are always available for interpolation.
@@ -383,6 +383,7 @@
field_member_of_group: "被指派者的群組"
field_assigned_to_role: "被指派者的角色"
field_text: 內容文字
field_visible: 可被看見
setting_app_title: 標題
setting_app_subtitle: 副標題
@@ -411,6 +412,7 @@
setting_issue_list_default_columns: 預設顯示於項目清單的欄位
setting_repositories_encodings: 版本庫編碼
setting_commit_logs_encoding: 送交訊息編碼
setting_emails_header: 電子郵件前頭說明
setting_emails_footer: 電子郵件附帶說明
setting_protocol: 協定
setting_per_page_options: 每頁顯示個數選項
@@ -813,10 +815,7 @@
label_search_titles_only: 僅搜尋標題
label_user_mail_option_all: "提醒與我的專案有關的全部事件"
label_user_mail_option_selected: "只提醒我所選擇專案中的事件..."
label_user_mail_option_none: "只提醒我觀察中或參與中的事件"
label_user_mail_option_only_my_events: "只提醒我觀察中或參與中的事物"
label_user_mail_option_only_assigned: "只提醒我被指派的事物"
label_user_mail_option_only_owner: "只提醒我作為擁有者的事物"
label_user_mail_option_none: "取消提醒"
label_user_mail_no_self_notified: "不提醒我自己所做的變更"
label_registration_activation_by_email: 透過電子郵件啟用帳戶
label_registration_manual_activation: 手動啟用帳戶
@@ -867,6 +866,8 @@
label_profile: 配置概況
label_subtask_plural: 子工作項目
label_project_copy_notifications: 在複製專案的過程中,傳送通知郵件
label_principal_search: "搜尋用戶或群組:"
label_user_search: "搜尋用戶:"
button_login: 登入
button_submit: 送出
@@ -1009,5 +1010,3 @@
enumeration_activities: 活動 (時間追蹤)
enumeration_system_activity: 系統活動
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -272,6 +272,7 @@ zh:
field_attr_lastname: 姓氏属性
field_attr_mail: 邮件属性
field_onthefly: 即时用户生成
field_start_date: 开始
field_done_ratio: 完成度
field_auth_source: 认证模式
field_hide_mail: 隐藏我的邮件地址
@@ -935,6 +936,5 @@ zh:
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
field_start_date: Start date
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"

View File

@@ -4,6 +4,45 @@ Redmine - project management software
Copyright (C) 2006-2010 Jean-Philippe Lang
http://www.redmine.org/
== 2010-12-23 v1.0.5
* #6656: Mercurial adapter loses seconds of commit times
* #6996: Migration trac(sqlite3) -> redmine(postgresql) doesnt escape ' char
* #7013: v-1.0.4 trunk - see {{count}} in page display rather than value
* #7016: redundant 'field_start_date' in ja.yml
* #7018: 'undefined method `reschedule_after' for nil:NilClass' on new issues
* #7024: E-mail notifications about Wiki changes.
* #7033: 'class' attribute of <pre> tag shouldn't be truncate
* #7035: CSV value separator in russian
* #7122: Issue-description Quote-button missing
* #7144: custom queries making use of deleted custom fields cause a 500 error
* #7162: Multiply defined label in french translation
== 2010-11-28 v1.0.4
* #5324: Git not working if color.ui is enabled
* #6447: Issues API doesn't allow full key auth for all actions
* #6457: Edit User group problem
* #6575: start date being filled with current date even when blank value is submitted
* #6740: Max attachment size, incorrect usage of 'KB'
* #6760: Select box sorted by ID instead of name in Issue Category
* #6766: Changing target version name can cause an internal error
* #6784: Redmine not working with i18n gem 0.4.2
* #6839: Hardcoded absolute links in my/page_layout
* #6841: Projects API doesn't allow full key auth for all actions
* #6860: svn: Write error: Broken pipe when browsing repository
* #6874: API should return XML description when creating a project
* #6932: submitting wrong parent task input creates a 500 error
* #6966: Records of Forums are remained, deleting project
* #6990: Layout problem in workflow overview
* #5117: mercurial_adapter should ensure the right LANG environment variable
* #6782: Traditional Chinese language file (to r4352)
* #6783: Swedish Translation for r4352
* #6804: Bugfix: spelling fixes
* #6814: Japanese Translation for r4362
* #6948: Bulgarian translation
* #6973: Update es.yml
== 2010-10-31 v1.0.3
* #4065: Redmine.pm doesn't work with LDAPS and a non-standard port
@@ -1112,7 +1151,7 @@ http://www.redmine.org/
* Search engines now supports pagination. Results are sorted in reverse chronological order
* Added "Estimated hours" attribute on issues
* A category with assigned issue can now be deleted. 2 options are proposed: remove assignments or reassign issues to another category
* Forum notifications are now also sent to the authors of the thread, even if they dont watch the board
* Forum notifications are now also sent to the authors of the thread, even if they don<EFBFBD>t watch the board
* Added an application setting to specify the application protocol (http or https) used to generate urls in emails
* Gantt chart: now starts at the current month by default
* Gantt chart: month count and zoom factor are automatically saved as user preferences
@@ -1120,7 +1159,7 @@ http://www.redmine.org/
* Added wiki index by date
* Added preview on add/edit issue form
* Emails footer can now be customized from the admin interface (Admin -> Email notifications)
* Default encodings for repository files can now be set in application settings (used to convert files content and diff to UTF-8 so that theyre properly displayed)
* Default encodings for repository files can now be set in application settings (used to convert files content and diff to UTF-8 so that they<EFBFBD>re properly displayed)
* Calendar: first day of week can now be set in lang files
* Automatic closing of duplicate issues
* Added a cross-project issue list
@@ -1132,7 +1171,7 @@ http://www.redmine.org/
* Added some accesskeys
* Added "Float" as a custom field format
* Added basic Theme support
* Added the ability to set the done ratio of issues fixed by commit (Nikolay Solakov)
* Added the ability to set the <EFBFBD>done ratio<EFBFBD> of issues fixed by commit (Nikolay Solakov)
* Added custom fields in issue related mail notifications
* Email notifications are now sent in plain text and html
* Gantt chart can now be exported to a graphic file (png). This functionality is only available if RMagick is installed.
@@ -1165,7 +1204,7 @@ http://www.redmine.org/
* Added Korean translation (Choi Jong Yoon)
* Fixed: the link to delete issue relations is displayed even if the user is not authorized to delete relations
* Performance improvement on calendar and gantt
* Fixed: wiki preview doesnt work on long entries
* Fixed: wiki preview doesn<EFBFBD>t work on long entries
* Fixed: queries with multiple custom fields return no result
* Fixed: Can not authenticate user against LDAP if its DN contains non-ascii characters
* Fixed: URL with ~ broken in wiki formatting
@@ -1176,7 +1215,7 @@ http://www.redmine.org/
* per project forums added
* added the ability to archive projects
* added Watch functionality on issues. It allows users to receive notifications about issue changes
* added <EFBFBD>Watch<EFBFBD> functionality on issues. It allows users to receive notifications about issue changes
* custom fields for issues can now be used as filters on issue list
* added per user custom queries
* commit messages are now scanned for referenced or fixed issue IDs (keywords defined in Admin -> Settings)
@@ -1217,7 +1256,7 @@ http://www.redmine.org/
* added swedish translation (Thomas Habets)
* italian translation update (Alessio Spadaro)
* japanese translation update (Satoru Kurashiki)
* fixed: error on history atom feed when theres no notes on an issue change
* fixed: error on history atom feed when there<EFBFBD>s no notes on an issue change
* fixed: error in journalizing an issue with longtext custom fields (Postgresql)
* fixed: creation of Oracle schema
* fixed: last day of the month not included in project activity

View File

@@ -9,14 +9,16 @@ http://www.redmine.org/
* Ruby 1.8.6 or 1.8.7
* RubyGems 1.3.1
* Ruby on Rails 2.3.5 (official downloadable Redmine releases are packaged with
the appropriate Rails version)
* Rack 1.0.1
* Rack 1.0.1 gem
* RubyGems 1.3.1
* Rake 0.8.3 gem
* Rake 0.8.3
* I18n 0.4.2 gem
* A database:
* MySQL (tested with MySQL 5)

View File

@@ -1078,7 +1078,7 @@ class RedCloth3 < String
line = "<redpre##{ @pre_list.length }>"
first.match(/<#{ OFFTAGS }([^>]*)>/)
tag = $1
$2.to_s.match(/(class\=\S+)/i)
$2.to_s.match(/(class\=("[^"]+"|'[^']+'))/i)
tag << " #{$1}" if $1
@pre_list << "<#{ tag }>#{ aftertag }"
end

View File

@@ -37,7 +37,7 @@ module Redmine
def format_date(date)
return nil unless date
Setting.date_format.blank? ? ::I18n.l(date.to_date, :count => date.strftime('%d')) : date.strftime(Setting.date_format)
Setting.date_format.blank? ? ::I18n.l(date.to_date) : date.strftime(Setting.date_format)
end
def format_time(time, include_date = true)
@@ -45,7 +45,7 @@ module Redmine
time = time.to_time if time.is_a?(String)
zone = User.current.time_zone
local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
Setting.time_format.blank? ? ::I18n.l(local, :count => local.strftime('%d'), :format => (include_date ? :default : :time)) :
Setting.time_format.blank? ? ::I18n.l(local, :format => (include_date ? :default : :time)) :
((include_date ? "#{format_date(time)} " : "") + "#{local.strftime(Setting.time_format)}")
end

View File

@@ -74,10 +74,10 @@ module Redmine
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= ''
identifier_from = 'last:1' unless identifier_from and identifier_from.to_i > 0
identifier_to = 1 unless identifier_to and identifier_to.to_i > 0
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1'
identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
revisions = Revisions.new
cmd = "#{BZR_BIN} log -v --show-ids -r#{identifier_to.to_i}..#{identifier_from} #{target(path)}"
cmd = "#{BZR_BIN} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
shellout(cmd) do |io|
revision = nil
parsing = nil
@@ -140,6 +140,9 @@ module Redmine
else
identifier_to = identifier_from.to_i - 1
end
if identifier_from
identifier_from = identifier_from.to_i
end
cmd = "#{BZR_BIN} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
diff = []
shellout(cmd) do |io|

View File

@@ -63,7 +63,7 @@ module Redmine
logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
path_with_project="#{url}#{with_leading_slash(path)}"
entries = Entries.new
cmd = "#{CVS_BIN} -d #{root_url} rls -e"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rls -e"
cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
cmd << " #{shell_quote path_with_project}"
shellout(cmd) do |io|
@@ -108,7 +108,7 @@ module Redmine
logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{root_url} rlog"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rlog"
cmd << " -d\">#{time_to_cvstime(identifier_from)}\"" if identifier_from
cmd << " #{shell_quote path_with_project}"
shellout(cmd) do |io|
@@ -229,7 +229,7 @@ module Redmine
def diff(path, identifier_from, identifier_to=nil)
logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rdiff -u -r#{identifier_to.to_i} -r#{identifier_from.to_i} #{shell_quote path_with_project}"
diff = []
shellout(cmd) do |io|
io.each_line do |line|
@@ -244,7 +244,7 @@ module Redmine
identifier = (identifier) ? identifier : "HEAD"
logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{root_url} co"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} co"
cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
cmd << " -p #{shell_quote path_with_project}"
cat = nil
@@ -256,10 +256,10 @@ module Redmine
end
def annotate(path, identifier=nil)
identifier = (identifier) ? identifier : "HEAD"
identifier = (identifier) ? identifier.to_i : "HEAD"
logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
blame = Annotate.new
shellout(cmd) do |io|
io.each_line do |line|

View File

@@ -66,7 +66,7 @@ module Redmine
path_prefix = (path.blank? ? '' : "#{path}/")
path = '.' if path.blank?
entries = Entries.new
cmd = "#{DARCS_BIN} annotate --repodir #{@url} --xml-output"
cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --xml-output"
cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
cmd << " #{shell_quote path}"
shellout(cmd) do |io|
@@ -90,7 +90,7 @@ module Redmine
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path = '.' if path.blank?
revisions = Revisions.new
cmd = "#{DARCS_BIN} changes --repodir #{@url} --xml-output"
cmd = "#{DARCS_BIN} changes --repodir #{shell_quote @url} --xml-output"
cmd << " --from-match #{shell_quote("hash #{identifier_from}")}" if identifier_from
cmd << " --last #{options[:limit].to_i}" if options[:limit]
shellout(cmd) do |io|
@@ -116,7 +116,7 @@ module Redmine
def diff(path, identifier_from, identifier_to=nil)
path = '*' if path.blank?
cmd = "#{DARCS_BIN} diff --repodir #{@url}"
cmd = "#{DARCS_BIN} diff --repodir #{shell_quote @url}"
if identifier_to.nil?
cmd << " --match #{shell_quote("hash #{identifier_from}")}"
else
@@ -135,7 +135,7 @@ module Redmine
end
def cat(path, identifier=nil)
cmd = "#{DARCS_BIN} show content --repodir #{@url}"
cmd = "#{DARCS_BIN} show content --repodir #{shell_quote @url}"
cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
cmd << " #{shell_quote path}"
cat = nil
@@ -170,7 +170,7 @@ module Redmine
# Retrieve changed paths for a single patch
def get_paths_for_patch(hash)
cmd = "#{DARCS_BIN} annotate --repodir #{@url} --summary --xml-output"
cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --summary --xml-output"
cmd << " --match #{shell_quote("hash #{hash}")} "
paths = []
shellout(cmd) do |io|

View File

@@ -117,7 +117,7 @@ module Redmine
cmd = "#{GIT_BIN} --git-dir #{target('')} log --no-color --raw --date=iso --pretty=fuller "
cmd << " --reverse " if options[:reverse]
cmd << " --all " if options[:all]
cmd << " -n #{options[:limit]} " if options[:limit]
cmd << " -n #{options[:limit].to_i} " if options[:limit]
cmd << "#{shell_quote(identifier_from + '..')}" if identifier_from
cmd << "#{shell_quote identifier_to}" if identifier_to
cmd << " --since=#{shell_quote(options[:since].strftime("%Y-%m-%d %H:%M:%S"))}" if options[:since]
@@ -211,7 +211,7 @@ module Redmine
if identifier_to
cmd = "#{GIT_BIN} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
else
cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote identifier_from}"
cmd = "#{GIT_BIN} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
end
cmd << " -- #{shell_quote path}" unless path.empty?
@@ -255,7 +255,7 @@ module Redmine
if identifier.nil?
identifier = 'HEAD'
end
cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote(identifier + ':' + path)}"
cmd = "#{GIT_BIN} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
cat = nil
shellout(cmd) do |io|
io.binmode

View File

@@ -1,7 +1,7 @@
changeset = 'This template must be used with --debug option\n'
changeset_quiet = 'This template must be used with --debug option\n'
changeset_verbose = 'This template must be used with --debug option\n'
changeset_debug = '<logentry revision="{rev}" node="{node|short}">\n<author>{author|escape}</author>\n<date>{date|isodate}</date>\n<paths>\n{file_mods}{file_adds}{file_dels}{file_copies}</paths>\n<msg>{desc|escape}</msg>\n{tags}</logentry>\n\n'
changeset_debug = '<logentry revision="{rev}" node="{node|short}">\n<author>{author|escape}</author>\n<date>{date|isodatesec}</date>\n<paths>\n{file_mods}{file_adds}{file_dels}{file_copies}</paths>\n<msg>{desc|escape}</msg>\n{tags}</logentry>\n\n'
file_mod = '<path action="M">{file_mod|escape}</path>\n'
file_add = '<path action="A">{file_add|escape}</path>\n'

View File

@@ -38,13 +38,13 @@ module Redmine
# release number (eg 0.9.5 or 1.0) or as a revision
# id composed of 12 hexa characters.
theversion = hgversion_from_command_line
if theversion.match(/^\d+(\.\d+)+/)
theversion.split(".").collect(&:to_i)
if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
m[2].scan(%r{\d+}).collect(&:to_i)
end
end
def hgversion_from_command_line
%x{#{HG_BIN} --version}.match(/\(version (.*)\)/)[1]
shellout("#{HG_BIN} --version") { |io| io.read }.to_s
end
def template_path
@@ -80,7 +80,7 @@ module Redmine
path ||= ''
entries = Entries.new
cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate"
cmd << " -r " + (identifier ? identifier.to_s : "tip")
cmd << " -r " + shell_quote(identifier ? identifier.to_s : "tip")
cmd << " " + shell_quote("path:#{path}") unless path.empty?
shellout(cmd) do |io|
io.each_line do |line|
@@ -112,7 +112,7 @@ module Redmine
cmd << " -r #{identifier_from.to_i}:"
end
cmd << " --limit #{options[:limit].to_i}" if options[:limit]
cmd << " #{path}" if path
cmd << " #{shell_quote path}" unless path.blank?
shellout(cmd) do |io|
begin
# HG doesn't close the XML Document...
@@ -157,6 +157,9 @@ module Redmine
else
identifier_to = identifier_from.to_i - 1
end
if identifier_from
identifier_from = identifier_from.to_i
end
cmd = "#{HG_BIN} -R #{target('')} diff -r #{identifier_to} -r #{identifier_from} --nodates"
cmd << " -I #{target(path)}" unless path.empty?
diff = []
@@ -171,7 +174,7 @@ module Redmine
def cat(path, identifier=nil)
cmd = "#{HG_BIN} -R #{target('')} cat"
cmd << " -r " + (identifier ? identifier.to_s : "tip")
cmd << " -r " + shell_quote(identifier ? identifier.to_s : "tip")
cmd << " #{target(path)}"
cat = nil
shellout(cmd) do |io|
@@ -186,7 +189,7 @@ module Redmine
path ||= ''
cmd = "#{HG_BIN} -R #{target('')}"
cmd << " annotate -n -u"
cmd << " -r " + (identifier ? identifier.to_s : "tip")
cmd << " -r " + shell_quote(identifier ? identifier.to_s : "tip")
cmd << " -r #{identifier.to_i}" if identifier
cmd << " #{target(path)}"
blame = Annotate.new

View File

@@ -36,8 +36,8 @@ module Redmine
version = nil
shellout(cmd) do |io|
# Read svn version in first returned line
if m = io.gets.to_s.match(%r{((\d+\.)+\d+)})
version = m[0].scan(%r{\d+}).collect(&:to_i)
if m = io.read.to_s.match(%r{\A(.*?)((\d+\.)+\d+)})
version = m[2].scan(%r{\d+}).collect(&:to_i)
end
end
return nil if $? && $?.exitstatus != 0
@@ -135,8 +135,8 @@ module Redmine
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= ''
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
identifier_from = (identifier_from && identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
identifier_to = (identifier_to && identifier_to.to_i > 0) ? identifier_to.to_i : 1
revisions = Revisions.new
cmd = "#{SVN_BIN} log --xml -r #{identifier_from}:#{identifier_to}"
cmd << credentials_string

View File

@@ -4,7 +4,7 @@ module Redmine
module VERSION #:nodoc:
MAJOR = 1
MINOR = 0
TINY = 3
TINY = 5
# Branch values:
# * official release: nil

View File

@@ -167,7 +167,7 @@ namespace :redmine do
has_many :attachments, :class_name => "TracAttachment",
:finder_sql => "SELECT DISTINCT attachment.* FROM #{TracMigrate::TracAttachment.table_name}" +
" WHERE #{TracMigrate::TracAttachment.table_name}.type = 'ticket'" +
' AND #{TracMigrate::TracAttachment.table_name}.id = \'#{id}\''
' AND #{TracMigrate::TracAttachment.table_name}.id = \'#{TracMigrate::TracAttachment.connection.quote_string(id.to_s)}\''
has_many :customs, :class_name => "TracTicketCustom", :foreign_key => :ticket
def ticket_type
@@ -207,7 +207,7 @@ namespace :redmine do
has_many :attachments, :class_name => "TracAttachment",
:finder_sql => "SELECT DISTINCT attachment.* FROM #{TracMigrate::TracAttachment.table_name}" +
" WHERE #{TracMigrate::TracAttachment.table_name}.type = 'wiki'" +
' AND #{TracMigrate::TracAttachment.table_name}.id = \'#{id}\''
' AND #{TracMigrate::TracAttachment.table_name}.id = \'#{TracMigrate::TracAttachment.connection.quote_string(id.to_s)}\''
def self.columns
# Hides readonly Trac field to prevent clash with AR readonly? method (Rails 2.0)

View File

@@ -145,3 +145,15 @@ attachments_012:
filename: version_file.zip
author_id: 2
content_type: application/octet-stream
attachments_013:
created_on: 2006-07-19 21:07:27 +02:00
container_type: Message
container_id: 1
downloads: 0
disk_filename: 060719210727_foo.zip
digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
id: 13
filesize: 452
filename: foo.zip
author_id: 2
content_type: application/octet-stream

View File

@@ -250,6 +250,9 @@ class IssuesControllerTest < ActionController::TestCase
get :show, :id => 1
assert_response :success
assert_tag :tag => 'a',
:content => /Quote/
assert_tag :tag => 'form',
:descendant => { :tag => 'fieldset',
:child => { :tag => 'legend',
@@ -382,6 +385,7 @@ class IssuesControllerTest < ActionController::TestCase
:subject => 'This is the test_new issue',
:description => 'This is the description',
:priority_id => 5,
:start_date => '2010-11-07',
:estimated_hours => '',
:custom_field_values => {'2' => 'Value for field 2'}}
end
@@ -392,12 +396,33 @@ class IssuesControllerTest < ActionController::TestCase
assert_equal 2, issue.author_id
assert_equal 3, issue.tracker_id
assert_equal 2, issue.status_id
assert_equal Date.parse('2010-11-07'), issue.start_date
assert_nil issue.estimated_hours
v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
assert_not_nil v
assert_equal 'Value for field 2', v.value
end
def test_post_create_without_start_date
@request.session[:user_id] = 2
assert_difference 'Issue.count' do
post :create, :project_id => 1,
:issue => {:tracker_id => 3,
:status_id => 2,
:subject => 'This is the test_new issue',
:description => 'This is the description',
:priority_id => 5,
:start_date => '',
:estimated_hours => '',
:custom_field_values => {'2' => 'Value for field 2'}}
end
assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
issue = Issue.find_by_subject('This is the test_new issue')
assert_not_nil issue
assert_nil issue.start_date
end
def test_post_create_and_continue
@request.session[:user_id] = 2
post :create, :project_id => 1,
@@ -476,6 +501,20 @@ class IssuesControllerTest < ActionController::TestCase
assert_not_nil issue
assert_equal Issue.find(2), issue.parent
end
def test_post_create_subissue_with_non_numeric_parent_id
@request.session[:user_id] = 2
assert_difference 'Issue.count' do
post :create, :project_id => 1,
:issue => {:tracker_id => 1,
:subject => 'This is a child issue',
:parent_issue_id => 'ABC'}
end
issue = Issue.find_by_subject('This is a child issue')
assert_not_nil issue
assert_nil issue.parent
end
def test_post_create_should_send_a_notification
ActionMailer::Base.deliveries.clear

View File

@@ -40,14 +40,20 @@ class JournalsControllerTest < ActionController::TestCase
def test_reply_to_issue
@request.session[:user_id] = 2
get :new, :id => 1
get :new, :id => 6
assert_response :success
assert_select_rjs :show, "update"
end
def test_reply_to_issue_without_permission
@request.session[:user_id] = 7
get :new, :id => 6
assert_response 403
end
def test_reply_to_note
@request.session[:user_id] = 2
get :new, :id => 1, :journal_id => 2
get :new, :id => 6, :journal_id => 4
assert_response :success
assert_select_rjs :show, "update"
end

View File

@@ -123,6 +123,15 @@ class VersionsControllerTest < ActionController::TestCase
assert_equal 'New version name', version.name
assert_equal Date.today, version.effective_date
end
def test_post_update_with_validation_failure
@request.session[:user_id] = 2
post :update, :id => 2,
:version => { :name => '',
:effective_date => Date.today.strftime("%Y-%m-%d")}
assert_response :success
assert_template 'edit'
end
def test_destroy
@request.session[:user_id] = 2

View File

@@ -1,6 +1,6 @@
require "#{File.dirname(__FILE__)}/../test_helper"
require "#{File.dirname(__FILE__)}/../../test_helper"
class DisabledRestApi < ActionController::IntegrationTest
class ApiTest::DisabledRestApiTest < ActionController::IntegrationTest
fixtures :all
def setup

View File

@@ -0,0 +1,31 @@
require "#{File.dirname(__FILE__)}/../../test_helper"
class ApiTest::HttpBasicLoginTest < ActionController::IntegrationTest
fixtures :all
def setup
Setting.rest_api_enabled = '1'
Setting.login_required = '1'
end
def teardown
Setting.rest_api_enabled = '0'
Setting.login_required = '0'
end
# Using the NewsController because it's a simple API.
context "get /news" do
setup do
project = Project.find('onlinestore')
EnabledModule.create(:project => project, :name => 'news')
end
context "in :xml format" do
should_allow_http_basic_auth_with_username_and_password(:get, "/projects/onlinestore/news.xml")
end
context "in :json format" do
should_allow_http_basic_auth_with_username_and_password(:get, "/projects/onlinestore/news.json")
end
end
end

View File

@@ -0,0 +1,27 @@
require "#{File.dirname(__FILE__)}/../../test_helper"
class ApiTest::HttpBasicLoginWithApiTokenTest < ActionController::IntegrationTest
fixtures :all
def setup
Setting.rest_api_enabled = '1'
Setting.login_required = '1'
end
def teardown
Setting.rest_api_enabled = '0'
Setting.login_required = '0'
end
# Using the NewsController because it's a simple API.
context "get /news" do
context "in :xml format" do
should_allow_http_basic_auth_with_key(:get, "/news.xml")
end
context "in :json format" do
should_allow_http_basic_auth_with_key(:get, "/news.json")
end
end
end

View File

@@ -0,0 +1,336 @@
# 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require "#{File.dirname(__FILE__)}/../../test_helper"
class ApiTest::IssuesTest < ActionController::IntegrationTest
fixtures :projects,
:users,
:roles,
:members,
:member_roles,
:issues,
:issue_statuses,
:versions,
:trackers,
:projects_trackers,
:issue_categories,
:enabled_modules,
:enumerations,
:attachments,
:workflows,
:custom_fields,
:custom_values,
:custom_fields_projects,
:custom_fields_trackers,
:time_entries,
:journals,
:journal_details,
:queries
def setup
Setting.rest_api_enabled = '1'
end
# Use a private project to make sure auth is really working and not just
# only showing public issues.
context "/index.xml" do
should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
end
context "/index.json" do
should_allow_api_authentication(:get, "/projects/private-child/issues.json")
end
context "/index.xml with filter" do
should_allow_api_authentication(:get, "/projects/private-child/issues.xml?status_id=5")
should "show only issues with the status_id" do
get '/issues.xml?status_id=5'
assert_tag :tag => 'issues',
:children => { :count => Issue.visible.count(:conditions => {:status_id => 5}),
:only => { :tag => 'issue' } }
end
end
context "/index.json with filter" do
should_allow_api_authentication(:get, "/projects/private-child/issues.json?status_id=5")
should "show only issues with the status_id" do
get '/issues.json?status_id=5'
json = ActiveSupport::JSON.decode(response.body)
status_ids_used = json.collect {|j| j['status_id'] }
assert_equal 3, status_ids_used.length
assert status_ids_used.all? {|id| id == 5 }
end
end
# Issue 6 is on a private project
context "/issues/6.xml" do
should_allow_api_authentication(:get, "/issues/6.xml")
end
context "/issues/6.json" do
should_allow_api_authentication(:get, "/issues/6.json")
end
context "POST /issues.xml" do
should_allow_api_authentication(:post,
'/issues.xml',
{:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
{:success_code => :created})
should "create an issue with the attributes" do
assert_difference('Issue.count') do
post '/issues.xml', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, :authorization => credentials('jsmith')
end
issue = Issue.first(:order => 'id DESC')
assert_equal 1, issue.project_id
assert_equal 2, issue.tracker_id
assert_equal 3, issue.status_id
assert_equal 'API test', issue.subject
end
end
context "POST /issues.xml with failure" do
should_allow_api_authentication(:post,
'/issues.xml',
{:issue => {:project_id => 1}},
{:success_code => :unprocessable_entity})
should "have an errors tag" do
assert_no_difference('Issue.count') do
post '/issues.xml', {:issue => {:project_id => 1}}, :authorization => credentials('jsmith')
end
assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
end
end
context "POST /issues.json" do
should_allow_api_authentication(:post,
'/issues.json',
{:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
{:success_code => :created})
should "create an issue with the attributes" do
assert_difference('Issue.count') do
post '/issues.json', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, :authorization => credentials('jsmith')
end
issue = Issue.first(:order => 'id DESC')
assert_equal 1, issue.project_id
assert_equal 2, issue.tracker_id
assert_equal 3, issue.status_id
assert_equal 'API test', issue.subject
end
end
context "POST /issues.json with failure" do
should_allow_api_authentication(:post,
'/issues.json',
{:issue => {:project_id => 1}},
{:success_code => :unprocessable_entity})
should "have an errors element" do
assert_no_difference('Issue.count') do
post '/issues.json', {:issue => {:project_id => 1}}, :authorization => credentials('jsmith')
end
json = ActiveSupport::JSON.decode(response.body)
assert_equal "can't be blank", json.first['subject']
end
end
# Issue 6 is on a private project
context "PUT /issues/6.xml" do
setup do
@parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
@headers = { :authorization => credentials('jsmith') }
end
should_allow_api_authentication(:put,
'/issues/6.xml',
{:issue => {:subject => 'API update', :notes => 'A new note'}},
{:success_code => :ok})
should "not create a new issue" do
assert_no_difference('Issue.count') do
put '/issues/6.xml', @parameters, @headers
end
end
should "create a new journal" do
assert_difference('Journal.count') do
put '/issues/6.xml', @parameters, @headers
end
end
should "add the note to the journal" do
put '/issues/6.xml', @parameters, @headers
journal = Journal.last
assert_equal "A new note", journal.notes
end
should "update the issue" do
put '/issues/6.xml', @parameters, @headers
issue = Issue.find(6)
assert_equal "API update", issue.subject
end
end
context "PUT /issues/6.xml with failed update" do
setup do
@parameters = {:issue => {:subject => ''}}
@headers = { :authorization => credentials('jsmith') }
end
should_allow_api_authentication(:put,
'/issues/6.xml',
{:issue => {:subject => ''}}, # Missing subject should fail
{:success_code => :unprocessable_entity})
should "not create a new issue" do
assert_no_difference('Issue.count') do
put '/issues/6.xml', @parameters, @headers
end
end
should "not create a new journal" do
assert_no_difference('Journal.count') do
put '/issues/6.xml', @parameters, @headers
end
end
should "have an errors tag" do
put '/issues/6.xml', @parameters, @headers
assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
end
end
context "PUT /issues/6.json" do
setup do
@parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
@headers = { :authorization => credentials('jsmith') }
end
should_allow_api_authentication(:put,
'/issues/6.json',
{:issue => {:subject => 'API update', :notes => 'A new note'}},
{:success_code => :ok})
should "not create a new issue" do
assert_no_difference('Issue.count') do
put '/issues/6.json', @parameters, @headers
end
end
should "create a new journal" do
assert_difference('Journal.count') do
put '/issues/6.json', @parameters, @headers
end
end
should "add the note to the journal" do
put '/issues/6.json', @parameters, @headers
journal = Journal.last
assert_equal "A new note", journal.notes
end
should "update the issue" do
put '/issues/6.json', @parameters, @headers
issue = Issue.find(6)
assert_equal "API update", issue.subject
end
end
context "PUT /issues/6.json with failed update" do
setup do
@parameters = {:issue => {:subject => ''}}
@headers = { :authorization => credentials('jsmith') }
end
should_allow_api_authentication(:put,
'/issues/6.json',
{:issue => {:subject => ''}}, # Missing subject should fail
{:success_code => :unprocessable_entity})
should "not create a new issue" do
assert_no_difference('Issue.count') do
put '/issues/6.json', @parameters, @headers
end
end
should "not create a new journal" do
assert_no_difference('Journal.count') do
put '/issues/6.json', @parameters, @headers
end
end
should "have an errors attribute" do
put '/issues/6.json', @parameters, @headers
json = ActiveSupport::JSON.decode(response.body)
assert_equal "can't be blank", json.first['subject']
end
end
context "DELETE /issues/1.xml" do
should_allow_api_authentication(:delete,
'/issues/6.xml',
{},
{:success_code => :ok})
should "delete the issue" do
assert_difference('Issue.count',-1) do
delete '/issues/6.xml', {}, :authorization => credentials('jsmith')
end
assert_nil Issue.find_by_id(6)
end
end
context "DELETE /issues/1.json" do
should_allow_api_authentication(:delete,
'/issues/6.json',
{},
{:success_code => :ok})
should "delete the issue" do
assert_difference('Issue.count',-1) do
delete '/issues/6.json', {}, :authorization => credentials('jsmith')
end
assert_nil Issue.find_by_id(6)
end
end
def credentials(user, password=nil)
ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
end
end

View File

@@ -15,9 +15,9 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require "#{File.dirname(__FILE__)}/../test_helper"
require "#{File.dirname(__FILE__)}/../../test_helper"
class ProjectsApiTest < ActionController::IntegrationTest
class ApiTest::ProjectsTest < ActionController::IntegrationTest
fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
:trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
:attachments, :custom_fields, :custom_values, :time_entries
@@ -31,23 +31,37 @@ class ProjectsApiTest < ActionController::IntegrationTest
assert_response :success
assert_equal 'application/xml', @response.content_type
end
context "GET /projects/2.xml" do
# TODO: A private project is needed because should_allow_api_authentication
# actually tests that authentication is *required*, not just allowed
should_allow_api_authentication(:get, "/projects/2.xml")
end
def test_show
get '/projects/1.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
end
def test_create
attributes = {:name => 'API test', :identifier => 'api-test'}
assert_difference 'Project.count' do
post '/projects.xml', {:project => attributes}, :authorization => credentials('admin')
end
assert_response :created
assert_equal 'application/xml', @response.content_type
project = Project.first(:order => 'id DESC')
attributes.each do |attribute, value|
assert_equal value, project.send(attribute)
context "POST /projects.xml" do
should_allow_api_authentication(:post,
'/projects.xml',
{:project => {:name => 'API test', :identifier => 'api-test'}},
{:success_code => :created})
should "create a project with the attributes" do
assert_difference('Project.count') do
post '/projects.xml', {:project => {:name => 'API test', :identifier => 'api-test'}}, :authorization => credentials('admin')
end
project = Project.first(:order => 'id DESC')
assert_equal 'API test', project.name
assert_equal 'api-test', project.identifier
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_tag 'project', :child => {:tag => 'id', :content => project.id.to_s}
end
end
@@ -61,16 +75,20 @@ class ProjectsApiTest < ActionController::IntegrationTest
assert_tag :errors, :child => {:tag => 'error', :content => "Identifier can't be blank"}
end
def test_update
attributes = {:name => 'API update'}
assert_no_difference 'Project.count' do
put '/projects/1.xml', {:project => attributes}, :authorization => credentials('jsmith')
end
assert_response :ok
assert_equal 'application/xml', @response.content_type
project = Project.find(1)
attributes.each do |attribute, value|
assert_equal value, project.send(attribute)
context "PUT /projects/2.xml" do
should_allow_api_authentication(:put,
'/projects/2.xml',
{:project => {:name => 'API test'}},
{:success_code => :ok})
should "update the project" do
assert_no_difference 'Project.count' do
put '/projects/2.xml', {:project => {:name => 'API update'}}, :authorization => credentials('jsmith')
end
assert_response :ok
assert_equal 'application/xml', @response.content_type
project = Project.find(2)
assert_equal 'API update', project.name
end
end
@@ -83,14 +101,20 @@ class ProjectsApiTest < ActionController::IntegrationTest
assert_equal 'application/xml', @response.content_type
assert_tag :errors, :child => {:tag => 'error', :content => "Name can't be blank"}
end
def test_destroy
assert_difference 'Project.count', -1 do
delete '/projects/2.xml', {}, :authorization => credentials('admin')
context "DELETE /projects/2.xml" do
should_allow_api_authentication(:delete,
'/projects/2.xml',
{},
{:success_code => :ok})
should "delete the project" do
assert_difference('Project.count',-1) do
delete '/projects/2.xml', {}, :authorization => credentials('admin')
end
assert_response :ok
assert_nil Project.find_by_id(2)
end
assert_response :ok
assert_equal 'application/xml', @response.content_type
assert_nil Project.find_by_id(2)
end
def credentials(user, password=nil)

View File

@@ -0,0 +1,26 @@
require "#{File.dirname(__FILE__)}/../../test_helper"
class ApiTest::TokenAuthenticationTest < ActionController::IntegrationTest
fixtures :all
def setup
Setting.rest_api_enabled = '1'
Setting.login_required = '1'
end
def teardown
Setting.rest_api_enabled = '0'
Setting.login_required = '0'
end
# Using the NewsController because it's a simple API.
context "get /news" do
context "in :xml format" do
should_allow_key_based_auth(:get, "/news.xml")
end
context "in :json format" do
should_allow_key_based_auth(:get, "/news.json")
end
end
end

View File

@@ -1,80 +0,0 @@
require "#{File.dirname(__FILE__)}/../test_helper"
class ApiTokenLoginTest < ActionController::IntegrationTest
fixtures :all
def setup
Setting.rest_api_enabled = '1'
Setting.login_required = '1'
end
def teardown
Setting.rest_api_enabled = '0'
Setting.login_required = '0'
end
# Using the NewsController because it's a simple API.
context "get /news" do
context "in :xml format" do
context "with a valid api token" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'api')
get "/news.xml?key=#{@token.value}"
end
should_respond_with :success
should_respond_with_content_type :xml
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid api token" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'feeds')
get "/news.xml?key=#{@token.value}"
end
should_respond_with :unauthorized
should_respond_with_content_type :xml
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
end
context "in :json format" do
context "with a valid api token" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'api')
get "/news.json?key=#{@token.value}"
end
should_respond_with :success
should_respond_with_content_type :json
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid api token" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'feeds')
get "/news.json?key=#{@token.value}"
end
should_respond_with :unauthorized
should_respond_with_content_type :json
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
end
end
end

View File

@@ -1,103 +0,0 @@
require "#{File.dirname(__FILE__)}/../test_helper"
class HttpBasicLoginTest < ActionController::IntegrationTest
fixtures :all
def setup
Setting.rest_api_enabled = '1'
Setting.login_required = '1'
end
def teardown
Setting.rest_api_enabled = '0'
Setting.login_required = '0'
end
# Using the NewsController because it's a simple API.
context "get /news" do
context "in :xml format" do
context "with a valid HTTP authentication" do
setup do
@user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
get "/news.xml", nil, :authorization => @authorization
end
should_respond_with :success
should_respond_with_content_type :xml
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid HTTP authentication" do
setup do
@user = User.generate_with_protected!
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password')
get "/news.xml", nil, :authorization => @authorization
end
should_respond_with :unauthorized
should_respond_with_content_type :xml
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
context "without credentials" do
setup do
get "/projects/onlinestore/news.xml"
end
should_respond_with :unauthorized
should_respond_with_content_type :xml
should "include_www_authenticate_header" do
assert @controller.response.headers.has_key?('WWW-Authenticate')
end
end
end
context "in :json format" do
context "with a valid HTTP authentication" do
setup do
@user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
get "/news.json", nil, :authorization => @authorization
end
should_respond_with :success
should_respond_with_content_type :json
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid HTTP authentication" do
setup do
@user = User.generate_with_protected!
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password')
get "/news.json", nil, :authorization => @authorization
end
should_respond_with :unauthorized
should_respond_with_content_type :json
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
end
context "without credentials" do
setup do
get "/projects/onlinestore/news.json"
end
should_respond_with :unauthorized
should_respond_with_content_type :json
should "include_www_authenticate_header" do
assert @controller.response.headers.has_key?('WWW-Authenticate')
end
end
end
end

View File

@@ -1,84 +0,0 @@
require "#{File.dirname(__FILE__)}/../test_helper"
class HttpBasicLoginWithApiTokenTest < ActionController::IntegrationTest
fixtures :all
def setup
Setting.rest_api_enabled = '1'
Setting.login_required = '1'
end
def teardown
Setting.rest_api_enabled = '0'
Setting.login_required = '0'
end
# Using the NewsController because it's a simple API.
context "get /news" do
context "in :xml format" do
context "with a valid HTTP authentication using the API token" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'api')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
get "/news.xml", nil, :authorization => @authorization
end
should_respond_with :success
should_respond_with_content_type :xml
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid HTTP authentication" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'feeds')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
get "/news.xml", nil, :authorization => @authorization
end
should_respond_with :unauthorized
should_respond_with_content_type :xml
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
end
context "in :json format" do
context "with a valid HTTP authentication" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'api')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'DoesNotMatter')
get "/news.json", nil, :authorization => @authorization
end
should_respond_with :success
should_respond_with_content_type :json
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid HTTP authentication" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'feeds')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'DoesNotMatter')
get "/news.json", nil, :authorization => @authorization
end
should_respond_with :unauthorized
should_respond_with_content_type :json
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
end
end
end

View File

@@ -1,349 +0,0 @@
# 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require "#{File.dirname(__FILE__)}/../test_helper"
class IssuesApiTest < ActionController::IntegrationTest
fixtures :projects,
:users,
:roles,
:members,
:member_roles,
:issues,
:issue_statuses,
:versions,
:trackers,
:projects_trackers,
:issue_categories,
:enabled_modules,
:enumerations,
:attachments,
:workflows,
:custom_fields,
:custom_values,
:custom_fields_projects,
:custom_fields_trackers,
:time_entries,
:journals,
:journal_details,
:queries
def setup
Setting.rest_api_enabled = '1'
end
context "/index.xml" do
setup do
get '/issues.xml'
end
should_respond_with :success
should_respond_with_content_type 'application/xml'
end
context "/index.json" do
setup do
get '/issues.json'
end
should_respond_with :success
should_respond_with_content_type 'application/json'
should 'return a valid JSON string' do
assert ActiveSupport::JSON.decode(response.body)
end
end
context "/index.xml with filter" do
setup do
get '/issues.xml?status_id=5'
end
should_respond_with :success
should_respond_with_content_type 'application/xml'
should "show only issues with the status_id" do
assert_tag :tag => 'issues',
:children => { :count => Issue.visible.count(:conditions => {:status_id => 5}),
:only => { :tag => 'issue' } }
end
end
context "/index.json with filter" do
setup do
get '/issues.json?status_id=5'
end
should_respond_with :success
should_respond_with_content_type 'application/json'
should 'return a valid JSON string' do
assert ActiveSupport::JSON.decode(response.body)
end
should "show only issues with the status_id" do
json = ActiveSupport::JSON.decode(response.body)
status_ids_used = json.collect {|j| j['status_id'] }
assert_equal 3, status_ids_used.length
assert status_ids_used.all? {|id| id == 5 }
end
end
context "/issues/1.xml" do
setup do
get '/issues/1.xml'
end
should_respond_with :success
should_respond_with_content_type 'application/xml'
end
context "/issues/1.json" do
setup do
get '/issues/1.json'
end
should_respond_with :success
should_respond_with_content_type 'application/json'
should 'return a valid JSON string' do
assert ActiveSupport::JSON.decode(response.body)
end
end
context "POST /issues.xml" do
setup do
@issue_count = Issue.count
@attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
post '/issues.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :created
should_respond_with_content_type 'application/xml'
should "create an issue with the attributes" do
assert_equal Issue.count, @issue_count + 1
issue = Issue.first(:order => 'id DESC')
@attributes.each do |attribute, value|
assert_equal value, issue.send(attribute)
end
end
end
context "POST /issues.xml with failure" do
setup do
@attributes = {:project_id => 1}
post '/issues.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :unprocessable_entity
should_respond_with_content_type 'application/xml'
should "have an errors tag" do
assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
end
end
context "POST /issues.json" do
setup do
@issue_count = Issue.count
@attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
post '/issues.json', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :created
should_respond_with_content_type 'application/json'
should "create an issue with the attributes" do
assert_equal Issue.count, @issue_count + 1
issue = Issue.first(:order => 'id DESC')
@attributes.each do |attribute, value|
assert_equal value, issue.send(attribute)
end
end
end
context "POST /issues.json with failure" do
setup do
@attributes = {:project_id => 1}
post '/issues.json', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :unprocessable_entity
should_respond_with_content_type 'application/json'
should "have an errors element" do
json = ActiveSupport::JSON.decode(response.body)
assert_equal "can't be blank", json.first['subject']
end
end
context "PUT /issues/1.xml" do
setup do
@issue_count = Issue.count
@journal_count = Journal.count
@attributes = {:subject => 'API update', :notes => 'A new note'}
put '/issues/1.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :ok
should_respond_with_content_type 'application/xml'
should "not create a new issue" do
assert_equal Issue.count, @issue_count
end
should "create a new journal" do
assert_equal Journal.count, @journal_count + 1
end
should "add the note to the journal" do
journal = Journal.last
assert_equal "A new note", journal.notes
end
should "update the issue" do
issue = Issue.find(1)
@attributes.each do |attribute, value|
assert_equal value, issue.send(attribute) unless attribute == :notes
end
end
end
context "PUT /issues/1.xml with failed update" do
setup do
@attributes = {:subject => ''}
@issue_count = Issue.count
@journal_count = Journal.count
put '/issues/1.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :unprocessable_entity
should_respond_with_content_type 'application/xml'
should "not create a new issue" do
assert_equal Issue.count, @issue_count
end
should "not create a new journal" do
assert_equal Journal.count, @journal_count
end
should "have an errors tag" do
assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
end
end
context "PUT /issues/1.json" do
setup do
@issue_count = Issue.count
@journal_count = Journal.count
@attributes = {:subject => 'API update', :notes => 'A new note'}
put '/issues/1.json', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :ok
should_respond_with_content_type 'application/json'
should "not create a new issue" do
assert_equal Issue.count, @issue_count
end
should "create a new journal" do
assert_equal Journal.count, @journal_count + 1
end
should "add the note to the journal" do
journal = Journal.last
assert_equal "A new note", journal.notes
end
should "update the issue" do
issue = Issue.find(1)
@attributes.each do |attribute, value|
assert_equal value, issue.send(attribute) unless attribute == :notes
end
end
end
context "PUT /issues/1.json with failed update" do
setup do
@attributes = {:subject => ''}
@issue_count = Issue.count
@journal_count = Journal.count
put '/issues/1.json', {:issue => @attributes}, :authorization => credentials('jsmith')
end
should_respond_with :unprocessable_entity
should_respond_with_content_type 'application/json'
should "not create a new issue" do
assert_equal Issue.count, @issue_count
end
should "not create a new journal" do
assert_equal Journal.count, @journal_count
end
should "have an errors attribute" do
json = ActiveSupport::JSON.decode(response.body)
assert_equal "can't be blank", json.first['subject']
end
end
context "DELETE /issues/1.xml" do
setup do
@issue_count = Issue.count
delete '/issues/1.xml', {}, :authorization => credentials('jsmith')
end
should_respond_with :ok
should_respond_with_content_type 'application/xml'
should "delete the issue" do
assert_equal Issue.count, @issue_count -1
assert_nil Issue.find_by_id(1)
end
end
context "DELETE /issues/1.json" do
setup do
@issue_count = Issue.count
delete '/issues/1.json', {}, :authorization => credentials('jsmith')
end
should_respond_with :ok
should_respond_with_content_type 'application/json'
should "delete the issue" do
assert_equal Issue.count, @issue_count -1
assert_nil Issue.find_by_id(1)
end
end
def credentials(user, password=nil)
ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
end
end

View File

@@ -181,4 +181,236 @@ class ActiveSupport::TestCase
assert !user.new_record?
end
end
# Test that a request allows the three types of API authentication
#
# * HTTP Basic with username and password
# * HTTP Basic with an api key for the username
# * Key based with the key=X parameter
#
# @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
# @param [String] url the request url
# @param [optional, Hash] parameters additional request parameters
# @param [optional, Hash] options additional options
# @option options [Symbol] :success_code Successful response code (:success)
# @option options [Symbol] :failure_code Failure response code (:unauthorized)
def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
should_allow_key_based_auth(http_method, url, parameters, options)
end
# Test that a request allows the username and password for HTTP BASIC
#
# @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
# @param [String] url the request url
# @param [optional, Hash] parameters additional request parameters
# @param [optional, Hash] options additional options
# @option options [Symbol] :success_code Successful response code (:success)
# @option options [Symbol] :failure_code Failure response code (:unauthorized)
def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
success_code = options[:success_code] || :success
failure_code = options[:failure_code] || :unauthorized
context "should allow http basic auth using a username and password for #{http_method} #{url}" do
context "with a valid HTTP authentication" do
setup do
@user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
send(http_method, url, parameters, {:authorization => @authorization})
end
should_respond_with success_code
should_respond_with_content_type_based_on_url(url)
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid HTTP authentication" do
setup do
@user = User.generate_with_protected!
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password')
send(http_method, url, parameters, {:authorization => @authorization})
end
should_respond_with failure_code
should_respond_with_content_type_based_on_url(url)
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
context "without credentials" do
setup do
send(http_method, url, parameters, {:authorization => ''})
end
should_respond_with failure_code
should_respond_with_content_type_based_on_url(url)
should "include_www_authenticate_header" do
assert @controller.response.headers.has_key?('WWW-Authenticate')
end
end
end
end
# Test that a request allows the API key with HTTP BASIC
#
# @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
# @param [String] url the request url
# @param [optional, Hash] parameters additional request parameters
# @param [optional, Hash] options additional options
# @option options [Symbol] :success_code Successful response code (:success)
# @option options [Symbol] :failure_code Failure response code (:unauthorized)
def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
success_code = options[:success_code] || :success
failure_code = options[:failure_code] || :unauthorized
context "should allow http basic auth with a key for #{http_method} #{url}" do
context "with a valid HTTP authentication using the API token" do
setup do
@user = User.generate_with_protected!(:admin => true)
@token = Token.generate!(:user => @user, :action => 'api')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
send(http_method, url, parameters, {:authorization => @authorization})
end
should_respond_with success_code
should_respond_with_content_type_based_on_url(url)
should_be_a_valid_response_string_based_on_url(url)
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid HTTP authentication" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'feeds')
@authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
send(http_method, url, parameters, {:authorization => @authorization})
end
should_respond_with failure_code
should_respond_with_content_type_based_on_url(url)
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
end
end
# Test that a request allows full key authentication
#
# @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
# @param [String] url the request url, without the key=ZXY parameter
# @param [optional, Hash] parameters additional request parameters
# @param [optional, Hash] options additional options
# @option options [Symbol] :success_code Successful response code (:success)
# @option options [Symbol] :failure_code Failure response code (:unauthorized)
def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
success_code = options[:success_code] || :success
failure_code = options[:failure_code] || :unauthorized
context "should allow key based auth using key=X for #{http_method} #{url}" do
context "with a valid api token" do
setup do
@user = User.generate_with_protected!(:admin => true)
@token = Token.generate!(:user => @user, :action => 'api')
# Simple url parse to add on ?key= or &key=
request_url = if url.match(/\?/)
url + "&key=#{@token.value}"
else
url + "?key=#{@token.value}"
end
send(http_method, request_url, parameters)
end
should_respond_with success_code
should_respond_with_content_type_based_on_url(url)
should_be_a_valid_response_string_based_on_url(url)
should "login as the user" do
assert_equal @user, User.current
end
end
context "with an invalid api token" do
setup do
@user = User.generate_with_protected!
@token = Token.generate!(:user => @user, :action => 'feeds')
# Simple url parse to add on ?key= or &key=
request_url = if url.match(/\?/)
url + "&key=#{@token.value}"
else
url + "?key=#{@token.value}"
end
send(http_method, request_url, parameters)
end
should_respond_with failure_code
should_respond_with_content_type_based_on_url(url)
should "not login as the user" do
assert_equal User.anonymous, User.current
end
end
end
end
# Uses should_respond_with_content_type based on what's in the url:
#
# '/project/issues.xml' => should_respond_with_content_type :xml
# '/project/issues.json' => should_respond_with_content_type :json
#
# @param [String] url Request
def self.should_respond_with_content_type_based_on_url(url)
case
when url.match(/xml/i)
should_respond_with_content_type :xml
when url.match(/json/i)
should_respond_with_content_type :json
else
raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
end
end
# Uses the url to assert which format the response should be in
#
# '/project/issues.xml' => should_be_a_valid_xml_string
# '/project/issues.json' => should_be_a_valid_json_string
#
# @param [String] url Request
def self.should_be_a_valid_response_string_based_on_url(url)
case
when url.match(/xml/i)
should_be_a_valid_xml_string
when url.match(/json/i)
should_be_a_valid_json_string
else
raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
end
end
# Checks that the response is a valid JSON string
def self.should_be_a_valid_json_string
should "be a valid JSON string (or empty)" do
assert (response.body.blank? || ActiveSupport::JSON.decode(response.body))
end
end
# Checks that the response is a valid XML string
def self.should_be_a_valid_xml_string
should "be a valid XML string" do
assert REXML::Document.new(response.body)
end
end
end
# Simple module to "namespace" all of the API tests
module ApiTest
end

View File

@@ -1,7 +1,7 @@
require File.dirname(__FILE__) + '/../test_helper'
class BoardTest < ActiveSupport::TestCase
fixtures :projects, :boards, :messages
fixtures :projects, :boards, :messages, :attachments, :watchers
def setup
@project = Project.find(1)
@@ -23,8 +23,13 @@ class BoardTest < ActiveSupport::TestCase
def test_destroy
board = Board.find(1)
assert board.destroy
# make sure that the associated messages are removed
assert_difference 'Message.count', -6 do
assert_difference 'Attachment.count', -1 do
assert_difference 'Watcher.count', -1 do
assert board.destroy
end
end
end
assert_equal 0, Message.count(:conditions => {:board_id => 1})
end
end

View File

@@ -284,6 +284,9 @@ RAW
"<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
# remove attributes except class
"<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
'<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
"<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
'<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
"<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
}
to_test.each { |text, result| assert_equal result, textilizable(text) }

View File

@@ -63,4 +63,14 @@ class IssueRelationTest < ActiveSupport::TestCase
assert_equal IssueRelation::TYPE_PRECEDES, relation.relation_type_for(from)
assert_equal IssueRelation::TYPE_FOLLOWS, relation.relation_type_for(to)
end
def test_set_issue_to_dates_without_issue_to
r = IssueRelation.new(:issue_from => Issue.new(:start_date => Date.today), :relation_type => IssueRelation::TYPE_PRECEDES, :delay => 1)
assert_nil r.set_issue_to_dates
end
def test_set_issue_to_dates_without_issues
r = IssueRelation.new(:relation_type => IssueRelation::TYPE_PRECEDES, :delay => 1)
assert_nil r.set_issue_to_dates
end
end

View File

@@ -29,7 +29,7 @@ class Redmine::I18nTest < ActiveSupport::TestCase
set_language_if_valid 'en'
today = Date.today
Setting.date_format = ''
assert_equal I18n.l(today, :count => today.strftime('%d')), format_date(today)
assert_equal I18n.l(today), format_date(today)
end
def test_date_format
@@ -47,7 +47,7 @@ class Redmine::I18nTest < ActiveSupport::TestCase
format_date(Date.today)
format_time(Time.now)
format_time(Time.now, false)
assert_not_equal 'default', ::I18n.l(Date.today, :count => Date.today.strftime('%d'), :format => :default), "date.formats.default missing in #{lang}"
assert_not_equal 'default', ::I18n.l(Date.today, :format => :default), "date.formats.default missing in #{lang}"
assert_not_equal 'time', ::I18n.l(Time.now, :format => :time), "time.formats.time missing in #{lang}"
end
assert l('date.day_names').is_a?(Array)
@@ -63,8 +63,8 @@ class Redmine::I18nTest < ActiveSupport::TestCase
now = Time.now
Setting.date_format = ''
Setting.time_format = ''
assert_equal I18n.l(now, :count => now.strftime('%d')), format_time(now)
assert_equal I18n.l(now, :count => now.strftime('%d'), :format => :time), format_time(now, false)
assert_equal I18n.l(now), format_time(now)
assert_equal I18n.l(now, :format => :time), format_time(now, false)
end
def test_time_format

View File

@@ -11,12 +11,15 @@ begin
REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
def test_hgversion
to_test = { "0.9.5" => [0,9,5],
"1.0" => [1,0],
"1e4ddc9ac9f7+20080325" => nil,
"1.0.1+20080525" => [1,0,1],
"1916e629a29d" => nil}
to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
"Mercurial Distributed SCM (1.0)\n" => [1,0],
"Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
"Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
"Mercurial Distributed SCM (1916e629a29d)\n" => nil,
"Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
"(1.6)\n(1.7)\n(1.8)" => [1,6],
"(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
to_test.each do |s, v|
test_hgversion_for(s, v)
end
@@ -26,8 +29,9 @@ begin
to_test = { [0,9,5] => "0.9.5",
[1,0] => "1.0",
[] => "1.0",
[1,0,1] => "1.0"}
[1,0,1] => "1.0",
[1,7] => "1.0",
[1,7,1] => "1.0"}
to_test.each do |v, template|
test_template_path_for(v, template)
end
@@ -49,5 +53,8 @@ begin
end
rescue LoadError
def test_fake; assert(false, "Requires mocha to run those tests") end
class MercurialMochaFake < ActiveSupport::TestCase
def test_fake; assert(false, "Requires mocha to run those tests") end
end
end

View File

@@ -305,6 +305,26 @@ class MailerTest < ActiveSupport::TestCase
end
end
def test_wiki_content_added
content = WikiContent.find(:first)
valid_languages.each do |lang|
Setting.default_language = lang.to_s
assert_difference 'ActionMailer::Base.deliveries.size' do
assert Mailer.deliver_wiki_content_added(content)
end
end
end
def test_wiki_content_updated
content = WikiContent.find(:first)
valid_languages.each do |lang|
Setting.default_language = lang.to_s
assert_difference 'ActionMailer::Base.deliveries.size' do
assert Mailer.deliver_wiki_content_updated(content)
end
end
end
def test_account_information
user = User.find(2)
valid_languages.each do |lang|

View File

@@ -225,6 +225,22 @@ class QueryTest < ActiveSupport::TestCase
q = Query.new
assert q.groupable_columns.detect {|c| c.is_a? QueryCustomFieldColumn}
end
def test_grouped_with_valid_column
q = Query.new(:group_by => 'status')
assert q.grouped?
assert_not_nil q.group_by_column
assert_equal :status, q.group_by_column.name
assert_not_nil q.group_by_statement
assert_equal 'status', q.group_by_statement
end
def test_grouped_with_invalid_column
q = Query.new(:group_by => 'foo')
assert !q.grouped?
assert_nil q.group_by_column
assert_nil q.group_by_statement
end
def test_default_sort
q = Query.new

View File

@@ -62,12 +62,20 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
assert_equal 2, @repository.entries("images", 2).size
end
def test_cat
assert @repository.scm.cat("sources/welcome_controller.rb", 2)
assert_nil @repository.scm.cat("sources/welcome_controller.rb")
end
def test_isodatesec
# Template keyword 'isodatesec' supported in Mercurial 1.0 and higher
if @repository.scm.class.client_version_above?([1, 0])
@repository.fetch_changesets
@repository.reload
rev0_committed_on = Time.gm(2007, 12, 14, 9, 22, 52)
assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on
end
end
else
puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
def test_fake; assert true end