Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f41fbccb1 | ||
|
|
aba0dc0371 | ||
|
|
902d765ab7 | ||
|
|
c6852b7af2 | ||
|
|
0bff40c1e1 | ||
|
|
40ca445071 | ||
|
|
f9bd713e3e | ||
|
|
ba99936295 | ||
|
|
63c4fb5b97 | ||
|
|
c02ecdd096 | ||
|
|
cb30fa49fc | ||
|
|
f4ada319d1 | ||
|
|
fdaf587652 | ||
|
|
142b837271 | ||
|
|
bd8e8bc71b | ||
|
|
a67f06910b | ||
|
|
ed52753a12 | ||
|
|
bac64c9ab4 | ||
|
|
b58382ef2e | ||
|
|
541a371b41 | ||
|
|
bdb888476d | ||
|
|
f5f55359a8 | ||
|
|
eb590ce1eb | ||
|
|
db64675310 | ||
|
|
92b37c0bea | ||
|
|
faa461c66a | ||
|
|
b5a409791c | ||
|
|
6c362a5a76 | ||
|
|
dbb26b08f8 | ||
|
|
2a7a95d746 | ||
|
|
c70ae6c735 | ||
|
|
e174c10d6d | ||
|
|
2b9a5bf6e2 | ||
|
|
d3b536e9a6 | ||
|
|
9b1eda1b22 | ||
|
|
f1f0704ef8 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 %>
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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}}"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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
@@ -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:"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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 don’t 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 they’re 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 doesn’t 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 there’s 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,7 +4,7 @@ module Redmine
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 1
|
||||
MINOR = 0
|
||||
TINY = 3
|
||||
TINY = 5
|
||||
|
||||
# Branch values:
|
||||
# * official release: nil
|
||||
|
||||
@@ -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)
|
||||
|
||||
12
test/fixtures/attachments.yml
vendored
12
test/fixtures/attachments.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
31
test/integration/api_test/http_basic_login_test.rb
Normal file
31
test/integration/api_test/http_basic_login_test.rb
Normal 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
|
||||
@@ -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
|
||||
336
test/integration/api_test/issues_test.rb
Normal file
336
test/integration/api_test/issues_test.rb
Normal 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
|
||||
@@ -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)
|
||||
26
test/integration/api_test/token_authentication_test.rb
Normal file
26
test/integration/api_test/token_authentication_test.rb
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -284,6 +284,9 @@ RAW
|
||||
"<!-- opening comment" => "<p><!-- 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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user