Project

General

Profile

« Previous | Next » 

Revision 106

svn browser merged in trunk

View differences:

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">&nbsp;</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/, "&nbsp;") %></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) %>&nbsp;</div>
16
<div class="square action_M"></div> <div style="float:left;"><%= l(:label_modified) %>&nbsp;</div>
17
<div class="square action_D"></div> <div style="float:left;"><%= l(:label_deleted) %>&nbsp;</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