diff --git a/.gitignore b/.gitignore
index ff5865c8..123812cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+/.project
+/.loadpath
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
diff --git a/.hgignore b/.hgignore
index 5f41fda1..733faf9f 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,5 +1,7 @@
syntax: glob
+.project
+.loadpath
config/additional_environment.rb
config/configuration.yml
config/database.yml
diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb
index 0c645e55..5d6f890f 100644
--- a/app/controllers/account_controller.rb
+++ b/app/controllers/account_controller.rb
@@ -129,7 +129,7 @@ class AccountController < ApplicationController
def logout_user
if User.current.logged?
- cookies.delete :autologin
+ cookies.delete Redmine::Configuration['autologin_cookie_name']
Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
self.logged_user = nil
end
@@ -211,15 +211,14 @@ class AccountController < ApplicationController
def set_autologin_cookie(user)
token = Token.create(:user => user, :action => 'autologin')
- cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin'
cookie_options = {
:value => token.value,
:expires => 1.year.from_now,
- :path => (Redmine::Configuration['autologin_cookie_path'] || '/'),
- :secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false),
+ :path => Redmine::Configuration['autologin_cookie_path'],
+ :secure => Redmine::Configuration['autologin_cookie_secure'],
:httponly => true
}
- cookies[cookie_name] = cookie_options
+ cookies[Redmine::Configuration['autologin_cookie_name']] = cookie_options
end
# Onthefly creation failed, display the registration form to fill/fix attributes
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 277b9621..10c7b653 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -19,6 +19,9 @@ require 'uri'
require 'cgi'
class ApplicationController < ActionController::Base
+
+ protected
+
include Redmine::I18n
layout 'base'
@@ -63,9 +66,9 @@ class ApplicationController < ActionController::Base
if session[:user_id]
# existing session
(User.active.find(session[:user_id]) rescue nil)
- elsif cookies[:autologin] && Setting.autologin?
+ elsif cookies[Redmine::Configuration['autologin_cookie_name']] && Setting.autologin?
# auto-login feature starts a new session
- user = User.try_to_autologin(cookies[:autologin])
+ user = User.try_to_autologin(cookies[Redmine::Configuration['autologin_cookie_name']])
session[:user_id] = user.id if user
user
elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
diff --git a/app/controllers/auto_completes_controller.rb b/app/controllers/auto_completes_controller.rb
index feb1cb23..021d5367 100644
--- a/app/controllers/auto_completes_controller.rb
+++ b/app/controllers/auto_completes_controller.rb
@@ -9,7 +9,10 @@ class AutoCompletesController < ApplicationController
@issues << query.visible.find_by_id(q.to_i)
end
unless q.blank?
- @issues += query.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
+ @issues += query.visible.find(:all,
+ :limit => 10,
+ :order => "#{Issue.table_name}.id ASC",
+ :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE :q OR CAST(#{Issue.table_name}.id AS CHAR(13)) LIKE :q", {:q => "%#{q.downcase}%" }])
end
@issues.compact!
render :layout => false
diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb
index d3b56e8c..1f65dc82 100644
--- a/app/controllers/journals_controller.rb
+++ b/app/controllers/journals_controller.rb
@@ -27,6 +27,7 @@ class JournalsController < ApplicationController
include QueriesHelper
helper :sort
include SortHelper
+ helper :custom_fields
def index
retrieve_query
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 50a6ff4a..de44f1d0 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -67,13 +67,13 @@ class RepositoriesController < ApplicationController
redirect_to :action => 'committers', :id => @project
end
end
-
+
def destroy
@repository.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
end
-
- def show
+
+ def show
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
@entries = @repository.entries(@path, @rev)
@@ -88,7 +88,7 @@ class RepositoriesController < ApplicationController
end
alias_method :browse, :show
-
+
def changes
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@@ -96,23 +96,23 @@ class RepositoriesController < ApplicationController
@properties = @repository.properties(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
end
-
+
def revisions
@changeset_count = @repository.changesets.count
@changeset_pages = Paginator.new self, @changeset_count,
- per_page_option,
- params['page']
+ per_page_option,
+ params['page']
@changesets = @repository.changesets.find(:all,
- :limit => @changeset_pages.items_per_page,
- :offset => @changeset_pages.current.offset,
- :include => [:user, :repository])
+ :limit => @changeset_pages.items_per_page,
+ :offset => @changeset_pages.current.offset,
+ :include => [:user, :repository])
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
end
end
-
+
def entry
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@@ -122,9 +122,10 @@ class RepositoriesController < ApplicationController
@content = @repository.cat(@path, @rev)
(show_error_not_found; return) unless @content
- if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
+ if 'raw' == params[:format] || @content.is_binary_data? ||
+ (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
# Force the download
- send_data @content, :filename => @path.split('/').last
+ send_data @content, :filename => filename_for_content_disposition(@path.split('/').last)
else
# Prevent empty lines when displaying a file with Windows style eol
@content.gsub!("\r\n", "\n")
@@ -135,7 +136,7 @@ class RepositoriesController < ApplicationController
def annotate
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
-
+
@annotate = @repository.scm.annotate(@path, @rev)
(render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
@changeset = @repository.find_changeset_by_name(@rev)
@@ -153,7 +154,7 @@ class RepositoriesController < ApplicationController
rescue ChangesetNotFound
show_error_not_found
end
-
+
def diff
if params[:format] == 'diff'
@diff = @repository.diff(@path, @rev, @rev_to)
@@ -185,11 +186,11 @@ class RepositoriesController < ApplicationController
end
end
- def stats
+ def stats
end
-
+
def graph
- data = nil
+ data = nil
case params[:graph]
when "commits_per_month"
data = graph_commits_per_month(@repository)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 2a213f5e..c7dadf04 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -92,7 +92,7 @@ class UsersController < ApplicationController
@user.safe_attributes = params[:user]
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
- @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
+ @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] if @user.change_password_allowed?
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@@ -135,10 +135,10 @@ class UsersController < ApplicationController
def update
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
- if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
+ @user.safe_attributes = params[:user]
+ if params[:user][:password].present? && @user.change_password_allowed?
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
end
- @user.safe_attributes = params[:user]
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
# TODO: Similar to My#account
@@ -151,7 +151,7 @@ class UsersController < ApplicationController
if was_activated
Mailer.deliver_account_activated(@user)
- elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
+ elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.change_password_allowed?
Mailer.deliver_account_information(@user, params[:user][:password])
end
diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index 63ea5223..bf3c045e 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -172,27 +172,27 @@ module RepositoriesHelper
end
def darcs_field_tags(form, repository)
- content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
+ content_tag('p', form.text_field(:url, :label => :label_darcs_path, :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
end
def mercurial_field_tags(form, repository)
- content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+ content_tag('p', form.text_field(:url, :label => :label_mercurial_path, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
def git_field_tags(form, repository)
- content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+ content_tag('p', form.text_field(:url, :label => :label_git_path, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
def cvs_field_tags(form, repository)
- content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
- content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
+ content_tag('p', form.text_field(:root_url, :label => :label_cvs_path, :size => 60, :required => true, :disabled => !repository.new_record?)) +
+ content_tag('p', form.text_field(:url, :label => :label_cvs_module, :size => 30, :required => true, :disabled => !repository.new_record?))
end
def bazaar_field_tags(form, repository)
- content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
+ content_tag('p', form.text_field(:url, :label => :label_bazaar_path, :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
end
def filesystem_field_tags(form, repository)
- content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+ content_tag('p', form.text_field(:url, :label => :label_filesystem_path, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
end
diff --git a/app/models/query.rb b/app/models/query.rb
index 1f033048..1772d68f 100644
--- a/app/models/query.rb
+++ b/app/models/query.rb
@@ -566,9 +566,19 @@ class Query < ActiveRecord::Base
sql = ''
case operator
when "="
- sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
+ if value.present?
+ sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
+ else
+ # empty set of allowed values produces no result
+ sql = "0=1"
+ end
when "!"
- sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
+ if value.present?
+ sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
+ else
+ # empty set of forbidden values allows all results
+ sql = "1=1"
+ end
when "!*"
sql = "#{db_table}.#{db_field} IS NULL"
sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter
diff --git a/app/views/issues/show.rhtml b/app/views/issues/show.rhtml
index cfe0288f..afb08747 100644
--- a/app/views/issues/show.rhtml
+++ b/app/views/issues/show.rhtml
@@ -1,6 +1,6 @@
<%= render :partial => 'action_menu' %>
-
<%= @issue.tracker.name %> #<%= @issue.id %>
+<%= @issue.tracker.name %> #<%= @issue.id %><%= call_hook(:view_issues_show_identifier, :issue => @issue) %>
<%= avatar(@issue.author, :size => "50") %>
diff --git a/app/views/layouts/mailer.text.html.erb b/app/views/layouts/mailer.text.html.erb
index e4860fd5..53ccca0d 100644
--- a/app/views/layouts/mailer.text.html.erb
+++ b/app/views/layouts/mailer.text.html.erb
@@ -26,7 +26,9 @@ hr {
+<%= call_hook(:view_layouts_mailer_html_before_content, self.assigns) %>
<%= yield %>
+<%= call_hook(:view_layouts_mailer_html_after_content, self.assigns) %>
diff --git a/app/views/layouts/mailer.text.plain.erb b/app/views/layouts/mailer.text.plain.erb
index 4b8c0b4f..d4e9c04d 100644
--- a/app/views/layouts/mailer.text.plain.erb
+++ b/app/views/layouts/mailer.text.plain.erb
@@ -1,4 +1,6 @@
<%= Setting.emails_header %>
+<%= call_hook(:view_layouts_mailer_plain_before_content, self.assigns) %>
<%= yield %>
+<%= call_hook(:view_layouts_mailer_plain_after_content, self.assigns) %>
--
<%= Setting.emails_footer %>
diff --git a/app/views/projects/show.rhtml b/app/views/projects/show.rhtml
index 414358ac..bfd27b4a 100644
--- a/app/views/projects/show.rhtml
+++ b/app/views/projects/show.rhtml
@@ -67,8 +67,13 @@
<% if @total_hours && User.current.allowed_to?(:view_time_entries, @project) %>
<%= l(:label_spent_time) %>
<%= l_hours(@total_hours) %>
-
<%= link_to(l(:label_details), {:controller => 'timelog', :action => 'index', :project_id => @project}) %> |
- <%= link_to(l(:label_report), {:controller => 'time_entry_reports', :action => 'report', :project_id => @project}) %>
+
+ <%= link_to(l(:label_details), {:controller => 'timelog', :action => 'index', :project_id => @project}) %> |
+ <%= link_to(l(:label_report), {:controller => 'time_entry_reports', :action => 'report', :project_id => @project}) %>
+ <% if authorize_for('timelog', 'new') %>
+ | <%= link_to l(:button_log_time), {:controller => 'timelog', :action => 'new', :project_id => @project} %>
+ <% end %>
+
<% end %>
<%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %>
<% end %>
diff --git a/app/views/settings/_display.rhtml b/app/views/settings/_display.rhtml
index 8e07b4ff..c6fe8339 100644
--- a/app/views/settings/_display.rhtml
+++ b/app/views/settings/_display.rhtml
@@ -15,7 +15,7 @@
<%= setting_check_box :gravatar_enabled %>
-
<%= setting_select :gravatar_default, [["Wavatars", 'wavatar'], ["Identicons", 'identicon'], ["Monster ids", 'monsterid']], :blank => :label_none %>
+
<%= setting_select :gravatar_default, [["Wavatars", 'wavatar'], ["Identicons", 'identicon'], ["Monster ids", 'monsterid'], ["Retro", "retro"]], :blank => :label_none %>
<%= submit_tag l(:button_save) %>
diff --git a/app/views/versions/show.rhtml b/app/views/versions/show.rhtml
index 13769a5b..25d93eaf 100644
--- a/app/views/versions/show.rhtml
+++ b/app/views/versions/show.rhtml
@@ -37,7 +37,7 @@
diff --git a/config/boot.rb b/config/boot.rb
index a8e494ed..bb844558 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -158,5 +158,8 @@ module Rails
end
end
+# working around deprecation in RubyGems 1.6
+require 'thread'
+
# All that for this:
Rails.boot!
diff --git a/config/configuration.yml.example b/config/configuration.yml.example
index 628d2799..0858d1ca 100644
--- a/config/configuration.yml.example
+++ b/config/configuration.yml.example
@@ -98,7 +98,19 @@ default:
# attachments_storage_path: /var/chiliproject/files
# attachments_storage_path: D:/chiliproject/files
attachments_storage_path:
-
+
+ # Path to the directories where themes are stored.
+ # Can be an absolute path or one relative to your ChiliProject instance.
+ # You can configure multiple paths.
+ # The default is the 'public/themes' directory in your ChiliProject instance.
+ # Examples:
+ # themes_storage_paths: public/themes
+ # themes_storage_paths:
+ # - public/themes
+ # - /opt/themes
+ # - D:/chiliproject/themes
+ themes_storage_path:
+
# Configuration of the autologin cookie.
# autologin_cookie_name: the name of the cookie (default: autologin)
# autologin_cookie_path: the cookie path (default: /)
diff --git a/config/initializers/10-patches.rb b/config/initializers/10-patches.rb
index 25b20523..7d201e85 100644
--- a/config/initializers/10-patches.rb
+++ b/config/initializers/10-patches.rb
@@ -86,7 +86,7 @@ module I18n
module Base
def warn_syntax_deprecation!(*args)
return if @skip_syntax_deprecation
- 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."
+ ActiveSupport::Deprecation.warn "The {{key}} interpolation syntax in I18n messages is deprecated and will be removed in ChiliProject 2.0. Please use %{key} instead. See the notice at https://www.chiliproject.org/boards/2/topics/243 for more information."
@skip_syntax_deprecation = true
end
end
diff --git a/config/locales/bg.yml b/config/locales/bg.yml
index 47e4803c..099d2a30 100644
--- a/config/locales/bg.yml
+++ b/config/locales/bg.yml
@@ -939,3 +939,10 @@ bg:
enumeration_system_activity: Системна активност
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/bs.yml b/config/locales/bs.yml
index 68229f24..40518869 100644
--- a/config/locales/bs.yml
+++ b/config/locales/bs.yml
@@ -953,3 +953,10 @@ bs:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index e9dd73ba..e2a31df4 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -942,3 +942,10 @@ ca:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index 39bb5a0f..cd6b77ec 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -939,3 +939,10 @@ cs:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/da.yml b/config/locales/da.yml
index da3110c0..758ff93c 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -955,3 +955,10 @@ da:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/de.yml b/config/locales/de.yml
index bcd40156..6be4d0a7 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -956,3 +956,10 @@ de:
notice_gantt_chart_truncated: Die Grafik ist unvollständig, da das Maximum der anzeigbaren Aufgaben überschritten wurde (%{max})
setting_gantt_items_limit: Maximale Anzahl von Aufgaben die im Gantt-Chart angezeigt werden.
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/el.yml b/config/locales/el.yml
index 934caa07..d9fe2b6a 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -939,3 +939,10 @@ el:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml
index 418b7979..ef00034a 100644
--- a/config/locales/en-GB.yml
+++ b/config/locales/en-GB.yml
@@ -943,3 +943,10 @@ en-GB:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: "Powered by %{link}"
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/en.yml b/config/locales/en.yml
index b55f9bb2..382273f5 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -793,7 +793,14 @@ en:
label_project_copy_notifications: Send email notifications during the project copy
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
-
+ label_git_path: Path to .git directory
+ label_darcs_path: Root directory
+ label_mercurial_path: Root directory
+ label_cvs_path: CVSROOT
+ label_cvs_module: Module
+ label_bazaar_path: Root directory
+ label_filesystem_path: Root directory
+
button_login: Login
button_submit: Submit
button_save: Save
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 84cb87b1..f7b868a7 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -976,3 +976,10 @@ es:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/eu.yml b/config/locales/eu.yml
index c48ec2fc..67983023 100644
--- a/config/locales/eu.yml
+++ b/config/locales/eu.yml
@@ -943,3 +943,10 @@ eu:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index 3705f972..628416f2 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -960,3 +960,10 @@ fi:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 01c172ab..b9fd3982 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -957,3 +957,10 @@ fr:
field_assigned_to_role: Rôle de l'assigné
setting_emails_header: Emails header
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index 51163c77..827d32af 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -951,3 +951,10 @@ gl:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/he.yml b/config/locales/he.yml
index 01ac9366..6b259570 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -944,3 +944,10 @@ he:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/hr.yml b/config/locales/hr.yml
index 702db593..cfae5edd 100644
--- a/config/locales/hr.yml
+++ b/config/locales/hr.yml
@@ -946,3 +946,10 @@ hr:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 82159ad0..e0616a08 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -958,3 +958,10 @@
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/id.yml b/config/locales/id.yml
index 1587f40f..8a33b393 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -947,3 +947,10 @@ id:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 456eb830..33c8e75c 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -940,3 +940,10 @@ it:
notice_gantt_chart_truncated: Il grafico è stato troncato perchè eccede il numero di oggetti (%{max}) da visualizzare
setting_gantt_items_limit: Massimo numero di oggetti da visualizzare sul diagramma di gantt
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index d55c5167..02025268 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -960,3 +960,10 @@ ja:
enumeration_activities: 作業分類 (時間トラッキング)
enumeration_system_activity: システム作業分類
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index 019f84ac..7772ac7a 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -991,3 +991,10 @@ ko:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 56026ef4..a943f610 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -999,3 +999,10 @@ lt:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/lv.yml b/config/locales/lv.yml
index 73320eec..8d47c896 100644
--- a/config/locales/lv.yml
+++ b/config/locales/lv.yml
@@ -934,3 +934,10 @@ lv:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/mk.yml b/config/locales/mk.yml
index 0cbc914e..2b39ba38 100644
--- a/config/locales/mk.yml
+++ b/config/locales/mk.yml
@@ -939,3 +939,10 @@ mk:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/mn.yml b/config/locales/mn.yml
index 6bf504c5..186c3133 100644
--- a/config/locales/mn.yml
+++ b/config/locales/mn.yml
@@ -940,3 +940,10 @@ mn:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 389ddb2e..4dcf7cf9 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -921,3 +921,10 @@ nl:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 776dfe97..5120869e 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -926,3 +926,10 @@
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 338eec7e..97bc9801 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -956,3 +956,10 @@ pl:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 5173b8fd..5258667d 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -959,3 +959,10 @@ pt-BR:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index d8474277..e9742865 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -943,3 +943,10 @@ pt:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/ro.yml b/config/locales/ro.yml
index 5ace8afb..115ba952 100644
--- a/config/locales/ro.yml
+++ b/config/locales/ro.yml
@@ -932,3 +932,10 @@ ro:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index e42b62f4..e2f5d2ba 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -1052,3 +1052,10 @@ ru:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Модуль
+ label_filesystem_path: Каталог
+ label_darcs_path: Каталог
+ label_bazaar_path: Каталог
+ label_cvs_path: CVSROOT
+ label_git_path: Путь к каталогу .git
+ label_mercurial_path: Каталог
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index 55cb83bb..f752a07a 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -934,3 +934,10 @@ sk:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/sl.yml b/config/locales/sl.yml
index 74fa1cae..b4b4bc34 100644
--- a/config/locales/sl.yml
+++ b/config/locales/sl.yml
@@ -935,3 +935,10 @@ sl:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml
index 59411b1f..512070ca 100644
--- a/config/locales/sr-YU.yml
+++ b/config/locales/sr-YU.yml
@@ -939,3 +939,10 @@ sr-YU:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 89b51a2a..9ea33b65 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -940,3 +940,10 @@ sr:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index f08f481c..cda64c85 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -980,3 +980,10 @@ sv:
enumeration_activities: Aktiviteter (tidsuppföljning)
enumeration_system_activity: Systemaktivitet
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/th.yml b/config/locales/th.yml
index b46f7454..5f174307 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -936,3 +936,10 @@ th:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 82d33792..1e698213 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -958,3 +958,10 @@ tr:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index aec46e55..bfa62a60 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -935,3 +935,10 @@ uk:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index 009296e6..bc7d4d25 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -990,3 +990,10 @@ vi:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index ae8dac27..e98d03ba 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -1020,3 +1020,10 @@
enumeration_activities: 活動 (時間追蹤)
enumeration_system_activity: 系統活動
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index 0f2fda6d..fb5dcf85 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -953,3 +953,10 @@ zh:
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
text_powered_by: Powered by %{link}
+ label_cvs_module: Module
+ label_filesystem_path: Root directory
+ label_darcs_path: Root directory
+ label_bazaar_path: Root directory
+ label_cvs_path: CVSROOT
+ label_git_path: Path to .git directory
+ label_mercurial_path: Root directory
diff --git a/doc/CHANGELOG.rdoc b/doc/CHANGELOG.rdoc
index 3ef823e0..193f15a6 100644
--- a/doc/CHANGELOG.rdoc
+++ b/doc/CHANGELOG.rdoc
@@ -1,5 +1,32 @@
= ChiliProject changelog
+== 2011-03-27 v1.2.0
+
+* Bug #209: Don't hardcode user viewable labels (like "Path to .git repository")
+* Bug #225: Support spaces in scm commands
+* Bug #250: Filter assignee group to Team leaders
+* Bug #251: Make Chili work with RubyGems 1.6
+* Bug #266: Fix monkey patching of rubytree in lib/redmine/menu_manager.rb
+* Bug #267: /issues/changes?format=atom is returning 500 Internal Error
+* Bug #270: Reposman.rb does not consider underscore to be valid char for a project identifier
+* Bug #273: custom autologin cookie name not read
+* Bug #278: Issue Form: Parent autocomplete won't work with issues under 3 charactors
+* Bug #280: Issues AutoComplete isn't searching issue ids
+* Bug #281: Cross project issues aren't showing their project on the Version page
+* Bug #282: Enhance Redmine::SafeAttributes to work for subclasses
+* Bug #302: Protect methods in ApplicationController
+* Bug #305: Toolbar for textile edit fields is buggy in IE8
+* Feature #199: [PATCH] Adding a hook in the heading on showing an issue
+* Feature #219: Add plugin hooks to the mailer layout
+* Feature #230: Allow the loadpaths of themes to be specified in configuration.yml
+* Feature #245: Merge Redmine.pm git smart-http functionality
+* Feature #271: Replace checks for "auth_source_id" with "change_password_allowed?" in UsersController
+* Feature #276: Add Log Time link to the sidebar on Project Overview
+* Feature #283: Check pre-i18n 0.4.2 depreciation
+* Feature #307: Add retro style gravatars
+* Task #246: Document git-smart-http integration
+* Task #308: Remove Redmine::VERSION::BRANCH
+
== 2011-02-27 v1.1.0
* Bug #109: Backport fix to display full TOC with present < p r e > tags
diff --git a/extra/svn/Redmine.pm b/extra/svn/Redmine.pm
index c96b248c..e1e1e171 100644
--- a/extra/svn/Redmine.pm
+++ b/extra/svn/Redmine.pm
@@ -93,6 +93,84 @@ S
And you need to upgrade at least reposman.rb (after r860).
+=head1 GIT SMART HTTP SUPPORT
+
+Git's smart HTTP protocol (available since Git 1.7.0) will not work with the
+above settings. Redmine.pm normally does access control depending on the HTTP
+method used: read-only methods are OK for everyone in public projects and
+members with read rights in private projects. The rest require membership with
+commit rights in the project.
+
+However, this scheme doesn't work for Git's smart HTTP protocol, as it will use
+POST even for a simple clone. Instead, read-only requests must be detected using
+the full URL (including the query string): anything that doesn't belong to the
+git-receive-pack service is read-only.
+
+To activate this mode of operation, add this line inside your
+block:
+
+ RedmineGitSmartHttp yes
+
+Here's a sample Apache configuration which integrates git-http-backend with
+a MySQL database and this new option:
+
+ SetEnv GIT_PROJECT_ROOT /var/www/git/
+ SetEnv GIT_HTTP_EXPORT_ALL
+ ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+
+ Order allow,deny
+ Allow from all
+
+ AuthType Basic
+ AuthName Git
+ Require valid-user
+
+ PerlAccessHandler Apache::Authn::Redmine::access_handler
+ PerlAuthenHandler Apache::Authn::Redmine::authen_handler
+ # for mysql
+ RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1"
+ RedmineDbUser "redmine"
+ RedmineDbPass "xxx"
+ RedmineGitSmartHttp yes
+
+
+Make sure that all the names of the repositories under /var/www/git/ match
+exactly the identifier for some project: /var/www/git/myproject.git won't work,
+due to the way this module extracts the identifier from the URL.
+/var/www/git/myproject will work, though. You can put both bare and non-bare
+repositories in /var/www/git, though bare repositories are strongly
+recommended. You should create them with the rights of the user running Redmine,
+like this:
+
+ cd /var/www/git
+ sudo -u user-running-redmine mkdir myproject
+ cd myproject
+ sudo -u user-running-redmine git init --bare
+
+Once you have activated this option, you have three options when cloning a
+repository:
+
+- Cloning using "http://user@host/git/repo" works, but will ask for the password
+ all the time.
+
+- Cloning with "http://user:pass@host/git/repo" does not have this problem, but
+ this could reveal accidentally your password to the console in some versions
+ of Git, and you would have to ensure that .git/config is not readable except
+ by the owner for each of your projects.
+
+- Use "http://host/git/repo", and store your credentials in the ~/.netrc
+ file. This is the recommended solution, as you only have one file to protect
+ and passwords will not be leaked accidentally to the console.
+
+ IMPORTANT NOTE: It is *very important* that the file cannot be read by other
+ users, as it will contain your password in cleartext. To create the file, you
+ can use the following commands, replacing yourhost, youruser and yourpassword
+ with the right values:
+
+ touch ~/.netrc
+ chmod 600 ~/.netrc
+ echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc
+
=cut
use strict;
@@ -142,6 +220,11 @@ my @directives = (
args_how => TAKE1,
errmsg => 'RedmineCacheCredsMax must be decimal number',
},
+ {
+ name => 'RedmineGitSmartHttp',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
);
sub RedmineDSN {
@@ -178,6 +261,17 @@ sub RedmineCacheCredsMax {
}
}
+sub RedmineGitSmartHttp {
+ my ($self, $parms, $arg) = @_;
+ $arg = lc $arg;
+
+ if ($arg eq "yes" || $arg eq "true") {
+ $self->{RedmineGitSmartHttp} = 1;
+ } else {
+ $self->{RedmineGitSmartHttp} = 0;
+ }
+}
+
sub trim {
my $string = shift;
$string =~ s/\s{2,}/ /g;
@@ -194,6 +288,23 @@ Apache2::Module::add(__PACKAGE__, \@directives);
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
+sub request_is_read_only {
+ my ($r) = @_;
+ my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
+
+ # Do we use Git's smart HTTP protocol, or not?
+ if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) {
+ my $uri = $r->unparsed_uri;
+ my $location = $r->location;
+ my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o;
+ return $is_read_only;
+ } else {
+ # Old behaviour: check the HTTP method
+ my $method = $r->method;
+ return defined $read_only_methods{$method};
+ }
+}
+
sub access_handler {
my $r = shift;
@@ -202,8 +313,7 @@ sub access_handler {
return FORBIDDEN;
}
- my $method = $r->method;
- return OK unless defined $read_only_methods{$method};
+ return OK unless request_is_read_only($r);
my $project_id = get_project_identifier($r);
@@ -320,7 +430,7 @@ sub is_member {
unless ($auth_source_id) {
my $method = $r->method;
- if ($hashed_password eq $pass_digest && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
+ if ($hashed_password eq $pass_digest && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
$ret = 1;
last;
}
@@ -338,9 +448,7 @@ sub is_member {
bindpw => $rowldap[4] ? $rowldap[4] : "",
filter => "(".$rowldap[6]."=%s)"
);
- my $method = $r->method;
- $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
-
+ $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
}
$sthldap->finish();
undef $sthldap;
diff --git a/extra/svn/reposman.rb b/extra/svn/reposman.rb
index c92e32c5..c9b9b0f5 100755
--- a/extra/svn/reposman.rb
+++ b/extra/svn/reposman.rb
@@ -7,7 +7,7 @@
# == Usage
#
# reposman [OPTIONS...] -s [DIR] -r [HOST]
-#
+#
# Examples:
# reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
# reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git
@@ -57,7 +57,7 @@
# -q, --quiet no log
#
# == References
-#
+#
# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos
@@ -225,9 +225,9 @@ end
def owner_name(file)
mswin? ?
$svn_owner :
- Etc.getpwuid( File.stat(file).uid ).name
+ Etc.getpwuid( File.stat(file).uid ).name
end
-
+
def mswin?
(RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
end
@@ -238,7 +238,7 @@ projects.each do |project|
if project.identifier.empty?
log("\tno identifier for project #{project.name}")
next
- elsif not project.identifier.match(/^[a-z0-9\-]+$/)
+ elsif not project.identifier.match(/^[a-z0-9\-_]+$/)
log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
next;
end
@@ -309,4 +309,3 @@ projects.each do |project|
end
end
-
\ No newline at end of file
diff --git a/lib/redmine/configuration.rb b/lib/redmine/configuration.rb
index 1d2443a9..d4832272 100644
--- a/lib/redmine/configuration.rb
+++ b/lib/redmine/configuration.rb
@@ -20,7 +20,11 @@ module Redmine
# Configuration default values
@defaults = {
- 'email_delivery' => nil
+ 'email_delivery' => nil,
+ # Autologin cookie defaults:
+ 'autologin_cookie_name' => 'autologin',
+ 'autologin_cookie_path' => '/',
+ 'autologin_cookie_secure' => false,
}
@config = nil
diff --git a/lib/redmine/menu_manager.rb b/lib/redmine/menu_manager.rb
index 996b1c5b..51f8ed64 100644
--- a/lib/redmine/menu_manager.rb
+++ b/lib/redmine/menu_manager.rb
@@ -23,12 +23,13 @@ module TreeNodePatch
base.class_eval do
attr_reader :last_items_count
- alias :old_initilize :initialize
- def initialize(name, content = nil)
- old_initilize(name, content)
+ def initialize_with_redmine(name, content = nil)
+ extend InstanceMethods
@last_items_count = 0
- extend(InstanceMethods)
+
+ initialize_without_redmine(name, content)
end
+ alias_method_chain :initialize, :redmine
end
end
@@ -99,7 +100,9 @@ module TreeNodePatch
end
end
end
-Tree::TreeNode.send(:include, TreeNodePatch)
+unless Tree::TreeNode.included_modules.include?(TreeNodePatch)
+ Tree::TreeNode.send(:include, TreeNodePatch)
+end
module Redmine
module MenuManager
diff --git a/lib/redmine/safe_attributes.rb b/lib/redmine/safe_attributes.rb
index 6f87a233..13db6a45 100644
--- a/lib/redmine/safe_attributes.rb
+++ b/lib/redmine/safe_attributes.rb
@@ -31,14 +31,19 @@ module Redmine
def safe_attributes(*args)
@safe_attributes ||= []
if args.empty?
- @safe_attributes
+ if superclass < Redmine::SafeAttributes
+ superclass.safe_attributes + @safe_attributes
+ else
+ @safe_attributes
+ end
else
options = args.last.is_a?(Hash) ? args.pop : {}
@safe_attributes << [args, options]
+ safe_attributes
end
end
end
-
+
# Returns an array that can be safely set by user or current user
#
# Example:
@@ -62,7 +67,7 @@ module Redmine
# # => {'title' => 'My book'}
def delete_unsafe_attributes(attrs, user=User.current)
safe = safe_attribute_names(user)
- attrs.dup.delete_if {|k,v| !safe.include?(k)}
+ attrs.dup.delete_if {|k,v| !safe.include?(k.to_s)}
end
# Sets attributes from attrs that are safe
diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb
index f98e9095..3442276b 100644
--- a/lib/redmine/scm/adapters/abstract_adapter.rb
+++ b/lib/redmine/scm/adapters/abstract_adapter.rb
@@ -25,6 +25,10 @@ module Redmine
class AbstractAdapter #:nodoc:
class << self
+ def client_command
+ ""
+ end
+
# Returns the version of the scm client
# Eg: [1, 5, 0] or [] if unknown
def client_version
@@ -45,8 +49,20 @@ module Redmine
def client_version_above?(v, options={})
((client_version <=> v) >= 0) || (client_version.empty? && options[:unknown])
end
+
+ def client_available
+ true
+ end
+
+ def shell_quote(str)
+ if Redmine::Platform.mswin?
+ '"' + str.gsub(/"/, '\\"') + '"'
+ else
+ "'" + str.gsub(/'/, "'\"'\"'") + "'"
+ end
+ end
end
-
+
def initialize(url, root_url=nil, login=nil, password=nil)
@url = url
@login = login if login && !login.empty?
@@ -138,7 +154,7 @@ module Redmine
path ||= ''
(path[-1,1] == "/") ? path : "#{path}/"
end
-
+
def without_leading_slash(path)
path ||= ''
path.gsub(%r{^/+}, '')
@@ -148,13 +164,9 @@ module Redmine
path ||= ''
(path[-1,1] == "/") ? path[0..-2] : path
end
-
+
def shell_quote(str)
- if Redmine::Platform.mswin?
- '"' + str.gsub(/"/, '\\"') + '"'
- else
- "'" + str.gsub(/'/, "'\"'\"'") + "'"
- end
+ self.class.shell_quote(str)
end
private
@@ -168,11 +180,11 @@ module Redmine
base = path.match(/^\//) ? root_url : url
shell_quote("#{base}/#{path}".gsub(/[?<>\*]/, ''))
end
-
+
def logger
self.class.logger
end
-
+
def shellout(cmd, &block)
self.class.shellout(cmd, &block)
end
diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb
index 96ec8693..1a11f639 100644
--- a/lib/redmine/scm/adapters/bazaar_adapter.rb
+++ b/lib/redmine/scm/adapters/bazaar_adapter.rb
@@ -19,15 +19,25 @@ require 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
- module Adapters
+ module Adapters
class BazaarAdapter < AbstractAdapter
-
+
# Bazaar executable name
BZR_BIN = Redmine::Configuration['scm_bazaar_command'] || "bzr"
-
+
+ class << self
+ def client_command
+ @@bin ||= BZR_BIN
+ end
+
+ def sq_bin
+ @@sq_bin ||= shell_quote(BZR_BIN)
+ end
+ end
+
# Get info about the repository
def info
- cmd = "#{BZR_BIN} revno #{target('')}"
+ cmd = "#{self.class.sq_bin} revno #{target('')}"
info = nil
shellout(cmd) do |io|
if io.read =~ %r{^(\d+)\r?$}
@@ -43,13 +53,13 @@ module Redmine
rescue CommandFailed
return nil
end
-
+
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def entries(path=nil, identifier=nil)
path ||= ''
entries = Entries.new
- cmd = "#{BZR_BIN} ls -v --show-ids"
+ cmd = "#{self.class.sq_bin} ls -v --show-ids"
identifier = -1 unless identifier && identifier.to_i > 0
cmd << " -r#{identifier.to_i}"
cmd << " #{target(path)}"
@@ -71,13 +81,13 @@ module Redmine
logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
entries.sort_by_name
end
-
+
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 : '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}..#{identifier_from} #{target(path)}"
+ cmd = "#{self.class.sq_bin} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
shellout(cmd) do |io|
revision = nil
parsing = nil
@@ -132,7 +142,7 @@ module Redmine
return nil if $? && $?.exitstatus != 0
revisions
end
-
+
def diff(path, identifier_from, identifier_to=nil)
path ||= ''
if identifier_to
@@ -143,7 +153,7 @@ module Redmine
if identifier_from
identifier_from = identifier_from.to_i
end
- cmd = "#{BZR_BIN} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
+ cmd = "#{self.class.sq_bin} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
diff = []
shellout(cmd) do |io|
io.each_line do |line|
@@ -153,9 +163,9 @@ module Redmine
#return nil if $? && $?.exitstatus != 0
diff
end
-
+
def cat(path, identifier=nil)
- cmd = "#{BZR_BIN} cat"
+ cmd = "#{self.class.sq_bin} cat"
cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
cmd << " #{target(path)}"
cat = nil
@@ -166,9 +176,9 @@ module Redmine
return nil if $? && $?.exitstatus != 0
cat
end
-
+
def annotate(path, identifier=nil)
- cmd = "#{BZR_BIN} annotate --all"
+ cmd = "#{self.class.sq_bin} annotate --all"
cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
cmd << " #{target(path)}"
blame = Annotate.new
diff --git a/lib/redmine/scm/adapters/cvs_adapter.rb b/lib/redmine/scm/adapters/cvs_adapter.rb
index 5c81e639..c3d3bf72 100644
--- a/lib/redmine/scm/adapters/cvs_adapter.rb
+++ b/lib/redmine/scm/adapters/cvs_adapter.rb
@@ -24,7 +24,17 @@ module Redmine
# CVS executable name
CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
-
+
+ class << self
+ def client_command
+ @@bin ||= CVS_BIN
+ end
+
+ def sq_bin
+ @@sq_bin ||= shell_quote(CVS_BIN)
+ end
+ end
+
# Guidelines for the input:
# url -> the project-path, relative to the cvsroot (eg. module name)
# root_url -> the good old, sometimes damned, CVSROOT
@@ -38,24 +48,24 @@ module Redmine
raise CommandFailed if root_url.blank?
@root_url = root_url
end
-
+
def root_url
@root_url
end
-
+
def url
@url
end
-
+
def info
logger.debug " info"
Info.new({:root_url => @root_url, :lastrev => nil})
end
-
+
def get_previous_revision(revision)
CvsRevisionHelper.new(revision).prevRev
end
-
+
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
# this method is used by the repository-browser (aka LIST)
@@ -63,14 +73,14 @@ module Redmine
logger.debug " entries '#{path}' with identifier '#{identifier}'"
path_with_project="#{url}#{with_leading_slash(path)}"
entries = Entries.new
- cmd = "#{CVS_BIN} -d #{shell_quote root_url} rls -e"
+ cmd = "#{self.class.sq_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|
io.each_line(){|line|
fields=line.chop.split('/',-1)
logger.debug(">>InspectLine #{fields.inspect}")
-
+
if fields[0]!="D"
entries << Entry.new({:name => fields[-5],
#:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
@@ -96,19 +106,19 @@ module Redmine
end
return nil if $? && $?.exitstatus != 0
entries.sort_by_name
- end
+ end
STARTLOG="----------------------------"
ENDLOG ="============================================================================="
-
+
# Returns all revisions found between identifier_from and identifier_to
# in the repository. both identifier have to be dates or nil.
# these method returns nothing but yield every result in block
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
logger.debug " revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
-
+
path_with_project="#{url}#{with_leading_slash(path)}"
- cmd = "#{CVS_BIN} -d #{shell_quote root_url} rlog"
+ cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rlog"
cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from
cmd << " #{shell_quote path_with_project}"
shellout(cmd) do |io|
@@ -123,7 +133,7 @@ module Redmine
file_state=nil
branch_map=nil
- io.each_line() do |line|
+ io.each_line() do |line|
if state!="revision" && /^#{ENDLOG}/ =~ line
commit_log=String.new
@@ -162,9 +172,9 @@ module Redmine
end
next
elsif state=="revision"
- if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
+ if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
if revision
-
+
revHelper=CvsRevisionHelper.new(revision)
revBranch="HEAD"
@@ -176,7 +186,7 @@ module Redmine
logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
- yield Revision.new({
+ yield Revision.new({
:time => date,
:author => author,
:message=>commit_log.chomp,
@@ -188,9 +198,9 @@ module Redmine
:kind=>'file',
:action=>file_state
}]
- })
+ })
end
-
+
commit_log=String.new
revision=nil
@@ -199,7 +209,7 @@ module Redmine
end
next
end
-
+
if /^branches: (.+)$/ =~ line
#TODO: version.branch = $1
elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
@@ -216,20 +226,20 @@ module Redmine
# version.line_minus = linechanges[2]
# else
# version.line_plus = 0
- # version.line_minus = 0
- # end
- else
+ # version.line_minus = 0
+ # end
+ else
commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
- end
- end
+ end
+ end
end
end
- end
-
+ end
+
def diff(path, identifier_from, identifier_to=nil)
logger.debug " diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
path_with_project="#{url}#{with_leading_slash(path)}"
- cmd = "#{CVS_BIN} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
+ cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
diff = []
shellout(cmd) do |io|
io.each_line do |line|
@@ -238,28 +248,29 @@ module Redmine
end
return nil if $? && $?.exitstatus != 0
diff
- end
-
+ end
+
def cat(path, identifier=nil)
identifier = (identifier) ? identifier : "HEAD"
logger.debug " cat path:'#{path}',identifier #{identifier}"
path_with_project="#{url}#{with_leading_slash(path)}"
- cmd = "#{CVS_BIN} -d #{shell_quote root_url} co"
+ cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
cmd << " -p #{shell_quote path_with_project}"
cat = nil
shellout(cmd) do |io|
+ io.binmode
cat = io.read
end
return nil if $? && $?.exitstatus != 0
cat
- end
+ end
def annotate(path, identifier=nil)
identifier = (identifier) ? identifier.to_i : "HEAD"
logger.debug " annotate path:'#{path}',identifier #{identifier}"
path_with_project="#{url}#{with_leading_slash(path)}"
- cmd = "#{CVS_BIN} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
+ cmd = "#{self.class.sq_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|
@@ -270,9 +281,9 @@ module Redmine
return nil if $? && $?.exitstatus != 0
blame
end
-
+
private
-
+
# Returns the root url without the connexion string
# :pserver:anonymous@foo.bar:/path => /path
# :ext:cvsservername:/path => /path
diff --git a/lib/redmine/scm/adapters/darcs_adapter.rb b/lib/redmine/scm/adapters/darcs_adapter.rb
index 4eaf4fff..4eed61e9 100644
--- a/lib/redmine/scm/adapters/darcs_adapter.rb
+++ b/lib/redmine/scm/adapters/darcs_adapter.rb
@@ -20,16 +20,24 @@ require 'rexml/document'
module Redmine
module Scm
- module Adapters
+ module Adapters
class DarcsAdapter < AbstractAdapter
# Darcs executable name
DARCS_BIN = Redmine::Configuration['scm_darcs_command'] || "darcs"
-
+
class << self
+ def client_command
+ @@bin ||= DARCS_BIN
+ end
+
+ def sq_bin
+ @@sq_bin ||= shell_quote(DARCS_BIN)
+ end
+
def client_version
@@client_version ||= (darcs_binary_version || [])
end
-
+
def darcs_binary_version
darcsversion = darcs_binary_version_from_command_line
if m = darcsversion.match(%r{\A(.*?)((\d+\.)+\d+)})
@@ -38,7 +46,7 @@ module Redmine
end
def darcs_binary_version_from_command_line
- shellout("#{DARCS_BIN} --version") { |io| io.read }.to_s
+ shellout("#{sq_bin} --version") { |io| io.read }.to_s
end
end
@@ -57,7 +65,7 @@ module Redmine
rev = revisions(nil,nil,nil,{:limit => 1})
rev ? Info.new({:root_url => @url, :lastrev => rev.last}) : nil
end
-
+
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def entries(path=nil, identifier=nil)
@@ -66,7 +74,7 @@ module Redmine
path = ( self.class.client_version_above?([2, 2, 0]) ? @url : '.' )
end
entries = Entries.new
- cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --xml-output"
+ cmd = "#{self.class.sq_bin} annotate --repodir #{shell_quote @url} --xml-output"
cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
cmd << " #{shell_quote path}"
shellout(cmd) do |io|
@@ -86,11 +94,11 @@ module Redmine
return nil if $? && $?.exitstatus != 0
entries.compact.sort_by_name
end
-
+
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path = '.' if path.blank?
revisions = Revisions.new
- cmd = "#{DARCS_BIN} changes --repodir #{shell_quote @url} --xml-output"
+ cmd = "#{self.class.sq_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|
@@ -113,10 +121,10 @@ module Redmine
return nil if $? && $?.exitstatus != 0
revisions
end
-
+
def diff(path, identifier_from, identifier_to=nil)
path = '*' if path.blank?
- cmd = "#{DARCS_BIN} diff --repodir #{shell_quote @url}"
+ cmd = "#{self.class.sq_bin} diff --repodir #{shell_quote @url}"
if identifier_to.nil?
cmd << " --match #{shell_quote("hash #{identifier_from}")}"
else
@@ -133,9 +141,9 @@ module Redmine
return nil if $? && $?.exitstatus != 0
diff
end
-
+
def cat(path, identifier=nil)
- cmd = "#{DARCS_BIN} show content --repodir #{shell_quote @url}"
+ cmd = "#{self.class.sq_bin} show content --repodir #{shell_quote @url}"
cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
cmd << " #{shell_quote path}"
cat = nil
@@ -148,7 +156,7 @@ module Redmine
end
private
-
+
# Returns an Entry from the given XML element
# or nil if the entry was deleted
def entry_from_xml(element, path_prefix)
@@ -196,10 +204,10 @@ module Redmine
end
paths
end
-
+
# Retrieve changed paths for a single patch
def get_paths_for_patch_raw(hash)
- cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --summary --xml-output"
+ cmd = "#{self.class.sq_bin} annotate --repodir #{shell_quote @url} --summary --xml-output"
cmd << " --match #{shell_quote("hash #{hash}")} "
paths = []
shellout(cmd) do |io|
diff --git a/lib/redmine/scm/adapters/filesystem_adapter.rb b/lib/redmine/scm/adapters/filesystem_adapter.rb
index 99296a09..b88892b5 100644
--- a/lib/redmine/scm/adapters/filesystem_adapter.rb
+++ b/lib/redmine/scm/adapters/filesystem_adapter.rb
@@ -25,7 +25,12 @@ module Redmine
module Scm
module Adapters
class FilesystemAdapter < AbstractAdapter
-
+
+ class << self
+ def client_available
+ true
+ end
+ end
def initialize(url, root_url=nil, login=nil, password=nil)
@url = with_trailling_slash(url)
diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb
index a40f4507..a241f839 100644
--- a/lib/redmine/scm/adapters/git_adapter.rb
+++ b/lib/redmine/scm/adapters/git_adapter.rb
@@ -19,11 +19,25 @@ require 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
- module Adapters
+ module Adapters
class GitAdapter < AbstractAdapter
# Git executable name
GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
+ class << self
+ def client_command
+ @@bin ||= GIT_BIN
+ end
+
+ def sq_bin
+ @@sq_bin ||= shell_quote(GIT_BIN)
+ end
+
+ def client_available
+ !client_version.empty?
+ end
+ end
+
def info
begin
Info.new(:root_url => url, :lastrev => lastrev('',nil))
@@ -35,7 +49,7 @@ module Redmine
def branches
return @branches if @branches
@branches = []
- cmd = "#{GIT_BIN} --git-dir #{target('')} branch --no-color"
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} branch --no-color"
shellout(cmd) do |io|
io.each_line do |line|
@branches << line.match('\s*\*?\s*(.*)$')[1]
@@ -46,7 +60,7 @@ module Redmine
def tags
return @tags if @tags
- cmd = "#{GIT_BIN} --git-dir #{target('')} tag"
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} tag"
shellout(cmd) do |io|
@tags = io.readlines.sort!.map{|t| t.strip}
end
@@ -59,7 +73,7 @@ module Redmine
def entries(path=nil, identifier=nil)
path ||= ''
entries = Entries.new
- cmd = "#{GIT_BIN} --git-dir #{target('')} ls-tree -l "
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} ls-tree -l "
cmd << shell_quote("HEAD:" + path) if identifier.nil?
cmd << shell_quote(identifier + ":" + path) if identifier
shellout(cmd) do |io|
@@ -86,7 +100,7 @@ module Redmine
def lastrev(path,rev)
return nil if path.nil?
- cmd = "#{GIT_BIN} --git-dir #{target('')} log --no-color --date=iso --pretty=fuller --no-merges -n 1 "
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} log --no-color --date=iso --pretty=fuller --no-merges -n 1 "
cmd << " #{shell_quote rev} " if rev
cmd << "-- #{shell_quote path} " unless path.empty?
lines = []
@@ -114,7 +128,7 @@ module Redmine
def revisions(path, identifier_from, identifier_to, options={})
revisions = Revisions.new
- cmd = "#{GIT_BIN} --git-dir #{target('')} log --no-color --raw --date=iso --pretty=fuller "
+ cmd = "#{self.class.sq_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].to_i} " if options[:limit]
@@ -209,9 +223,9 @@ module Redmine
path ||= ''
if identifier_to
- cmd = "#{GIT_BIN} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
else
- cmd = "#{GIT_BIN} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
end
cmd << " -- #{shell_quote path}" unless path.empty?
@@ -227,7 +241,7 @@ module Redmine
def annotate(path, identifier=nil)
identifier = 'HEAD' if identifier.blank?
- cmd = "#{GIT_BIN} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}"
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}"
blame = Annotate.new
content = nil
shellout(cmd) { |io| io.binmode; content = io.read }
@@ -255,7 +269,7 @@ module Redmine
if identifier.nil?
identifier = 'HEAD'
end
- cmd = "#{GIT_BIN} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
+ cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
cat = nil
shellout(cmd) do |io|
io.binmode
diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb
index f64e70b2..f8f925d4 100644
--- a/lib/redmine/scm/adapters/mercurial_adapter.rb
+++ b/lib/redmine/scm/adapters/mercurial_adapter.rb
@@ -20,7 +20,7 @@ require 'cgi'
module Redmine
module Scm
- module Adapters
+ module Adapters
class MercurialAdapter < AbstractAdapter
# Mercurial executable name
@@ -30,11 +30,23 @@ module Redmine
TEMPLATE_EXTENSION = "tmpl"
class << self
+ def client_command
+ @@bin ||= HG_BIN
+ end
+
+ def sq_bin
+ @@sq_bin ||= shell_quote(HG_BIN)
+ end
+
def client_version
@@client_version ||= (hgversion || [])
end
- def hgversion
+ def client_available
+ !client_version.empty?
+ end
+
+ def hgversion
# The hg version is expressed either as a
# release number (eg 0.9.5 or 1.0) or as a revision
# id composed of 12 hexa characters.
@@ -45,7 +57,7 @@ module Redmine
end
def hgversion_from_command_line
- shellout("#{HG_BIN} --version") { |io| io.read }.to_s
+ shellout("#{sq_bin} --version") { |io| io.read }.to_s
end
def template_path
@@ -63,7 +75,7 @@ module Redmine
end
def info
- cmd = "#{HG_BIN} -R #{target('')} root"
+ cmd = "#{self.class.sq_bin} -R #{target('')} root"
root_url = nil
shellout(cmd) do |io|
root_url = io.read
@@ -80,7 +92,7 @@ module Redmine
def entries(path=nil, identifier=nil)
path ||= ''
entries = Entries.new
- cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate"
+ cmd = "#{self.class.sq_bin} -R #{target('')} --cwd #{target('')} locate"
cmd << " -r #{hgrev(identifier)}"
cmd << " " + shell_quote("path:#{path}") unless path.empty?
shellout(cmd) do |io|
@@ -106,7 +118,7 @@ module Redmine
# makes Mercurial produce a xml output.
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
revisions = Revisions.new
- cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}"
+ cmd = "#{self.class.sq_bin} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}"
if identifier_from && identifier_to
cmd << " -r #{hgrev(identifier_from)}:#{hgrev(identifier_to)}"
elsif identifier_from
@@ -164,7 +176,7 @@ module Redmine
return []
end
end
- cmd = "#{HG_BIN} -R #{target('')} --config diff.git=false diff --nodates #{diff_args}"
+ cmd = "#{self.class.sq_bin} -R #{target('')} --config diff.git=false diff --nodates #{diff_args}"
cmd << " -I #{target(path)}" unless path.empty?
shellout(cmd) do |io|
io.each_line do |line|
@@ -176,7 +188,7 @@ module Redmine
end
def cat(path, identifier=nil)
- cmd = "#{HG_BIN} -R #{target('')} cat"
+ cmd = "#{self.class.sq_bin} -R #{target('')} cat"
cmd << " -r #{hgrev(identifier)}"
cmd << " #{target(path)}"
cat = nil
@@ -190,7 +202,7 @@ module Redmine
def annotate(path, identifier=nil)
path ||= ''
- cmd = "#{HG_BIN} -R #{target('')}"
+ cmd = "#{self.class.sq_bin} -R #{target('')}"
cmd << " annotate -ncu"
cmd << " -r #{hgrev(identifier)}"
cmd << " #{target(path)}"
diff --git a/lib/redmine/scm/adapters/subversion_adapter.rb b/lib/redmine/scm/adapters/subversion_adapter.rb
index 28ea55e2..c17d7a5e 100644
--- a/lib/redmine/scm/adapters/subversion_adapter.rb
+++ b/lib/redmine/scm/adapters/subversion_adapter.rb
@@ -20,19 +20,27 @@ require 'uri'
module Redmine
module Scm
- module Adapters
+ module Adapters
class SubversionAdapter < AbstractAdapter
-
+
# SVN executable name
SVN_BIN = Redmine::Configuration['scm_subversion_command'] || "svn"
-
+
class << self
+ def client_command
+ @@bin ||= SVN_BIN
+ end
+
+ def sq_bin
+ @@sq_bin ||= shell_quote(SVN_BIN)
+ end
+
def client_version
@@client_version ||= (svn_binary_version || [])
end
-
+
def svn_binary_version
- cmd = "#{SVN_BIN} --version"
+ cmd = "#{sq_bin} --version"
version = nil
shellout(cmd) do |io|
# Read svn version in first returned line
@@ -44,10 +52,10 @@ module Redmine
version
end
end
-
+
# Get info about the svn repository
def info
- cmd = "#{SVN_BIN} info --xml #{target}"
+ cmd = "#{self.class.sq_bin} info --xml #{target}"
cmd << credentials_string
info = nil
shellout(cmd) do |io|
@@ -70,14 +78,14 @@ module Redmine
rescue CommandFailed
return nil
end
-
+
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def entries(path=nil, identifier=nil)
path ||= ''
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
entries = Entries.new
- cmd = "#{SVN_BIN} list --xml #{target(path)}@#{identifier}"
+ cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"
cmd << credentials_string
shellout(cmd) do |io|
output = io.read
@@ -110,13 +118,13 @@ module Redmine
logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
entries.sort_by_name
end
-
+
def properties(path, identifier=nil)
# proplist xml output supported in svn 1.5.0 and higher
return nil unless self.class.client_version_above?([1, 5, 0])
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
- cmd = "#{SVN_BIN} proplist --verbose --xml #{target(path)}@#{identifier}"
+ cmd = "#{self.class.sq_bin} proplist --verbose --xml #{target(path)}@#{identifier}"
cmd << credentials_string
properties = {}
shellout(cmd) do |io|
@@ -132,13 +140,13 @@ module Redmine
return nil if $? && $?.exitstatus != 0
properties
end
-
+
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= ''
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 = "#{self.class.sq_bin} log --xml -r #{identifier_from}:#{identifier_to}"
cmd << credentials_string
cmd << " --verbose " if options[:with_paths]
cmd << " --limit #{options[:limit].to_i}" if options[:limit]
@@ -171,13 +179,13 @@ module Redmine
return nil if $? && $?.exitstatus != 0
revisions
end
-
+
def diff(path, identifier_from, identifier_to=nil, type="inline")
path ||= ''
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : ''
identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1)
-
- cmd = "#{SVN_BIN} diff -r "
+
+ cmd = "#{self.class.sq_bin} diff -r "
cmd << "#{identifier_to}:"
cmd << "#{identifier_from}"
cmd << " #{target(path)}@#{identifier_from}"
@@ -191,10 +199,10 @@ module Redmine
return nil if $? && $?.exitstatus != 0
diff
end
-
+
def cat(path, identifier=nil)
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
- cmd = "#{SVN_BIN} cat #{target(path)}@#{identifier}"
+ cmd = "#{self.class.sq_bin} cat #{target(path)}@#{identifier}"
cmd << credentials_string
cat = nil
shellout(cmd) do |io|
@@ -204,10 +212,10 @@ module Redmine
return nil if $? && $?.exitstatus != 0
cat
end
-
+
def annotate(path, identifier=nil)
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
- cmd = "#{SVN_BIN} blame #{target(path)}@#{identifier}"
+ cmd = "#{self.class.sq_bin} blame #{target(path)}@#{identifier}"
cmd << credentials_string
blame = Annotate.new
shellout(cmd) do |io|
diff --git a/lib/redmine/themes.rb b/lib/redmine/themes.rb
index 77833cb3..697b4a31 100644
--- a/lib/redmine/themes.rb
+++ b/lib/redmine/themes.rb
@@ -89,11 +89,22 @@ module Redmine
private
def self.scan_themes
- dirs = Dir.glob("#{Rails.public_path}/themes/*").select do |f|
- # A theme should at least override application.css
- File.directory?(f) && File.exist?("#{f}/stylesheets/application.css")
- end
- dirs.collect {|dir| Theme.new(dir)}.sort
+ theme_paths.inject([]) do |themes, path|
+ dirs = Dir.glob(File.join(path, '*')).select do |f|
+ # A theme should at least override application.css
+ File.directory?(f) && File.exist?("#{f}/stylesheets/application.css")
+ end
+ themes += dirs.collect { |dir| Theme.new(dir) }
+ end.sort
+ end
+
+ def self.theme_paths
+ paths = Redmine::Configuration['themes_storage_path']
+ paths = [paths] unless paths.is_a?(Array)
+ paths.flatten!; paths.compact!
+
+ paths = ["#{Rails.public_path}/themes"] if paths.empty?
+ paths.collect { |p| File.expand_path(p, Rails.root) }
end
end
end
diff --git a/lib/redmine/version.rb b/lib/redmine/version.rb
index 8daf00f5..60f06efd 100644
--- a/lib/redmine/version.rb
+++ b/lib/redmine/version.rb
@@ -3,18 +3,10 @@ require 'rexml/document'
module Redmine
module VERSION #:nodoc:
MAJOR = 1
- MINOR = 1
+ MINOR = 2
PATCH = 0
TINY = PATCH # Redmine compat
- # Branches:
- # * stable - released version
- # * master - stable development
- # * unstable - future development
- #
- # Source: https://www.chiliproject.org/projects/chiliproject/wiki/ChiliProject_Repository
- BRANCH = 'master'
-
def self.revision
revision = nil
entries_path = "#{RAILS_ROOT}/.svn/entries"
@@ -37,7 +29,7 @@ module Redmine
end
REVISION = self.revision
- ARRAY = [MAJOR, MINOR, PATCH, BRANCH, REVISION].compact
+ ARRAY = [MAJOR, MINOR, PATCH, REVISION].compact
STRING = ARRAY.join('.')
def self.to_a; ARRAY end
diff --git a/lib/tasks/testing.rake b/lib/tasks/testing.rake
index 1f43a115..266dcf9c 100644
--- a/lib/tasks/testing.rake
+++ b/lib/tasks/testing.rake
@@ -51,7 +51,8 @@ namespace :test do
(supported_scms - [:subversion, :mercurial]).each do |scm|
desc "Creates a test #{scm} repository"
task scm => :create_dir do
- system "gunzip < test/fixtures/repositories/#{scm}_repository.tar.gz | tar -xv -C tmp/test"
+ # system "gunzip < test/fixtures/repositories/#{scm}_repository.tar.gz | tar -xv -C tmp/test"
+ system "tar -xvz -C tmp/test -f test/fixtures/repositories/#{scm}_repository.tar.gz"
end
end
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
index 1679d83d..26671685 100644
--- a/public/javascripts/application.js
+++ b/public/javascripts/application.js
@@ -210,7 +210,7 @@ function observeParentIssueField(url) {
new Ajax.Autocompleter('issue_parent_issue_id',
'parent_issue_candidates',
url,
- { minChars: 3,
+ { minChars: 1,
frequency: 0.5,
paramName: 'q',
updateElement: function(value) {
@@ -222,7 +222,7 @@ function observeRelatedIssueField(url) {
new Ajax.Autocompleter('relation_issue_to_id',
'related_issue_candidates',
url,
- { minChars: 3,
+ { minChars: 1,
frequency: 0.5,
paramName: 'q',
updateElement: function(value) {
diff --git a/public/javascripts/jstoolbar/jstoolbar.js b/public/javascripts/jstoolbar/jstoolbar.js
index 3c3a3433..dbc973fe 100644
--- a/public/javascripts/jstoolbar/jstoolbar.js
+++ b/public/javascripts/jstoolbar/jstoolbar.js
@@ -242,108 +242,107 @@ jsToolBar.prototype = {
this.encloseSelection(stag,etag);
},
-
- encloseLineSelection: function(prefix, suffix, fn) {
- this.textarea.focus();
-
- prefix = prefix || '';
- suffix = suffix || '';
-
- var start, end, sel, scrollPos, subst, res;
-
- if (typeof(document["selection"]) != "undefined") {
- sel = document.selection.createRange().text;
- } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
- start = this.textarea.selectionStart;
- end = this.textarea.selectionEnd;
- scrollPos = this.textarea.scrollTop;
- // go to the start of the line
- start = this.textarea.value.substring(0, start).replace(/[^\r\n]*$/g,'').length;
- // go to the end of the line
- end = this.textarea.value.length - this.textarea.value.substring(end, this.textarea.value.length).replace(/^[^\r\n]*/, '').length;
- sel = this.textarea.value.substring(start, end);
- }
-
- if (sel.match(/ $/)) { // exclude ending space char, if any
- sel = sel.substring(0, sel.length - 1);
- suffix = suffix + " ";
- }
-
- if (typeof(fn) == 'function') {
- res = (sel) ? fn.call(this,sel) : fn('');
- } else {
- res = (sel) ? sel : '';
- }
-
- subst = prefix + res + suffix;
-
- if (typeof(document["selection"]) != "undefined") {
- document.selection.createRange().text = subst;
- var range = this.textarea.createTextRange();
- range.collapse(false);
- range.move('character', -suffix.length);
- range.select();
- } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
- this.textarea.value = this.textarea.value.substring(0, start) + subst +
- this.textarea.value.substring(end);
- if (sel) {
- this.textarea.setSelectionRange(start + subst.length, start + subst.length);
- } else {
- this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
- }
- this.textarea.scrollTop = scrollPos;
- }
- },
-
- encloseSelection: function(prefix, suffix, fn) {
- this.textarea.focus();
-
- prefix = prefix || '';
- suffix = suffix || '';
-
- var start, end, sel, scrollPos, subst, res;
-
- if (typeof(document["selection"]) != "undefined") {
- sel = document.selection.createRange().text;
- } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
- start = this.textarea.selectionStart;
- end = this.textarea.selectionEnd;
- scrollPos = this.textarea.scrollTop;
- sel = this.textarea.value.substring(start, end);
- }
-
- if (sel.match(/ $/)) { // exclude ending space char, if any
- sel = sel.substring(0, sel.length - 1);
- suffix = suffix + " ";
- }
-
- if (typeof(fn) == 'function') {
- res = (sel) ? fn.call(this,sel) : fn('');
- } else {
- res = (sel) ? sel : '';
- }
-
- subst = prefix + res + suffix;
-
- if (typeof(document["selection"]) != "undefined") {
- document.selection.createRange().text = subst;
- var range = this.textarea.createTextRange();
- range.collapse(false);
- range.move('character', -suffix.length);
- range.select();
-// this.textarea.caretPos -= suffix.length;
- } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
- this.textarea.value = this.textarea.value.substring(0, start) + subst +
- this.textarea.value.substring(end);
- if (sel) {
- this.textarea.setSelectionRange(start + subst.length, start + subst.length);
- } else {
- this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
- }
- this.textarea.scrollTop = scrollPos;
- }
- },
-
+
+ encloseLineSelection: function (prefix, suffix, fn) {
+ this.textarea.focus();
+ prefix = prefix || '';
+ suffix = suffix || '';
+ var start, end, sel, scrollPos, subst, res;
+ if (typeof(document["selection"]) != "undefined") {
+ // just makes it work in IE8 somehow
+ var range = document.selection.createRange();
+ var bookmark = range.getBookmark();
+ var origParent = range.parentElement();
+ // we move the starting point of the selection to the last newline
+ try {
+ while (range.text[0] != "\n" && range.text[0] != "\r") {
+ bookmark = range.getBookmark();
+ range.moveStart("character", -1);
+ if (origParent != range.parentElement()) {
+ throw "Outside of Textarea";
+ }
+ }
+ range.moveStart("character", 1);
+ } catch(err) {
+ if (err == "Outside of Textarea")
+ range.moveToBookmark(bookmark);
+ else
+ throw err;
+ }
+ if (range.text.match(/ $/))
+ range.moveEnd("character", -1);
+ sel = range.text;
+ } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+ start = this.textarea.selectionStart;
+ end = this.textarea.selectionEnd;
+ scrollPos = this.textarea.scrollTop;
+ // go to the start of the line
+ start = this.textarea.value.substring(0, start).replace(/[^\r\n]*$/g,'').length;
+ // go to the end of the line
+ end = this.textarea.value.length - this.textarea.value.substring(end, this.textarea.value.length).replace(/^[^\r\n]*/, '').length;
+ sel = this.textarea.value.substring(start, end);
+ }
+ if (sel.match(/ $/)) {
+ sel = sel.substring(0, sel.length - 1);
+ suffix = suffix + " ";
+ }
+ if (typeof(fn) == 'function') {
+ res = (sel) ? fn.call(this, sel) : fn('');
+ } else {
+ res = (sel) ? sel : '';
+ }
+ subst = prefix + res + suffix;
+ if (typeof(document["selection"]) != "undefined") {
+ range.text = subst;
+ this.textarea.caretPos -= suffix.length;
+ } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+ this.textarea.value = this.textarea.value.substring(0, start) + subst + this.textarea.value.substring(end);
+ if (sel) {
+ this.textarea.setSelectionRange(start + subst.length, start + subst.length);
+ } else {
+ this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
+ }
+ this.textarea.scrollTop = scrollPos;
+ }
+ },
+
+ encloseSelection: function (prefix, suffix, fn) {
+ this.textarea.focus();
+ prefix = prefix || '';
+ suffix = suffix || '';
+ var start, end, sel, scrollPos, subst, res;
+ if (typeof(document["selection"]) != "undefined") {
+ sel = document.selection.createRange().text;
+ } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+ start = this.textarea.selectionStart;
+ end = this.textarea.selectionEnd;
+ scrollPos = this.textarea.scrollTop;
+ sel = this.textarea.value.substring(start, end);
+ }
+ if (sel.match(/ $/)) {
+ sel = sel.substring(0, sel.length - 1);
+ suffix = suffix + " ";
+ }
+ if (typeof(fn) == 'function') {
+ res = (sel) ? fn.call(this, sel) : fn('');
+ } else {
+ res = (sel) ? sel : '';
+ }
+ subst = prefix + res + suffix;
+ if (typeof(document["selection"]) != "undefined") {
+ var range = document.selection.createRange().text = subst;
+ this.textarea.caretPos -= suffix.length;
+ } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+ this.textarea.value = this.textarea.value.substring(0, start) + subst + this.textarea.value.substring(end);
+ if (sel) {
+ this.textarea.setSelectionRange(start + subst.length, start + subst.length);
+ } else {
+ this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
+ }
+ this.textarea.scrollTop = scrollPos;
+ }
+ },
+
stripBaseURL: function(url) {
if (this.base_url != '') {
var pos = url.indexOf(this.base_url);
diff --git a/test/functional/application_controller_test.rb b/test/functional/application_controller_test.rb
index 57f3e091..5f94a96e 100644
--- a/test/functional/application_controller_test.rb
+++ b/test/functional/application_controller_test.rb
@@ -47,55 +47,55 @@ class ApplicationControllerTest < ActionController::TestCase
context "test_api_offset_and_limit" do
context "without params" do
should "return 0, 25" do
- assert_equal [0, 25], @controller.api_offset_and_limit({})
+ assert_equal [0, 25], @controller.send(:api_offset_and_limit, {})
end
end
context "with limit" do
should "return 0, limit" do
- assert_equal [0, 30], @controller.api_offset_and_limit({:limit => 30})
+ assert_equal [0, 30], @controller.send(:api_offset_and_limit, {:limit => 30})
end
should "not exceed 100" do
- assert_equal [0, 100], @controller.api_offset_and_limit({:limit => 120})
+ assert_equal [0, 100], @controller.send(:api_offset_and_limit, {:limit => 120})
end
should "not be negative" do
- assert_equal [0, 25], @controller.api_offset_and_limit({:limit => -10})
+ assert_equal [0, 25], @controller.send(:api_offset_and_limit, {:limit => -10})
end
end
context "with offset" do
should "return offset, 25" do
- assert_equal [10, 25], @controller.api_offset_and_limit({:offset => 10})
+ assert_equal [10, 25], @controller.send(:api_offset_and_limit, {:offset => 10})
end
should "not be negative" do
- assert_equal [0, 25], @controller.api_offset_and_limit({:offset => -10})
+ assert_equal [0, 25], @controller.send(:api_offset_and_limit, {:offset => -10})
end
context "and limit" do
should "return offset, limit" do
- assert_equal [10, 50], @controller.api_offset_and_limit({:offset => 10, :limit => 50})
+ assert_equal [10, 50], @controller.send(:api_offset_and_limit, {:offset => 10, :limit => 50})
end
end
end
context "with page" do
should "return offset, 25" do
- assert_equal [0, 25], @controller.api_offset_and_limit({:page => 1})
- assert_equal [50, 25], @controller.api_offset_and_limit({:page => 3})
+ assert_equal [0, 25], @controller.send(:api_offset_and_limit, {:page => 1})
+ assert_equal [50, 25], @controller.send(:api_offset_and_limit, {:page => 3})
end
should "not be negative" do
- assert_equal [0, 25], @controller.api_offset_and_limit({:page => 0})
- assert_equal [0, 25], @controller.api_offset_and_limit({:page => -2})
+ assert_equal [0, 25], @controller.send(:api_offset_and_limit, {:page => 0})
+ assert_equal [0, 25], @controller.send(:api_offset_and_limit, {:page => -2})
end
context "and limit" do
should "return offset, limit" do
- assert_equal [0, 100], @controller.api_offset_and_limit({:page => 1, :limit => 100})
- assert_equal [200, 100], @controller.api_offset_and_limit({:page => 3, :limit => 100})
+ assert_equal [0, 100], @controller.send(:api_offset_and_limit, {:page => 1, :limit => 100})
+ assert_equal [200, 100], @controller.send(:api_offset_and_limit, {:page => 3, :limit => 100})
end
end
end
diff --git a/test/functional/auto_completes_controller_test.rb b/test/functional/auto_completes_controller_test.rb
index bad8c3bd..db246c30 100644
--- a/test/functional/auto_completes_controller_test.rb
+++ b/test/functional/auto_completes_controller_test.rb
@@ -16,6 +16,24 @@ class AutoCompletesControllerTest < ActionController::TestCase
assert_not_nil assigns(:issues)
assert assigns(:issues).include?(Issue.find(13))
end
+
+ test 'should return issues matching a given id' do
+ @project = Project.find('subproject1')
+ @issue_21 = Issue.generate_for_project!(@project, :id => 21)
+ @issue_101 = Issue.generate_for_project!(@project, :id => 101)
+ @issue_102 = Issue.generate_for_project!(@project, :id => 102)
+ @issue_with_subject = Issue.generate_for_project!(@project, :subject => 'This has 1 in the subject')
+
+ get :issues, :project_id => @project.id, :q => '1'
+
+ assert_response :success
+ assert_not_nil assigns(:issues)
+ assert assigns(:issues).include?(Issue.find(13))
+ assert assigns(:issues).include?(@issue_21)
+ assert assigns(:issues).include?(@issue_101)
+ assert assigns(:issues).include?(@issue_102)
+ assert assigns(:issues).include?(@issue_with_subject)
+ end
def test_auto_complete_with_scope_all_and_cross_project_relations
Setting.cross_project_issue_relations = '1'
diff --git a/test/integration/account_test.rb b/test/integration/account_test.rb
index 4943f862..178852f6 100644
--- a/test/integration/account_test.rb
+++ b/test/integration/account_test.rb
@@ -50,7 +50,7 @@ class AccountTest < ActionController::IntegrationTest
assert_equal user, token.user
assert_equal 'autologin', token.action
assert_equal user.id, session[:user_id]
- assert_equal token.value, cookies['autologin']
+ assert_equal token.value, cookies[Redmine::Configuration['autologin_cookie_name']]
# Session is cleared
reset!
@@ -60,7 +60,7 @@ class AccountTest < ActionController::IntegrationTest
assert_nil user.reload.last_login_on
# User comes back with his autologin cookie
- cookies[:autologin] = token.value
+ cookies[Redmine::Configuration['autologin_cookie_name']] = token.value
get '/my/page'
assert_response :success
assert_template 'my/page'
diff --git a/test/unit/lib/redmine/safe_attributes_test.rb b/test/unit/lib/redmine/safe_attributes_test.rb
index 9498a438..195b4446 100644
--- a/test/unit/lib/redmine/safe_attributes_test.rb
+++ b/test/unit/lib/redmine/safe_attributes_test.rb
@@ -35,11 +35,16 @@ class Redmine::SafeAttributesTest < ActiveSupport::TestCase
end
class Book < Base
- attr_accessor :title
+ attr_accessor :title, :isbn
include Redmine::SafeAttributes
safe_attributes :title
end
+
+ class PublishedBook < Book
+ safe_attributes :isbn
+ end
+
def test_safe_attribute_names
p = Person.new
assert_equal ['firstname', 'lastname'], p.safe_attribute_names(User.anonymous)
@@ -84,4 +89,25 @@ class Redmine::SafeAttributesTest < ActiveSupport::TestCase
assert_equal 'Smith', p.lastname
assert_equal 'jsmith', p.login
end
+
+ def test_with_indifferent_access
+ p = Person.new
+ p.safe_attributes = {'firstname' => 'Jack', :lastname => 'Miller'}
+ assert_equal 'Jack', p.firstname
+ assert_equal 'Miller', p.lastname
+ end
+
+ def test_use_safe_attributes_in_subclasses
+ b = Book.new
+ p = PublishedBook.new
+
+ b.safe_attributes = {'title' => 'My awesome Ruby Book', 'isbn' => '1221132343'}
+ p.safe_attributes = {'title' => 'The Pickaxe', 'isbn' => '1934356085'}
+
+ assert_equal 'My awesome Ruby Book', b.title
+ assert_nil b.isbn
+
+ assert_equal 'The Pickaxe', p.title
+ assert_equal '1934356085', p.isbn
+ end
end
diff --git a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb
new file mode 100644
index 00000000..82372bbb
--- /dev/null
+++ b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb
@@ -0,0 +1,25 @@
+require File.expand_path('../../../../../../test_helper', __FILE__)
+begin
+ require 'mocha'
+
+ class BazaarAdapterTest < ActiveSupport::TestCase
+
+ REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/bazaar_repository'
+
+ if File.directory?(REPOSITORY_PATH)
+ def setup
+ @adapter = Redmine::Scm::Adapters::BazaarAdapter.new(MODULE_NAME, REPOSITORY_PATH)
+ end
+
+ else
+ puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!"
+ def test_fake; assert true end
+ end
+ end
+
+rescue LoadError
+ class BazaarMochaFake < ActiveSupport::TestCase
+ def test_fake; assert(false, "Requires mocha to run those tests") end
+ end
+end
+
diff --git a/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb
index e063d44f..b6b4b6ee 100644
--- a/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb
@@ -5,6 +5,7 @@ begin
class CvsAdapterTest < ActiveSupport::TestCase
REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/cvs_repository'
+ REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
MODULE_NAME = 'test'
if File.directory?(REPOSITORY_PATH)
diff --git a/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb
index bc9959dc..049296d9 100644
--- a/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb
@@ -1,71 +1,92 @@
# encoding: utf-8
+# This file includes UTF-8 "Felix Schäfer".
+# We need to consider Ruby 1.9 compatibility.
+
require File.expand_path('../../../../../../test_helper', __FILE__)
+begin
+ require 'mocha'
-class GitAdapterTest < ActiveSupport::TestCase
- REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
+ class GitAdapterTest < ActiveSupport::TestCase
+ REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
- if File.directory?(REPOSITORY_PATH)
- def setup
- @adapter = Redmine::Scm::Adapters::GitAdapter.new(REPOSITORY_PATH)
- end
+ if File.directory?(REPOSITORY_PATH)
+ def setup
+ @adapter = Redmine::Scm::Adapters::GitAdapter.new(REPOSITORY_PATH)
+ end
- def test_branches
- assert_equal @adapter.branches, ['master', 'test_branch']
- end
+ def test_branches
+ assert_equal @adapter.branches, ['master', 'test_branch']
+ end
- def test_getting_all_revisions
- assert_equal 15, @adapter.revisions('',nil,nil,:all => true).length
+ def test_getting_all_revisions
+ assert_equal 15, @adapter.revisions('',nil,nil,:all => true).length
+ end
+
+ def test_getting_certain_revisions
+ assert_equal 1, @adapter.revisions('','899a15d^','899a15d').length
+ end
+
+ def test_getting_revisions_with_spaces_in_filename
+ assert_equal 1, @adapter.revisions("filemane with spaces.txt",
+ nil, nil, :all => true).length
+ end
+
+ def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
+ assert_equal " filename with a leading space.txt ",
+ @adapter.revisions(" filename with a leading space.txt ",
+ nil, nil, :all => true)[0].paths[0][:path]
+ end
+
+ def test_getting_entries_with_leading_and_trailing_spaces_in_filename
+ assert_equal " filename with a leading space.txt ",
+ @adapter.entries('',
+ '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
+ end
+
+ def test_annotate
+ annotate = @adapter.annotate('sources/watchers_controller.rb')
+ assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
+ assert_equal 41, annotate.lines.size
+ assert_equal "# This program is free software; you can redistribute it and/or", annotate.lines[4].strip
+ assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
+ annotate.revisions[4].identifier
+ assert_equal "jsmith", annotate.revisions[4].author
+ end
+
+ def test_annotate_moved_file
+ annotate = @adapter.annotate('renamed_test.txt')
+ assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
+ assert_equal 2, annotate.lines.size
+ end
+
+ def test_last_rev
+ last_rev = @adapter.lastrev("README",
+ "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
+ assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
+ assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
+ assert_equal "Adam Soltys ", last_rev.author
+ assert_equal "2009-06-24 05:27:38".to_time, last_rev.time
+ end
+
+ def test_last_rev_with_spaces_in_filename
+ last_rev = @adapter.lastrev("filemane with spaces.txt",
+ "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
+ assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
+ assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
+ assert_equal "Felix Schäfer ",
+ last_rev.author
+ assert_equal "2010-09-18 19:59:46".to_time, last_rev.time
+ end
+ else
+ puts "Git test repository NOT FOUND. Skipping unit tests !!!"
+ def test_fake; assert true end
end
-
- def test_getting_certain_revisions
- assert_equal 1, @adapter.revisions('','899a15d^','899a15d').length
- end
-
- def test_getting_revisions_with_spaces_in_filename
- assert_equal 1, @adapter.revisions("filemane with spaces.txt", nil, nil, :all => true).length
- end
-
- def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
- assert_equal " filename with a leading space.txt ", @adapter.revisions(" filename with a leading space.txt ", nil, nil, :all => true)[0].paths[0][:path]
- end
-
- def test_getting_entries_with_leading_and_trailing_spaces_in_filename
- assert_equal " filename with a leading space.txt ", @adapter.entries('', '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
- end
-
- def test_annotate
- annotate = @adapter.annotate('sources/watchers_controller.rb')
- assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
- assert_equal 41, annotate.lines.size
- assert_equal "# This program is free software; you can redistribute it and/or", annotate.lines[4].strip
- assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", annotate.revisions[4].identifier
- assert_equal "jsmith", annotate.revisions[4].author
- end
-
- def test_annotate_moved_file
- annotate = @adapter.annotate('renamed_test.txt')
- assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
- assert_equal 2, annotate.lines.size
- end
-
- def test_last_rev
- last_rev = @adapter.lastrev("README", "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
- assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
- assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
- assert_equal "Adam Soltys ", last_rev.author
- assert_equal "2009-06-24 05:27:38".to_time, last_rev.time
- end
-
- def test_last_rev_with_spaces_in_filename
- last_rev = @adapter.lastrev("filemane with spaces.txt", "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
- assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
- assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
- assert_equal "Felix Schäfer ", last_rev.author
- assert_equal "2010-09-18 19:59:46".to_time, last_rev.time
- end
- else
- puts "Git test repository NOT FOUND. Skipping unit tests !!!"
- def test_fake; assert true end
+ end
+
+rescue LoadError
+ class GitMochaFake < ActiveSupport::TestCase
+ def test_fake; assert(false, "Requires mocha to run those tests") end
end
end
+
diff --git a/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb
index 99e8ceb2..966a8292 100644
--- a/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb
+++ b/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb
@@ -17,15 +17,27 @@
require File.expand_path('../../../../../../test_helper', __FILE__)
-class SubversionAdapterTest < ActiveSupport::TestCase
+begin
+ require 'mocha'
+
+ class SubversionAdapterTest < ActiveSupport::TestCase
- if repository_configured?('subversion')
- def test_client_version
- v = Redmine::Scm::Adapters::SubversionAdapter.client_version
- assert v.is_a?(Array)
+ if repository_configured?('subversion')
+ def setup
+ end
+
+ def test_client_version
+ v = Redmine::Scm::Adapters::SubversionAdapter.client_version
+ assert v.is_a?(Array)
+ end
+ else
+ puts "Subversion test repository NOT FOUND. Skipping unit tests !!!"
+ def test_fake; assert true end
end
- else
- puts "Subversion test repository NOT FOUND. Skipping unit tests !!!"
- def test_fake; assert true end
+ end
+
+rescue LoadError
+ class SubversionMochaFake < ActiveSupport::TestCase
+ def test_fake; assert(false, "Requires mocha to run those tests") end
end
end
diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb
index 4d85a2b2..a20c14e9 100644
--- a/test/unit/query_test.rb
+++ b/test/unit/query_test.rb
@@ -467,6 +467,7 @@ class QueryTest < ActiveSupport::TestCase
@group2 = Group.generate!.reload
@group2.users << @user_in_group2
+ @empty_group = Group.generate!.reload
end
should "search assigned to for users in the group" do
@@ -484,7 +485,6 @@ class QueryTest < ActiveSupport::TestCase
# Users not in a group
assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
assert_find_issues_with_query_is_successful @query
-
end
should "search assigned to any group member (all)" do
@@ -494,7 +494,22 @@ class QueryTest < ActiveSupport::TestCase
# Only users in a group
assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
assert_find_issues_with_query_is_successful @query
-
+ end
+
+ should "return no results on empty set" do
+ @query = Query.new(:name => '_')
+ @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
+
+ assert_query_statement_includes @query, "(0=1)"
+ assert find_issues_with_query(@query).empty?
+ end
+
+ should "return results on disallowed empty set" do
+ @query = Query.new(:name => '_')
+ @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
+
+ assert_query_statement_includes @query, "(1=1)"
+ assert_find_issues_with_query_is_successful @query
end
end
@@ -507,6 +522,7 @@ class QueryTest < ActiveSupport::TestCase
@manager_role = Role.generate!(:name => 'Manager')
@developer_role = Role.generate!(:name => 'Developer')
+ @empty_role = Role.generate!(:name => 'Empty')
@project = Project.generate!
@manager = User.generate!
@@ -540,6 +556,22 @@ class QueryTest < ActiveSupport::TestCase
assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@manager.id}','#{@developer.id}','#{@boss.id}')"
assert_find_issues_with_query_is_successful @query
end
+
+ should "return no results on empty set" do
+ @query = Query.new(:name => '_')
+ @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
+
+ assert_query_statement_includes @query, "(0=1)"
+ assert find_issues_with_query(@query).empty?
+ end
+
+ should "return results on disallowed empty set" do
+ @query = Query.new(:name => '_')
+ @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
+
+ assert_query_statement_includes @query, "(1=1)"
+ assert_find_issues_with_query_is_successful @query
+ end
end
end