Revision 106
Added by Jean-Philippe Lang over 18 years ago
trunk/app/controllers/projects_controller.rb | ||
---|---|---|
62 | 62 |
@project.custom_fields = CustomField.find(@params[:custom_field_ids]) if @params[:custom_field_ids] |
63 | 63 |
@custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) } |
64 | 64 |
@project.custom_values = @custom_values |
65 |
if params[:repository_enabled] && params[:repository_enabled] == "1" |
|
66 |
@project.repository = Repository.new |
|
67 |
@project.repository.attributes = params[:repository] |
|
68 |
end |
|
65 | 69 |
if @project.save |
66 | 70 |
flash[:notice] = l(:notice_successful_create) |
67 | 71 |
redirect_to :controller => 'admin', :action => 'projects' |
... | ... | |
96 | 100 |
@custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) } |
97 | 101 |
@project.custom_values = @custom_values |
98 | 102 |
end |
99 |
if @project.update_attributes(params[:project]) |
|
103 |
if params[:repository_enabled] |
|
104 |
case params[:repository_enabled] |
|
105 |
when "0" |
|
106 |
@project.repository = nil |
|
107 |
when "1" |
|
108 |
@project.repository ||= Repository.new |
|
109 |
@project.repository.attributes = params[:repository] |
|
110 |
end |
|
111 |
end |
|
112 |
@project.attributes = params[:project] |
|
113 |
if @project.save |
|
100 | 114 |
flash[:notice] = l(:notice_successful_update) |
101 | 115 |
redirect_to :action => 'settings', :id => @project |
102 | 116 |
else |
trunk/app/controllers/repositories_controller.rb | ||
---|---|---|
1 |
# redMine - project management software |
|
2 |
# Copyright (C) 2006 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU General Public License |
|
15 |
# along with this program; if not, write to the Free Software |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 |
|
|
18 |
class RepositoriesController < ApplicationController |
|
19 |
layout 'base' |
|
20 |
before_filter :find_project, :authorize |
|
21 |
|
|
22 |
def show |
|
23 |
@entries = @repository.scm.entries('') |
|
24 |
show_error and return unless @entries |
|
25 |
@latest_revision = @entries.revisions.latest |
|
26 |
end |
|
27 |
|
|
28 |
def browse |
|
29 |
@entries = @repository.scm.entries(@path, @rev) |
|
30 |
show_error and return unless @entries |
|
31 |
end |
|
32 |
|
|
33 |
def revisions |
|
34 |
@entry = @repository.scm.entry(@path, @rev) |
|
35 |
@revisions = @repository.scm.revisions(@path, @rev) |
|
36 |
show_error and return unless @entry && @revisions |
|
37 |
end |
|
38 |
|
|
39 |
def entry |
|
40 |
if 'raw' == params[:format] |
|
41 |
content = @repository.scm.cat(@path, @rev) |
|
42 |
show_error and return unless content |
|
43 |
send_data content, :filename => @path.split('/').last |
|
44 |
end |
|
45 |
end |
|
46 |
|
|
47 |
def revision |
|
48 |
@revisions = @repository.scm.revisions '', @rev, @rev, :with_paths => true |
|
49 |
show_error and return unless @revisions |
|
50 |
@revision = @revisions.first |
|
51 |
end |
|
52 |
|
|
53 |
def diff |
|
54 |
@rev_to = params[:rev_to] || (@rev-1) |
|
55 |
@diff = @repository.scm.diff(params[:path], @rev, @rev_to) |
|
56 |
show_error and return unless @diff |
|
57 |
end |
|
58 |
|
|
59 |
private |
|
60 |
def find_project |
|
61 |
@project = Project.find(params[:id]) |
|
62 |
@repository = @project.repository |
|
63 |
@path = params[:path].squeeze('/').gsub(/^\//, '') if params[:path] |
|
64 |
@path ||= '' |
|
65 |
@rev = params[:rev].to_i if params[:rev] and params[:rev].to_i > 0 |
|
66 |
end |
|
67 |
|
|
68 |
def show_error |
|
69 |
flash.now[:notice] = l(:notice_scm_error) |
|
70 |
render :nothing => true, :layout => true |
|
71 |
end |
|
72 |
end |
trunk/app/helpers/application_helper.rb | ||
---|---|---|
164 | 164 |
return super if options.delete :no_label |
165 | 165 |
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") |
166 | 166 |
label = @template.content_tag("label", label_text, |
167 |
:class => (@object.errors[field] ? "error" : nil), |
|
167 |
:class => (@object && @object.errors[field] ? "error" : nil),
|
|
168 | 168 |
:for => (@object_name.to_s + "_" + field.to_s)) |
169 | 169 |
label + super |
170 | 170 |
end |
... | ... | |
175 | 175 |
def select(field, choices, options = {}, html_options = {}) |
176 | 176 |
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") |
177 | 177 |
label = @template.content_tag("label", label_text, |
178 |
:class => (@object.errors[field] ? "error" : nil), |
|
178 |
:class => (@object && @object.errors[field] ? "error" : nil),
|
|
179 | 179 |
:for => (@object_name.to_s + "_" + field.to_s)) |
180 | 180 |
label + super |
181 | 181 |
end |
trunk/app/helpers/repositories_helper.rb | ||
---|---|---|
1 |
# redMine - project management software |
|
2 |
# Copyright (C) 2006 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU General Public License |
|
15 |
# along with this program; if not, write to the Free Software |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 |
|
|
18 |
module RepositoriesHelper |
|
19 |
end |
trunk/app/models/permission.rb | ||
---|---|---|
30 | 30 |
1100 => :label_news_plural, |
31 | 31 |
1200 => :label_document_plural, |
32 | 32 |
1300 => :label_attachment_plural, |
33 |
1400 => :label_repository |
|
33 | 34 |
}.freeze |
34 | 35 |
|
35 | 36 |
@@cached_perms_for_public = nil |
trunk/app/models/project.rb | ||
---|---|---|
25 | 25 |
has_many :documents, :dependent => true |
26 | 26 |
has_many :news, :dependent => true, :include => :author |
27 | 27 |
has_many :issue_categories, :dependent => true, :order => "issue_categories.name" |
28 |
has_one :repository, :dependent => true |
|
28 | 29 |
has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => 'custom_fields_projects', :association_foreign_key => 'custom_field_id' |
29 | 30 |
acts_as_tree :order => "name", :counter_cache => true |
30 | 31 |
|
31 | 32 |
validates_presence_of :name, :description |
32 | 33 |
validates_uniqueness_of :name |
33 | 34 |
validates_associated :custom_values, :on => :update |
35 |
validates_associated :repository |
|
34 | 36 |
validates_format_of :name, :with => /^[\w\s\'\-]*$/i |
35 | 37 |
|
36 | 38 |
# returns 5 last created projects |
trunk/app/models/repository.rb | ||
---|---|---|
1 |
# redMine - project management software |
|
2 |
# Copyright (C) 2006 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU General Public License |
|
15 |
# along with this program; if not, write to the Free Software |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 |
|
|
18 |
class Repository < ActiveRecord::Base |
|
19 |
belongs_to :project |
|
20 |
validates_presence_of :url |
|
21 |
validates_format_of :url, :with => /^(http|https|svn):\/\/.+/i |
|
22 |
|
|
23 |
@scm = nil |
|
24 |
|
|
25 |
def scm |
|
26 |
@scm ||= SvnRepos::Base.new url |
|
27 |
end |
|
28 |
end |
trunk/app/models/svn_repos.rb | ||
---|---|---|
1 |
# redMine - project management software |
|
2 |
# Copyright (C) 2006 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU General Public License |
|
15 |
# along with this program; if not, write to the Free Software |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 |
|
|
18 |
require 'rexml/document' |
|
19 |
|
|
20 |
module SvnRepos |
|
21 |
|
|
22 |
class CommandFailed < StandardError #:nodoc: |
|
23 |
end |
|
24 |
|
|
25 |
class Base |
|
26 |
@url = nil |
|
27 |
@login = nil |
|
28 |
@password = nil |
|
29 |
|
|
30 |
def initialize(url, login=nil, password=nil) |
|
31 |
@url = url |
|
32 |
@login = login if login && !login.empty? |
|
33 |
@password = (password || "") if @login |
|
34 |
end |
|
35 |
|
|
36 |
# Returns the entry identified by path and revision identifier |
|
37 |
# or nil if entry doesn't exist in the repository |
|
38 |
def entry(path=nil, identifier=nil) |
|
39 |
e = entries(path, identifier) |
|
40 |
e ? e.first : nil |
|
41 |
end |
|
42 |
|
|
43 |
# Returns an Entries collection |
|
44 |
# or nil if the given path doesn't exist in the repository |
|
45 |
def entries(path=nil, identifier=nil) |
|
46 |
path ||= '' |
|
47 |
identifier = 'HEAD' unless identifier and identifier > 0 |
|
48 |
entries = Entries.new |
|
49 |
cmd = "svn list --xml #{target(path)}@#{identifier}" |
|
50 |
shellout(cmd) do |io| |
|
51 |
begin |
|
52 |
doc = REXML::Document.new(io) |
|
53 |
doc.elements.each("lists/list/entry") do |entry| |
|
54 |
entries << Entry.new({:name => entry.elements['name'].text, |
|
55 |
:path => ((path.empty? ? "" : "#{path}/") + entry.elements['name'].text), |
|
56 |
:kind => entry.attributes['kind'], |
|
57 |
:size => (entry.elements['size'] and entry.elements['size'].text).to_i, |
|
58 |
:lastrev => Revision.new({ |
|
59 |
:identifier => entry.elements['commit'].attributes['revision'], |
|
60 |
:time => Time.parse(entry.elements['commit'].elements['date'].text), |
|
61 |
:author => entry.elements['commit'].elements['author'].text |
|
62 |
}) |
|
63 |
}) |
|
64 |
end |
|
65 |
rescue |
|
66 |
end |
|
67 |
end |
|
68 |
return nil if $? && $?.exitstatus != 0 |
|
69 |
entries.sort_by_name |
|
70 |
rescue Errno::ENOENT => e |
|
71 |
raise CommandFailed |
|
72 |
end |
|
73 |
|
|
74 |
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) |
|
75 |
path ||= '' |
|
76 |
identifier_from = 'HEAD' unless identifier_from and identifier_from.to_i > 0 |
|
77 |
identifier_to = 1 unless identifier_to and identifier_to.to_i > 0 |
|
78 |
revisions = Revisions.new |
|
79 |
cmd = "svn log --xml -r #{identifier_from}:#{identifier_to} " |
|
80 |
cmd << "--verbose " if options[:with_paths] |
|
81 |
cmd << target(path) |
|
82 |
shellout(cmd) do |io| |
|
83 |
begin |
|
84 |
doc = REXML::Document.new(io) |
|
85 |
doc.elements.each("log/logentry") do |logentry| |
|
86 |
paths = [] |
|
87 |
logentry.elements.each("paths/path") do |path| |
|
88 |
paths << {:action => path.attributes['action'], |
|
89 |
:path => path.text |
|
90 |
} |
|
91 |
end |
|
92 |
paths.sort! { |x,y| x[:path] <=> y[:path] } |
|
93 |
|
|
94 |
revisions << Revision.new({:identifier => logentry.attributes['revision'], |
|
95 |
:author => logentry.elements['author'].text, |
|
96 |
:time => Time.parse(logentry.elements['date'].text), |
|
97 |
:message => logentry.elements['msg'].text, |
|
98 |
:paths => paths |
|
99 |
}) |
|
100 |
end |
|
101 |
rescue |
|
102 |
end |
|
103 |
end |
|
104 |
return nil if $? && $?.exitstatus != 0 |
|
105 |
revisions |
|
106 |
rescue Errno::ENOENT => e |
|
107 |
raise CommandFailed |
|
108 |
end |
|
109 |
|
|
110 |
def diff(path, identifier_from, identifier_to=nil) |
|
111 |
path ||= '' |
|
112 |
if identifier_to and identifier_to.to_i > 0 |
|
113 |
identifier_to = identifier_to.to_i |
|
114 |
else |
|
115 |
identifier_to = identifier_from.to_i - 1 |
|
116 |
end |
|
117 |
cmd = "svn diff -r " |
|
118 |
cmd << "#{identifier_to}:" |
|
119 |
cmd << "#{identifier_from}" |
|
120 |
cmd << "#{target(path)}@#{identifier_from}" |
|
121 |
diff = [] |
|
122 |
shellout(cmd) do |io| |
|
123 |
io.each_line do |line| |
|
124 |
diff << line |
|
125 |
end |
|
126 |
end |
|
127 |
return nil if $? && $?.exitstatus != 0 |
|
128 |
diff |
|
129 |
rescue Errno::ENOENT => e |
|
130 |
raise CommandFailed |
|
131 |
end |
|
132 |
|
|
133 |
def cat(path, identifier=nil) |
|
134 |
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" |
|
135 |
cmd = "svn cat #{target(path)}@#{identifier}" |
|
136 |
cat = nil |
|
137 |
shellout(cmd) do |io| |
|
138 |
cat = io.read |
|
139 |
end |
|
140 |
return nil if $? && $?.exitstatus != 0 |
|
141 |
cat |
|
142 |
rescue Errno::ENOENT => e |
|
143 |
raise CommandFailed |
|
144 |
end |
|
145 |
|
|
146 |
private |
|
147 |
def target(path) |
|
148 |
" \"" << "#{@url}/#{path}".gsub(/["'?<>\*]/, '') << "\"" |
|
149 |
end |
|
150 |
|
|
151 |
def logger |
|
152 |
RAILS_DEFAULT_LOGGER |
|
153 |
end |
|
154 |
|
|
155 |
def shellout(cmd, &block) |
|
156 |
logger.debug "Shelling out: #{cmd}" if logger && logger.debug? |
|
157 |
IO.popen(cmd) do |io| |
|
158 |
block.call(io) if block_given? |
|
159 |
end |
|
160 |
end |
|
161 |
end |
|
162 |
|
|
163 |
class Entries < Array |
|
164 |
def sort_by_name |
|
165 |
sort {|x,y| |
|
166 |
if x.kind == y.kind |
|
167 |
x.name <=> y.name |
|
168 |
else |
|
169 |
x.kind <=> y.kind |
|
170 |
end |
|
171 |
} |
|
172 |
end |
|
173 |
|
|
174 |
def revisions |
|
175 |
revisions ||= Revisions.new(collect{|entry| entry.lastrev}) |
|
176 |
end |
|
177 |
end |
|
178 |
|
|
179 |
class Entry |
|
180 |
attr_accessor :name, :path, :kind, :size, :lastrev |
|
181 |
def initialize(attributes={}) |
|
182 |
self.name = attributes[:name] if attributes[:name] |
|
183 |
self.path = attributes[:path] if attributes[:path] |
|
184 |
self.kind = attributes[:kind] if attributes[:kind] |
|
185 |
self.size = attributes[:size].to_i if attributes[:size] |
|
186 |
self.lastrev = attributes[:lastrev] |
|
187 |
end |
|
188 |
|
|
189 |
def is_file? |
|
190 |
'file' == self.kind |
|
191 |
end |
|
192 |
|
|
193 |
def is_dir? |
|
194 |
'dir' == self.kind |
|
195 |
end |
|
196 |
end |
|
197 |
|
|
198 |
class Revisions < Array |
|
199 |
def latest |
|
200 |
sort {|x,y| x.time <=> y.time}.last |
|
201 |
end |
|
202 |
end |
|
203 |
|
|
204 |
class Revision |
|
205 |
attr_accessor :identifier, :author, :time, :message, :paths |
|
206 |
def initialize(attributes={}) |
|
207 |
self.identifier = attributes[:identifier] |
|
208 |
self.author = attributes[:author] |
|
209 |
self.time = attributes[:time] |
|
210 |
self.message = attributes[:message] || "" |
|
211 |
self.paths = attributes[:paths] |
|
212 |
end |
|
213 |
end |
|
214 |
end |
trunk/app/views/layouts/base.rhtml | ||
---|---|---|
91 | 91 |
<%= link_to l(:label_document_plural), {:controller => 'projects', :action => 'list_documents', :id => @project }, :class => "menuItem" %> |
92 | 92 |
<%= link_to l(:label_member_plural), {:controller => 'projects', :action => 'list_members', :id => @project }, :class => "menuItem" %> |
93 | 93 |
<%= link_to l(:label_attachment_plural), {:controller => 'projects', :action => 'list_files', :id => @project }, :class => "menuItem" %> |
94 |
<%= link_to l(:label_repository), {:controller => 'repositories', :action => 'show', :id => @project}, :class => "menuItem" if @project.repository and [email protected]_record? %></li> |
|
94 | 95 |
<%= link_to_if_authorized l(:label_settings), {:controller => 'projects', :action => 'settings', :id => @project }, :class => "menuItem" %> |
95 | 96 |
</div> |
96 | 97 |
<% end %> |
... | ... | |
112 | 113 |
<li><%= link_to l(:label_document_plural), :controller => 'projects', :action => 'list_documents', :id => @project %></li> |
113 | 114 |
<li><%= link_to l(:label_member_plural), :controller => 'projects', :action => 'list_members', :id => @project %></li> |
114 | 115 |
<li><%= link_to l(:label_attachment_plural), :controller => 'projects', :action => 'list_files', :id => @project %></li> |
116 |
<li><%= link_to l(:label_repository), :controller => 'repositories', :action => 'show', :id => @project if @project.repository and [email protected]_record? %></li> |
|
115 | 117 |
<li><%= link_to_if_authorized l(:label_settings), :controller => 'projects', :action => 'settings', :id => @project %></li> |
116 | 118 |
</ul> |
117 | 119 |
<% end %> |
trunk/app/views/projects/_form.rhtml | ||
---|---|---|
1 | 1 |
<%= error_messages_for 'project' %> |
2 |
|
|
2 | 3 |
<div class="box"> |
3 | 4 |
<!--[form:project]--> |
4 | 5 |
<p><%= f.text_field :name, :required => true %></p> |
... | ... | |
22 | 23 |
<%= custom_field.name %> |
23 | 24 |
<% end %></p> |
24 | 25 |
<% end %> |
25 |
<!--[eoform:project]-->
|
|
26 |
<!--[eoform:project]--> |
|
26 | 27 |
</div> |
28 |
|
|
29 |
<div class="box"><h3><%= check_box_tag "repository_enabled", 1, [email protected]?, :onclick => "Element.toggle('repository');" %> <%= l(:label_repository) %></h3> |
|
30 |
<%= hidden_field_tag "repository_enabled", 0 %> |
|
31 |
<div id="repository"> |
|
32 |
<% fields_for :repository, @project.repository, { :builder => TabularFormBuilder, :lang => current_language} do |repository| %> |
|
33 |
<p><%= repository.text_field :url, :size => 60, :required => true %><br />(http://, https://, svn://)</p> |
|
34 |
<% end %> |
|
35 |
</div> |
|
36 |
<%= javascript_tag "Element.hide('repository');" if @project.repository.nil? %> |
|
37 |
</div> |
trunk/app/views/repositories/_dir_list.rhtml | ||
---|---|---|
1 |
<table class="list"> |
|
2 |
<thead><tr> |
|
3 |
<th><%= l(:field_name) %></th> |
|
4 |
<th><%= l(:field_filesize) %></th> |
|
5 |
<th><%= l(:label_revision) %></th> |
|
6 |
<th><%= l(:field_author) %></th> |
|
7 |
<th><%= l(:label_date) %></th> |
|
8 |
</tr></thead> |
|
9 |
<tbody> |
|
10 |
<% total_size = 0 |
|
11 |
@entries.each do |entry| %> |
|
12 |
<tr class="<%= cycle 'odd', 'even' %>"> |
|
13 |
<td><%= link_to h(entry.name), { :action => (entry.is_dir? ? 'browse' : 'revisions'), :id => @project, :path => entry.path, :rev => @rev }, :class => "icon " + (entry.is_dir? ? 'folder' : 'file') %></td> |
|
14 |
<td align="right"><%= human_size(entry.size) unless entry.is_dir? %></td> |
|
15 |
<td align="right"><%= link_to entry.lastrev.identifier, :action => 'revision', :id => @project, :rev => entry.lastrev.identifier %></td> |
|
16 |
<td align="center"><em><%=h entry.lastrev.author %></em></td> |
|
17 |
<td align="center"><%= format_time(entry.lastrev.time) %></td> |
|
18 |
</tr> |
|
19 |
<% total_size += entry.size |
|
20 |
end %> |
|
21 |
</tbody> |
|
22 |
</table> |
|
23 |
<p align="right"><em><%= l(:label_total) %>: <%= human_size(total_size) %></em></p> |
trunk/app/views/repositories/_navigation.rhtml | ||
---|---|---|
1 |
<%= link_to 'root', :action => 'browse', :id => @project, :path => '', :rev => @rev %> |
|
2 |
<% |
|
3 |
dirs = path.split('/') |
|
4 |
if 'file' == kind |
|
5 |
filename = dirs.pop |
|
6 |
end |
|
7 |
link_path = '' |
|
8 |
dirs.each do |dir| |
|
9 |
link_path << '/' unless link_path.empty? |
|
10 |
link_path << "#{dir}" |
|
11 |
%> |
|
12 |
/ <%= link_to h(dir), :action => 'browse', :id => @project, :path => link_path, :rev => @rev %> |
|
13 |
<% end %> |
|
14 |
<% if filename %> |
|
15 |
/ <%= link_to h(filename), :action => 'revisions', :id => @project, :path => "#{link_path}/#{filename}", :rev => @rev %> |
|
16 |
<% end %> |
|
17 |
|
|
18 |
<%= "@ #{revision}" if revision %> |
trunk/app/views/repositories/browse.rhtml | ||
---|---|---|
1 |
<%= stylesheet_link_tag "scm" %> |
|
2 |
|
|
3 |
<div class="contextual"> |
|
4 |
<%= start_form_tag %> |
|
5 |
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %> |
|
6 |
<%= submit_tag 'OK' %> |
|
7 |
</div> |
|
8 |
|
|
9 |
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2> |
|
10 |
|
|
11 |
<%= render :partial => 'dir_list' %> |
trunk/app/views/repositories/diff.rhtml | ||
---|---|---|
1 |
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2> |
|
2 |
|
|
3 |
<%= stylesheet_link_tag "scm" %> |
|
4 |
|
|
5 |
<table class="list"> |
|
6 |
<thead><tr><th>@<%= @rev %></th><th>@<%= @rev_to %></th><th></th></tr></thead> |
|
7 |
<tbody> |
|
8 |
<% parsing = false |
|
9 |
line_num_l = 0 |
|
10 |
line_num_r = 0 %> |
|
11 |
<% @diff.each do |line| %> |
|
12 |
<% |
|
13 |
if line =~ /^@@ (\+|\-)(\d+),\d+ (\+|\-)(\d+),\d+ @@/ |
|
14 |
line_num_l = $2.to_i |
|
15 |
line_num_r = $4.to_i |
|
16 |
if parsing %> |
|
17 |
<tr class="spacing"><td colspan="3"> </td></tr> |
|
18 |
<% end |
|
19 |
parsing = true |
|
20 |
next |
|
21 |
end |
|
22 |
next unless parsing |
|
23 |
%> |
|
24 |
|
|
25 |
<tr> |
|
26 |
|
|
27 |
<% case line[0, 1] |
|
28 |
when " " %> |
|
29 |
<th class="line-num"><%= line_num_l %></th> |
|
30 |
<th class="line-num"><%= line_num_r %></th> |
|
31 |
<td class="line-code"> |
|
32 |
<% line_num_l = line_num_l + 1 |
|
33 |
line_num_r = line_num_r + 1 |
|
34 |
|
|
35 |
when "-" %> |
|
36 |
<th class="line-num"></th> |
|
37 |
<th class="line-num"><%= line_num_r %></th> |
|
38 |
<td class="line-code" style="background: #fdd;"> |
|
39 |
<% line_num_r = line_num_r + 1 |
|
40 |
|
|
41 |
when "+" %> |
|
42 |
<th class="line-num"><%= line_num_l %></th> |
|
43 |
<th class="line-num"></th> |
|
44 |
<td class="line-code" style="background: #dfd;"> |
|
45 |
<% line_num_l = line_num_l + 1 |
|
46 |
|
|
47 |
else |
|
48 |
next |
|
49 |
end %> |
|
50 |
|
|
51 |
<%= h(line[1..-1]).gsub(/\s/, " ") %></td></tr> |
|
52 |
|
|
53 |
<% end %> |
|
54 |
</tbody> |
|
55 |
</table> |
trunk/app/views/repositories/revision.rhtml | ||
---|---|---|
1 |
<%= stylesheet_link_tag "scm" %> |
|
2 |
|
|
3 |
<div class="contextual"> |
|
4 |
<%= start_form_tag %> |
|
5 |
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %> |
|
6 |
<%= submit_tag 'OK' %> |
|
7 |
</div> |
|
8 |
|
|
9 |
<h2><%= l(:label_revision) %> <%= @revision.identifier %></h2> |
|
10 |
|
|
11 |
<p><em><%= @revision.author %>, <%= format_time(@revision.time) %></em></p> |
|
12 |
<%= simple_format @revision.message %> |
|
13 |
|
|
14 |
<div style="float:right;"> |
|
15 |
<div class="square action_A"></div> <div style="float:left;"><%= l(:label_added) %> </div> |
|
16 |
<div class="square action_M"></div> <div style="float:left;"><%= l(:label_modified) %> </div> |
|
17 |
<div class="square action_D"></div> <div style="float:left;"><%= l(:label_deleted) %> </div> |
|
18 |
</div> |
|
19 |
|
|
20 |
<h3><%= l(:label_attachment_plural) %></h3> |
|
21 |
<table class="list"> |
|
22 |
<tbody> |
|
23 |
<% @revision.paths.each do |path| %> |
|
24 |
<tr class="<%= cycle 'odd', 'even' %>"> |
|
25 |
<td><div class="square action_<%= path[:action] %>"></div> <%= path[:path] %></td> |
|
26 |
<td> |
|
27 |
<% if path[:action] == "M" %> |
|
28 |
<%= link_to 'View diff', :action => 'diff', :id => @project, :path => path[:path].gsub(/^\//, ''), :rev => @revision.identifier %> |
|
29 |
<% end %> |
|
30 |
</td> |
|
31 |
</tr> |
|
32 |
<% end %> |
|
33 |
</tbody> |
|
34 |
</table> |
|
35 |
<p><%= lwr(:label_modification, @revision.paths.length) %></p> |
trunk/app/views/repositories/revisions.rhtml | ||
---|---|---|
1 |
<%= stylesheet_link_tag "scm" %> |
|
2 |
|
|
3 |
<div class="contextual"> |
|
4 |
<%= start_form_tag %> |
|
5 |
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %> |
|
6 |
<%= submit_tag 'OK' %> |
|
7 |
</div> |
|
8 |
|
|
9 |
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => @entry.kind, :revision => @rev } %></h2> |
|
10 |
|
|
11 |
<% if @entry.is_file? %> |
|
12 |
<h3><%=h @entry.name %></h3> |
|
13 |
<p><%= link_to 'Download', {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' }, :class => "icon file" %> (<%= human_size @entry.size %>)</p> |
|
14 |
<% end %> |
|
15 |
|
|
16 |
<h3>Revisions</h3> |
|
17 |
|
|
18 |
<table class="list"> |
|
19 |
<thead><tr> |
|
20 |
<th>#</th> |
|
21 |
<th><%= l(:field_author) %></th> |
|
22 |
<th><%= l(:label_date) %></th> |
|
23 |
<th><%= l(:field_description) %></th> |
|
24 |
<th></th> |
|
25 |
</tr></thead> |
|
26 |
<tbody> |
|
27 |
<% @revisions.each do |revision| %> |
|
28 |
<tr class="<%= cycle 'odd', 'even' %>"> |
|
29 |
<th align="center"><%= link_to revision.identifier, :action => 'revision', :id => @project, :rev => revision.identifier %></th> |
|
30 |
<td align="center"><em><%=h revision.author %></em></td> |
|
31 |
<td align="center"><%= format_time(revision.time) %></td> |
|
32 |
<td width="70%"><%= simple_format(h(revision.message)) %></td> |
|
33 |
<td align="center"><%= link_to 'Diff', :action => 'diff', :id => @project, :path => @path, :rev => revision.identifier if @entry.is_file? && revision != @revisions.last %></td> |
|
34 |
</tr> |
|
35 |
<% end %> |
|
36 |
</tbody> |
|
37 |
</table> |
|
38 |
<p><%= lwr(:label_modification, @revisions.length) %></p> |
trunk/app/views/repositories/show.rhtml | ||
---|---|---|
1 |
<%= stylesheet_link_tag "scm" %> |
|
2 |
|
|
3 |
<h2><%= l(:label_repository) %></h2> |
|
4 |
|
|
5 |
<h3><%= l(:label_revision_plural) %></h3> |
|
6 |
<% if @latest_revision %> |
|
7 |
<p><%= l(:label_latest_revision) %>: |
|
8 |
<%= link_to @latest_revision.identifier, :action => 'revision', :id => @project, :rev => @latest_revision.identifier %><br /> |
|
9 |
<em><%= @latest_revision.author %>, <%= format_time(@latest_revision.time) %></em></p> |
|
10 |
<% end %> |
|
11 |
<p><%= link_to l(:label_view_revisions), :action => 'revisions', :id => @project %></p> |
|
12 |
|
|
13 |
|
|
14 |
<h3><%= l(:label_browse) %></h3> |
|
15 |
<%= render :partial => 'dir_list' %> |
trunk/config/routes.rb | ||
---|---|---|
9 | 9 |
# You can have the root of your site routed by hooking up '' |
10 | 10 |
# -- just remember to delete public/index.html. |
11 | 11 |
map.connect '', :controller => "welcome" |
12 |
|
|
12 |
|
|
13 |
map.connect 'repositories/:action/:id/:path', :controller => 'repositories' |
|
13 | 14 |
map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow' |
14 | 15 |
map.connect 'help/:ctrl/:page', :controller => 'help' |
15 | 16 |
map.connect ':controller/:action/:id/:sort_key/:sort_order' |
trunk/db/migrate/015_create_repositories.rb | ||
---|---|---|
1 |
class CreateRepositories < ActiveRecord::Migration |
|
2 |
def self.up |
|
3 |
create_table :repositories, :force => true do |t| |
|
4 |
t.column "project_id", :integer, :default => 0, :null => false |
|
5 |
t.column "url", :string, :default => "", :null => false |
|
6 |
end |
|
7 |
end |
|
8 |
|
|
9 |
def self.down |
|
10 |
drop_table :repositories |
|
11 |
end |
|
12 |
end |
trunk/db/migrate/016_add_repositories_permissions.rb | ||
---|---|---|
1 |
class AddRepositoriesPermissions < ActiveRecord::Migration |
|
2 |
def self.up |
|
3 |
Permission.create :controller => "repositories", :action => "show", :description => "button_view", :sort => 1450, :is_public => true |
|
4 |
Permission.create :controller => "repositories", :action => "browse", :description => "label_browse", :sort => 1460, :is_public => true |
|
5 |
Permission.create :controller => "repositories", :action => "entry", :description => "entry", :sort => 1462, :is_public => true |
|
6 |
Permission.create :controller => "repositories", :action => "revisions", :description => "label_view_revisions", :sort => 1470, :is_public => true |
|
7 |
Permission.create :controller => "repositories", :action => "revision", :description => "label_view_revisions", :sort => 1472, :is_public => true |
|
8 |
Permission.create :controller => "repositories", :action => "diff", :description => "diff", :sort => 1480, :is_public => true |
|
9 |
end |
|
10 |
|
|
11 |
def self.down |
|
12 |
Permission.find(:first, :conditions => ["controller=? and action=?", 'repositories', 'show']).destroy |
|
13 |
Permission.find(:first, :conditions => ["controller=? and action=?", 'repositories', 'browse']).destroy |
|
14 |
Permission.find(:first, :conditions => ["controller=? and action=?", 'repositories', 'entry']).destroy |
|
15 |
Permission.find(:first, :conditions => ["controller=? and action=?", 'repositories', 'revisions']).destroy |
|
16 |
Permission.find(:first, :conditions => ["controller=? and action=?", 'repositories', 'revision']).destroy |
|
17 |
Permission.find(:first, :conditions => ["controller=? and action=?", 'repositories', 'diff']).destroy |
|
18 |
end |
|
19 |
end |
trunk/doc/CHANGELOG | ||
---|---|---|
7 | 7 |
|
8 | 8 |
== xx/xx/2006 v0.x.x |
9 | 9 |
|
10 |
* simple SVN browser added (just needs svn binaries in PATH) |
|
10 | 11 |
* comments can now be added on news |
11 | 12 |
* "my page" is now customizable |
12 | 13 |
* more powerfull and savable filters for issues lists |
trunk/lang/de.yml | ||
---|---|---|
63 | 63 |
notice_successful_connection: Erfolgreicher Anschluß. |
64 | 64 |
notice_file_not_found: Erbetene Akte besteht nicht oder ist gelöscht worden. |
65 | 65 |
notice_locking_conflict: Data have been updated by another user. |
66 |
notice_scm_error: Eintragung und/oder Neuausgabe besteht nicht im Behälter. |
|
66 | 67 |
|
67 | 68 |
mail_subject_lost_password: Dein redMine Kennwort |
68 | 69 |
mail_subject_register: redMine Kontoaktivierung |
... | ... | |
136 | 137 |
field_done_ratio: %% Getan |
137 | 138 |
field_hide_mail: Mein email address verstecken |
138 | 139 |
field_comment: Anmerkung |
140 |
field_url: URL |
|
139 | 141 |
|
140 | 142 |
label_user: Benutzer |
141 | 143 |
label_user_plural: Benutzer |
... | ... | |
282 | 284 |
label_contains: enthält |
283 | 285 |
label_not_contains: enthält nicht |
284 | 286 |
label_day_plural: Tage |
287 |
label_repository: SVN Behälter |
|
288 |
label_browse: Grasen |
|
289 |
label_modification: %d änderung |
|
290 |
label_modification_plural: %d änderungen |
|
291 |
label_revision: Neuausgabe |
|
292 |
label_revision_plural: Neuausgaben |
|
293 |
label_added: hinzugefügt |
|
294 |
label_modified: geändert |
|
295 |
label_deleted: gelöscht |
|
296 |
label_latest_revision: Neueste Neuausgabe |
|
297 |
label_view_revisions: Die Neuausgaben ansehen |
|
285 | 298 |
|
286 | 299 |
button_login: Einloggen |
287 | 300 |
button_submit: Einreichen |
trunk/lang/en.yml | ||
---|---|---|
63 | 63 |
notice_successful_connection: Successful connection. |
64 | 64 |
notice_file_not_found: Requested file doesn't exist or has been deleted. |
65 | 65 |
notice_locking_conflict: Data have been updated by another user. |
66 |
notice_scm_error: Entry and/or revision doesn't exist in the repository. |
|
66 | 67 |
|
67 | 68 |
mail_subject_lost_password: Your redMine password |
68 | 69 |
mail_subject_register: redMine account activation |
... | ... | |
136 | 137 |
field_done_ratio: %% Done |
137 | 138 |
field_hide_mail: Hide my email address |
138 | 139 |
field_comment: Comment |
140 |
field_url: URL |
|
139 | 141 |
|
140 | 142 |
label_user: User |
141 | 143 |
label_user_plural: Users |
... | ... | |
282 | 284 |
label_contains: contains |
283 | 285 |
label_not_contains: doesn't contain |
284 | 286 |
label_day_plural: days |
287 |
label_repository: SVN Repository |
|
288 |
label_browse: Browse |
|
289 |
label_modification: %d change |
|
290 |
label_modification_plural: %d changes |
|
291 |
label_revision: Revision |
|
292 |
label_revision_plural: Revisions |
|
293 |
label_added: added |
|
294 |
label_modified: modified |
|
295 |
label_deleted: deleted |
|
296 |
label_latest_revision: Latest revision |
|
297 |
label_view_revisions: View revisions |
|
285 | 298 |
|
286 | 299 |
button_login: Login |
287 | 300 |
button_submit: Submit |
trunk/lang/es.yml | ||
---|---|---|
63 | 63 |
notice_successful_connection: Successful connection. |
64 | 64 |
notice_file_not_found: Requested file doesn't exist or has been deleted. |
65 | 65 |
notice_locking_conflict: Data have been updated by another user. |
66 |
notice_scm_error: La entrada y/o la revisión no existe en el depósito. |
|
66 | 67 |
|
67 | 68 |
mail_subject_lost_password: Tu contraseña del redMine |
68 | 69 |
mail_subject_register: Activación de la cuenta del redMine |
... | ... | |
136 | 137 |
field_done_ratio: %% Realizado |
137 | 138 |
field_hide_mail: Ocultar mi email address |
138 | 139 |
field_comment: Comentario |
140 |
field_url: URL |
|
139 | 141 |
|
140 | 142 |
label_user: Usuario |
141 | 143 |
label_user_plural: Usuarios |
... | ... | |
282 | 284 |
label_contains: contiene |
283 | 285 |
label_not_contains: no contiene |
284 | 286 |
label_day_plural: días |
287 |
label_repository: Depósito SVN |
|
288 |
label_browse: Hojear |
|
289 |
label_modification: %d modificación |
|
290 |
label_modification_plural: %d modificaciones |
|
291 |
label_revision: Revisión |
|
292 |
label_revision_plural: Revisiones |
|
293 |
label_added: agregado |
|
294 |
label_modified: modificado |
|
295 |
label_deleted: suprimido |
|
296 |
label_latest_revision: La revisión más última |
|
297 |
label_view_revisions: Ver las revisiones |
|
285 | 298 |
|
286 | 299 |
button_login: Conexión |
287 | 300 |
button_submit: Someter |
trunk/lang/fr.yml | ||
---|---|---|
63 | 63 |
notice_successful_connection: Connection réussie. |
64 | 64 |
notice_file_not_found: Le fichier demandé n'existe pas ou a été supprimé. |
65 | 65 |
notice_locking_conflict: Les données ont été mises à jour par un autre utilisateur. Mise à jour impossible. |
66 |
notice_scm_error: L'entrée et/ou la révision demandée n'existe pas dans le dépôt. |
|
66 | 67 |
|
67 | 68 |
mail_subject_lost_password: Votre mot de passe redMine |
68 | 69 |
mail_subject_register: Activation de votre compte redMine |
... | ... | |
137 | 138 |
field_auth_source: Mode d'authentification |
138 | 139 |
field_hide_mail: Cacher mon adresse mail |
139 | 140 |
field_comment: Commentaire |
141 |
field_url: URL |
|
140 | 142 |
|
141 | 143 |
label_user: Utilisateur |
142 | 144 |
label_user_plural: Utilisateurs |
... | ... | |
283 | 285 |
label_contains: contient |
284 | 286 |
label_not_contains: ne contient pas |
285 | 287 |
label_day_plural: jours |
288 |
label_repository: Dépôt SVN |
|
289 |
label_browse: Parcourir |
|
290 |
label_modification: %d modification |
|
291 |
label_modification_plural: %d modifications |
|
292 |
label_revision: Révision |
|
293 |
label_revision_plural: Révisions |
|
294 |
label_added: ajouté |
|
295 |
label_modified: modifié |
|
296 |
label_deleted: supprimé |
|
297 |
label_latest_revision: Dernière révision |
|
298 |
label_view_revisions: Voir les révisions |
|
286 | 299 |
|
287 | 300 |
button_login: Connexion |
288 | 301 |
button_submit: Soumettre |
trunk/public/stylesheets/application.css | ||
---|---|---|
473 | 473 |
font-size: 0.8em; |
474 | 474 |
} |
475 | 475 |
|
476 |
.contextual select { |
|
476 |
.contextual select, .contextual input {
|
|
477 | 477 |
font-size: 1em; |
478 | 478 |
} |
479 | 479 |
|
trunk/public/stylesheets/scm.css | ||
---|---|---|
1 |
|
|
2 |
|
|
3 |
div.square { |
|
4 |
border: 1px solid #999; |
|
5 |
float: left; |
|
6 |
margin: .4em .5em 0 0; |
|
7 |
overflow: hidden; |
|
8 |
width: .6em; height: .6em; |
|
9 |
} |
|
10 |
|
|
11 |
div.action_M { background: #fd8 } |
|
12 |
div.action_D { background: #f88 } |
|
13 |
div.action_A { background: #bfb } |
|
14 |
|
|
15 |
table.list { |
|
16 |
width:100%; |
|
17 |
border-collapse: collapse; |
|
18 |
border: 1px dotted #d0d0d0; |
|
19 |
margin-bottom: 6px; |
|
20 |
} |
|
21 |
|
|
22 |
table.list thead th { |
|
23 |
text-align: center; |
|
24 |
background: #eee; |
|
25 |
border: 1px solid #d7d7d7; |
|
26 |
} |
|
27 |
|
|
28 |
table.list tbody th { |
|
29 |
font-weight: normal; |
|
30 |
text-align: center; |
|
31 |
background: #eed; |
|
32 |
border: 1px solid #d7d7d7; |
|
33 |
} |
|
34 |
|
|
35 |
.icon { |
|
36 |
background-position: 0% 40%; |
|
37 |
background-repeat: no-repeat; |
|
38 |
padding-left: 20px; |
|
39 |
} |
|
40 |
|
|
41 |
.folder { background-image: url(../images/folder.png); } |
|
42 |
.file { background-image: url(../images/file.png); } |
|
43 |
|
|
44 |
|
|
45 |
|
|
46 |
tr.spacing { |
|
47 |
border: 1px solid #d7d7d7; |
|
48 |
} |
|
49 |
|
|
50 |
.line-num { |
|
51 |
border: 1px solid #d7d7d7; |
|
52 |
font-size: 0.8em; |
|
53 |
text-align: right; |
|
54 |
width: 3em; |
|
55 |
padding-right: 3px; |
|
56 |
} |
|
57 |
|
|
58 |
.line-code { |
|
59 |
font-family: "Courier New", monospace; |
|
60 |
font-size: 1em; |
|
61 |
} |
|
62 |
|
|
63 |
table p { |
|
64 |
margin:0; |
|
65 |
padding:0; |
|
66 |
} |
Also available in: Unified diff
svn browser merged in trunk