Project

General

Profile

Patch #2525 » 0002-Redmine-management-of-Git-repositories.patch

I've lost some files in previous patch. This is corrected version. - Sentinel T, 2009-01-18 14:59

View differences:

app/helpers/repositories_helper.rb
164 164
  end
165 165

  
166 166
  def git_field_tags(form, repository)
167
      content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
167
      if Setting.serve_git_repositories? and (repository == nil or repository.url.blank?)
168
          content_tag('p', form.text_field(:url, :value => GitManager.repositories_root + '/' + repository.project.identifier + '.git'), :label => 'Path to .git directory', :size => 60, :required => true)
169
      else
170
          content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
171
      end
168 172
  end
169 173

  
170 174
  def cvs_field_tags(form, repository)
app/models/authorized_keys_entry.rb
1
require "strscan"
2
require 'thread'
3

  
4
class AuthorizedKeysEntry
5
  COMMENT=/^#/
6
  BLANK=/^\s+$/
7
  AUTHORIZED_KEYS_PARSER=/^(?:(.+) )?(ssh-dss|ssh-rsa) ([^ ]+)(?: (.+))?$/
8
  KEY_TYPES=['ssh-dss','ssh-rsa']
9
  KEY_FORMAT=/\A[\w\/\+\=]+\z/
10
  IDENTIFIER_FORMAT=/\A[\w@\.\-]+\z/
11
  @@authorized_keys_filename="~/.ssh/authorized_keys"
12

  
13
  attr_reader :identifier, :key, :type
14
  attr_accessor :options
15

  
16
  @authorized_keys_lock = Mutex.new
17

  
18
  # Create object from string from authorized_keys
19
  def initialize(authorized_keys_string = nil)
20
    @key = ''
21
    @options = []
22
    create_from_authorized_keys(authorized_keys_string) if authorized_keys_string != nil
23
  end
24

  
25
  def identifier=(identifier)
26
    @identifier = identifier
27
    @validated = false
28
  end
29

  
30
  def key=(key)
31
    @key = key
32
    @validated = false
33
  end
34

  
35
  def type=(type)
36
    @type = type
37
    @validated = false
38
  end
39

  
40
  def save
41
    raise "Invalid key format" if !validate
42
    AuthorizedKeysEntry.save_entry(self)
43
  end
44

  
45
  def self.find_by_identifier(identifier)
46
    read_entries do |entry|
47
      return entry if entry.identifier == identifier
48
    end
49
    return nil
50
  end
51

  
52
  def validate
53
    return true if @key == ""
54
    return false if !@options.kind_of?(Array)
55
    return true if @validated
56
    # Clean key from any garbage user might have entered while copying the key
57
    @key = @key.gsub(/\A(ssh-dss|ssh-rsa)\s/,"").gsub(/\s\w+@[\.\w\-\n]+\s*\Z/,"").gsub(/\s/,'')
58
    return false if (@key=~KEY_FORMAT) == nil or !KEY_TYPES.include?(@type)
59
    return false if (@identifier=~IDENTIFIER_FORMAT) == nil
60
      
61
    Tempfile.open("redmine") do |file|
62
      file << get_id_dsa_pub_format
63
      file.close
64
      @validated = system "ssh-keygen -B -f #{file.path()} &> /dev/null"
65
    end
66
    return @validated
67
  end
68

  
69
  def to_s
70
    get_authorized_keys_format
71
  end
72

  
73
  def self.authorized_keys_filename=(filename)
74
    @@authorized_keys_filename = filename
75
  end
76

  
77
  private
78

  
79
  def create_from_authorized_keys(line)
80
    m = AUTHORIZED_KEYS_PARSER.match(line)
81
    raise ArgumentError, "Invalid format of authorized_keys entry" if m == nil
82

  
83
    @options, @type, @key, @identifier = m[1], m[2], m[3], m[4]
84
    @options = AuthorizedKeysEntry.parse_options(@options)
85
  end
86

  
87
  def get_authorized_keys_format
88
    raise ArgumentError, 'Entry must have the type set' if @type == nil
89
    s = ""
90
    s += @options.join(",") + " " if !@options.empty?
91
    s += @type + " " + @key
92
    s += " " + @identifier if @identifier
93
    return s
94
  end
95

  
96
  def get_id_dsa_pub_format
97
    raise ArgumentError, 'Entry must have the type set' if @type == nil
98
    s = ""
99
    s += @type + " " + @key
100
    s += " " + @identifier if @identifier
101
    return s
102
  end
103

  
104
  def self.parse_options(options)
105
    result = []
106
    return result if options == nil
107
    scanner = StringScanner.new(options)
108
    while !scanner.eos?
109
      scanner.skip(/[ \t]*/)
110
      # scan a long option
111
      if out = scanner.scan(/[-a-z0-9A-Z_]+=\".*?\"/) or out = scanner.scan(/[-a-z0-9A-Z_]+/)
112
        result << out
113
      else
114
        # found an unscannable token, let's abort
115
        break
116
      end
117
      # eat a comma
118
      scanner.skip(/[\t]*,[\t]*/)
119
    end
120
    return result
121
  end
122

  
123
  def self.read_entries
124
    filename = File.expand_path(@@authorized_keys_filename)
125
    return if !File.exists?(filename)
126

  
127
    File.open(filename) do |file|
128
      file.flock(File::LOCK_SH)
129
      begin
130
        file.each_line do |line|
131
          next if COMMENT.match(line) or BLANK.match(line)
132
          yield AuthorizedKeysEntry.new(line)
133
        end
134
      ensure
135
        file.flock(File::LOCK_UN)
136
      end
137
    end 
138
  end
139

  
140
  def self.save_entry(entry)
141
    filename = File.expand_path(@@authorized_keys_filename)
142

  
143
    FileUtils.mkdir_p(File.dirname(filename))
144

  
145
    found = false
146
    entries = []
147
    file = nil
148
    begin
149
      if File.exist?(filename)
150
        file = File.open(filename, "r")
151
        # TODO: somehow handle flock blocking
152
        file.flock(File::LOCK_EX)
153
        file.each_line do |line|
154
          next if COMMENT.match(line) or BLANK.match(line)
155
          entries << AuthorizedKeysEntry.new(line)
156
        end
157

  
158
        entries.each do |e|
159
          if entry.identifier == e.identifier
160
            return if entry.key == e.key and entry.type == e.type
161

  
162
            found = true
163
	    if entry.key != ""
164
              e.key = entry.key
165
              e.type = entry.type
166
              e.options = entry.options
167
            else
168
              entries.delete(e)
169
            end
170
	    break
171
          end
172
        end
173
      end
174

  
175
      if !found and entry.key != ""
176
        entries << entry
177
      end
178
 
179
      File.open(filename, "w") do |write_file|
180
        write_file.flock(File::LOCK_EX) if file == nil
181
        entries.each do |entry|
182
          write_file << entry.to_s + "\n"
183
        end
184
      end
185
    ensure
186
      file.close if file != nil
187
    end
188
  end
189
end
190

  
app/models/changeset.rb
70 70
    scan_comment_for_issue_ids
71 71
  end
72 72
  require 'pp'
73
  
74
  def scan_comment_for_issue_ids
75
    return if comments.blank?
73

  
74
  # returns issue ids found in message
75
  # three arrays are returned, references issues, fixed ones and wrong numbers (not Issues)
76
  def self.find_issue_ids(message, project)
77
    return [[],[]] if message.blank?
76 78
    # keywords used to reference issues
77 79
    ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
78 80
    # keywords used to fix issues
79 81
    fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
80
    # status and optional done ratio applied
81
    fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
82
    done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
83 82
    
84 83
    kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
85
    return if kw_regexp.blank?
84
    return [[],[]] if kw_regexp.blank?
86 85
    
87 86
    referenced_issues = []
87
    fixed_issues = []
88
    wrong_issue_ids = []
88 89
    
89 90
    if ref_keywords.delete('*')
90 91
      # find any issue ID in the comments
91 92
      target_issue_ids = []
92
      comments.scan(%r{([\s\(,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
93
      referenced_issues += repository.project.issues.find_all_by_id(target_issue_ids)
93
      message.scan(%r{([\s\(,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
94
      found_issues = project.issues.find_all_by_id(target_issue_ids)
95
      references_issues += found_issues
96
      found_issues.each { |issue| target_issue_ids.delete(issue.id.to_s) }
97
      wrong_issue_ids += target_issue_ids
94 98
    end
95 99
    
96
    comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
100
    message.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
97 101
      action = match[0]
98 102
      target_issue_ids = match[1].scan(/\d+/)
99
      target_issues = repository.project.issues.find_all_by_id(target_issue_ids)
100
      if fix_status && fix_keywords.include?(action.downcase)
101
        # update status of issues
102
        logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
103
        target_issues.each do |issue|
104
          # the issue may have been updated by the closure of another one (eg. duplicate)
105
          issue.reload
106
          # don't change the status is the issue is closed
107
          next if issue.status.is_closed?
108
          csettext = "r#{self.revision}"
109
          if self.scmid && (! (csettext =~ /^r[0-9]+$/))
110
            csettext = "commit:\"#{self.scmid}\""
111
          end
112
          journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
113
          issue.status = fix_status
114
          issue.done_ratio = done_ratio if done_ratio
115
          issue.save
116
          Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
117
        end
103
      target_issues = project.issues.find_all_by_id(target_issue_ids)
104
      target_issues.each { |issue| target_issue_ids.delete(issue.id.to_s) }
105
      wrong_issue_ids += target_issue_ids
106
      if fix_keywords.include?(action.downcase)
107
        fixed_issues += target_issues
108
      else
109
        referenced_issues += target_issues
118 110
      end
119
      referenced_issues += target_issues
120 111
    end
121
    
122
    self.issues = referenced_issues.uniq
112
    return [referenced_issues.uniq, fixed_issues.uniq, wrong_issue_ids.uniq]
113
  end
114
  
115
  def scan_comment_for_issue_ids
116
    return if comments.blank?
117
    # status and optional done ratio applied
118
    fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
119
    done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
120

  
121
    referenced_issues, fixed_issues, wrong_issues = Changeset.find_issue_ids(comments, repository.project)
122

  
123
    # update status of issues
124
    logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
125
    fixed_issues.each do |issue|
126
      # the issue may have been updated by the closure of another one (eg. duplicate)
127
      issue.reload
128
      # don't change the status is the issue is closed
129
      next if issue.status.is_closed?
130
      csettext = "r#{self.revision}"
131
      if self.scmid && (! (csettext =~ /^r[0-9]+$/))
132
        csettext = "commit:\"#{self.scmid}\""
133
      end
134
      journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
135
      issue.status = fix_status
136
      issue.done_ratio = done_ratio if done_ratio
137
      issue.save
138
      Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
139
    end
140
    self.issues = (referenced_issues + fixed_issues).uniq
123 141
  end
124 142
  
125 143
  # Returns the previous changeset
app/models/git_checks/committer.rb
1
class GitChecks::Committer < GitManager::CommitCheck
2
  def self.check(revision, branch, user, role, project, git)
3
    # Check: committer name and email
4
    if revision.author != "#{user.name} <#{user.mail}>"
5
      return [ "Commit author name or email is wrong",
6
	       "    Execute following commands and _recreate_ commit:",
7
               "    git config --global user.name \"#{user.name}\"",
8
	       "                git config --global user.email #{user.mail}",
9
      ]
10
    end
11
    return []
12
  end
13
end
14

  
app/models/git_checks/delete_branch.rb
1
class GitChecks::DeleteBranch < GitManager::RefCheck
2
  def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
3
    if new_rev_type == "delete"
4
      return "Deleting branch can be done only by repository manager"
5
    end
6
    return []
7
  end
8
end
9

  
app/models/git_checks/fast_forward.rb
1
class GitChecks::FastForward < GitManager::RefCheck
2
  def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
3
    # Check: fast forward
4
    if old_rev != git.class::EMPTY_COMMIT and git.merge_base(old_rev, new_rev) != old_rev
5
      return "Only fast-forward commits are allowed"
6
    end
7
    return []
8
  end
9
end
10

  
app/models/git_checks/initial_commit.rb
1
class GitChecks::InitialCommit < GitManager::RefCheck
2
  def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
3
    if old_rev == git.class::EMPTY_COMMIT and !role.allowed_to?(:manage_repository)
4
      return "Initial commit can be done only by repository manager"
5
    end
6
    return []
7
  end
8
end
9

  
app/models/git_checks/issue.rb
1
class GitChecks::Issue < GitManager::CommitCheck
2
  def self.check(revision, branch, user, role, project, git)
3
    references_issues, fixes_issues, wrong_issues = Changeset.find_issue_ids(revision.message, project)
4
    issues = references_issues + fixes_issues
5

  
6
    errors = []
7

  
8
    if !wrong_issues.empty?
9
      errors << "Some issue numbers are wrong: #{wrong_issues.join(',')}"
10
    end
11

  
12
    issues.each do |issue_number|
13
      issue = Issue.find(issue_number)
14
      if issue.closed?
15
        errors << "Issue \##{issue.id} is closed. Reopen it and commit again"
16
      end
17
      if issue.assigned_to != user
18
        if issue.assigned_to != nil
19
          owner_info = "It belongs to #{issue.assigned_to.firstname} #{issue.assigned_to.lastname}"
20
        else
21
          owner_info = "It is unassigned"
22
        end
23
        errors << "Issue \##{issue.id} is not assigned to You. #{owner_info}"
24
      end
25
    end
26
    return errors
27
  end
28
end
29

  
app/models/git_manager.rb
1
require 'net/http'
2
require 'uri'
3
require 'redmine/scm/adapters/git_adapter'
4

  
5
class GitManager
6
  COMMANDS_READONLY = [
7
    'git-upload-pack',
8
  ]
9
  COMMANDS_WRITE = [
10
    'git-receive-pack',
11
  ]
12
  # TODO: make configurable
13
  REPOSITORIES_ROOT='~/repositories/'
14
  REDMINE_LOGIN_ENV_NAME='REDMINE_LOGIN'
15

  
16
  def self.repositories_root
17
    return File.expand_path(REPOSITORIES_ROOT)
18
  end
19

  
20
  
21
  # Executed by SSH when somebody logs into Redmine's SSH account
22
  # using public key.
23
  # Restricts the user to access only Git repositories he is allowed to.
24
  def self.serve
25
    begin
26
      command = serve_get_original_command
27
      user = serve_get_user
28
      git_command, project_identifier = parse_git_command(command)
29
      project, repository = find_project_and_repo(project_identifier)
30
      role = user.role_for_project(project)
31

  
32
      if COMMANDS_READONLY.include?(git_command)
33
        if !role.allowed_to?(:view_changesets)
34
          raise "User #{user} (#{role.name}) is not allowed to read project #{project}\n"
35
        end
36
        if !repository.scm.info
37
          raise "Empty repository. Push some commit first"
38
        end
39
      elsif COMMANDS_WRITE.include?(git_command)
40
        if !role.allowed_to?(:commit_access)
41
          raise "User #{user} (#{role.name}) is not allowed to write to project #{project}\n"
42
        end
43
        if !repository.scm.info
44
          warn "Creating new repository.\n"
45
          repository.scm.init(project.description)
46
        end
47
      else
48
        raise "Unknown command '#{verb}'"
49
      end
50
    
51
    rescue
52
      warn "Error: #{$!}.\n"
53
      exit 1
54
    end
55

  
56
    ENV[REDMINE_LOGIN_ENV_NAME]=user.login
57
    exec 'git', 'shell', '-c', "#{git_command} '#{repository.url}'"
58
  end
59

  
60
  def self.get_authorized_keys_options_for_login(login)
61
    return [ 'command="ruby ' + RAILS_ROOT + '/script/runner GitManager.serve \'' + login + '\' -e ' + ENV["RAILS_ENV"] +
62
	  '"', 'no-port-forwarding','no-X11-forwarding','no-agent-forwarding','no-pty' ]
63
  end
64

  
65

  
66
  private
67
  def self.serve_get_original_command
68
    command = ENV["SSH_ORIGINAL_COMMAND"]
69
    if command == nil or command==""
70
      raise "SSH_ORIGINAL_COMMAND not set. Use Git to access this account"
71
    end
72
    if command=~/\n/
73
      raise "Command contains new line character"
74
    end
75
    return command
76
  end
77

  
78
  def self.serve_get_user
79
    login = ARGV[0]
80
    if login == nil or login==""
81
      raise "Needs login as parameter"
82
    end
83
    user = User.find_by_login(login)
84
    if user == nil
85
      raise "User not found #{login}"
86
    end
87
    return user
88
  end
89

  
90
  def self.parse_git_command(command)
91
    verb, args = command.split(" ",2)
92
    if verb == 'git'
93
       subverb, args = args.split(" ",2)
94
       verb = "%s-%s" % [ verb, subverb ]
95
    end
96
    project_identifier = args.gsub("'","")
97
    return verb, project_identifier
98
  end
99

  
100
  def self.find_project_and_repo(project_identifier)
101
    project = Project.find_by_identifier(project_identifier)
102
    if project == nil
103
      raise "Project not found \"#{project_identifier}\""
104
    end
105

  
106
    repository = project.repository
107
    if repository == nil
108
      raise "Project #{project} does not have repository\n"
109
    end
110

  
111
    if repository.class != Repository::Git
112
      raise "Project #{project} has non git repository #{repository.type}"
113
    end
114
    return project, repository
115
  end
116

  
117

  
118
  public
119

  
120
  class RefCheck
121
    @@checks = []
122

  
123
    # Check Git ref, subclassess should override
124
    def check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
125
      return true
126
    end
127

  
128
    def self.inherited(subclass)
129
      @@checks << subclass
130
    end
131

  
132
    def self.get_checks
133
      return @@checks
134
    end
135
  end
136
  class CommitCheck
137
    @@checks = []
138

  
139
    # Check Git ref, subclassess should override
140
    def check(revision, branch, user, role, project, git)
141
      return true
142
    end
143

  
144
    def self.inherited(subclass)
145
      @@checks << subclass
146
    end
147

  
148
    def self.get_checks
149
      return @@checks
150
    end
151
  end
152

  
153
  def self.load_checks
154
    files = Dir.glob(RAILS_ROOT + "/app/models/git_checks/*.rb")
155
    files.each do |f|
156
      f.sub!(/\A#{RAILS_ROOT}/,'')
157
      f.split('/')[3..-1].join('/').split('.').first.camelize.constantize
158
    end
159
  end
160
  load_checks
161

  
162
  def self.check_commits
163
    login = ENV[REDMINE_LOGIN_ENV_NAME]
164
    if login == nil
165
      warn "Redmine: Local user, not running checks"
166
      exit 0
167
    end
168

  
169
    begin
170
      user = User.find_by_login(login)
171
      raise "User not found" if user == nil
172
      repository = Repository.find_by_url(Dir.getwd)
173
      raise "Repository not managed by Redmine" if repository == nil
174
      raise "Non git repository" if repository.class != Repository::Git
175
      project = repository.project
176
      role = user.role_for_project(project)
177
      git = repository.scm
178
      
179
      warn ""
180
      warn "-------------------------------------------------------------"
181
      warn "Redmine is checking your changes for correctness..."
182
      warn "Authenticated as #{user.name} (#{role.name} in #{project.name})"
183

  
184
      error_found = false
185

  
186
      warn "Changes:"
187
      STDIN.each_line do |line|
188
        old_rev, new_rev, branch = line.split(" ")
189
      
190
        if new_rev == git.class::EMPTY_COMMIT
191
          new_rev_type = "delete"
192
        else
193
          new_rev_type = git.get_object_type(new_rev)
194
        end
195
        revisions = nil
196
        if new_rev_type == "commit"
197
          revisions = git.revisions("", old_rev, new_rev)
198
        end
199
        warn "    Ref: #{branch} type: #{new_rev_type}"
200

  
201
	errors = []
202
	# TODO: make checks configurable
203
        RefCheck.get_checks.each do |check|
204
          result = check.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
205
          if result.kind_of?(Array)
206
            errors += result
207
	  else
208
            errors << result
209
          end
210
        end
211
        errors.each do |error|
212
          warn "            Error: #{error}"
213
        end
214
	error_found = true if !errors.empty?
215

  
216
        if revisions != nil
217
	  revisions.each do |revision|
218
	    warn "        Commit: #{revision.identifier}"
219

  
220
	    errors = []
221
            CommitCheck.get_checks.each do |check|
222
              result = check.check(revision, branch, user, role, project, git)
223
              if result.kind_of?(Array)
224
                errors += result
225
	      else
226
                errors << result
227
              end
228
            end
229
            errors.each do |error|
230
              warn "            Error: #{error}"
231
            end
232
	    error_found = true if !errors.empty?
233
          end
234
        end
235
      end
236
    end
237
    
238
    if error_found
239
      if !role.allowed_to?(:manage_repository)
240
        warn "Some commits were rejected. Correct them and try the push again."
241
        warn "-------------------------------------------------------------"
242
	warn ""
243
        exit 1
244
      else
245
        warn "You are repository manager. Checks results are ignored. "
246
        warn "-------------------------------------------------------------"
247
	exit 0
248
      end
249
    end
250
    warn "Changes look OK"
251
    warn "-------------------------------------------------------------"
252
    exit 0
253
  end
254

  
255
end
256

  
app/models/user.rb
65 65
  validates_length_of :password, :minimum => 4, :allow_nil => true
66 66
  validates_confirmation_of :password, :allow_nil => true
67 67

  
68
  def ssh_key
69
    return nil if ! Setting.serve_git_repositories?
70
    load_ssh_key if @ssh_key_entry == nil
71
    return @ssh_key_entry.key
72
  end
73

  
74
  def ssh_key_type
75
    return nil if ! Setting.serve_git_repositories?
76
    load_ssh_key if @ssh_key_entry == nil
77
    return @ssh_key_entry.type
78
  end
79

  
80
  def ssh_key=(key)
81
    return if ! Setting.serve_git_repositories?
82
    new_ssh_key if @ssh_key_entry == nil
83
    key = key.strip if key != nil
84
    @ssh_key_entry.key = key
85
  end
86

  
87
  def ssh_key_type=(key_type)
88
    return if ! Setting.serve_git_repositories?
89
    new_ssh_key if @ssh_key_entry == nil
90
    @ssh_key_entry.type = key_type
91
  end
92

  
93
  def validate
94
    return if !Setting.serve_git_repositories?
95
    return if @ssh_key_entry == nil
96

  
97
    errors.add(:ssh_key, "is not valid") if !@ssh_key_entry.validate
98
  end
99

  
68 100
  def before_create
69 101
    self.mail_notification = false
70 102
    true
......
74 106
    # update hashed_password if password was set
75 107
    self.hashed_password = User.hash_password(self.password) if self.password
76 108
  end
109

  
110
  def after_save
111
    return if ! Setting.serve_git_repositories?
112
    return if @ssh_key_entry == nil
113
    
114
    @ssh_key_entry.options = GitManager.get_authorized_keys_options_for_login(login)
115

  
116
    @ssh_key_entry.save 
117
  end
77 118
  
78 119
  def reload(*args)
79 120
    @name = nil
......
271 312
  def self.hash_password(clear_password)
272 313
    Digest::SHA1.hexdigest(clear_password || "")
273 314
  end
315

  
316
  def new_ssh_key
317
    @ssh_key_entry = AuthorizedKeysEntry.new
318
    @ssh_key_entry.identifier = login
319
  end
320

  
321
  def load_ssh_key
322
    @ssh_key_entry = AuthorizedKeysEntry.find_by_identifier(login)
323
    new_ssh_key if @ssh_key_entry == nil
324
  end
274 325
end
275 326

  
276 327
class AnonymousUser < User
app/views/my/account.rhtml
16 16
<p><%= f.text_field :mail, :required => true %></p>
17 17
<p><%= f.select :language, lang_options_for_select %></p>
18 18

  
19
<% if Setting.serve_git_repositories? %>
20
  <p><%= f.select :ssh_key_type, AuthorizedKeysEntry::KEY_TYPES %></p>
21
  <p><%= f.text_area :ssh_key, :rows => 10 %></p>
22
<% end %>
23

  
19 24
<% @user.custom_field_values.select(&:editable?).each do |value| %>
20 25
	<p><%= custom_field_tag_with_label :user, value %></p>
21 26
<% end %>
app/views/settings/_repositories.rhtml
14 14
<%= hidden_field_tag 'settings[enabled_scm][]', '' %>
15 15
</p>
16 16

  
17
<% # TODO: Should be disabled when Git SCM is not enabled 
18
%>
19
<p><label><%= l(:setting_serve_git_repositories) %></label>
20
<%= check_box_tag 'settings[serve_git_repositories]', 1, Setting.serve_git_repositories? %>
21
<%= hidden_field_tag 'settings[serve_git_repositories]', 0 %>
22
</p>
23

  
24

  
17 25
<p><label><%= l(:setting_repositories_encodings) %></label>
18 26
<%= text_field_tag 'settings[repositories_encodings]', Setting.repositories_encodings, :size => 60 %><br /><em><%= l(:text_comma_separated) %></em></p>
19 27

  
config/settings.yml
77 77
  default: 1
78 78
sys_api_enabled:
79 79
  default: 0
80
serve_git_repositories:
81
  default: 0
80 82
commit_ref_keywords:
81 83
  default: 'refs,references,IssueID'
82 84
commit_fix_keywords:
lang/bg.yml
92 92
field_firstname: Име
93 93
field_lastname: Фамилия
94 94
field_mail: Email
95
field_ssh_key: SSH Public Key
96
field_ssh_key_type: SSH Public Key Type
95 97
field_filename: Файл
96 98
field_filesize: Големина
97 99
field_downloads: Downloads
......
181 183
setting_feeds_limit: Лимит на Feeds
182 184
setting_autofetch_changesets: Автоматично обработване на ревизиите
183 185
setting_sys_api_enabled: Разрешаване на WS за управление
186
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
184 187
setting_commit_ref_keywords: Отбелязващи ключови думи
185 188
setting_commit_fix_keywords: Приключващи ключови думи
186 189
setting_autologin: Автоматичен вход
lang/ca.yml
106 106
field_firstname: Nom
107 107
field_lastname: Cognom
108 108
field_mail: Correu electrònic 
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Fitxer
110 112
field_filesize: Mida
111 113
field_downloads: Baixades
......
202 204
setting_default_projects_public: Els projectes nous són públics per defecte
203 205
setting_autofetch_changesets: Omple automàticament les publicacions
204 206
setting_sys_api_enabled: Habilita el WS per a la gestió del dipòsit
207
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
205 208
setting_commit_ref_keywords: Paraules claus per a la referència
206 209
setting_commit_fix_keywords: Paraules claus per a la correcció
207 210
setting_autologin: Entrada automàtica
lang/cs.yml
106 106
field_firstname: Jméno
107 107
field_lastname: Příjmení
108 108
field_mail: Email
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Soubor
110 112
field_filesize: Velikost
111 113
field_downloads: Staženo
......
201 203
setting_default_projects_public: Nové projekty nastavovat jako veřejné
202 204
setting_autofetch_changesets: Autofetch commits
203 205
setting_sys_api_enabled: Povolit WS pro správu repozitory
206
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
204 207
setting_commit_ref_keywords: Klíčová slova pro odkazy
205 208
setting_commit_fix_keywords: Klíčová slova pro uzavření
206 209
setting_autologin: Automatické přihlašování
lang/da.yml
106 106
field_firstname: Fornavn
107 107
field_lastname: Efternavn
108 108
field_mail: E-mail
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Fil
110 112
field_filesize: Størrelse
111 113
field_downloads: Downloads
......
202 204
setting_default_projects_public: Nye projekter er som standard offentlige
203 205
setting_autofetch_changesets: Hent automatisk commits
204 206
setting_sys_api_enabled: Aktiver webservice til versionsstyring
207
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
205 208
setting_commit_ref_keywords: Nøgleord for sagsreferencer
206 209
setting_commit_fix_keywords: Nøgleord for lukning af sager
207 210
setting_autologin: Autologin
lang/de.yml
106 106
field_firstname: Vorname
107 107
field_lastname: Nachname
108 108
field_mail: E-Mail
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Datei
110 112
field_filesize: Größe
111 113
field_downloads: Downloads
......
203 205
setting_default_projects_public: Neue Projekte sind standardmäßig öffentlich
204 206
setting_autofetch_changesets: Changesets automatisch abrufen
205 207
setting_sys_api_enabled: Webservice zur Verwaltung der Projektarchive benutzen
208
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
206 209
setting_commit_ref_keywords: Schlüsselwörter (Beziehungen)
207 210
setting_commit_fix_keywords: Schlüsselwörter (Status)
208 211
setting_autologin: Automatische Anmeldung
lang/en.yml
108 108
field_firstname: Firstname
109 109
field_lastname: Lastname
110 110
field_mail: Email
111
field_ssh_key: SSH Public Key
112
field_ssh_key_type: SSH Public Key Type
111 113
field_filename: File
112 114
field_filesize: Size
113 115
field_downloads: Downloads
......
206 208
setting_default_projects_public: New projects are public by default
207 209
setting_autofetch_changesets: Autofetch commits
208 210
setting_sys_api_enabled: Enable WS for repository management
211
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
209 212
setting_commit_ref_keywords: Referencing keywords
210 213
setting_commit_fix_keywords: Fixing keywords
211 214
setting_autologin: Autologin
lang/es.yml
150 150
field_lastname: Apellido
151 151
field_login: Identificador
152 152
field_mail: Correo electrónico
153
field_ssh_key: SSH Public Key
154
field_ssh_key_type: SSH Public Key Type
153 155
field_mail_notification: Notificaciones por correo
154 156
field_max_length: Longitud máxima
155 157
field_min_length: Longitud mínima
......
627 629
setting_self_registration: Registro permitido
628 630
setting_sequential_project_identifiers: Generar identificadores de proyecto
629 631
setting_sys_api_enabled: Habilitar SW para la gestión del repositorio
632
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
630 633
setting_text_formatting: Formato de texto
631 634
setting_time_format: Formato de hora
632 635
setting_user_format: Formato de nombre de usuario
lang/fi.yml
101 101
field_firstname: Etunimi
102 102
field_lastname: Sukunimi
103 103
field_mail: Sähköposti
104
field_ssh_key: SSH Public Key
105
field_ssh_key_type: SSH Public Key Type
104 106
field_filename: Tiedosto
105 107
field_filesize: Koko
106 108
field_downloads: Latausta
......
194 196
setting_feeds_limit: Syötteen sisällön raja
195 197
setting_autofetch_changesets: Automaattisten muutosjoukkojen haku
196 198
setting_sys_api_enabled: Salli WS tietovaraston hallintaan
199
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
197 200
setting_commit_ref_keywords: Viittaavat hakusanat
198 201
setting_commit_fix_keywords: Korjaavat hakusanat
199 202
setting_autologin: Automaatinen kirjautuminen
lang/fr.yml
108 108
field_firstname: Prénom
109 109
field_lastname: Nom
110 110
field_mail: Email
111
field_ssh_key: SSH Public Key
112
field_ssh_key_type: SSH Public Key Type
111 113
field_filename: Fichier
112 114
field_filesize: Taille
113 115
field_downloads: Téléchargements
......
206 208
setting_default_projects_public: Définir les nouveaux projects comme publics par défaut
207 209
setting_autofetch_changesets: Récupération auto. des commits
208 210
setting_sys_api_enabled: Activer les WS pour la gestion des dépôts
211
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
209 212
setting_commit_ref_keywords: Mot-clés de référencement
210 213
setting_commit_fix_keywords: Mot-clés de résolution
211 214
setting_autologin: Autologin
lang/he.yml
94 94
field_firstname: שם פרטי
95 95
field_lastname: שם משפחה
96 96
field_mail: דוא"ל
97
field_ssh_key: SSH Public Key
98
field_ssh_key_type: SSH Public Key Type
97 99
field_filename: קובץ
98 100
field_filesize: גודל
99 101
field_downloads: הורדות
......
184 186
setting_feeds_limit: גבול תוכן הזנות
185 187
setting_autofetch_changesets: משיכה אוטומתי של עידכונים
186 188
setting_sys_api_enabled: אפשר WS לניהול המאגר
189
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
187 190
setting_commit_ref_keywords: מילות מפתח מקשרות
188 191
setting_commit_fix_keywords: מילות מפתח מתקנות
189 192
setting_autologin: חיבור אוטומטי
lang/hu.yml
103 103
field_firstname: Keresztnév
104 104
field_lastname: Vezetéknév
105 105
field_mail: E-mail
106
field_ssh_key: SSH Public Key
107
field_ssh_key_type: SSH Public Key Type
106 108
field_filename: Fájl
107 109
field_filesize: Méret
108 110
field_downloads: Letöltések
......
198 200
setting_default_projects_public: Az új projektek alapértelmezés szerint nyilvánosak
199 201
setting_autofetch_changesets: Commitok automatikus lehúzása
200 202
setting_sys_api_enabled: WS engedélyezése a tárolók kezeléséhez
203
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
201 204
setting_commit_ref_keywords: Hivatkozó kulcsszavak
202 205
setting_commit_fix_keywords: Javítások kulcsszavai
203 206
setting_autologin: Automatikus bejelentkezés
lang/it.yml
92 92
field_firstname: Nome
93 93
field_lastname: Cognome
94 94
field_mail: Email
95
field_ssh_key: SSH Public Key
96
field_ssh_key_type: SSH Public Key Type
95 97
field_filename: File
96 98
field_filesize: Dimensione
97 99
field_downloads: Download
......
181 183
setting_feeds_limit: Limite contenuti del feed
182 184
setting_autofetch_changesets: Acquisisci automaticamente le commit
183 185
setting_sys_api_enabled: Abilita WS per la gestione del repository
186
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
184 187
setting_commit_ref_keywords: Referencing keywords
185 188
setting_commit_fix_keywords: Fixing keywords
186 189
setting_autologin: Login automatico
lang/ja.yml
93 93
field_firstname: 名前
94 94
field_lastname: 苗字
95 95
field_mail: メールアドレス
96
field_ssh_key: SSH Public Key
97
field_ssh_key_type: SSH Public Key Type
96 98
field_filename: ファイル
97 99
field_filesize: サイズ
98 100
field_downloads: ダウンロード
......
182 184
setting_feeds_limit: フィード内容の上限
183 185
setting_autofetch_changesets: コミットを自動取得する
184 186
setting_sys_api_enabled: リポジトリ管理用のWeb Serviceを有効にする
187
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
185 188
setting_commit_ref_keywords: 参照用キーワード
186 189
setting_commit_fix_keywords: 修正用キーワード
187 190
setting_autologin: 自動ログイン
lang/ko.yml
94 94
field_firstname: 이름
95 95
field_lastname: 성
96 96
field_mail: 메일
97
field_ssh_key: SSH Public Key
98
field_ssh_key_type: SSH Public Key Type
97 99
field_filename: 파일
98 100
field_filesize: 크기
99 101
field_downloads: 다운로드
......
184 186
setting_feeds_limit: 내용 피드(RSS Feed) 제한 개수
185 187
setting_autofetch_changesets: 커밋된 변경묶음을 자동으로 가져오기
186 188
setting_sys_api_enabled: 저장소 관리자에 WS 를 허용
189
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
187 190
setting_commit_ref_keywords: 일감 참조에 사용할 키워드들
188 191
setting_commit_fix_keywords: 일감 해결에 사용할 키워드들 
189 192
setting_autologin: 자동 로그인
lang/lt.yml
106 106
field_firstname: Vardas
107 107
field_lastname: Pavardė
108 108
field_mail: Email
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Byla
110 112
field_filesize: Dydis
111 113
field_downloads: Atsiuntimai
......
203 205
setting_default_projects_public: Naujas projektas viešas pagal nutylėjimą
204 206
setting_autofetch_changesets: Automatinis pakeitimų siuntimas
205 207
setting_sys_api_enabled: Įgalinkite WS sandėlio vadybai
208
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
206 209
setting_commit_ref_keywords: Nurodymo reikšminiai žodžiai
207 210
setting_commit_fix_keywords: Fiksavimo reikšminiai žodžiai
208 211
setting_autologin: Autoregistracija
lang/nl.yml
92 92
field_firstname: Voornaam
93 93
field_lastname: Achternaam
94 94
field_mail: E-mail
95
field_ssh_key: SSH Public Key
96
field_ssh_key_type: SSH Public Key Type
95 97
field_filename: Bestand
96 98
field_filesize: Grootte
97 99
field_downloads: Downloads
......
181 183
setting_feeds_limit: Feedinhoud limiet
182 184
setting_autofetch_changesets: Haal commits automatisch op
183 185
setting_sys_api_enabled: Gebruik WS voor repository beheer
186
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
184 187
setting_commit_ref_keywords: Refererende trefwoorden
185 188
setting_commit_fix_keywords: Gefixeerde trefwoorden
186 189
setting_autologin: Automatisch inloggen
lang/no.yml
105 105
field_firstname: Fornavn
106 106
field_lastname: Etternavn
107 107
field_mail: E-post
108
field_ssh_key: SSH Public Key
109
field_ssh_key_type: SSH Public Key Type
108 110
field_filename: Fil
109 111
field_filesize: Størrelse
110 112
field_downloads: Nedlastinger
......
200 202
setting_default_projects_public: Nye prosjekter er offentlige som standard
201 203
setting_autofetch_changesets: Autohenting av innsendinger
202 204
setting_sys_api_enabled: Aktiver webservice for depot-administrasjon
205
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
203 206
setting_commit_ref_keywords: Nøkkelord for referanse
204 207
setting_commit_fix_keywords: Nøkkelord for retting
205 208
setting_autologin: Autoinnlogging
lang/pl.yml
161 161
field_lastname: Nazwisko
162 162
field_login: Login
163 163
field_mail: Email
164
field_ssh_key: Klucz publiczny SSH
165
field_ssh_key_type: Typ klucza SSH
164 166
field_mail_notification: Powiadomienia Email
165 167
field_max_length: Maksymalna długość
166 168
field_min_length: Minimalna długość
......
657 659
setting_self_registration: Własna rejestracja umożliwiona
658 660
setting_sequential_project_identifiers: Generuj sekwencyjne identyfikatory projektów
659 661
setting_sys_api_enabled: Włączenie WS do zarządzania repozytorium
662
setting_serve_git_repositories: Udostępnij repozytoria GIT poprzez konto SSH Redmine
660 663
setting_text_formatting: Formatowanie tekstu
661 664
setting_time_format: Format czasu
662 665
setting_user_format: Personalny format wyświetlania
lang/pt-br.yml
105 105
field_firstname: Nome
106 106
field_lastname: Sobrenome
107 107
field_mail: Email
108
field_ssh_key: SSH Public Key
109
field_ssh_key_type: SSH Public Key Type
108 110
field_filename: Arquivo
109 111
field_filesize: Tamanho
110 112
field_downloads: Downloads
......
201 203
setting_default_projects_public: Novos projetos são públicos por padrão
202 204
setting_autofetch_changesets: Auto-obter commits
203 205
setting_sys_api_enabled: Ativa WS para gerenciamento do repositório
206
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
204 207
setting_commit_ref_keywords: Palavras de referência
205 208
setting_commit_fix_keywords: Palavras de fechamento
206 209
setting_autologin: Auto-login
lang/pt.yml
107 107
field_firstname: Nome
108 108
field_lastname: Apelido
109 109
field_mail: E-mail
110
field_ssh_key: SSH Public Key
111
field_ssh_key_type: SSH Public Key Type
110 112
field_filename: Ficheiro
111 113
field_filesize: Tamanho
112 114
field_downloads: Downloads
......
203 205
setting_default_projects_public: Projectos novos são públicos por omissão
204 206
setting_autofetch_changesets: Buscar automaticamente commits
205 207
setting_sys_api_enabled: Activar Web Service para gestão do repositório
208
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
206 209
setting_commit_ref_keywords: Palavras-chave de referência
207 210
setting_commit_fix_keywords: Palavras-chave de fecho
208 211
setting_autologin: Login automático
lang/ro.yml
92 92
field_firstname: Nume
93 93
field_lastname: Prenume
94 94
field_mail: Email
95
field_ssh_key: SSH Public Key
96
field_ssh_key_type: SSH Public Key Type
95 97
field_filename: Fisier
96 98
field_filesize: Marimea fisierului
97 99
field_downloads: Download
......
181 183
setting_feeds_limit: Limita continut feed
182 184
setting_autofetch_changesets: Autofetch commits
183 185
setting_sys_api_enabled: Setare WS pentru managementul stocului (repository)
186
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
184 187
setting_commit_ref_keywords: Cuvinte cheie de referinta
185 188
setting_commit_fix_keywords: Cuvinte cheie de rezolvare
186 189
setting_autologin: Autentificare automata
lang/ru.yml
165 165
field_lastname: Фамилия
166 166
field_login: Пользователь
167 167
field_mail: Email
168
field_ssh_key: SSH Public Key
169
field_ssh_key_type: SSH Public Key Type
168 170
field_mail_notification: Уведомления по email
169 171
field_max_length: Максимальная длина
170 172
field_min_length: Минимальная длина
......
673 675
setting_self_registration: Возможна саморегистрация
674 676
setting_sequential_project_identifiers: Генерировать последовательные идентификаторы проектов
675 677
setting_sys_api_enabled: Разрешить WS для управления хранилищем
678
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
676 679
setting_text_formatting: Форматирование текста
677 680
setting_time_format: Формат времени
678 681
setting_user_format: Формат отображения имени
lang/sk.yml
106 106
field_firstname: Meno
107 107
field_lastname: Priezvisko
108 108
field_mail: Email
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Súbor
110 112
field_filesize: Veľkosť
111 113
field_downloads: Stiahnuté
......
201 203
setting_default_projects_public: Nové projekty nastavovať ako verejné
202 204
setting_autofetch_changesets: Automatický prenos zmien
203 205
setting_sys_api_enabled: Povolit WS pre správu repozitory
206
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
204 207
setting_commit_ref_keywords: Klúčové slová pre odkazy
205 208
setting_commit_fix_keywords: Klúčové slová pre uzavretie
206 209
setting_autologin: Automatické prihlasovanie
lang/sr.yml
96 96
field_firstname: Ime
97 97
field_lastname: Prezime
98 98
field_mail: Email
99
field_ssh_key: SSH Public Key
100
field_ssh_key_type: SSH Public Key Type
99 101
field_filename: Fajl
100 102
field_filesize: Veličina
101 103
field_downloads: Preuzimanja
......
186 188
setting_feeds_limit: Feed content limit
187 189
setting_autofetch_changesets: Autofetch commits
188 190
setting_sys_api_enabled: Ukljuci WS za menadžment spremišta
191
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
189 192
setting_commit_ref_keywords: Referentne ključne reči
190 193
setting_commit_fix_keywords: Fiksne ključne reči
191 194
setting_autologin: Automatsko prijavljivanje
lang/sv.yml
106 106
field_firstname: Förnamn
107 107
field_lastname: Efternamn
108 108
field_mail: Mail
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Fil
110 112
field_filesize: Storlek
111 113
field_downloads: Nerladdningar
......
203 205
setting_default_projects_public: Nya projekt är publika som standard
204 206
setting_autofetch_changesets: Automatisk hämtning av commits
205 207
setting_sys_api_enabled: Aktivera WS för repository-hantering
208
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
206 209
setting_commit_ref_keywords: Referens-nyckelord
207 210
setting_commit_fix_keywords: Fix-nyckelord
208 211
setting_autologin: Automatisk inloggning
lang/th.yml
103 103
field_firstname: ชื่อ
104 104
field_lastname: นามสกุล
105 105
field_mail: อีเมล์
106
field_ssh_key: SSH Public Key
107
field_ssh_key_type: SSH Public Key Type
106 108
field_filename: แฟ้ม
107 109
field_filesize: ขนาด
108 110
field_downloads: ดาวน์โหลด
......
198 200
setting_default_projects_public: โครงการใหม่มีค่าเริ่มต้นเป็น สาธารณะ
199 201
setting_autofetch_changesets: ดึง commits อัตโนมัติ
200 202
setting_sys_api_enabled: เปิดใช้ WS สำหรับการจัดการที่เก็บต้นฉบับ
203
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
201 204
setting_commit_ref_keywords: คำสำคัญ Referencing
202 205
setting_commit_fix_keywords: คำสำคัญ Fixing
203 206
setting_autologin: เข้าระบบอัตโนมัติ
lang/tr.yml
102 102
field_firstname: Ad
103 103
field_lastname: Soyad
104 104
field_mail: E-Posta
105
field_ssh_key: SSH Public Key
106
field_ssh_key_type: SSH Public Key Type
105 107
field_filename: Dosya
106 108
field_filesize: Boyut
107 109
field_downloads: İndirilenler
......
197 199
setting_default_projects_public: Yeni projeler varsayılan olarak herkese açık
198 200
setting_autofetch_changesets: Otomatik gönderi al
199 201
setting_sys_api_enabled: Depo yönetimi için WS'yi etkinleştir
202
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
200 203
setting_commit_ref_keywords: Başvuru Kelimeleri
201 204
setting_commit_fix_keywords: Sabitleme kelimeleri
202 205
setting_autologin: Otomatik Giriş
lang/uk.yml
97 97
field_firstname: Ім'я
98 98
field_lastname: Прізвище
99 99
field_mail: Ел. пошта
100
field_ssh_key: SSH Public Key
101
field_ssh_key_type: SSH Public Key Type
100 102
field_filename: Файл
101 103
field_filesize: Розмір
102 104
field_downloads: Завантаження
......
189 191
setting_feeds_limit: Обмеження змісту подачі
190 192
setting_autofetch_changesets: Автоматично доставати доповнення
191 193
setting_sys_api_enabled: Дозволити WS для управління репозиторієм
194
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
192 195
setting_commit_ref_keywords: Ключові слова для посилання
193 196
setting_commit_fix_keywords: Призначення ключових слів
194 197
setting_autologin: Автоматичний вхід
lang/vn.yml
106 106
field_firstname: Tên lót + Tên
107 107
field_lastname: Họ
108 108
field_mail: Email
109
field_ssh_key: SSH Public Key
110
field_ssh_key_type: SSH Public Key Type
109 111
field_filename: Tập tin
110 112
field_filesize: Cỡ
111 113
field_downloads: Tải về
......
202 204
setting_default_projects_public: Dự án mặc định là công cộng
... This diff was truncated because it exceeds the maximum size that can be displayed.
(2-2/5)