Compare commits

...

26 Commits
2.2.0 ... 1.1.0

Author SHA1 Message Date
Jean-Philippe Lang
17517575ec tagged version 1.1.0
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/1.1.0@4672 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-09 16:27:39 +00:00
Jean-Philippe Lang
f8af1bebd7 Merged r4670 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4671 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-09 15:58:15 +00:00
Jean-Philippe Lang
6e695a4d1a Merged r4645 to r4651 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4660 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 10:14:00 +00:00
Jean-Philippe Lang
e5b7c94cb4 Merged r4643 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4659 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 10:09:04 +00:00
Jean-Philippe Lang
9028e664c4 Merged r4656 and r4657 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4658 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 10:04:38 +00:00
Toshi MARUYAMA
6318affd31 Merged r4652 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4653 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-08 00:24:43 +00:00
Jean-Philippe Lang
8794cd3344 Merged r4610 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4649 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-06 21:04:24 +00:00
Toshi MARUYAMA
2fcd4e5271 Merged r4636 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4642 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:12:08 +00:00
Toshi MARUYAMA
ea60705ca7 Merged r4635 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4641 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:11:47 +00:00
Toshi MARUYAMA
90bce4e366 Merged r4634 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4640 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:11:21 +00:00
Toshi MARUYAMA
9f7cc355ad Merged r4633 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4639 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:10:58 +00:00
Toshi MARUYAMA
6ee4c0bac7 Merged r4632 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4638 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:10:38 +00:00
Toshi MARUYAMA
b0f0bd1848 Merged r4631 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4637 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-05 15:10:17 +00:00
Toshi MARUYAMA
fe563a8802 Merged r4629 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4630 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-04 16:39:45 +00:00
Toshi MARUYAMA
8ebab00767 Merged r4625 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4628 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-03 10:55:07 +00:00
Toshi MARUYAMA
d97297e45d Merged r4624 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4627 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-03 10:54:29 +00:00
Toshi MARUYAMA
f06500dcce Merged r4623 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4626 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-03 10:53:47 +00:00
Jean-Philippe Lang
b098e2b63f Merged r4621 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4622 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 14:37:55 +00:00
Jean-Philippe Lang
151a49b319 Merged r4618 and r4619 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4620 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 12:17:39 +00:00
Jean-Philippe Lang
ded234794e Merged r4615 and r4616 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4617 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 11:44:19 +00:00
Toshi MARUYAMA
119732c3ee Merged r4613 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4614 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 09:52:29 +00:00
Toshi MARUYAMA
730fcef844 Merged r4611 from trunk (Mercurial sorting).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4612 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-02 06:14:59 +00:00
Toshi MARUYAMA
1cb33f3a95 Merged r4608 from trunk (repository: switch darcs cat test if cat supports).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4609 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-01 22:05:48 +00:00
Toshi MARUYAMA
69edd3c53f Merged r4606 from trunk (.hgignore).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4607 e93f8b46-1217-0410-a6f0-8f06a7374b81
2011-01-01 21:20:25 +00:00
Jean-Philippe Lang
ad784e2146 Merged locales updates (r4592 to 4595) from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4596 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-30 15:54:47 +00:00
Jean-Philippe Lang
c783ae4b3d 1.1-stable branch added
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/1.1-stable@4585 e93f8b46-1217-0410-a6f0-8f06a7374b81
2010-12-30 15:09:18 +00:00
53 changed files with 840 additions and 317 deletions

22
.hgignore Normal file
View File

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

View File

@@ -44,6 +44,8 @@ class IssuesController < ApplicationController
include AttachmentsHelper
helper :queries
include QueriesHelper
helper :repositories
include RepositoriesHelper
helper :sort
include SortHelper
include IssuesHelper

View File

@@ -32,9 +32,6 @@ class ProjectsController < ApplicationController
end
end
# TODO: convert to PUT only
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
helper :sort
include SortHelper
helper :custom_fields
@@ -71,13 +68,13 @@ class ProjectsController < ApplicationController
@project = Project.new(params[:project])
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new
@project.safe_attributes = params[:project]
@project.enabled_module_names = params[:enabled_modules] if params[:enabled_modules]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
# Add current user as a project member if he is not admin
@@ -124,13 +121,13 @@ class ProjectsController < ApplicationController
if validate_parent_id && @project.copy(@source_project, :only => params[:only])
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'projects', :action => 'settings'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
elsif !@project.new_record?
# Project was created
# But some objects were not copied due to validation failures
# (eg. issues from disabled trackers)
# TODO: inform about that
redirect_to :controller => 'projects', :action => 'settings'
redirect_to :controller => 'projects', :action => 'settings', :id => @project
end
end
end
@@ -184,6 +181,8 @@ class ProjectsController < ApplicationController
def edit
end
# TODO: convert to PUT only
verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@@ -205,9 +204,10 @@ class ProjectsController < ApplicationController
end
end
end
verify :method => :post, :only => :modules, :render => {:nothing => true, :status => :method_not_allowed }
def modules
@project.enabled_module_names = params[:enabled_modules]
@project.enabled_module_names = params[:enabled_module_names]
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project, :tab => 'modules'
end

View File

@@ -174,6 +174,9 @@ class RepositoriesController < ApplicationController
@diff = @repository.diff(@path, @rev, @rev_to)
show_error_not_found unless @diff
end
@changeset = @repository.find_changeset_by_name(@rev)
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
end
end

View File

@@ -104,8 +104,10 @@ module ApplicationHelper
# * :text - Link text (default to the formatted revision)
def link_to_revision(revision, project, options={})
text = options.delete(:text) || format_revision(revision)
rev = revision.respond_to?(:identifier) ? revision.identifier : revision
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
:title => l(:label_revision_id, format_revision(revision)))
end
# Generates a link to a project if active

View File

@@ -18,8 +18,12 @@
require 'iconv'
module RepositoriesHelper
def format_revision(txt)
txt.to_s[0,8]
def format_revision(revision)
if revision.respond_to? :format_identifier
revision.format_identifier
else
revision.to_s
end
end
def truncate_at_line_break(text, length = 255)
@@ -87,7 +91,7 @@ module RepositoriesHelper
:action => 'show',
:id => @project,
:path => path_param,
:rev => @changeset.revision)
:rev => @changeset.identifier)
output << "<li class='#{style}'>#{text}</li>"
output << render_changes_tree(s)
elsif c = tree[file][:c]
@@ -97,13 +101,13 @@ module RepositoriesHelper
:action => 'entry',
:id => @project,
:path => path_param,
:rev => @changeset.revision) unless c.action == 'D'
:rev => @changeset.identifier) unless c.action == 'D'
text << " - #{c.revision}" unless c.revision.blank?
text << ' (' + link_to('diff', :controller => 'repositories',
:action => 'diff',
:id => @project,
:path => path_param,
:rev => @changeset.revision) + ') ' if c.action == 'M'
:rev => @changeset.identifier) + ') ' if c.action == 'M'
text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank?
output << "<li class='#{style}'>#{text}</li>"
end

View File

@@ -23,10 +23,10 @@ class Changeset < ActiveRecord::Base
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
:description => :long_comments,
:datetime => :committed_on,
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.revision}}
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
acts_as_searchable :columns => 'comments',
:include => {:repository => :project},
@@ -47,6 +47,15 @@ class Changeset < ActiveRecord::Base
def revision=(r)
write_attribute :revision, (r.nil? ? nil : r.to_s)
end
# Returns the identifier of this changeset; depending on repository backends
def identifier
if repository.class.respond_to? :changeset_identifier
repository.class.changeset_identifier self
else
revision.to_s
end
end
def comments=(comment)
write_attribute(:comments, Changeset.normalize_comments(comment))
@@ -56,6 +65,15 @@ class Changeset < ActiveRecord::Base
self.commit_date = date
super
end
# Returns the readable identifier
def format_identifier
if repository.class.respond_to? :format_changeset_identifier
repository.class.format_changeset_identifier self
else
identifier
end
end
def committer=(arg)
write_attribute(:committer, self.class.to_utf8(arg.to_s))

View File

@@ -66,7 +66,7 @@ class Project < ActiveRecord::Base
:url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o}},
:author => nil
attr_protected :status, :enabled_module_names
attr_protected :status
validates_presence_of :name, :identifier
validates_uniqueness_of :identifier
@@ -533,6 +533,9 @@ class Project < ActiveRecord::Base
'tracker_ids',
'issue_custom_field_ids'
safe_attributes 'enabled_module_names',
:if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) }
# Returns an array of projects that are in this project's hierarchy
#
# Example: parents, children, siblings
@@ -740,7 +743,12 @@ class Project < ActiveRecord::Base
# Copies members from +project+
def copy_members(project)
project.memberships.each do |member|
# Copy users first, then groups to handle members with inherited and given roles
members_to_copy = []
members_to_copy += project.memberships.select {|m| m.principal.is_a?(User)}
members_to_copy += project.memberships.select {|m| !m.principal.is_a?(User)}
members_to_copy.each do |member|
new_member = Member.new
new_member.attributes = member.attributes.dup.except("id", "project_id", "created_on")
# only copy non inherited roles

View File

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

View File

@@ -18,6 +18,9 @@
require 'redmine/scm/adapters/mercurial_adapter'
class Repository::Mercurial < Repository
# sort changesets by revision number
has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id'
attr_protected :root_url
validates_presence_of :url
@@ -52,6 +55,18 @@ class Repository::Mercurial < Repository
entries
end
# Returns the latest changesets for +path+; sorted by revision number
def latest_changesets(path, rev, limit=10)
if path.blank?
changesets.find(:all, :include => :user, :limit => limit)
else
changes.find(:all, :include => {:changeset => :user},
:conditions => ["path = ?", path.with_leading_slash],
:order => "#{Changeset.table_name}.id DESC",
:limit => limit).collect(&:changeset)
end
end
def fetch_changesets
scm_info = scm.info
if scm_info

View File

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

View File

@@ -69,7 +69,7 @@
<div id="footer">
<div class="bgl"><div class="bgr">
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2010 Jean-Philippe Lang
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2011 Jean-Philippe Lang
</div></div>
</div>
</div>

View File

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

View File

@@ -2,18 +2,6 @@
<% labelled_tabular_form_for :project, @project, :url => { :action => "create" } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend>
<% Redmine::AccessControl.available_project_modules.each do |m| %>
<label class="floating">
<%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %>
<%= l_or_humanize(m, :prefix => "project_module_") %>
</label>
<% end %>
<%= hidden_field_tag 'enabled_modules[]', '' %>
</fieldset>
<%= submit_tag l(:button_save) %>
<%= javascript_tag "Form.Element.focus('project_name');" %>
<% end %>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -127,7 +127,7 @@ bg:
general_text_Yes: 'Да'
general_text_no: 'не'
general_text_yes: 'да'
general_lang_name: 'Bulgarian'
general_lang_name: 'Bulgarian (Български)'
general_csv_separator: ','
general_csv_decimal_separator: '.'
general_csv_encoding: UTF-8
@@ -225,7 +225,6 @@ bg:
field_attr_lastname: Атрибут Фамилия (Lastname)
field_attr_mail: Атрибут Email
field_onthefly: Динамично създаване на потребител
field_start_date: Начална дата
field_done_ratio: % Прогрес
field_auth_source: Начин на оторизация
field_hide_mail: Скрий e-mail адреса ми
@@ -730,8 +729,8 @@ bg:
button_quote: Цитат
setting_sequential_project_identifiers: Генериране на последователни проектни идентификатори
notice_unable_delete_version: Невъзможност за изтриване на версия
label_renamed: преименуван
label_copied: копиран
label_renamed: преименувано
label_copied: копирано
setting_plain_text_mail: само чист текст (без HTML)
permission_view_files: Разглеждане на файлове
permission_edit_issues: Редактиране на задачи
@@ -859,7 +858,7 @@ bg:
setting_issue_done_ratio_issue_status: Използване на състоянието на задачите
error_issue_done_ratios_not_updated: Процентът на завършените задачи не е обновен.
error_workflow_copy_target: Моля изберете тракер(и) и роля (роли).
setting_issue_done_ratio_issue_field: Използване на поле 'задача'
setting_issue_done_ratio_issue_field: Използване на поле '% Прогрес'
label_copy_same_as_target: Също като целта
label_copy_target: Цел
notice_issue_done_ratios_updated: Обновен процент на завършените задачи.
@@ -923,12 +922,13 @@ bg:
label_user_mail_option_only_assigned: Само за неща, назначени на мен
button_edit_associated_wikipage: "Редактиране на асоциираната Wiki страница: %{page_title}"
field_assigned_to_role: Assignee's role
field_start_date: Начална дата
label_principal_search: "Търсене на потребител или група:"
label_user_search: "Търсене на потребител:"
field_visible: Видим
setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
setting_commit_logtime_activity_id: Дейност при отчитане на работното време
text_time_logged_by_changeset: Приложено в ревизия %{value}.
setting_commit_logtime_enabled: Разрешаване на отчитането на работното време
notice_gantt_chart_truncated: Мрежовият график е съкратен, понеже броят на обектите, които могат да бъдат показани е твърде голям (%{max})
setting_gantt_items_limit: Максимален брой обекти, които да се показват в мрежов график

View File

@@ -858,6 +858,7 @@ de:
text_subprojects_destroy_warning: "Dessen Unterprojekte (%{value}) werden ebenfalls gelöscht."
text_workflow_edit: Workflow zum Bearbeiten auswählen
text_are_you_sure: Sind Sie sicher?
text_are_you_sure_with_children: "Lösche Aufgabe und alle Unteraufgaben?"
text_journal_changed: "%{label} wurde von %{old} zu %{new} geändert"
text_journal_set_to: "%{label} wurde auf %{value} gesetzt"
text_journal_deleted: "%{label} %{old} wurde gelöscht"
@@ -936,20 +937,19 @@ de:
enumeration_activities: Aktivitäten (Zeiterfassung)
enumeration_system_activity: System-Aktivität
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of
setting_default_notification_option: Default notification option
label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
label_user_mail_option_only_assigned: Only for things I am assigned to
notice_not_authorized_archived_project: The project you're trying to access has been archived.
label_user_mail_option_none: No events
field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role
field_visible: Visible
setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
field_text: Textfeld
label_user_mail_option_only_owner: Nur für Aufgaben die ich angelegt habe
setting_default_notification_option: Standard Benachrichtigungsoptionen
label_user_mail_option_only_my_events: Nur für Aufgaben die ich beobachte oder an welchen ich mitarbeite
label_user_mail_option_only_assigned: Nur für Aufgaben für die ich zuständig bin.
notice_not_authorized_archived_project: Das Projekt wurde archiviert und ist daher nicht nicht verfügbar.
label_user_mail_option_none: keine Ereignisse
field_member_of_group: Zuständigkeitsgruppe
field_assigned_to_role: Zuständigkeitsrolle
field_visible: Sichtbar
setting_emails_header: Emailkopf
setting_commit_logtime_activity_id: Aktivität für die Zeiterfassung
text_time_logged_by_changeset: Angewendet in Changeset %{value}.
setting_commit_logtime_enabled: Aktiviere Zeitlogging
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.

View File

@@ -1,6 +1,7 @@
# Italian translations for Ruby on Rails
# by Claudio Poli (masterkain@gmail.com)
# by Diego Pierotto (ita.translations@tiscali.it)
# by Emidio Stani (emidiostani@gmail.com)
it:
direction: ltr
@@ -126,7 +127,7 @@ it:
greater_than_start_date: "deve essere maggiore della data di partenza"
not_same_project: "non appartiene allo stesso progetto"
circular_dependency: "Questa relazione creerebbe una dipendenza circolare"
cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
cant_link_an_issue_with_a_descendant: "Una segnalazione non può essere collegata a una delle sue discendenti"
actionview_instancetag_blank_option: Scegli
@@ -141,11 +142,11 @@ it:
general_pdf_encoding: ISO-8859-1
general_first_day_of_week: '1'
notice_account_updated: L'utente è stata aggiornato.
notice_account_updated: L'utente è stato aggiornato.
notice_account_invalid_creditentials: Nome utente o password non validi.
notice_account_password_updated: La password è stata aggiornata.
notice_account_wrong_password: Password errata
notice_account_register_done: L'utente è stata creato.
notice_account_register_done: L'utente è stato creato.
notice_account_unknown_email: Utente sconosciuto.
notice_can_t_change_password: Questo utente utilizza un metodo di autenticazione esterno. Impossibile cambiare la password.
notice_account_lost_email_sent: Ti è stata spedita una email con le istruzioni per cambiare la password.
@@ -462,7 +463,7 @@ it:
label_all_words: Tutte le parole
label_wiki: Wiki
label_wiki_edit: Modifica wiki
label_wiki_edit_plural: Modfiche wiki
label_wiki_edit_plural: Modifiche wiki
label_wiki_page: Pagina Wiki
label_wiki_page_plural: Pagine wiki
label_index_by_title: Ordina per titolo
@@ -913,26 +914,26 @@ it:
text_zoom_in: Aumenta ingrandimento
notice_unable_delete_time_entry: Impossibile eliminare il valore time log.
label_overall_spent_time: Totale tempo impiegato
field_time_entries: Log time
field_time_entries: Tempo di collegamento
project_module_gantt: Gantt
project_module_calendar: Calendar
button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
text_are_you_sure_with_children: Delete issue and all child issues?
field_text: Text field
label_user_mail_option_only_owner: Only for things I am the owner of
setting_default_notification_option: Default notification option
label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
label_user_mail_option_only_assigned: Only for things I am assigned to
label_user_mail_option_none: No events
field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role
notice_not_authorized_archived_project: The project you're trying to access has been archived.
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
field_visible: Visible
setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
project_module_calendar: Calendario
button_edit_associated_wikipage: "Modifica la pagina wiki associata: %{page_title}"
text_are_you_sure_with_children: Eliminare la segnalazione e tutte le discendenti?
field_text: Campo di testo
label_user_mail_option_only_owner: Solo se io sono il proprietario
setting_default_notification_option: Opzione di notifica predefinita
label_user_mail_option_only_my_events: Solo se sono un osservatore o sono coinvolto
label_user_mail_option_only_assigned: Solo quando mi assegnano attività
label_user_mail_option_none: Nessun evento
field_member_of_group: Gruppo dell'assegnatario
field_assigned_to_role: Ruolo dell'assegnatario
notice_not_authorized_archived_project: Il progetto a cui stai accedendo è stato archiviato.
label_principal_search: "Cerca utente o gruppo:"
label_user_search: "Cerca utente:"
field_visible: Visibile
setting_emails_header: Intestazione email
setting_commit_logtime_activity_id: Attività per il tempo di collegamento
text_time_logged_by_changeset: Usato nel changeset %{value}.
setting_commit_logtime_enabled: Abilita registrazione del tempo di collegamento
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

View File

@@ -188,6 +188,7 @@ ja:
notice_unable_delete_version: バージョンを削除できません
notice_unable_delete_time_entry: 作業時間を削除できません
notice_issue_done_ratios_updated: チケットの進捗が更新されました。
notice_gantt_chart_truncated: ガントチャートは、最大表示項目数(%{max})を超えたたため切り捨てられました。
error_can_t_load_default_data: "デフォルト設定がロードできませんでした: %{value}"
error_scm_not_found: リポジトリに、エントリ/リビジョンが存在しません。
@@ -380,6 +381,9 @@ ja:
setting_start_of_week: 週の開始曜日
setting_rest_api_enabled: RESTによるWebサービスを有効にする
setting_default_notification_option: デフォルトのメール通知オプション
setting_commit_logtime_enabled: コミット時に作業時間を記録する
setting_commit_logtime_activity_id: 作業時間の作業分類
setting_gantt_items_limit: ガントチャート最大表示項目数
permission_add_project: プロジェクトの追加
permission_add_subprojects: サブプロジェクトの追加
@@ -901,6 +905,7 @@ ja:
text_no_configuration_data: "ロール、トラッカー、チケットのステータス、ワークフローがまだ設定されていません。\nデフォルト設定のロードを強くお勧めします。ロードした後、それを修正することができます。"
text_load_default_configuration: デフォルト設定をロード
text_status_changed_by_changeset: "更新履歴 %{value} で適用されました。"
text_time_logged_by_changeset: "更新履歴 %{value} で適用されました。"
text_issues_destroy_confirmation: '本当に選択したチケットを削除しますか?'
text_select_project_modules: 'このプロジェクトで使用するモジュールを選択してください:'
text_default_administrator_account_changed: デフォルト管理アカウントが変更済
@@ -952,8 +957,3 @@ ja:
enumeration_doc_categories: 文書カテゴリ
enumeration_activities: 作業分類 (時間トラッキング)
enumeration_system_activity: システム作業分類
setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart

View File

@@ -334,7 +334,7 @@ pl:
label_auth_source_plural: Tryby identyfikacji
label_authentication: Identyfikacja
label_blocked_by: zablokowane przez
label_blocks: blokady
label_blocks: blokuje
label_board: Forum
label_board_new: Nowe forum
label_board_plural: Fora
@@ -410,7 +410,7 @@ pl:
label_download_plural: "%{count} Pobrania"
label_downloads_abbr: Pobieranie
label_duplicated_by: zduplikowane przez
label_duplicates: duplikaty
label_duplicates: duplikuje
label_end_to_end: koniec do końca
label_end_to_start: koniec do początku
label_enumeration_new: Nowa wartość

View File

@@ -248,6 +248,7 @@
notice_unable_delete_version: 無法刪除版本。
notice_unable_delete_time_entry: 無法刪除工時記錄項目。
notice_issue_done_ratios_updated: 項目完成百分比已更新。
notice_gantt_chart_truncated: "由於項目數量超過可顯示數量的最大值 (%{max}),故此甘特圖尾部已被截斷"
error_can_t_load_default_data: "無法載入預設組態: %{value}"
error_scm_not_found: "在 SCM 儲存庫中找不到這個項目或版次。"
@@ -442,6 +443,7 @@
setting_default_notification_option: 預設通知選項
setting_commit_logtime_enabled: 啟用送交中的時間記錄
setting_commit_logtime_activity_id: 時間記錄對應的活動
setting_gantt_items_limit: 甘特圖中項目顯示數量的最大值
permission_add_project: 建立專案
permission_add_subprojects: 建立子專案
@@ -1015,6 +1017,3 @@
enumeration_doc_categories: 文件分類
enumeration_activities: 活動 (時間追蹤)
enumeration_system_activity: 系統活動
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart

View File

@@ -17,5 +17,6 @@ class AddUniqueIndexOnMembers < ActiveRecord::Migration
end
def self.down
remove_index :members, [:user_id, :project_id]
end
end

View File

@@ -1,9 +1,80 @@
== Redmine changelog
Redmine - project management software
Copyright (C) 2006-2010 Jean-Philippe Lang
Copyright (C) 2006-2011 Jean-Philippe Lang
http://www.redmine.org/
== 2011-01-09 v1.1.0
* Defect #2038: Italics in wiki headers show-up wrong in the toc
* Defect #3449: Redmine Takes Too Long On Large Mercurial Repository
* Defect #3567: Sorting for changesets might go wrong on Mercurial repos
* Defect #3707: {{toc}} doesn't work with {{include}}
* Defect #5096: Redmine hangs up while browsing Git repository
* Defect #6000: Safe Attributes prevents plugin extension of Issue model...
* Defect #6064: Modules not assigned to projects created via API
* Defect #6110: MailHandler should allow updating Issue Priority and Custom fields
* Defect #6136: JSON API holds less information than XML API
* Defect #6345: xml used by rest API is invalid
* Defect #6348: Gantt chart PDF rendering errors
* Defect #6403: Updating an issue with custom fields fails
* Defect #6467: "Member of role", "Member of group" filter not work correctly
* Defect #6473: New gantt broken after clearing issue filters
* Defect #6541: Email notifications send to everybody
* Defect #6549: Notification settings not migrated properly
* Defect #6591: Acronyms must have a minimum of three characters
* Defect #6674: Delete time log broken after changes to REST
* Defect #6681: Mercurial, Bazaar and Darcs auto close issue text should be commit id instead of revision number
* Defect #6724: Wiki uploads does not work anymore (SVN 4266)
* Defect #6746: Wiki links are broken on Activity page
* Defect #6747: Wiki diff does not work since r4265
* Defect #6763: New gantt charts: subject displayed twice on issues
* Defect #6826: Clicking "Add" twice creates duplicate member record
* Defect #6844: Unchecking status filter on the issue list has no effect
* Defect #6895: Wrong Polish translation of "blocks"
* Defect #6943: Migration from boolean to varchar fails on PostgreSQL 8.1
* Defect #7064: Mercurial adapter does not recognize non alphabetic nor numeric in UTF-8 copied files
* Defect #7128: New gantt chart does not render subtasks under parent task
* Defect #7135: paging mechanism returns the same last page forever
* Defect #7188: Activity page not refreshed when changing language
* Defect #7195: Apply CLI-supplied defaults for incoming mail only to new issues not replies
* Defect #7197: Tracker reset to default when replying to an issue email
* Defect #7213: Copy project does not copy all roles and permissions
* Defect #7225: Project settings: Trackers & Custom fields only relevant if module Issue tracking is active
* Feature #630: Allow non-unique names for projects
* Feature #1738: Add a "Visible" flag to project/user custom fields
* Feature #2803: Support for Javascript in Themes
* Feature #2852: Clean Incoming Email of quoted text "----- Reply above this line ------"
* Feature #2995: Improve error message when trying to access an archived project
* Feature #3170: Autocomplete issue relations on subject
* Feature #3503: Administrator Be Able To Modify Email settings Of Users
* Feature #4155: Automatic spent time logging from commit messages
* Feature #5136: Parent select on Wiki rename page
* Feature #5338: Descendants (subtasks) should be available via REST API
* Feature #5494: Wiki TOC should display heading from level 4
* Feature #5594: Improve MailHandler's keyword handling
* Feature #5622: Allow version to be set via incoming email
* Feature #5712: Reload themes
* Feature #5869: Issue filters by Group and Role
* Feature #6092: Truncate Git revision labels in Activity page/feed and allow configurable length
* Feature #6112: Accept localized keywords when receiving emails
* Feature #6140: REST issues response with issue count limit and offset
* Feature #6260: REST API for Users
* Feature #6276: Gantt Chart rewrite
* Feature #6446: Remove length limits on project identifier and name
* Feature #6628: Improvements in truncate email
* Feature #6779: Project JSON API
* Feature #6823: REST API for time tracker.
* Feature #7072: REST API for news
* Feature #7111: Expose more detail on journal entries
* Feature #7141: REST API: get information about current user
* Patch #4807: Allow to set the done_ratio field with the incoming mail system
* Patch #5441: Initialize TimeEntry attributes with params[:time_entry]
* Patch #6762: Use GET instead of POST to retrieve context_menu
* Patch #7160: French translation ofr "not_a_date" is missing
* Patch #7212: Missing remove_index in AddUniqueIndexOnMembers down migration
== 2010-12-23 v1.0.5
* #6656: Mercurial adapter loses seconds of commit times

View File

@@ -271,7 +271,8 @@ module Redmine
end
class Revision
attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
attr_accessor :scmid, :name, :author, :time, :message, :paths, :revision, :branch
attr_writer :identifier
def initialize(attributes={})
self.identifier = attributes[:identifier]
@@ -285,6 +286,16 @@ module Redmine
self.branch = attributes[:branch]
end
# Returns the identifier of this revision; see also Changeset model
def identifier
(@identifier || revision).to_s
end
# Returns the readable identifier.
def format_identifier
identifier
end
def save(repo)
Changeset.transaction do
changeset = Changeset.new(

View File

@@ -89,12 +89,13 @@ module Redmine
cmd = "#{GIT_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?
shellout(cmd) do |io|
begin
id = io.gets.split[1]
author = io.gets.match('Author:\s+(.*)$')[1]
2.times { io.gets }
time = Time.parse(io.gets.match('CommitDate:\s+(.*)$')[1]).localtime
lines = []
shellout(cmd) { |io| lines = io.readlines }
return nil if $? && $?.exitstatus != 0
begin
id = lines[0].split[1]
author = lines[1].match('Author:\s+(.*)$')[1]
time = Time.parse(lines[4].match('CommitDate:\s+(.*)$')[1]).localtime
Revision.new({
:identifier => id,
@@ -104,10 +105,9 @@ module Redmine
:message => nil,
:paths => nil
})
rescue NoMethodError => e
rescue NoMethodError => e
logger.error("The revision '#{path}' has a wrong format")
return nil
end
end
end
@@ -264,6 +264,13 @@ module Redmine
return nil if $? && $?.exitstatus != 0
cat
end
class Revision < Redmine::Scm::Adapters::Revision
# Returns the readable identifier
def format_identifier
identifier[0,8]
end
end
end
end
end

View File

@@ -3,10 +3,10 @@ changeset_quiet = 'This template must be used with --debug option\n'
changeset_verbose = 'This template must be used with --debug option\n'
changeset_debug = '<logentry revision="{rev}" node="{node|short}">\n<author>{author|escape}</author>\n<date>{date|isodate}</date>\n<paths>\n{files}{file_adds}{file_dels}{file_copies}</paths>\n<msg>{desc|escape}</msg>\n{tags}</logentry>\n\n'
file = '<path action="M">{file|escape}</path>\n'
file_add = '<path action="A">{file_add|escape}</path>\n'
file_del = '<path action="D">{file_del|escape}</path>\n'
file_copy = '<path-copied copyfrom-path="{source|escape}">{name|urlescape}</path-copied>\n'
file = '<path action="M">{file|urlescape}</path>\n'
file_add = '<path action="A">{file_add|urlescape}</path>\n'
file_del = '<path action="D">{file_del|urlescape}</path>\n'
file_copy = '<path-copied copyfrom-path="{source|urlescape}">{name|urlescape}</path-copied>\n'
tag = '<tag>{tag|escape}</tag>\n'
header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n'
# footer="</log>"

View File

@@ -3,10 +3,10 @@ changeset_quiet = 'This template must be used with --debug option\n'
changeset_verbose = 'This template must be used with --debug option\n'
changeset_debug = '<logentry revision="{rev}" node="{node|short}">\n<author>{author|escape}</author>\n<date>{date|isodatesec}</date>\n<paths>\n{file_mods}{file_adds}{file_dels}{file_copies}</paths>\n<msg>{desc|escape}</msg>\n{tags}</logentry>\n\n'
file_mod = '<path action="M">{file_mod|escape}</path>\n'
file_add = '<path action="A">{file_add|escape}</path>\n'
file_del = '<path action="D">{file_del|escape}</path>\n'
file_copy = '<path-copied copyfrom-path="{source|escape}">{name|urlescape}</path-copied>\n'
file_mod = '<path action="M">{file_mod|urlescape}</path>\n'
file_add = '<path action="A">{file_add|urlescape}</path>\n'
file_del = '<path action="D">{file_del|urlescape}</path>\n'
file_copy = '<path-copied copyfrom-path="{source|urlescape}">{name|urlescape}</path-copied>\n'
tag = '<tag>{tag|escape}</tag>\n'
header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n'
# footer="</log>"

View File

@@ -16,6 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'redmine/scm/adapters/abstract_adapter'
require 'cgi'
module Redmine
module Scm
@@ -127,8 +128,8 @@ module Redmine
from_rev = logentry.attributes['revision']
end
paths << {:action => path.attributes['action'],
:path => "/#{path.text}",
:from_path => from_path ? "/#{from_path}" : nil,
:path => "/#{CGI.unescape(path.text)}",
:from_path => from_path ? "/#{CGI.unescape(from_path)}" : nil,
:from_revision => from_rev ? from_rev : nil
}
end

View File

@@ -3,8 +3,8 @@ require 'rexml/document'
module Redmine
module VERSION #:nodoc:
MAJOR = 1
MINOR = 0
TINY = 5
MINOR = 1
TINY = 0
# Branch values:
# * official release: nil

View File

@@ -16,6 +16,7 @@ namespace :ci do
Rake::Task["db:create"].invoke
Rake::Task["db:migrate"].invoke
Rake::Task["db:schema:dump"].invoke
Rake::Task["test:scm:update"].invoke
end
desc "Build Redmine"

View File

@@ -1,68 +1,82 @@
### From http://svn.geekdaily.org/public/rails/plugins/generally_useful/tasks/coverage_via_rcov.rake
namespace :test do
desc 'Measures test coverage'
task :coverage do
rm_f "coverage"
rm_f "coverage.data"
rcov = "rcov --rails --aggregate coverage.data --text-summary -Ilib --html"
files = Dir.glob("test/**/*_test.rb").join(" ")
system("#{rcov} #{files}")
system("open coverage/index.html") if PLATFORM['darwin']
end
desc 'Run unit and functional scm tests'
task :scm do
errors = %w(test:scm:units test:scm:functionals).collect do |task|
begin
Rake::Task[task].invoke
nil
rescue => e
task
end
end.compact
abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any?
end
namespace :scm do
namespace :setup do
desc "Creates directory for test repositories"
task :create_dir do
FileUtils.mkdir_p Rails.root + '/tmp/test'
end
supported_scms = [:subversion, :cvs, :bazaar, :mercurial, :git, :darcs, :filesystem]
desc "Creates a test subversion repository"
task :subversion => :create_dir do
repo_path = "tmp/test/subversion_repository"
system "svnadmin create #{repo_path}"
system "gunzip < test/fixtures/repositories/subversion_repository.dump.gz | svnadmin load #{repo_path}"
end
(supported_scms - [:subversion]).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"
end
end
desc "Creates all test repositories"
task :all => supported_scms
end
Rake::TestTask.new(:units => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/unit/repository*_test.rb'] + FileList['test/unit/lib/redmine/scm/**/*_test.rb']
end
Rake::Task['test:scm:units'].comment = "Run the scm unit tests"
Rake::TestTask.new(:functionals => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/functional/repositories*_test.rb']
end
Rake::Task['test:scm:functionals'].comment = "Run the scm functional tests"
end
end
### From http://svn.geekdaily.org/public/rails/plugins/generally_useful/tasks/coverage_via_rcov.rake
namespace :test do
desc 'Measures test coverage'
task :coverage do
rm_f "coverage"
rm_f "coverage.data"
rcov = "rcov --rails --aggregate coverage.data --text-summary -Ilib --html"
files = Dir.glob("test/**/*_test.rb").join(" ")
system("#{rcov} #{files}")
system("open coverage/index.html") if PLATFORM['darwin']
end
desc 'Run unit and functional scm tests'
task :scm do
errors = %w(test:scm:units test:scm:functionals).collect do |task|
begin
Rake::Task[task].invoke
nil
rescue => e
task
end
end.compact
abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any?
end
namespace :scm do
namespace :setup do
desc "Creates directory for test repositories"
task :create_dir do
FileUtils.mkdir_p Rails.root + '/tmp/test'
end
supported_scms = [:subversion, :cvs, :bazaar, :mercurial, :git, :darcs, :filesystem]
desc "Creates a test subversion repository"
task :subversion => :create_dir do
repo_path = "tmp/test/subversion_repository"
system "svnadmin create #{repo_path}"
system "gunzip < test/fixtures/repositories/subversion_repository.dump.gz | svnadmin load #{repo_path}"
end
(supported_scms - [:subversion]).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"
end
end
desc "Creates all test repositories"
task :all => supported_scms
end
desc "Updates installed test repositories"
task :update do
require 'fileutils'
Dir.glob("tmp/test/*_repository").each do |dir|
next unless File.basename(dir) =~ %r{^(.+)_repository$} && File.directory?(dir)
scm = $1
next unless fixture = Dir.glob("test/fixtures/repositories/#{scm}_repository.*").first
next if File.stat(dir).ctime > File.stat(fixture).mtime
FileUtils.rm_rf dir
Rake::Task["test:scm:setup:#{scm}"].execute
end
end
Rake::TestTask.new(:units => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/unit/repository*_test.rb'] + FileList['test/unit/lib/redmine/scm/**/*_test.rb']
end
Rake::Task['test:scm:units'].comment = "Run the scm unit tests"
Rake::TestTask.new(:functionals => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/functional/repositories*_test.rb']
end
Rake::Task['test:scm:functionals'].comment = "Run the scm functional tests"
end
end

View File

@@ -232,6 +232,24 @@ function observeRelatedIssueField(url) {
});
}
function setVisible(id, visible) {
var el = $(id);
if (el) {if (visible) {el.show();} else {el.hide();}}
}
function observeProjectModules() {
var f = function() {
/* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */
var c = ($('project_enabled_module_names_issue_tracking').checked == true);
setVisible('project_trackers', c);
setVisible('project_issue_custom_fields', c);
};
Event.observe(window, 'load', f);
Event.observe('project_enabled_module_names_issue_tracking', 'change', f);
}
/* shows and hides ajax indicator */
Ajax.Responders.register({
onCreate: function(){

View File

@@ -179,6 +179,8 @@ tr.user td { white-space: nowrap; }
tr.user.locked, tr.user.registered { color: #aaa; }
tr.user.locked a, tr.user.registered a { color: #aaa; }
tr.wiki-page-version td.updated_on, tr.wiki-page-version td.author {text-align:center;}
tr.time-entry { text-align: center; white-space: nowrap; }
tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }

View File

@@ -7,6 +7,7 @@ roles_001:
---
- :add_project
- :edit_project
- :select_project_modules
- :manage_members
- :manage_versions
- :manage_categories

View File

@@ -154,7 +154,8 @@ class ProjectsControllerTest < ActionController::TestCase
:custom_field_values => { '3' => 'Beta' },
:tracker_ids => ['1', '3'],
# an issue custom field that is not for all project
:issue_custom_field_ids => ['9']
:issue_custom_field_ids => ['9'],
:enabled_module_names => ['issue_tracking', 'news', 'repository']
}
assert_redirected_to '/projects/blog/settings'
@@ -167,6 +168,7 @@ class ProjectsControllerTest < ActionController::TestCase
assert_nil project.parent
assert_equal 'Beta', project.custom_value_for(3).value
assert_equal [1, 3], project.trackers.map(&:id).sort
assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
assert project.issue_custom_fields.include?(IssueCustomField.find(9))
end
@@ -197,7 +199,9 @@ class ProjectsControllerTest < ActionController::TestCase
:description => "weblog",
:identifier => "blog",
:is_public => 1,
:custom_field_values => { '3' => 'Beta' }
:custom_field_values => { '3' => 'Beta' },
:tracker_ids => ['1', '3'],
:enabled_module_names => ['issue_tracking', 'news', 'repository']
}
assert_redirected_to '/projects/blog/settings'
@@ -206,6 +210,8 @@ class ProjectsControllerTest < ActionController::TestCase
assert_kind_of Project, project
assert_equal 'weblog', project.description
assert_equal true, project.is_public?
assert_equal [1, 3], project.trackers.map(&:id).sort
assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
# User should be added as a project member
assert User.find(9).member_of?(project)
@@ -282,6 +288,12 @@ class ProjectsControllerTest < ActionController::TestCase
end
end
def test_create_should_not_accept_get
@request.session[:user_id] = 1
get :create
assert_response :method_not_allowed
end
def test_show_by_id
get :show, :id => 1
assert_response :success
@@ -359,6 +371,21 @@ class ProjectsControllerTest < ActionController::TestCase
project = Project.find(1)
assert_equal 'Test changed name', project.name
end
def test_modules
@request.session[:user_id] = 2
Project.find(1).enabled_module_names = ['issue_tracking', 'news']
post :modules, :id => 1, :enabled_module_names => ['issue_tracking', 'repository', 'documents']
assert_redirected_to '/projects/ecookbook/settings/modules'
assert_equal ['documents', 'issue_tracking', 'repository'], Project.find(1).enabled_module_names.sort
end
def test_modules_should_not_allow_get
@request.session[:user_id] = 1
get :modules, :id => 1
assert_response :method_not_allowed
end
def test_get_destroy
@request.session[:user_id] = 1 # admin
@@ -429,7 +456,7 @@ class ProjectsControllerTest < ActionController::TestCase
@request.session[:user_id] = 1 # admin
post :copy, :id => 1, :project => {:name => 'Copy', :identifier => 'unique-copy'}
assert_response :redirect
assert_redirected_to :controller => 'projects', :action => 'settings'
assert_redirected_to :controller => 'projects', :action => 'settings', :id => 'unique-copy'
end
end

View File

@@ -49,10 +49,10 @@ class RepositoriesMercurialControllerTest < ActionController::TestCase
assert_response :success
assert_template 'show'
assert_not_nil assigns(:entries)
assert_equal 3, assigns(:entries).size
assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
assert_equal 4, assigns(:entries).size
assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
end
def test_show_directory
@@ -74,7 +74,21 @@ class RepositoriesMercurialControllerTest < ActionController::TestCase
assert_not_nil assigns(:entries)
assert_equal ['delete.png'], assigns(:entries).collect(&:name)
end
def test_show_directory_sql_escape_percent
get :show, :id => 3, :path => ['sql_escape', 'percent%dir'], :rev => 13
assert_response :success
assert_template 'show'
assert_not_nil assigns(:entries)
assert_equal ['percent%file1.txt', 'percentfile1.txt'], assigns(:entries).collect(&:name)
changesets = assigns(:changesets)
## This is not yet implemented.
# assert_not_nil changesets
# assert_equal %w(13 11 10 9), changesets.collect(&:revision)
end
def test_changes
get :changes, :id => 3, :path => ['images', 'edit.png']
assert_response :success
@@ -86,10 +100,10 @@ class RepositoriesMercurialControllerTest < ActionController::TestCase
get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb']
assert_response :success
assert_template 'entry'
# Line 19
# Line 10
assert_tag :tag => 'th',
:content => /10/,
:attributes => { :class => /line-num/ },
:content => '10',
:attributes => { :class => 'line-num' },
:sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
end
@@ -115,7 +129,7 @@ class RepositoriesMercurialControllerTest < ActionController::TestCase
assert_template 'diff'
# Line 22 removed
assert_tag :tag => 'th',
:content => /22/,
:content => '22',
:sibling => { :tag => 'td',
:attributes => { :class => /diff_out/ },
:content => /def remove/ }
@@ -125,10 +139,29 @@ class RepositoriesMercurialControllerTest < ActionController::TestCase
get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb']
assert_response :success
assert_template 'annotate'
# Line 23, revision 4
assert_tag :tag => 'th', :content => /23/,
:sibling => { :tag => 'td', :child => { :tag => 'a', :content => /4/ } },
:sibling => { :tag => 'td', :content => /jsmith/ },
# Line 23, revision 4:def6d2f1254a
assert_tag :tag => 'th',
:content => '23',
:attributes => { :class => 'line-num' },
:sibling =>
{
:tag => 'td',
:attributes => { :class => 'revision' },
:child => { :tag => 'a', :content => '4' }
# :child => { :tag => 'a', :content => /4:def6d2f1/ }
}
assert_tag :tag => 'th',
:content => '23',
:attributes => { :class => 'line-num' },
:sibling =>
{
:tag => 'td' ,
:content => 'jsmith' ,
:attributes => { :class => 'author' },
}
assert_tag :tag => 'th',
:content => '23',
:attributes => { :class => 'line-num' },
:sibling => { :tag => 'td', :content => /watcher =/ }
end
else

View File

@@ -122,12 +122,35 @@ class ApiTest::ProjectsTest < ActionController::IntegrationTest
project = Project.first(:order => 'id DESC')
assert_equal 'API test', project.name
assert_equal 'api-test', project.identifier
assert_equal ['issue_tracking', 'repository'], project.enabled_module_names
assert_equal ['issue_tracking', 'repository'], project.enabled_module_names.sort
assert_equal Tracker.all.size, project.trackers.size
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_tag 'project', :child => {:tag => 'id', :content => project.id.to_s}
end
should "accept enabled_module_names attribute" do
@parameters[:project].merge!({:enabled_module_names => ['issue_tracking', 'news', 'time_tracking']})
assert_difference('Project.count') do
post '/projects.xml', @parameters, :authorization => credentials('admin')
end
project = Project.first(:order => 'id DESC')
assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort
end
should "accept tracker_ids attribute" do
@parameters[:project].merge!({:tracker_ids => [1, 3]})
assert_difference('Project.count') do
post '/projects.xml', @parameters, :authorization => credentials('admin')
end
project = Project.first(:order => 'id DESC')
assert_equal [1, 3], project.trackers.map(&:id).sort
end
end
end
@@ -171,6 +194,28 @@ class ApiTest::ProjectsTest < ActionController::IntegrationTest
project = Project.find(2)
assert_equal 'API update', project.name
end
should "accept enabled_module_names attribute" do
@parameters[:project].merge!({:enabled_module_names => ['issue_tracking', 'news', 'time_tracking']})
assert_no_difference 'Project.count' do
put '/projects/2.xml', @parameters, :authorization => credentials('admin')
end
assert_response :ok
project = Project.find(2)
assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort
end
should "accept tracker_ids attribute" do
@parameters[:project].merge!({:tracker_ids => [1, 3]})
assert_no_difference 'Project.count' do
put '/projects/2.xml', @parameters, :authorization => credentials('admin')
end
assert_response :ok
project = Project.find(2)
assert_equal [1, 3], project.trackers.map(&:id).sort
end
end
end

View File

@@ -218,4 +218,9 @@ class ChangesetTest < ActiveSupport::TestCase
c.comments = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
assert_equal "Texte encod en ISO-8859-1.", c.comments
end
def test_identifier
c = Changeset.find_by_revision('1')
assert_equal c.revision, c.identifier
end
end

View File

@@ -1,57 +1,70 @@
require File.expand_path('../../../../../../test_helper', __FILE__)
begin
require 'mocha'
class MercurialAdapterTest < ActiveSupport::TestCase
TEMPLATES_DIR = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATES_DIR
TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME
TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION
REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
def test_hgversion
to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
"Mercurial Distributed SCM (1.0)\n" => [1,0],
"Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
"Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
"Mercurial Distributed SCM (1916e629a29d)\n" => nil,
"Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
"(1.6)\n(1.7)\n(1.8)" => [1,6],
"(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
to_test.each do |s, v|
test_hgversion_for(s, v)
REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
if File.directory?(REPOSITORY_PATH)
def setup
@adapter = Redmine::Scm::Adapters::MercurialAdapter.new(REPOSITORY_PATH)
end
end
def test_template_path
to_test = { [0,9,5] => "0.9.5",
[1,0] => "1.0",
[] => "1.0",
[1,0,1] => "1.0",
[1,7] => "1.0",
[1,7,1] => "1.0"}
to_test.each do |v, template|
test_template_path_for(v, template)
def test_hgversion
to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
"Mercurial Distributed SCM (1.0)\n" => [1,0],
"Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
"Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
"Mercurial Distributed SCM (1916e629a29d)\n" => nil,
"Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
"(1.6)\n(1.7)\n(1.8)" => [1,6],
"(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
to_test.each do |s, v|
test_hgversion_for(s, v)
end
end
end
private
def test_hgversion_for(hgversion, version)
Redmine::Scm::Adapters::MercurialAdapter.expects(:hgversion_from_command_line).returns(hgversion)
adapter = Redmine::Scm::Adapters::MercurialAdapter
assert_equal version, adapter.hgversion
end
def test_template_path_for(version, template)
adapter = Redmine::Scm::Adapters::MercurialAdapter
assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}", adapter.template_path_for(version)
assert File.exist?(adapter.template_path_for(version))
def test_template_path
to_test = { [0,9,5] => "0.9.5",
[1,0] => "1.0",
[] => "1.0",
[1,0,1] => "1.0",
[1,7] => "1.0",
[1,7,1] => "1.0" }
to_test.each do |v, template|
test_template_path_for(v, template)
end
end
def test_cat
assert @adapter.cat("sources/welcome_controller.rb", 2)
assert_nil @adapter.cat("sources/welcome_controller.rb")
end
private
def test_hgversion_for(hgversion, version)
@adapter.class.expects(:hgversion_from_command_line).returns(hgversion)
assert_equal version, @adapter.class.hgversion
end
def test_template_path_for(version, template)
assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}",
@adapter.class.template_path_for(version)
assert File.exist?(@adapter.class.template_path_for(version))
end
else
puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
def test_fake; assert true end
end
end
rescue LoadError
class MercurialMochaFake < ActiveSupport::TestCase
def test_fake; assert(false, "Requires mocha to run those tests") end

View File

@@ -19,40 +19,100 @@ require File.expand_path('../../test_helper', __FILE__)
class ProjectNestedSetTest < ActiveSupport::TestCase
def setup
Project.delete_all
end
def test_destroy_root_and_chldren_should_not_mess_up_the_tree
a = Project.create!(:name => 'Project A', :identifier => 'projecta')
a1 = Project.create!(:name => 'Project A1', :identifier => 'projecta1')
a2 = Project.create!(:name => 'Project A2', :identifier => 'projecta2')
a1.set_parent!(a)
a2.set_parent!(a)
b = Project.create!(:name => 'Project B', :identifier => 'projectb')
b1 = Project.create!(:name => 'Project B1', :identifier => 'projectb1')
b1.set_parent!(b)
a.reload
a1.reload
a2.reload
b.reload
b1.reload
assert_equal [nil, 1, 6], [a.parent_id, a.lft, a.rgt]
assert_equal [a.id, 2, 3], [a1.parent_id, a1.lft, a1.rgt]
assert_equal [a.id, 4, 5], [a2.parent_id, a2.lft, a2.rgt]
assert_equal [nil, 7, 10], [b.parent_id, b.lft, b.rgt]
assert_equal [b.id, 8, 9], [b1.parent_id, b1.lft, b1.rgt]
assert_difference 'Project.count', -3 do
a.destroy
context "nested set" do
setup do
Project.delete_all
@a = Project.create!(:name => 'Project A', :identifier => 'projecta')
@a1 = Project.create!(:name => 'Project A1', :identifier => 'projecta1')
@a1.set_parent!(@a)
@a2 = Project.create!(:name => 'Project A2', :identifier => 'projecta2')
@a2.set_parent!(@a)
@b = Project.create!(:name => 'Project B', :identifier => 'projectb')
@b1 = Project.create!(:name => 'Project B1', :identifier => 'projectb1')
@b1.set_parent!(@b)
@b11 = Project.create!(:name => 'Project B11', :identifier => 'projectb11')
@b11.set_parent!(@b1)
@b2 = Project.create!(:name => 'Project B2', :identifier => 'projectb2')
@b2.set_parent!(@b)
@c = Project.create!(:name => 'Project C', :identifier => 'projectc')
@c1 = Project.create!(:name => 'Project C1', :identifier => 'projectc1')
@c1.set_parent!(@c)
[@a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1].each(&:reload)
end
b.reload
b1.reload
assert_equal [nil, 1, 4], [b.parent_id, b.lft, b.rgt]
assert_equal [b.id, 2, 3], [b1.parent_id, b1.lft, b1.rgt]
context "#create" do
should "build valid tree" do
assert_nested_set_values({
@a => [nil, 1, 6],
@a1 => [@a.id, 2, 3],
@a2 => [@a.id, 4, 5],
@b => [nil, 7, 14],
@b1 => [@b.id, 8, 11],
@b11 => [@b1.id,9, 10],
@b2 => [@b.id,12, 13],
@c => [nil, 15, 18],
@c1 => [@c.id,16, 17]
})
end
end
context "#set_parent!" do
should "keep valid tree" do
assert_no_difference 'Project.count' do
Project.find_by_name('Project B1').set_parent!(Project.find_by_name('Project A2'))
end
assert_nested_set_values({
@a => [nil, 1, 10],
@a2 => [@a.id, 4, 9],
@b1 => [@a2.id,5, 8],
@b11 => [@b1.id,6, 7],
@b => [nil, 11, 14],
@c => [nil, 15, 18]
})
end
end
context "#destroy" do
context "a root with children" do
should "not mess up the tree" do
assert_difference 'Project.count', -4 do
Project.find_by_name('Project B').destroy
end
assert_nested_set_values({
@a => [nil, 1, 6],
@a1 => [@a.id, 2, 3],
@a2 => [@a.id, 4, 5],
@c => [nil, 7, 10],
@c1 => [@c.id, 8, 9]
})
end
end
context "a child with children" do
should "not mess up the tree" do
assert_difference 'Project.count', -2 do
Project.find_by_name('Project B1').destroy
end
assert_nested_set_values({
@a => [nil, 1, 6],
@b => [nil, 7, 10],
@b2 => [@b.id, 8, 9],
@c => [nil, 11, 14]
})
end
end
end
end
end
def assert_nested_set_values(h)
assert Project.valid?
h.each do |project, expected|
project.reload
assert_equal expected, [project.parent_id, project.lft, project.rgt], "Unexpected nested set values for #{project.name}"
end
end
end

View File

@@ -770,6 +770,22 @@ class ProjectTest < ActiveSupport::TestCase
assert_equal @project, membership.project
end
end
should "copy memberships with groups and additional roles" do
group = Group.create!(:lastname => "Copy group")
user = User.find(7)
group.users << user
# group role
Member.create!(:project_id => @source_project.id, :principal => group, :role_ids => [2])
member = Member.find_by_user_id_and_project_id(user.id, @source_project.id)
# additional role
member.role_ids = [1]
assert @project.copy(@source_project)
member = Member.find_by_user_id_and_project_id(user.id, @project.id)
assert_not_nil member
assert_equal [1, 2], member.role_ids.sort
end
should "copy project specific queries" do
assert @project.valid?

View File

@@ -77,7 +77,7 @@ class RepositoryBazaarTest < ActiveSupport::TestCase
def test_annotate
annotate = @repository.scm.annotate('doc-mkdir.txt')
assert_equal 17, annotate.lines.size
assert_equal 1, annotate.revisions[0].identifier
assert_equal '1', annotate.revisions[0].identifier
assert_equal 'jsmith@', annotate.revisions[0].author
assert_equal 'mkdir', annotate.lines[0]
end

View File

@@ -54,12 +54,14 @@ class RepositoryDarcsTest < ActiveSupport::TestCase
assert entries.detect {|e| e.name == 'watchers_controller.rb'}
assert_nil entries.detect {|e| e.name == 'welcome_controller.rb'}
end
def test_cat
@repository.fetch_changesets
cat = @repository.cat("sources/welcome_controller.rb", 2)
assert_not_nil cat
assert cat.include?('class WelcomeController < ApplicationController')
if @repository.scm.supports_cat?
@repository.fetch_changesets
cat = @repository.cat("sources/welcome_controller.rb", 2)
assert_not_nil cat
assert cat.include?('class WelcomeController < ApplicationController')
end
end
else
puts "Darcs test repository NOT FOUND. Skipping unit tests !!!"

View File

@@ -18,7 +18,7 @@
require File.expand_path('../../test_helper', __FILE__)
class RepositoryGitTest < ActiveSupport::TestCase
fixtures :projects
fixtures :projects, :repositories, :enabled_modules, :users, :roles
# No '..' in the repository path
REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
@@ -62,6 +62,29 @@ class RepositoryGitTest < ActiveSupport::TestCase
@repository.fetch_changesets
assert_equal 15, @repository.changesets.count
end
def test_identifier
@repository.fetch_changesets
@repository.reload
c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
assert_equal c.scmid, c.identifier
end
def test_format_identifier
@repository.fetch_changesets
@repository.reload
c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
assert_equal c.format_identifier, '7234cb27'
end
def test_activities
c = Changeset.new(:repository => @repository, :committed_on => Time.now,
:revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
:scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
:comments => 'test')
assert c.event_title.include?('abc7234c:')
assert_equal c.event_url[:rev], 'abc7234cb2750b63f47bff735edc50a1c0a433c2'
end
else
puts "Git test repository NOT FOUND. Skipping unit tests !!!"
def test_fake; assert true end

View File

@@ -33,8 +33,8 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
@repository.fetch_changesets
@repository.reload
assert_equal 6, @repository.changesets.count
assert_equal 11, @repository.changes.count
assert_equal 17, @repository.changesets.count
assert_equal 25, @repository.changes.count
assert_equal "Initial import.\nThe repository contains 3 files.", @repository.changesets.find_by_revision('0').comments
end
@@ -46,7 +46,7 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
assert_equal 3, @repository.changesets.count
@repository.fetch_changesets
assert_equal 6, @repository.changesets.count
assert_equal 17, @repository.changesets.count
end
def test_entries
@@ -55,18 +55,11 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
end
def test_locate_on_outdated_repository
# Change the working dir state
%x{hg -R #{REPOSITORY_PATH} up -r 0}
assert_equal 1, @repository.entries("images", 0).size
assert_equal 2, @repository.entries("images").size
assert_equal 2, @repository.entries("images", 2).size
end
def test_cat
assert @repository.scm.cat("sources/welcome_controller.rb", 2)
assert_nil @repository.scm.cat("sources/welcome_controller.rb")
end
def test_isodatesec
# Template keyword 'isodatesec' supported in Mercurial 1.0 and higher
if @repository.scm.class.client_version_above?([1, 0])
@@ -76,6 +69,58 @@ class RepositoryMercurialTest < ActiveSupport::TestCase
assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on
end
end
def test_changeset_order_by_revision
@repository.fetch_changesets
@repository.reload
c0 = @repository.latest_changeset
c1 = @repository.changesets.find_by_revision('0')
# sorted by revision (id), not by date
assert c0.revision.to_i > c1.revision.to_i
assert c0.committed_on < c1.committed_on
end
def test_latest_changesets
@repository.fetch_changesets
@repository.reload
# with_limit
changesets = @repository.latest_changesets('', nil, 2)
assert_equal @repository.latest_changesets('', nil)[0, 2], changesets
# with_filepath
changesets = @repository.latest_changesets('/sql_escape/percent%dir/percent%file1.txt', nil)
assert_equal %w|11 10 9|, changesets.collect(&:revision)
changesets = @repository.latest_changesets('/sql_escape/underscore_dir/understrike_file.txt', nil)
assert_equal %w|12 9|, changesets.collect(&:revision)
end
def test_copied_files
@repository.fetch_changesets
@repository.reload
cs1 = @repository.changesets.find_by_revision('13')
c1 = cs1.changes
assert_equal 2, c1.size
assert_equal 'A', c1[0].action
assert_equal '/sql_escape/percent%dir/percentfile1.txt', c1[0].path
assert_equal '/sql_escape/percent%dir/percent%file1.txt', c1[0].from_path
assert_equal 'A', c1[1].action
assert_equal '/sql_escape/underscore_dir/understrike-file.txt', c1[1].path
assert_equal '/sql_escape/underscore_dir/understrike_file.txt', c1[1].from_path
cs2 = @repository.changesets.find_by_revision('15')
c2 = cs2.changes
assert_equal 1, c2.size
assert_equal 'A', c2[0].action
assert_equal '/README (1)[2]&,%.-3_4', c2[0].path
assert_equal '/README', c2[0].from_path
end
else
puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
def test_fake; assert true end

View File

@@ -18,7 +18,7 @@
require File.expand_path('../../test_helper', __FILE__)
class RepositorySubversionTest < ActiveSupport::TestCase
fixtures :projects, :repositories
fixtures :projects, :repositories, :enabled_modules, :users, :roles
def setup
@project = Project.find(1)
@@ -88,6 +88,46 @@ class RepositorySubversionTest < ActiveSupport::TestCase
assert_equal 1, entries.size, 'Expect a single entry'
assert_equal 'README.txt', entries.first.name
end
def test_identifier
@repository.fetch_changesets
@repository.reload
c = @repository.changesets.find_by_revision('1')
assert_equal c.revision, c.identifier
end
def test_identifier_nine_digit
c = Changeset.new(:repository => @repository, :committed_on => Time.now,
:revision => '123456789', :comments => 'test')
assert_equal c.identifier, c.revision
end
def test_format_identifier
@repository.fetch_changesets
@repository.reload
c = @repository.changesets.find_by_revision('1')
assert_equal c.format_identifier, c.revision
end
def test_format_identifier_nine_digit
c = Changeset.new(:repository => @repository, :committed_on => Time.now,
:revision => '123456789', :comments => 'test')
assert_equal c.format_identifier, c.revision
end
def test_activities
c = Changeset.new(:repository => @repository, :committed_on => Time.now,
:revision => '1', :comments => 'test')
assert c.event_title.include?('1:')
assert_equal c.event_url[:rev], '1'
end
def test_activities_nine_digit
c = Changeset.new(:repository => @repository, :committed_on => Time.now,
:revision => '123456789', :comments => 'test')
assert c.event_title.include?('123456789:')
assert_equal c.event_url[:rev], '123456789'
end
else
puts "Subversion test repository NOT FOUND. Skipping unit tests !!!"
def test_fake; assert true end