Revision 1297:0a574315af3e .svn/pristine/67

View differences:

.svn/pristine/67/670602b1a0a590019c6607a3c26246d319412236.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  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 File.expand_path('../../../test_helper', __FILE__)
19

  
20
class RoutingMessagesTest < ActionController::IntegrationTest
21
  def test_messages
22
    assert_routing(
23
        { :method => 'get', :path => "/boards/22/topics/2" },
24
        { :controller => 'messages', :action => 'show', :id => '2',
25
          :board_id => '22' }
26
      )
27
    assert_routing(
28
        { :method => 'get', :path => "/boards/lala/topics/new" },
29
        { :controller => 'messages', :action => 'new', :board_id => 'lala' }
30
      )
31
    assert_routing(
32
        { :method => 'get', :path => "/boards/lala/topics/22/edit" },
33
        { :controller => 'messages', :action => 'edit', :id => '22',
34
          :board_id => 'lala' }
35
      )
36
    assert_routing(
37
        { :method => 'post', :path => "/boards/lala/topics/quote/22" },
38
        { :controller => 'messages', :action => 'quote', :id => '22',
39
          :board_id => 'lala' }
40
      )
41
    assert_routing(
42
        { :method => 'post', :path => "/boards/lala/topics/new" },
43
        { :controller => 'messages', :action => 'new', :board_id => 'lala' }
44
      )
45
    assert_routing(
46
        { :method => 'post', :path => "/boards/lala/topics/preview" },
47
        { :controller => 'messages', :action => 'preview',
48
          :board_id => 'lala' }
49
      )
50
    assert_routing(
51
        { :method => 'post', :path => "/boards/lala/topics/22/edit" },
52
        { :controller => 'messages', :action => 'edit', :id => '22',
53
          :board_id => 'lala' }
54
      )
55
    assert_routing(
56
        { :method => 'post', :path => "/boards/22/topics/555/replies" },
57
        { :controller => 'messages', :action => 'reply', :id => '555',
58
          :board_id => '22' }
59
      )
60
    assert_routing(
61
        { :method => 'post', :path => "/boards/22/topics/555/destroy" },
62
        { :controller => 'messages', :action => 'destroy', :id => '555',
63
          :board_id => '22' }
64
      )
65
  end
66
end
.svn/pristine/67/671278aa9eb41ffc1745cf0aca4827c983ebe8c5.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  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 File.expand_path('../../../test_helper', __FILE__)
19

  
20
class ApiTest::IssueRelationsTest < ActionController::IntegrationTest
21
  fixtures :projects, :trackers, :issue_statuses, :issues,
22
           :enumerations, :users, :issue_categories,
23
           :projects_trackers,
24
           :roles,
25
           :member_roles,
26
           :members,
27
           :enabled_modules,
28
           :workflows,
29
           :issue_relations
30

  
31
  def setup
32
    Setting.rest_api_enabled = '1'
33
  end
34

  
35
  context "/issues/:issue_id/relations" do
36
    context "GET" do
37
      should "return issue relations" do
38
        get '/issues/9/relations.xml', {}, credentials('jsmith')
39

  
40
        assert_response :success
41
        assert_equal 'application/xml', @response.content_type
42

  
43
        assert_tag :tag => 'relations',
44
          :attributes => { :type => 'array' },
45
          :child => {
46
            :tag => 'relation',
47
            :child => {
48
              :tag => 'id',
49
              :content => '1'
50
            }
51
          }
52
      end
53
    end
54

  
55
    context "POST" do
56
      should "create a relation" do
57
        assert_difference('IssueRelation.count') do
58
          post '/issues/2/relations.xml', {:relation => {:issue_to_id => 7, :relation_type => 'relates'}}, credentials('jsmith')
59
        end
60

  
61
        relation = IssueRelation.first(:order => 'id DESC')
62
        assert_equal 2, relation.issue_from_id
63
        assert_equal 7, relation.issue_to_id
64
        assert_equal 'relates', relation.relation_type
65

  
66
        assert_response :created
67
        assert_equal 'application/xml', @response.content_type
68
        assert_tag 'relation', :child => {:tag => 'id', :content => relation.id.to_s}
69
      end
70

  
71
      context "with failure" do
72
        should "return the errors" do
73
          assert_no_difference('IssueRelation.count') do
74
            post '/issues/2/relations.xml', {:relation => {:issue_to_id => 7, :relation_type => 'foo'}}, credentials('jsmith')
75
          end
76

  
77
          assert_response :unprocessable_entity
78
          assert_tag :errors, :child => {:tag => 'error', :content => /relation_type is not included in the list/}
79
        end
80
      end
81
    end
82
  end
83

  
84
  context "/relations/:id" do
85
    context "GET" do
86
      should "return the relation" do
87
        get '/relations/2.xml', {}, credentials('jsmith')
88

  
89
        assert_response :success
90
        assert_equal 'application/xml', @response.content_type
91
        assert_tag 'relation', :child => {:tag => 'id', :content => '2'}
92
      end
93
    end
94

  
95
    context "DELETE" do
96
      should "delete the relation" do
97
        assert_difference('IssueRelation.count', -1) do
98
          delete '/relations/2.xml', {}, credentials('jsmith')
99
        end
100

  
101
        assert_response :ok
102
        assert_equal '', @response.body
103
        assert_nil IssueRelation.find_by_id(2)
104
      end
105
    end
106
  end
107
end
.svn/pristine/67/673796e159cf85c017eed0e9ba7ade50c519b16a.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  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 File.expand_path('../../test_helper', __FILE__)
19

  
20
class BoardsControllerTest < ActionController::TestCase
21
  fixtures :projects, :users, :members, :member_roles, :roles, :boards, :messages, :enabled_modules
22

  
23
  def setup
24
    User.current = nil
25
  end
26

  
27
  def test_index
28
    get :index, :project_id => 1
29
    assert_response :success
30
    assert_template 'index'
31
    assert_not_nil assigns(:boards)
32
    assert_not_nil assigns(:project)
33
  end
34

  
35
  def test_index_not_found
36
    get :index, :project_id => 97
37
    assert_response 404
38
  end
39

  
40
  def test_index_should_show_messages_if_only_one_board
41
    Project.find(1).boards.slice(1..-1).each(&:destroy)
42

  
43
    get :index, :project_id => 1
44
    assert_response :success
45
    assert_template 'show'
46
    assert_not_nil assigns(:topics)
47
  end
48

  
49
  def test_show
50
    get :show, :project_id => 1, :id => 1
51
    assert_response :success
52
    assert_template 'show'
53
    assert_not_nil assigns(:board)
54
    assert_not_nil assigns(:project)
55
    assert_not_nil assigns(:topics)
56
  end
57

  
58
  def test_show_should_display_sticky_messages_first
59
    Message.update_all(:sticky => 0)
60
    Message.update_all({:sticky => 1}, {:id => 1})
61

  
62
    get :show, :project_id => 1, :id => 1
63
    assert_response :success
64

  
65
    topics = assigns(:topics)
66
    assert_not_nil topics
67
    assert topics.size > 1, "topics size was #{topics.size}"
68
    assert topics.first.sticky?
69
    assert topics.first.updated_on < topics.second.updated_on
70
  end
71

  
72
  def test_show_with_permission_should_display_the_new_message_form
73
    @request.session[:user_id] = 2
74
    get :show, :project_id => 1, :id => 1
75
    assert_response :success
76
    assert_template 'show'
77

  
78
    assert_tag 'form', :attributes => {:id => 'message-form'}
79
    assert_tag 'input', :attributes => {:name => 'message[subject]'}
80
  end
81

  
82
  def test_show_atom
83
    get :show, :project_id => 1, :id => 1, :format => 'atom'
84
    assert_response :success
85
    assert_template 'common/feed'
86
    assert_not_nil assigns(:board)
87
    assert_not_nil assigns(:project)
88
    assert_not_nil assigns(:messages)
89
  end
90

  
91
  def test_show_not_found
92
    get :index, :project_id => 1, :id => 97
93
    assert_response 404
94
  end
95

  
96
  def test_new
97
    @request.session[:user_id] = 2
98
    get :new, :project_id => 1
99
    assert_response :success
100
    assert_template 'new'
101

  
102
    assert_select 'select[name=?]', 'board[parent_id]' do
103
      assert_select 'option', (Project.find(1).boards.size + 1)
104
      assert_select 'option[value=]', :text => ''
105
      assert_select 'option[value=1]', :text => 'Help'
106
    end
107
  end
108

  
109
  def test_new_without_project_boards
110
    Project.find(1).boards.delete_all
111
    @request.session[:user_id] = 2
112

  
113
    get :new, :project_id => 1
114
    assert_response :success
115
    assert_template 'new'
116

  
117
    assert_select 'select[name=?]', 'board[parent_id]', 0
118
  end
119

  
120
  def test_create
121
    @request.session[:user_id] = 2
122
    assert_difference 'Board.count' do
123
      post :create, :project_id => 1, :board => { :name => 'Testing', :description => 'Testing board creation'}
124
    end
125
    assert_redirected_to '/projects/ecookbook/settings/boards'
126
    board = Board.first(:order => 'id DESC')
127
    assert_equal 'Testing', board.name
128
    assert_equal 'Testing board creation', board.description
129
  end
130

  
131
  def test_create_with_parent
132
    @request.session[:user_id] = 2
133
    assert_difference 'Board.count' do
134
      post :create, :project_id => 1, :board => { :name => 'Testing', :description => 'Testing', :parent_id => 2}
135
    end
136
    assert_redirected_to '/projects/ecookbook/settings/boards'
137
    board = Board.first(:order => 'id DESC')
138
    assert_equal Board.find(2), board.parent
139
  end
140

  
141
  def test_create_with_failure
142
    @request.session[:user_id] = 2
143
    assert_no_difference 'Board.count' do
144
      post :create, :project_id => 1, :board => { :name => '', :description => 'Testing board creation'}
145
    end
146
    assert_response :success
147
    assert_template 'new'
148
  end
149

  
150
  def test_edit
151
    @request.session[:user_id] = 2
152
    get :edit, :project_id => 1, :id => 2
153
    assert_response :success
154
    assert_template 'edit'
155
  end
156

  
157
  def test_edit_with_parent
158
    board = Board.generate!(:project_id => 1, :parent_id => 2)
159
    @request.session[:user_id] = 2
160
    get :edit, :project_id => 1, :id => board.id
161
    assert_response :success
162
    assert_template 'edit'
163

  
164
    assert_select 'select[name=?]', 'board[parent_id]' do
165
      assert_select 'option[value=2][selected=selected]'
166
    end
167
  end
168

  
169
  def test_update
170
    @request.session[:user_id] = 2
171
    assert_no_difference 'Board.count' do
172
      put :update, :project_id => 1, :id => 2, :board => { :name => 'Testing', :description => 'Testing board update'}
173
    end
174
    assert_redirected_to '/projects/ecookbook/settings/boards'
175
    assert_equal 'Testing', Board.find(2).name
176
  end
177

  
178
  def test_update_position
179
    @request.session[:user_id] = 2
180
    put :update, :project_id => 1, :id => 2, :board => { :move_to => 'highest'}
181
    assert_redirected_to '/projects/ecookbook/settings/boards'
182
    board = Board.find(2)
183
    assert_equal 1, board.position
184
  end
185

  
186
  def test_update_with_failure
187
    @request.session[:user_id] = 2
188
    put :update, :project_id => 1, :id => 2, :board => { :name => '', :description => 'Testing board update'}
189
    assert_response :success
190
    assert_template 'edit'
191
  end
192

  
193
  def test_destroy
194
    @request.session[:user_id] = 2
195
    assert_difference 'Board.count', -1 do
196
      delete :destroy, :project_id => 1, :id => 2
197
    end
198
    assert_redirected_to '/projects/ecookbook/settings/boards'
199
    assert_nil Board.find_by_id(2)
200
  end
201
end
.svn/pristine/67/67564d7eda2e47c3ebd8a07a650fb0bd7bbca60d.svn-base
1
<div class="splitcontent">
2
<div class="splitcontentleft">
3
<% i = 0 %>
4
<% split_on = (@issue.custom_field_values.size / 2.0).ceil - 1 %>
5
<% @issue.editable_custom_field_values.each do |value| %>
6
  <p><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %></p>
7
<% if i == split_on -%>
8
</div><div class="splitcontentright">
9
<% end -%>
10
<% i += 1 -%>
11
<% end -%>
12
</div>
13
</div>
.svn/pristine/67/676b4cb5bfe9b002838d027234fb7519b7bba5b1.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  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 GroupsController < ApplicationController
19
  layout 'admin'
20

  
21
  before_filter :require_admin
22
  before_filter :find_group, :except => [:index, :new, :create]
23
  accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user
24

  
25
  helper :custom_fields
26

  
27
  def index
28
    @groups = Group.sorted.all
29

  
30
    respond_to do |format|
31
      format.html
32
      format.api
33
    end
34
  end
35

  
36
  def show
37
    respond_to do |format|
38
      format.html
39
      format.api
40
    end
41
  end
42

  
43
  def new
44
    @group = Group.new
45
  end
46

  
47
  def create
48
    @group = Group.new
49
    @group.safe_attributes = params[:group]
50

  
51
    respond_to do |format|
52
      if @group.save
53
        format.html {
54
          flash[:notice] = l(:notice_successful_create)
55
          redirect_to(params[:continue] ? new_group_path : groups_path)
56
        }
57
        format.api  { render :action => 'show', :status => :created, :location => group_url(@group) }
58
      else
59
        format.html { render :action => "new" }
60
        format.api  { render_validation_errors(@group) }
61
      end
62
    end
63
  end
64

  
65
  def edit
66
  end
67

  
68
  def update
69
    @group.safe_attributes = params[:group]
70

  
71
    respond_to do |format|
72
      if @group.save
73
        flash[:notice] = l(:notice_successful_update)
74
        format.html { redirect_to(groups_path) }
75
        format.api  { render_api_ok }
76
      else
77
        format.html { render :action => "edit" }
78
        format.api  { render_validation_errors(@group) }
79
      end
80
    end
81
  end
82

  
83
  def destroy
84
    @group.destroy
85

  
86
    respond_to do |format|
87
      format.html { redirect_to(groups_url) }
88
      format.api  { render_api_ok }
89
    end
90
  end
91

  
92
  def add_users
93
    @users = User.find_all_by_id(params[:user_id] || params[:user_ids])
94
    @group.users << @users if request.post?
95
    respond_to do |format|
96
      format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
97
      format.js
98
      format.api { render_api_ok }
99
    end
100
  end
101

  
102
  def remove_user
103
    @group.users.delete(User.find(params[:user_id])) if request.delete?
104
    respond_to do |format|
105
      format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
106
      format.js
107
      format.api { render_api_ok }
108
    end
109
  end
110

  
111
  def autocomplete_for_user
112
    @users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
113
    render :layout => false
114
  end
115

  
116
  def edit_membership
117
    @membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
118
    @membership.save if request.post?
119
    respond_to do |format|
120
      format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
121
      format.js
122
    end
123
  end
124

  
125
  def destroy_membership
126
    Member.find(params[:membership_id]).destroy if request.post?
127
    respond_to do |format|
128
      format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
129
      format.js
130
    end
131
  end
132

  
133
  private
134

  
135
  def find_group
136
    @group = Group.find(params[:id])
137
  rescue ActiveRecord::RecordNotFound
138
    render_404
139
  end
140
end
.svn/pristine/67/676be5a662e77fd1edb667b7618e0c6a2f07f82e.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  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 File.expand_path('../../../test_helper', __FILE__)
19

  
20
class RoutingBoardsTest < ActionController::IntegrationTest
21
  def test_boards
22
    assert_routing(
23
        { :method => 'get', :path => "/projects/world_domination/boards" },
24
        { :controller => 'boards', :action => 'index', :project_id => 'world_domination' }
25
      )
26
    assert_routing(
27
        { :method => 'get', :path => "/projects/world_domination/boards/new" },
28
        { :controller => 'boards', :action => 'new', :project_id => 'world_domination' }
29
      )
30
    assert_routing(
31
        { :method => 'get', :path => "/projects/world_domination/boards/44" },
32
        { :controller => 'boards', :action => 'show', :project_id => 'world_domination',
33
          :id => '44' }
34
      )
35
    assert_routing(
36
        { :method => 'get', :path => "/projects/world_domination/boards/44.atom" },
37
        { :controller => 'boards', :action => 'show', :project_id => 'world_domination',
38
          :id => '44', :format => 'atom' }
39
      )
40
    assert_routing(
41
        { :method => 'get', :path => "/projects/world_domination/boards/44/edit" },
42
        { :controller => 'boards', :action => 'edit', :project_id => 'world_domination',
43
          :id => '44' }
44
      )
45
    assert_routing(
46
        { :method => 'post', :path => "/projects/world_domination/boards" },
47
        { :controller => 'boards', :action => 'create', :project_id => 'world_domination' }
48
      )
49
    assert_routing(
50
        { :method => 'put', :path => "/projects/world_domination/boards/44" },
51
        { :controller => 'boards', :action => 'update', :project_id => 'world_domination',
52
          :id => '44' }
53
      )
54
    assert_routing(
55
        { :method => 'delete', :path => "/projects/world_domination/boards/44" },
56
        { :controller => 'boards', :action => 'destroy', :project_id => 'world_domination',
57
          :id => '44' }
58
      )
59
  end
60
end
.svn/pristine/67/676f5dde2b525663017469a924ccb9594114d3c2.svn-base
1
class SetDefaultRepositories < ActiveRecord::Migration
2
  def self.up
3
    Repository.update_all(["is_default = ?", false])
4
    # Sets the last repository as default in case multiple repositories exist for the same project
5
    Repository.connection.select_values("SELECT r.id FROM #{Repository.table_name} r" +
6
      " WHERE r.id = (SELECT max(r1.id) FROM #{Repository.table_name} r1 WHERE r1.project_id = r.project_id)").each do |i|
7
        Repository.update_all(["is_default = ?", true], ["id = ?", i])
8
    end
9
  end
10

  
11
  def self.down
12
    Repository.update_all(["is_default = ?", false])
13
  end
14
end
.svn/pristine/67/67cbbb936bbd8a3a195700edbb70375a0e5e221d.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  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 Redmine
19
  module MenuManager
20
    class MenuError < StandardError #:nodoc:
21
    end
22

  
23
    module MenuController
24
      def self.included(base)
25
        base.extend(ClassMethods)
26
      end
27

  
28
      module ClassMethods
29
        @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
30
        mattr_accessor :menu_items
31

  
32
        # Set the menu item name for a controller or specific actions
33
        # Examples:
34
        #   * menu_item :tickets # => sets the menu name to :tickets for the whole controller
35
        #   * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
36
        #   * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
37
        #
38
        # The default menu item name for a controller is controller_name by default
39
        # Eg. the default menu item name for ProjectsController is :projects
40
        def menu_item(id, options = {})
41
          if actions = options[:only]
42
            actions = [] << actions unless actions.is_a?(Array)
43
            actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
44
          else
45
            menu_items[controller_name.to_sym][:default] = id
46
          end
47
        end
48
      end
49

  
50
      def menu_items
51
        self.class.menu_items
52
      end
53

  
54
      # Returns the menu item name according to the current action
55
      def current_menu_item
56
        @current_menu_item ||= menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
57
                                 menu_items[controller_name.to_sym][:default]
58
      end
59

  
60
      # Redirects user to the menu item of the given project
61
      # Returns false if user is not authorized
62
      def redirect_to_project_menu_item(project, name)
63
        item = Redmine::MenuManager.items(:project_menu).detect {|i| i.name.to_s == name.to_s}
64
        if item && User.current.allowed_to?(item.url, project) && (item.condition.nil? || item.condition.call(project))
65
          redirect_to({item.param => project}.merge(item.url))
66
          return true
67
        end
68
        false
69
      end
70
    end
71

  
72
    module MenuHelper
73
      # Returns the current menu item name
74
      def current_menu_item
75
        controller.current_menu_item
76
      end
77

  
78
      # Renders the application main menu
79
      def render_main_menu(project)
80
        render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
81
      end
82

  
83
      def display_main_menu?(project)
84
        menu_name = project && !project.new_record? ? :project_menu : :application_menu
85
        Redmine::MenuManager.items(menu_name).children.present?
86
      end
87

  
88
      def render_menu(menu, project=nil)
89
        links = []
90
        menu_items_for(menu, project) do |node|
91
          links << render_menu_node(node, project)
92
        end
93
        links.empty? ? nil : content_tag('ul', links.join("\n").html_safe)
94
      end
95

  
96
      def render_menu_node(node, project=nil)
97
        if node.children.present? || !node.child_menus.nil?
98
          return render_menu_node_with_children(node, project)
99
        else
100
          caption, url, selected = extract_node_details(node, project)
101
          return content_tag('li',
102
                               render_single_menu_node(node, caption, url, selected))
103
        end
104
      end
105

  
106
      def render_menu_node_with_children(node, project=nil)
107
        caption, url, selected = extract_node_details(node, project)
108

  
109
        html = [].tap do |html|
110
          html << '<li>'
111
          # Parent
112
          html << render_single_menu_node(node, caption, url, selected)
113

  
114
          # Standard children
115
          standard_children_list = "".html_safe.tap do |child_html|
116
            node.children.each do |child|
117
              child_html << render_menu_node(child, project)
118
            end
119
          end
120

  
121
          html << content_tag(:ul, standard_children_list, :class => 'menu-children') unless standard_children_list.empty?
122

  
123
          # Unattached children
124
          unattached_children_list = render_unattached_children_menu(node, project)
125
          html << content_tag(:ul, unattached_children_list, :class => 'menu-children unattached') unless unattached_children_list.blank?
126

  
127
          html << '</li>'
128
        end
129
        return html.join("\n").html_safe
130
      end
131

  
132
      # Returns a list of unattached children menu items
133
      def render_unattached_children_menu(node, project)
134
        return nil unless node.child_menus
135

  
136
        "".html_safe.tap do |child_html|
137
          unattached_children = node.child_menus.call(project)
138
          # Tree nodes support #each so we need to do object detection
139
          if unattached_children.is_a? Array
140
            unattached_children.each do |child|
141
              child_html << content_tag(:li, render_unattached_menu_item(child, project))
142
            end
143
          else
144
            raise MenuError, ":child_menus must be an array of MenuItems"
145
          end
146
        end
147
      end
148

  
149
      def render_single_menu_node(item, caption, url, selected)
150
        link_to(h(caption), url, item.html_options(:selected => selected))
151
      end
152

  
153
      def render_unattached_menu_item(menu_item, project)
154
        raise MenuError, ":child_menus must be an array of MenuItems" unless menu_item.is_a? MenuItem
155

  
156
        if User.current.allowed_to?(menu_item.url, project)
157
          link_to(h(menu_item.caption),
158
                  menu_item.url,
159
                  menu_item.html_options)
160
        end
161
      end
162

  
163
      def menu_items_for(menu, project=nil)
164
        items = []
165
        Redmine::MenuManager.items(menu).root.children.each do |node|
166
          if allowed_node?(node, User.current, project)
167
            if block_given?
168
              yield node
169
            else
170
              items << node  # TODO: not used?
171
            end
172
          end
173
        end
174
        return block_given? ? nil : items
175
      end
176

  
177
      def extract_node_details(node, project=nil)
178
        item = node
179
        url = case item.url
180
        when Hash
181
          project.nil? ? item.url : {item.param => project}.merge(item.url)
182
        when Symbol
183
          send(item.url)
184
        else
185
          item.url
186
        end
187
        caption = item.caption(project)
188
        return [caption, url, (current_menu_item == item.name)]
189
      end
190

  
191
      # Checks if a user is allowed to access the menu item by:
192
      #
193
      # * Checking the conditions of the item
194
      # * Checking the url target (project only)
195
      def allowed_node?(node, user, project)
196
        if node.condition && !node.condition.call(project)
197
          # Condition that doesn't pass
198
          return false
199
        end
200

  
201
        if project
202
          return user && user.allowed_to?(node.url, project)
203
        else
204
          # outside a project, all menu items allowed
205
          return true
206
        end
207
      end
208
    end
209

  
210
    class << self
211
      def map(menu_name)
212
        @items ||= {}
213
        mapper = Mapper.new(menu_name.to_sym, @items)
214
        if block_given?
215
          yield mapper
216
        else
217
          mapper
218
        end
219
      end
220

  
221
      def items(menu_name)
222
        @items[menu_name.to_sym] || MenuNode.new(:root, {})
223
      end
224
    end
225

  
226
    class Mapper
227
      def initialize(menu, items)
228
        items[menu] ||= MenuNode.new(:root, {})
229
        @menu = menu
230
        @menu_items = items[menu]
231
      end
232

  
233
      # Adds an item at the end of the menu. Available options:
234
      # * param: the parameter name that is used for the project id (default is :id)
235
      # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
236
      # * caption that can be:
237
      #   * a localized string Symbol
238
      #   * a String
239
      #   * a Proc that can take the project as argument
240
      # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
241
      # * parent: menu item will be added as a child of another named menu (eg. :parent => :issues)
242
      # * children: a Proc that is called before rendering the item. The Proc should return an array of MenuItems, which will be added as children to this item.
243
      #   eg. :children => Proc.new {|project| [Redmine::MenuManager::MenuItem.new(...)] }
244
      # * last: menu item will stay at the end (eg. :last => true)
245
      # * html_options: a hash of html options that are passed to link_to
246
      def push(name, url, options={})
247
        options = options.dup
248

  
249
        if options[:parent]
250
          subtree = self.find(options[:parent])
251
          if subtree
252
            target_root = subtree
253
          else
254
            target_root = @menu_items.root
255
          end
256

  
257
        else
258
          target_root = @menu_items.root
259
        end
260

  
261
        # menu item position
262
        if first = options.delete(:first)
263
          target_root.prepend(MenuItem.new(name, url, options))
264
        elsif before = options.delete(:before)
265

  
266
          if exists?(before)
267
            target_root.add_at(MenuItem.new(name, url, options), position_of(before))
268
          else
269
            target_root.add(MenuItem.new(name, url, options))
270
          end
271

  
272
        elsif after = options.delete(:after)
273

  
274
          if exists?(after)
275
            target_root.add_at(MenuItem.new(name, url, options), position_of(after) + 1)
276
          else
277
            target_root.add(MenuItem.new(name, url, options))
278
          end
279

  
280
        elsif options[:last] # don't delete, needs to be stored
281
          target_root.add_last(MenuItem.new(name, url, options))
282
        else
283
          target_root.add(MenuItem.new(name, url, options))
284
        end
285
      end
286

  
287
      # Removes a menu item
288
      def delete(name)
289
        if found = self.find(name)
290
          @menu_items.remove!(found)
291
        end
292
      end
293

  
294
      # Checks if a menu item exists
295
      def exists?(name)
296
        @menu_items.any? {|node| node.name == name}
297
      end
298

  
299
      def find(name)
300
        @menu_items.find {|node| node.name == name}
301
      end
302

  
303
      def position_of(name)
304
        @menu_items.each do |node|
305
          if node.name == name
306
            return node.position
307
          end
308
        end
309
      end
310
    end
311

  
312
    class MenuNode
313
      include Enumerable
314
      attr_accessor :parent
315
      attr_reader :last_items_count, :name
316

  
317
      def initialize(name, content = nil)
318
        @name = name
319
        @children = []
320
        @last_items_count = 0
321
      end
322

  
323
      def children
324
        if block_given?
325
          @children.each {|child| yield child}
326
        else
327
          @children
328
        end
329
      end
330

  
331
      # Returns the number of descendants + 1
332
      def size
333
        @children.inject(1) {|sum, node| sum + node.size}
334
      end
335

  
336
      def each &block
337
        yield self
338
        children { |child| child.each(&block) }
339
      end
340

  
341
      # Adds a child at first position
342
      def prepend(child)
343
        add_at(child, 0)
344
      end
345

  
346
      # Adds a child at given position
347
      def add_at(child, position)
348
        raise "Child already added" if find {|node| node.name == child.name}
349

  
350
        @children = @children.insert(position, child)
351
        child.parent = self
352
        child
353
      end
354

  
355
      # Adds a child as last child
356
      def add_last(child)
357
        add_at(child, -1)
358
        @last_items_count += 1
359
        child
360
      end
361

  
362
      # Adds a child
363
      def add(child)
364
        position = @children.size - @last_items_count
365
        add_at(child, position)
366
      end
367
      alias :<< :add
368

  
369
      # Removes a child
370
      def remove!(child)
371
        @children.delete(child)
372
        @last_items_count -= +1 if child && child.last
373
        child.parent = nil
374
        child
375
      end
376

  
377
      # Returns the position for this node in it's parent
378
      def position
379
        self.parent.children.index(self)
380
      end
381

  
382
      # Returns the root for this node
383
      def root
384
        root = self
385
        root = root.parent while root.parent
386
        root
387
      end
388
    end
389

  
390
    class MenuItem < MenuNode
391
      include Redmine::I18n
392
      attr_reader :name, :url, :param, :condition, :parent, :child_menus, :last
393

  
394
      def initialize(name, url, options)
395
        raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
396
        raise ArgumentError, "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
397
        raise ArgumentError, "Cannot set the :parent to be the same as this item" if options[:parent] == name.to_sym
398
        raise ArgumentError, "Invalid option :children for menu item '#{name}'" if options[:children] && !options[:children].respond_to?(:call)
399
        @name = name
400
        @url = url
401
        @condition = options[:if]
402
        @param = options[:param] || :id
403
        @caption = options[:caption]
404
        @html_options = options[:html] || {}
405
        # Adds a unique class to each menu item based on its name
406
        @html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
407
        @parent = options[:parent]
408
        @child_menus = options[:children]
409
        @last = options[:last] || false
410
        super @name.to_sym
411
      end
412

  
413
      def caption(project=nil)
414
        if @caption.is_a?(Proc)
415
          c = @caption.call(project).to_s
416
          c = @name.to_s.humanize if c.blank?
417
          c
418
        else
419
          if @caption.nil?
420
            l_or_humanize(name, :prefix => 'label_')
421
          else
422
            @caption.is_a?(Symbol) ? l(@caption) : @caption
423
          end
424
        end
425
      end
426

  
427
      def html_options(options={})
428
        if options[:selected]
429
          o = @html_options.dup
430
          o[:class] += ' selected'
431
          o
432
        else
433
          @html_options
434
        end
435
      end
436
    end
437
  end
438
end
.svn/pristine/67/67cf5cccf9960052155966022aeefcfe4c1324e2.svn-base
1
xml.instruct!
2
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
3
  xml.title   @title
4
  xml.link    "rel" => "self", "href" => url_for(:format => 'atom', :key => User.current.rss_key, :only_path => false)
5
  xml.link    "rel" => "alternate", "href" => home_url(:only_path => false)
6
  xml.id      url_for(:controller => 'welcome', :only_path => false)
7
  xml.updated((@journals.first ? @journals.first.event_datetime : Time.now).xmlschema)
8
  xml.author  { xml.name "#{Setting.app_title}" }
9
  @journals.each do |change|
10
    issue = change.issue
11
    xml.entry do
12
      xml.title   "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
13
      xml.link    "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
14
      xml.id      url_for(:controller => 'issues' , :action => 'show', :id => issue, :journal_id => change, :only_path => false)
15
      xml.updated change.created_on.xmlschema
16
      xml.author do
17
        xml.name change.user.name
18
        xml.email(change.user.mail) if change.user.is_a?(User) && !change.user.mail.blank? && !change.user.pref.hide_mail
19
      end
20
      xml.content "type" => "html" do
21
        xml.text! '<ul>'
22
        details_to_strings(change.details, false).each do |string|
23
          xml.text! '<li>' + string + '</li>'
24
        end
25
        xml.text! '</ul>'
26
        xml.text! textilizable(change, :notes, :only_path => false) unless change.notes.blank?
27
      end
28
    end
29
  end
30
end
.svn/pristine/67/67d42ac4101639d32c2a0940b1f3ea1663d8884f.svn-base
1
# Chinese (China) translations for Ruby on Rails
2
# by tsechingho (http://github.com/tsechingho)
3
zh:
4
  # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
5
  direction: ltr
6
  jquery:
7
    locale: "zh-CN"
8
  date:
9
    formats:
10
      # Use the strftime parameters for formats.
11
      # When no format has been given, it uses default.
12
      # You can provide other formats here if you like!
13
      default: "%Y-%m-%d"
14
      short: "%b%d日"
15
      long: "%Y年%b%d日"
16

  
17
    day_names: [星期天, 星期一, 星期二, 星期三, 星期四, 星期五, 星期六]
18
    abbr_day_names: [日, 一, 二, 三, 四, 五, 六]
19

  
20
    # Don't forget the nil at the beginning; there's no such thing as a 0th month
21
    month_names: [~, 一月, 二月, 三月, 四月, 五月, 六月, 七月, 八月, 九月, 十月, 十一月, 十二月]
22
    abbr_month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
23
    # Used in date_select and datime_select.
24
    order:
25
      - :year
26
      - :month
27
      - :day
28

  
29
  time:
30
    formats:
31
      default: "%Y年%b%d日 %A %H:%M:%S"
32
      time: "%H:%M"
33
      short: "%b%d日 %H:%M"
34
      long: "%Y年%b%d日 %H:%M"
35
    am: "上午"
36
    pm: "下午"
37

  
38
  datetime:
39
    distance_in_words:
40
      half_a_minute: "半分钟"
41
      less_than_x_seconds:
42
        one: "一秒内"
43
        other: "少于 %{count} 秒"
44
      x_seconds:
45
        one: "一秒"
46
        other: "%{count} 秒"
47
      less_than_x_minutes:
48
        one: "一分钟内"
49
        other: "少于 %{count} 分钟"
50
      x_minutes:
51
        one: "一分钟"
52
        other: "%{count} 分钟"
53
      about_x_hours:
54
        one: "大约一小时"
55
        other: "大约 %{count} 小时"
56
      x_hours:
57
        one:   "1 小时"
58
        other: "%{count} 小时"
59
      x_days:
60
        one: "一天"
61
        other: "%{count} 天"
62
      about_x_months:
63
        one: "大约一个月"
64
        other: "大约 %{count} 个月"
65
      x_months:
66
        one: "一个月"
67
        other: "%{count} 个月"
68
      about_x_years:
69
        one: "大约一年"
70
        other: "大约 %{count} 年"
71
      over_x_years:
72
        one: "超过一年"
73
        other: "超过 %{count} 年"
74
      almost_x_years:
75
        one:   "将近 1 年"
76
        other: "将近 %{count} 年"
77

  
78
  number:
79
    # Default format for numbers
80
    format:
81
      separator: "."
82
      delimiter: ""
83
      precision: 3
84
    human:
85
      format:
86
        delimiter: ""
87
        precision: 3
88
      storage_units:
89
        format: "%n %u"
90
        units:
91
          byte:
92
            one: "Byte"
93
            other: "Bytes"
94
          kb: "KB"
95
          mb: "MB"
96
          gb: "GB"
97
          tb: "TB"
98

  
99
# Used in array.to_sentence.
100
  support:
101
    array:
102
      sentence_connector: "和"
103
      skip_last_comma: false
104

  
105
  activerecord:
106
    errors:
107
      template:
108
        header:
109
          one:    "由于发生了一个错误 %{model} 无法保存"
110
          other:  "%{count} 个错误使得 %{model} 无法保存"
111
      messages:
112
        inclusion: "不包含于列表中"
113
        exclusion: "是保留关键字"
114
        invalid: "是无效的"
115
        confirmation: "与确认值不匹配"
116
        accepted: "必须是可被接受的"
117
        empty: "不能留空"
118
        blank: "不能为空字符"
119
        too_long: "过长(最长为 %{count} 个字符)"
120
        too_short: "过短(最短为 %{count} 个字符)"
121
        wrong_length: "长度非法(必须为 %{count} 个字符)"
122
        taken: "已经被使用"
123
        not_a_number: "不是数字"
124
        not_a_date: "不是合法日期"
125
        greater_than: "必须大于 %{count}"
126
        greater_than_or_equal_to: "必须大于或等于 %{count}"
127
        equal_to: "必须等于 %{count}"
128
        less_than: "必须小于 %{count}"
129
        less_than_or_equal_to: "必须小于或等于 %{count}"
130
        odd: "必须为单数"
131
        even: "必须为双数"
132
        greater_than_start_date: "必须在起始日期之后"
133
        not_same_project: "不属于同一个项目"
134
        circular_dependency: "此关联将导致循环依赖"
135
        cant_link_an_issue_with_a_descendant: "问题不能关联到它的子任务"
136

  
137
  actionview_instancetag_blank_option: 请选择
138

  
139
  general_text_No: '否'
140
  general_text_Yes: '是'
141
  general_text_no: '否'
142
  general_text_yes: '是'
143
  general_lang_name: 'Simplified Chinese (简体中文)'
144
  general_csv_separator: ','
145
  general_csv_decimal_separator: '.'
146
  general_csv_encoding: gb18030
147
  general_pdf_encoding: gb18030
148
  general_first_day_of_week: '7'
149

  
150
  notice_account_updated: 帐号更新成功
151
  notice_account_invalid_creditentials: 无效的用户名或密码
152
  notice_account_password_updated: 密码更新成功
153
  notice_account_wrong_password: 密码错误
154
  notice_account_register_done: 帐号创建成功,请使用注册确认邮件中的链接来激活您的帐号。
155
  notice_account_unknown_email: 未知用户
156
  notice_can_t_change_password: 该帐号使用了外部认证,因此无法更改密码。
157
  notice_account_lost_email_sent: 系统已将引导您设置新密码的邮件发送给您。
158
  notice_account_activated: 您的帐号已被激活。您现在可以登录了。
159
  notice_successful_create: 创建成功
160
  notice_successful_update: 更新成功
161
  notice_successful_delete: 删除成功
162
  notice_successful_connection: 连接成功
163
  notice_file_not_found: 您访问的页面不存在或已被删除。
164
  notice_locking_conflict: 数据已被另一位用户更新
165
  notice_not_authorized: 对不起,您无权访问此页面。
166
  notice_not_authorized_archived_project: 要访问的项目已经归档。
167
  notice_email_sent: "邮件已发送至 %{value}"
168
  notice_email_error: "发送邮件时发生错误 (%{value})"
169
  notice_feeds_access_key_reseted: 您的RSS存取键已被重置。
170
  notice_api_access_key_reseted: 您的API访问键已被重置。
171
  notice_failed_to_save_issues: "%{count} 个问题保存失败(共选择 %{total} 个问题):%{ids}."
172
  notice_failed_to_save_members: "成员保存失败: %{errors}."
173
  notice_no_issue_selected: "未选择任何问题!请选择您要编辑的问题。"
174
  notice_account_pending: "您的帐号已被成功创建,正在等待管理员的审核。"
175
  notice_default_data_loaded: 成功载入默认设置。
176
  notice_unable_delete_version: 无法删除版本
177
  notice_unable_delete_time_entry: 无法删除工时
178
  notice_issue_done_ratios_updated: 问题完成度已更新。
179
  notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})"
180

  
181
  error_can_t_load_default_data: "无法载入默认设置:%{value}"
182
  error_scm_not_found: "版本库中不存在该条目和(或)其修订版本。"
183
  error_scm_command_failed: "访问版本库时发生错误:%{value}"
184
  error_scm_annotate: "该条目不存在或无法追溯。"
185
  error_issue_not_found_in_project: '问题不存在或不属于此项目'
186
  error_no_tracker_in_project: 该项目未设定跟踪标签,请检查项目配置。
187
  error_no_default_issue_status: 未设置默认的问题状态。请检查系统设置("管理" -> "问题状态")。
188
  error_can_not_delete_custom_field: 无法删除自定义属性
189
  error_can_not_delete_tracker: "该跟踪标签已包含问题,无法删除"
190
  error_can_not_remove_role: "该角色正在使用中,无法删除"
191
  error_can_not_reopen_issue_on_closed_version: 该问题被关联到一个已经关闭的版本,因此无法重新打开。
192
  error_can_not_archive_project: 该项目无法被存档
193
  error_issue_done_ratios_not_updated: 问题完成度未能被更新。
194
  error_workflow_copy_source: 请选择一个源跟踪标签或者角色
195
  error_workflow_copy_target: 请选择目标跟踪标签和角色
196
  error_unable_delete_issue_status: '无法删除问题状态'
197
  error_unable_to_connect: "无法连接 (%{value})"
198
  warning_attachments_not_saved: "%{count} 个文件保存失败"
199

  
200
  mail_subject_lost_password: "您的 %{value} 密码"
201
  mail_body_lost_password: '请点击以下链接来修改您的密码:'
202
  mail_subject_register: "%{value}帐号激活"
203
  mail_body_register: '请点击以下链接来激活您的帐号:'
204
  mail_body_account_information_external: "您可以使用您的 %{value} 帐号来登录。"
205
  mail_body_account_information: 您的帐号信息
206
  mail_subject_account_activation_request: "%{value}帐号激活请求"
207
  mail_body_account_activation_request: "新用户(%{value})已完成注册,正在等候您的审核:"
208
  mail_subject_reminder: "%{count} 个问题需要尽快解决 (%{days})"
209
  mail_body_reminder: "指派给您的 %{count} 个问题需要在 %{days} 天内完成:"
210
  mail_subject_wiki_content_added: "'%{id}' wiki页面已添加"
211
  mail_body_wiki_content_added: "'%{id}' wiki页面已由 %{author} 添加。"
212
  mail_subject_wiki_content_updated: "'%{id}' wiki页面已更新。"
213
  mail_body_wiki_content_updated: "'%{id}' wiki页面已由 %{author} 更新。"
214

  
215
  gui_validation_error: 1 个错误
216
  gui_validation_error_plural: "%{count} 个错误"
217

  
218
  field_name: 名称
219
  field_description: 描述
220
  field_summary: 摘要
221
  field_is_required: 必填
222
  field_firstname: 名字
223
  field_lastname: 姓氏
224
  field_mail: 邮件地址
225
  field_filename: 文件
226
  field_filesize: 大小
227
  field_downloads: 下载次数
228
  field_author: 作者
229
  field_created_on: 创建于
230
  field_updated_on: 更新于
231
  field_field_format: 格式
232
  field_is_for_all: 用于所有项目
233
  field_possible_values: 可能的值
234
  field_regexp: 正则表达式
235
  field_min_length: 最小长度
236
  field_max_length: 最大长度
237
  field_value: 值
238
  field_category: 类别
239
  field_title: 标题
240
  field_project: 项目
241
  field_issue: 问题
242
  field_status: 状态
243
  field_notes: 说明
244
  field_is_closed: 已关闭的问题
245
  field_is_default: 默认值
246
  field_tracker: 跟踪
247
  field_subject: 主题
248
  field_due_date: 计划完成日期
249
  field_assigned_to: 指派给
250
  field_priority: 优先级
251
  field_fixed_version: 目标版本
252
  field_user: 用户
253
  field_principal: 用户/用户组
254
  field_role: 角色
255
  field_homepage: 主页
256
  field_is_public: 公开
257
  field_parent: 上级项目
258
  field_is_in_roadmap: 在路线图中显示
259
  field_login: 登录名
260
  field_mail_notification: 邮件通知
261
  field_admin: 管理员
262
  field_last_login_on: 最后登录
263
  field_language: 语言
264
  field_effective_date: 日期
265
  field_password: 密码
266
  field_new_password: 新密码
267
  field_password_confirmation: 确认
268
  field_version: 版本
269
  field_type: 类型
270
  field_host: 主机
271
  field_port: 端口
272
  field_account: 帐号
273
  field_base_dn: Base DN
274
  field_attr_login: 登录名属性
275
  field_attr_firstname: 名字属性
276
  field_attr_lastname: 姓氏属性
277
  field_attr_mail: 邮件属性
278
  field_onthefly: 即时用户生成
279
  field_start_date: 开始日期
280
  field_done_ratio: "% 完成"
281
  field_auth_source: 认证模式
282
  field_hide_mail: 隐藏我的邮件地址
283
  field_comments: 注释
284
  field_url: URL
285
  field_start_page: 起始页
286
  field_subproject: 子项目
287
  field_hours: 小时
288
  field_activity: 活动
289
  field_spent_on: 日期
290
  field_identifier: 标识
291
  field_is_filter: 作为过滤条件
292
  field_issue_to: 相关问题
293
  field_delay: 延期
294
  field_assignable: 问题可指派给此角色
295
  field_redirect_existing_links: 重定向到现有链接
296
  field_estimated_hours: 预期时间
297
  field_column_names: 列
298
  field_time_entries: 工时
299
  field_time_zone: 时区
300
  field_searchable: 可用作搜索条件
301
  field_default_value: 默认值
302
  field_comments_sorting: 显示注释
303
  field_parent_title: 上级页面
304
  field_editable: 可编辑
305
  field_watcher: 跟踪者
306
  field_identity_url: OpenID URL
307
  field_content: 内容
308
  field_group_by: 根据此条件分组
309
  field_sharing: 共享
310
  field_parent_issue: 父任务
311
  field_member_of_group: 用户组的成员
312
  field_assigned_to_role: 角色的成员
313
  field_text: 文本字段
314
  field_visible: 可见的
315

  
316
  setting_app_title: 应用程序标题
317
  setting_app_subtitle: 应用程序子标题
318
  setting_welcome_text: 欢迎文字
319
  setting_default_language: 默认语言
320
  setting_login_required: 要求认证
321
  setting_self_registration: 允许自注册
322
  setting_attachment_max_size: 附件大小限制
323
  setting_issues_export_limit: 问题导出条目的限制
324
  setting_mail_from: 邮件发件人地址
325
  setting_bcc_recipients: 使用密件抄送 (bcc)
326
  setting_plain_text_mail: 纯文本(无HTML)
327
  setting_host_name: 主机名称
328
  setting_text_formatting: 文本格式
329
  setting_wiki_compression: 压缩Wiki历史文档
330
  setting_feeds_limit: RSS Feed内容条数限制
331
  setting_default_projects_public: 新建项目默认为公开项目
332
  setting_autofetch_changesets: 自动获取程序变更
333
  setting_sys_api_enabled: 启用用于版本库管理的Web Service
334
  setting_commit_ref_keywords: 用于引用问题的关键字
335
  setting_commit_fix_keywords: 用于解决问题的关键字
336
  setting_autologin: 自动登录
337
  setting_date_format: 日期格式
338
  setting_time_format: 时间格式
339
  setting_cross_project_issue_relations: 允许不同项目之间的问题关联
340
  setting_issue_list_default_columns: 问题列表中显示的默认列
341
  setting_emails_header: 邮件头
342
  setting_emails_footer: 邮件签名
343
  setting_protocol: 协议
344
  setting_per_page_options: 每页显示条目个数的设置
345
  setting_user_format: 用户显示格式
346
  setting_activity_days_default: 在项目活动中显示的天数
347
  setting_display_subprojects_issues: 在项目页面上默认显示子项目的问题
348
  setting_enabled_scm: 启用 SCM
349
  setting_mail_handler_body_delimiters: 在这些行之后截断邮件
350
  setting_mail_handler_api_enabled: 启用用于接收邮件的服务
351
  setting_mail_handler_api_key: API key
352
  setting_sequential_project_identifiers: 顺序产生项目标识
353
  setting_gravatar_enabled: 使用Gravatar用户头像
354
  setting_gravatar_default: 默认的Gravatar头像
355
  setting_diff_max_lines_displayed: 查看差别页面上显示的最大行数
356
  setting_file_max_size_displayed: 允许直接显示的最大文本文件
357
  setting_repository_log_display_limit: 在文件变更记录页面上显示的最大修订版本数量
358
  setting_openid: 允许使用OpenID登录和注册
359
  setting_password_min_length: 最短密码长度
360
  setting_new_project_user_role_id: 非管理员用户新建项目时将被赋予的(在该项目中的)角色
361
  setting_default_projects_modules: 新建项目默认启用的模块
362
  setting_issue_done_ratio: 计算问题完成度:
363
  setting_issue_done_ratio_issue_field: 使用问题(的完成度)属性
364
  setting_issue_done_ratio_issue_status: 使用问题状态
365
  setting_start_of_week: 日历开始于
366
  setting_rest_api_enabled: 启用REST web service
367
  setting_cache_formatted_text: 缓存格式化文字
368
  setting_default_notification_option: 默认提醒选项
369
  setting_commit_logtime_enabled: 激活时间日志
370
  setting_commit_logtime_activity_id: 记录的活动
371
  setting_gantt_items_limit: 在甘特图上显示的最大记录数
372

  
373
  permission_add_project: 新建项目
374
  permission_add_subprojects: 新建子项目
375
  permission_edit_project: 编辑项目
376
  permission_select_project_modules: 选择项目模块
377
  permission_manage_members: 管理成员
378
  permission_manage_project_activities: 管理项目活动
379
  permission_manage_versions: 管理版本
380
  permission_manage_categories: 管理问题类别
381
  permission_view_issues: 查看问题
382
  permission_add_issues: 新建问题
383
  permission_edit_issues: 更新问题
384
  permission_manage_issue_relations: 管理问题关联
385
  permission_add_issue_notes: 添加说明
386
  permission_edit_issue_notes: 编辑说明
387
  permission_edit_own_issue_notes: 编辑自己的说明
388
  permission_move_issues: 移动问题
389
  permission_delete_issues: 删除问题
390
  permission_manage_public_queries: 管理公开的查询
391
  permission_save_queries: 保存查询
392
  permission_view_gantt: 查看甘特图
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff