Revision 1297:0a574315af3e .svn/pristine/67
| .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: 查看甘特图 |
|
Also available in: Unified diff