Revision 1298:4f746d8966dd .svn/pristine/2d
| .svn/pristine/2d/2d1b6f7772fdd853c88983e5f12b084f72e3159d.svn-base | ||
|---|---|---|
| 1 |
class AddCommitAccessPermission < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
Role.all.select { |r| not r.builtin? }.each do |r|
|
|
| 4 |
r.add_permission!(:commit_access) |
|
| 5 |
end |
|
| 6 |
end |
|
| 7 |
|
|
| 8 |
def self.down |
|
| 9 |
Role.all.select { |r| not r.builtin? }.each do |r|
|
|
| 10 |
r.remove_permission!(:commit_access) |
|
| 11 |
end |
|
| 12 |
end |
|
| 13 |
end |
|
| .svn/pristine/2d/2d2fc73f05e5360a853859d4a6d6261e16112400.svn-base | ||
|---|---|---|
| 1 |
<div class="contextual"> |
|
| 2 |
<%= link_to l(:label_profile), user_path(@user), :class => 'icon icon-user' %> |
|
| 3 |
<%= change_status_link(@user) %> |
|
| 4 |
<%= link_to(l(:button_delete), @user, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del') if User.current != @user %> |
|
| 5 |
</div> |
|
| 6 |
|
|
| 7 |
<h2><%= link_to l(:label_user_plural), users_path %> » <%=h @user.login %></h2> |
|
| 8 |
|
|
| 9 |
<%= render_tabs user_settings_tabs %> |
|
| 10 |
|
|
| 11 |
<% html_title(l(:label_user), @user.login, l(:label_administration)) -%> |
|
| .svn/pristine/2d/2d3d652686003e0e75f2451ee9050de946a60dda.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 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 |
# Generic exception for when the AuthSource can not be reached |
|
| 19 |
# (eg. can not connect to the LDAP) |
|
| 20 |
class AuthSourceException < Exception; end |
|
| 21 |
class AuthSourceTimeoutException < AuthSourceException; end |
|
| 22 |
|
|
| 23 |
class AuthSource < ActiveRecord::Base |
|
| 24 |
include Redmine::SubclassFactory |
|
| 25 |
include Redmine::Ciphering |
|
| 26 |
|
|
| 27 |
has_many :users |
|
| 28 |
|
|
| 29 |
validates_presence_of :name |
|
| 30 |
validates_uniqueness_of :name |
|
| 31 |
validates_length_of :name, :maximum => 60 |
|
| 32 |
|
|
| 33 |
def authenticate(login, password) |
|
| 34 |
end |
|
| 35 |
|
|
| 36 |
def test_connection |
|
| 37 |
end |
|
| 38 |
|
|
| 39 |
def auth_method_name |
|
| 40 |
"Abstract" |
|
| 41 |
end |
|
| 42 |
|
|
| 43 |
def account_password |
|
| 44 |
read_ciphered_attribute(:account_password) |
|
| 45 |
end |
|
| 46 |
|
|
| 47 |
def account_password=(arg) |
|
| 48 |
write_ciphered_attribute(:account_password, arg) |
|
| 49 |
end |
|
| 50 |
|
|
| 51 |
def searchable? |
|
| 52 |
false |
|
| 53 |
end |
|
| 54 |
|
|
| 55 |
def self.search(q) |
|
| 56 |
results = [] |
|
| 57 |
AuthSource.all.each do |source| |
|
| 58 |
begin |
|
| 59 |
if source.searchable? |
|
| 60 |
results += source.search(q) |
|
| 61 |
end |
|
| 62 |
rescue AuthSourceException => e |
|
| 63 |
logger.error "Error while searching users in #{source.name}: #{e.message}"
|
|
| 64 |
end |
|
| 65 |
end |
|
| 66 |
results |
|
| 67 |
end |
|
| 68 |
|
|
| 69 |
def allow_password_changes? |
|
| 70 |
self.class.allow_password_changes? |
|
| 71 |
end |
|
| 72 |
|
|
| 73 |
# Does this auth source backend allow password changes? |
|
| 74 |
def self.allow_password_changes? |
|
| 75 |
false |
|
| 76 |
end |
|
| 77 |
|
|
| 78 |
# Try to authenticate a user not yet registered against available sources |
|
| 79 |
def self.authenticate(login, password) |
|
| 80 |
AuthSource.where(:onthefly_register => true).all.each do |source| |
|
| 81 |
begin |
|
| 82 |
logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
|
|
| 83 |
attrs = source.authenticate(login, password) |
|
| 84 |
rescue => e |
|
| 85 |
logger.error "Error during authentication: #{e.message}"
|
|
| 86 |
attrs = nil |
|
| 87 |
end |
|
| 88 |
return attrs if attrs |
|
| 89 |
end |
|
| 90 |
return nil |
|
| 91 |
end |
|
| 92 |
end |
|
| .svn/pristine/2d/2d611b348b4b0790a149102dbbb27f7bf647e472.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 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 Redmine::PluginTest < ActiveSupport::TestCase |
|
| 21 |
|
|
| 22 |
def setup |
|
| 23 |
@klass = Redmine::Plugin |
|
| 24 |
# In case some real plugins are installed |
|
| 25 |
@klass.clear |
|
| 26 |
end |
|
| 27 |
|
|
| 28 |
def teardown |
|
| 29 |
@klass.clear |
|
| 30 |
end |
|
| 31 |
|
|
| 32 |
def test_register |
|
| 33 |
@klass.register :foo do |
|
| 34 |
name 'Foo plugin' |
|
| 35 |
url 'http://example.net/plugins/foo' |
|
| 36 |
author 'John Smith' |
|
| 37 |
author_url 'http://example.net/jsmith' |
|
| 38 |
description 'This is a test plugin' |
|
| 39 |
version '0.0.1' |
|
| 40 |
settings :default => {'sample_setting' => 'value', 'foo'=>'bar'}, :partial => 'foo/settings'
|
|
| 41 |
end |
|
| 42 |
|
|
| 43 |
assert_equal 1, @klass.all.size |
|
| 44 |
|
|
| 45 |
plugin = @klass.find('foo')
|
|
| 46 |
assert plugin.is_a?(Redmine::Plugin) |
|
| 47 |
assert_equal :foo, plugin.id |
|
| 48 |
assert_equal 'Foo plugin', plugin.name |
|
| 49 |
assert_equal 'http://example.net/plugins/foo', plugin.url |
|
| 50 |
assert_equal 'John Smith', plugin.author |
|
| 51 |
assert_equal 'http://example.net/jsmith', plugin.author_url |
|
| 52 |
assert_equal 'This is a test plugin', plugin.description |
|
| 53 |
assert_equal '0.0.1', plugin.version |
|
| 54 |
end |
|
| 55 |
|
|
| 56 |
def test_installed |
|
| 57 |
@klass.register(:foo) {}
|
|
| 58 |
|
|
| 59 |
assert_equal true, @klass.installed?(:foo) |
|
| 60 |
assert_equal false, @klass.installed?(:bar) |
|
| 61 |
end |
|
| 62 |
|
|
| 63 |
def test_menu |
|
| 64 |
assert_difference 'Redmine::MenuManager.items(:project_menu).size' do |
|
| 65 |
@klass.register :foo do |
|
| 66 |
menu :project_menu, :foo_menu_item, '/foo', :caption => 'Foo' |
|
| 67 |
end |
|
| 68 |
end |
|
| 69 |
menu_item = Redmine::MenuManager.items(:project_menu).detect {|i| i.name == :foo_menu_item}
|
|
| 70 |
assert_not_nil menu_item |
|
| 71 |
assert_equal 'Foo', menu_item.caption |
|
| 72 |
assert_equal '/foo', menu_item.url |
|
| 73 |
end |
|
| 74 |
|
|
| 75 |
def test_delete_menu_item |
|
| 76 |
Redmine::MenuManager.map(:project_menu).push(:foo_menu_item, '/foo', :caption => 'Foo') |
|
| 77 |
|
|
| 78 |
assert_difference 'Redmine::MenuManager.items(:project_menu).size', -1 do |
|
| 79 |
@klass.register :foo do |
|
| 80 |
delete_menu_item :project_menu, :foo_menu_item |
|
| 81 |
end |
|
| 82 |
end |
|
| 83 |
assert_nil Redmine::MenuManager.items(:project_menu).detect {|i| i.name == :foo_menu_item}
|
|
| 84 |
end |
|
| 85 |
|
|
| 86 |
def test_requires_redmine |
|
| 87 |
plugin = Redmine::Plugin.register(:foo) {}
|
|
| 88 |
Redmine::VERSION.stubs(:to_a).returns([2, 1, 3, "stable", 10817]) |
|
| 89 |
|
|
| 90 |
# Specific version without hash |
|
| 91 |
assert plugin.requires_redmine('2.1.3')
|
|
| 92 |
assert plugin.requires_redmine('2.1')
|
|
| 93 |
assert_raise Redmine::PluginRequirementError do |
|
| 94 |
plugin.requires_redmine('2.1.4')
|
|
| 95 |
end |
|
| 96 |
assert_raise Redmine::PluginRequirementError do |
|
| 97 |
plugin.requires_redmine('2.2')
|
|
| 98 |
end |
|
| 99 |
|
|
| 100 |
# Specific version |
|
| 101 |
assert plugin.requires_redmine(:version => '2.1.3') |
|
| 102 |
assert plugin.requires_redmine(:version => ['2.1.3', '2.2.0']) |
|
| 103 |
assert plugin.requires_redmine(:version => '2.1') |
|
| 104 |
assert_raise Redmine::PluginRequirementError do |
|
| 105 |
plugin.requires_redmine(:version => '2.2.0') |
|
| 106 |
end |
|
| 107 |
assert_raise Redmine::PluginRequirementError do |
|
| 108 |
plugin.requires_redmine(:version => ['2.1.4', '2.2.0']) |
|
| 109 |
end |
|
| 110 |
assert_raise Redmine::PluginRequirementError do |
|
| 111 |
plugin.requires_redmine(:version => '2.2') |
|
| 112 |
end |
|
| 113 |
|
|
| 114 |
# Version range |
|
| 115 |
assert plugin.requires_redmine(:version => '2.0.0'..'2.2.4') |
|
| 116 |
assert plugin.requires_redmine(:version => '2.1.3'..'2.2.4') |
|
| 117 |
assert plugin.requires_redmine(:version => '2.0.0'..'2.1.3') |
|
| 118 |
assert plugin.requires_redmine(:version => '2.0'..'2.2') |
|
| 119 |
assert plugin.requires_redmine(:version => '2.1'..'2.2') |
|
| 120 |
assert plugin.requires_redmine(:version => '2.0'..'2.1') |
|
| 121 |
assert_raise Redmine::PluginRequirementError do |
|
| 122 |
plugin.requires_redmine(:version => '2.1.4'..'2.2.4') |
|
| 123 |
end |
|
| 124 |
|
|
| 125 |
|
|
| 126 |
# Version or higher |
|
| 127 |
assert plugin.requires_redmine(:version_or_higher => '0.1.0') |
|
| 128 |
assert plugin.requires_redmine(:version_or_higher => '2.1.3') |
|
| 129 |
assert plugin.requires_redmine(:version_or_higher => '2.1') |
|
| 130 |
assert_raise Redmine::PluginRequirementError do |
|
| 131 |
plugin.requires_redmine(:version_or_higher => '2.2.0') |
|
| 132 |
end |
|
| 133 |
assert_raise Redmine::PluginRequirementError do |
|
| 134 |
plugin.requires_redmine(:version_or_higher => '2.2') |
|
| 135 |
end |
|
| 136 |
end |
|
| 137 |
|
|
| 138 |
def test_requires_redmine_plugin |
|
| 139 |
test = self |
|
| 140 |
other_version = '0.5.0' |
|
| 141 |
|
|
| 142 |
@klass.register :other do |
|
| 143 |
name 'Other' |
|
| 144 |
version other_version |
|
| 145 |
end |
|
| 146 |
|
|
| 147 |
@klass.register :foo do |
|
| 148 |
test.assert requires_redmine_plugin(:other, :version_or_higher => '0.1.0') |
|
| 149 |
test.assert requires_redmine_plugin(:other, :version_or_higher => other_version) |
|
| 150 |
test.assert requires_redmine_plugin(:other, other_version) |
|
| 151 |
test.assert_raise Redmine::PluginRequirementError do |
|
| 152 |
requires_redmine_plugin(:other, :version_or_higher => '99.0.0') |
|
| 153 |
end |
|
| 154 |
|
|
| 155 |
test.assert requires_redmine_plugin(:other, :version => other_version) |
|
| 156 |
test.assert requires_redmine_plugin(:other, :version => [other_version, '99.0.0']) |
|
| 157 |
test.assert_raise Redmine::PluginRequirementError do |
|
| 158 |
requires_redmine_plugin(:other, :version => '99.0.0') |
|
| 159 |
end |
|
| 160 |
test.assert_raise Redmine::PluginRequirementError do |
|
| 161 |
requires_redmine_plugin(:other, :version => ['98.0.0', '99.0.0']) |
|
| 162 |
end |
|
| 163 |
# Missing plugin |
|
| 164 |
test.assert_raise Redmine::PluginNotFound do |
|
| 165 |
requires_redmine_plugin(:missing, :version_or_higher => '0.1.0') |
|
| 166 |
end |
|
| 167 |
test.assert_raise Redmine::PluginNotFound do |
|
| 168 |
requires_redmine_plugin(:missing, '0.1.0') |
|
| 169 |
end |
|
| 170 |
test.assert_raise Redmine::PluginNotFound do |
|
| 171 |
requires_redmine_plugin(:missing, :version => '0.1.0') |
|
| 172 |
end |
|
| 173 |
|
|
| 174 |
end |
|
| 175 |
end |
|
| 176 |
end |
|
| .svn/pristine/2d/2d85d0783f59c365e1251c736bcbc44da38bc043.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 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 WikiPageTest < ActiveSupport::TestCase |
|
| 21 |
fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions |
|
| 22 |
|
|
| 23 |
def setup |
|
| 24 |
@wiki = Wiki.find(1) |
|
| 25 |
@page = @wiki.pages.first |
|
| 26 |
end |
|
| 27 |
|
|
| 28 |
def test_create |
|
| 29 |
page = WikiPage.new(:wiki => @wiki) |
|
| 30 |
assert !page.save |
|
| 31 |
assert_equal 1, page.errors.count |
|
| 32 |
|
|
| 33 |
page.title = "Page" |
|
| 34 |
assert page.save |
|
| 35 |
page.reload |
|
| 36 |
assert !page.protected? |
|
| 37 |
|
|
| 38 |
@wiki.reload |
|
| 39 |
assert @wiki.pages.include?(page) |
|
| 40 |
end |
|
| 41 |
|
|
| 42 |
def test_sidebar_should_be_protected_by_default |
|
| 43 |
page = @wiki.find_or_new_page('sidebar')
|
|
| 44 |
assert page.new_record? |
|
| 45 |
assert page.protected? |
|
| 46 |
end |
|
| 47 |
|
|
| 48 |
def test_find_or_new_page |
|
| 49 |
page = @wiki.find_or_new_page("CookBook documentation")
|
|
| 50 |
assert_kind_of WikiPage, page |
|
| 51 |
assert !page.new_record? |
|
| 52 |
|
|
| 53 |
page = @wiki.find_or_new_page("Non existing page")
|
|
| 54 |
assert_kind_of WikiPage, page |
|
| 55 |
assert page.new_record? |
|
| 56 |
end |
|
| 57 |
|
|
| 58 |
def test_parent_title |
|
| 59 |
page = WikiPage.find_by_title('Another_page')
|
|
| 60 |
assert_nil page.parent_title |
|
| 61 |
|
|
| 62 |
page = WikiPage.find_by_title('Page_with_an_inline_image')
|
|
| 63 |
assert_equal 'CookBook documentation', page.parent_title |
|
| 64 |
end |
|
| 65 |
|
|
| 66 |
def test_assign_parent |
|
| 67 |
page = WikiPage.find_by_title('Another_page')
|
|
| 68 |
page.parent_title = 'CookBook documentation' |
|
| 69 |
assert page.save |
|
| 70 |
page.reload |
|
| 71 |
assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
|
|
| 72 |
end |
|
| 73 |
|
|
| 74 |
def test_unassign_parent |
|
| 75 |
page = WikiPage.find_by_title('Page_with_an_inline_image')
|
|
| 76 |
page.parent_title = '' |
|
| 77 |
assert page.save |
|
| 78 |
page.reload |
|
| 79 |
assert_nil page.parent |
|
| 80 |
end |
|
| 81 |
|
|
| 82 |
def test_parent_validation |
|
| 83 |
page = WikiPage.find_by_title('CookBook_documentation')
|
|
| 84 |
|
|
| 85 |
# A page that doesn't exist |
|
| 86 |
page.parent_title = 'Unknown title' |
|
| 87 |
assert !page.save |
|
| 88 |
assert_include I18n.translate('activerecord.errors.messages.invalid'),
|
|
| 89 |
page.errors[:parent_title] |
|
| 90 |
# A child page |
|
| 91 |
page.parent_title = 'Page_with_an_inline_image' |
|
| 92 |
assert !page.save |
|
| 93 |
assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
|
|
| 94 |
page.errors[:parent_title] |
|
| 95 |
# The page itself |
|
| 96 |
page.parent_title = 'CookBook_documentation' |
|
| 97 |
assert !page.save |
|
| 98 |
assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
|
|
| 99 |
page.errors[:parent_title] |
|
| 100 |
page.parent_title = 'Another_page' |
|
| 101 |
assert page.save |
|
| 102 |
end |
|
| 103 |
|
|
| 104 |
def test_destroy |
|
| 105 |
page = WikiPage.find(1) |
|
| 106 |
page.destroy |
|
| 107 |
assert_nil WikiPage.find_by_id(1) |
|
| 108 |
# make sure that page content and its history are deleted |
|
| 109 |
assert WikiContent.find_all_by_page_id(1).empty? |
|
| 110 |
assert WikiContent.versioned_class.find_all_by_page_id(1).empty? |
|
| 111 |
end |
|
| 112 |
|
|
| 113 |
def test_destroy_should_not_nullify_children |
|
| 114 |
page = WikiPage.find(2) |
|
| 115 |
child_ids = page.child_ids |
|
| 116 |
assert child_ids.any? |
|
| 117 |
page.destroy |
|
| 118 |
assert_nil WikiPage.find_by_id(2) |
|
| 119 |
|
|
| 120 |
children = WikiPage.find_all_by_id(child_ids) |
|
| 121 |
assert_equal child_ids.size, children.size |
|
| 122 |
children.each do |child| |
|
| 123 |
assert_nil child.parent_id |
|
| 124 |
end |
|
| 125 |
end |
|
| 126 |
|
|
| 127 |
def test_updated_on_eager_load |
|
| 128 |
page = WikiPage.with_updated_on.first(:order => 'id') |
|
| 129 |
assert page.is_a?(WikiPage) |
|
| 130 |
assert_not_nil page.read_attribute(:updated_on) |
|
| 131 |
assert_equal Time.gm(2007, 3, 6, 23, 10, 51), page.content.updated_on |
|
| 132 |
assert_equal page.content.updated_on, page.updated_on |
|
| 133 |
assert_not_nil page.read_attribute(:version) |
|
| 134 |
end |
|
| 135 |
|
|
| 136 |
def test_descendants |
|
| 137 |
page = WikiPage.create!(:wiki => @wiki, :title => 'Parent') |
|
| 138 |
child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page) |
|
| 139 |
child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1) |
|
| 140 |
child111 = WikiPage.create!(:wiki => @wiki, :title => 'Child111', :parent => child11) |
|
| 141 |
child2 = WikiPage.create!(:wiki => @wiki, :title => 'Child2', :parent => page) |
|
| 142 |
|
|
| 143 |
assert_equal %w(Child1 Child11 Child111 Child2), page.descendants.map(&:title).sort |
|
| 144 |
assert_equal %w(Child1 Child11 Child111 Child2), page.descendants(nil).map(&:title).sort |
|
| 145 |
assert_equal %w(Child1 Child11 Child2), page.descendants(2).map(&:title).sort |
|
| 146 |
assert_equal %w(Child1 Child2), page.descendants(1).map(&:title).sort |
|
| 147 |
|
|
| 148 |
assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants.map(&:title).sort |
|
| 149 |
assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants(nil).map(&:title).sort |
|
| 150 |
assert_equal %w(Child1 Child11 Child2 Parent), page.self_and_descendants(2).map(&:title).sort |
|
| 151 |
assert_equal %w(Child1 Child2 Parent), page.self_and_descendants(1).map(&:title).sort |
|
| 152 |
end |
|
| 153 |
|
|
| 154 |
def test_diff_for_page_with_deleted_version_should_pick_the_previous_available_version |
|
| 155 |
WikiContent::Version.find_by_page_id_and_version(1, 2).destroy |
|
| 156 |
|
|
| 157 |
page = WikiPage.find(1) |
|
| 158 |
diff = page.diff(3) |
|
| 159 |
assert_not_nil diff |
|
| 160 |
assert_equal 3, diff.content_to.version |
|
| 161 |
assert_equal 1, diff.content_from.version |
|
| 162 |
end |
|
| 163 |
end |
|
| .svn/pristine/2d/2dbd0564d495b47bea2303b32f1609a8bf5c4b1a.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 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 RoutingJournalsTest < ActionController::IntegrationTest |
|
| 21 |
def test_journals |
|
| 22 |
assert_routing( |
|
| 23 |
{ :method => 'post', :path => "/issues/1/quoted" },
|
|
| 24 |
{ :controller => 'journals', :action => 'new', :id => '1' }
|
|
| 25 |
) |
|
| 26 |
assert_routing( |
|
| 27 |
{ :method => 'get', :path => "/issues/changes" },
|
|
| 28 |
{ :controller => 'journals', :action => 'index' }
|
|
| 29 |
) |
|
| 30 |
assert_routing( |
|
| 31 |
{ :method => 'get', :path => "/journals/diff/1" },
|
|
| 32 |
{ :controller => 'journals', :action => 'diff', :id => '1' }
|
|
| 33 |
) |
|
| 34 |
["get", "post"].each do |method| |
|
| 35 |
assert_routing( |
|
| 36 |
{ :method => method, :path => "/journals/edit/1" },
|
|
| 37 |
{ :controller => 'journals', :action => 'edit', :id => '1' }
|
|
| 38 |
) |
|
| 39 |
end |
|
| 40 |
end |
|
| 41 |
end |
|
| .svn/pristine/2d/2dbde491b7a799c190d49fd409652b2fbab77ca3.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 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 News < ActiveRecord::Base |
|
| 19 |
include Redmine::SafeAttributes |
|
| 20 |
belongs_to :project |
|
| 21 |
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' |
|
| 22 |
has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on" |
|
| 23 |
|
|
| 24 |
validates_presence_of :title, :description |
|
| 25 |
validates_length_of :title, :maximum => 60 |
|
| 26 |
validates_length_of :summary, :maximum => 255 |
|
| 27 |
|
|
| 28 |
acts_as_attachable :delete_permission => :manage_news |
|
| 29 |
acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
|
|
| 30 |
acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
|
|
| 31 |
acts_as_activity_provider :find_options => {:include => [:project, :author]},
|
|
| 32 |
:author_key => :author_id |
|
| 33 |
acts_as_watchable |
|
| 34 |
|
|
| 35 |
after_create :add_author_as_watcher |
|
| 36 |
|
|
| 37 |
scope :visible, lambda {|*args|
|
|
| 38 |
includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_news, *args)) |
|
| 39 |
} |
|
| 40 |
|
|
| 41 |
safe_attributes 'title', 'summary', 'description' |
|
| 42 |
|
|
| 43 |
def visible?(user=User.current) |
|
| 44 |
!user.nil? && user.allowed_to?(:view_news, project) |
|
| 45 |
end |
|
| 46 |
|
|
| 47 |
# Returns true if the news can be commented by user |
|
| 48 |
def commentable?(user=User.current) |
|
| 49 |
user.allowed_to?(:comment_news, project) |
|
| 50 |
end |
|
| 51 |
|
|
| 52 |
def recipients |
|
| 53 |
project.users.select {|user| user.notify_about?(self)}.map(&:mail)
|
|
| 54 |
end |
|
| 55 |
|
|
| 56 |
# returns latest news for projects visible by user |
|
| 57 |
def self.latest(user = User.current, count = 5) |
|
| 58 |
visible(user).includes([:author, :project]).order("#{News.table_name}.created_on DESC").limit(count).all
|
|
| 59 |
end |
|
| 60 |
|
|
| 61 |
private |
|
| 62 |
|
|
| 63 |
def add_author_as_watcher |
|
| 64 |
Watcher.create(:watchable => self, :user => author) |
|
| 65 |
end |
|
| 66 |
end |
|
| .svn/pristine/2d/2ddfd454c5c1fbd92838296a3b268aeb1e87e384.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 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 Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base |
|
| 21 |
fixtures :users, :groups_users |
|
| 22 |
|
|
| 23 |
def setup |
|
| 24 |
Setting.rest_api_enabled = '1' |
|
| 25 |
end |
|
| 26 |
|
|
| 27 |
context "GET /groups" do |
|
| 28 |
context ".xml" do |
|
| 29 |
should "require authentication" do |
|
| 30 |
get '/groups.xml' |
|
| 31 |
assert_response 401 |
|
| 32 |
end |
|
| 33 |
|
|
| 34 |
should "return groups" do |
|
| 35 |
get '/groups.xml', {}, credentials('admin')
|
|
| 36 |
assert_response :success |
|
| 37 |
assert_equal 'application/xml', response.content_type |
|
| 38 |
|
|
| 39 |
assert_select 'groups' do |
|
| 40 |
assert_select 'group' do |
|
| 41 |
assert_select 'name', :text => 'A Team' |
|
| 42 |
assert_select 'id', :text => '10' |
|
| 43 |
end |
|
| 44 |
end |
|
| 45 |
end |
|
| 46 |
end |
|
| 47 |
|
|
| 48 |
context ".json" do |
|
| 49 |
should "require authentication" do |
|
| 50 |
get '/groups.json' |
|
| 51 |
assert_response 401 |
|
| 52 |
end |
|
| 53 |
|
|
| 54 |
should "return groups" do |
|
| 55 |
get '/groups.json', {}, credentials('admin')
|
|
| 56 |
assert_response :success |
|
| 57 |
assert_equal 'application/json', response.content_type |
|
| 58 |
|
|
| 59 |
json = MultiJson.load(response.body) |
|
| 60 |
groups = json['groups'] |
|
| 61 |
assert_kind_of Array, groups |
|
| 62 |
group = groups.detect {|g| g['name'] == 'A Team'}
|
|
| 63 |
assert_not_nil group |
|
| 64 |
assert_equal({'id' => 10, 'name' => 'A Team'}, group)
|
|
| 65 |
end |
|
| 66 |
end |
|
| 67 |
end |
|
| 68 |
|
|
| 69 |
context "GET /groups/:id" do |
|
| 70 |
context ".xml" do |
|
| 71 |
should "return the group with its users" do |
|
| 72 |
get '/groups/10.xml', {}, credentials('admin')
|
|
| 73 |
assert_response :success |
|
| 74 |
assert_equal 'application/xml', response.content_type |
|
| 75 |
|
|
| 76 |
assert_select 'group' do |
|
| 77 |
assert_select 'name', :text => 'A Team' |
|
| 78 |
assert_select 'id', :text => '10' |
|
| 79 |
end |
|
| 80 |
end |
|
| 81 |
|
|
| 82 |
should "include users if requested" do |
|
| 83 |
get '/groups/10.xml?include=users', {}, credentials('admin')
|
|
| 84 |
assert_response :success |
|
| 85 |
assert_equal 'application/xml', response.content_type |
|
| 86 |
|
|
| 87 |
assert_select 'group' do |
|
| 88 |
assert_select 'users' do |
|
| 89 |
assert_select 'user', Group.find(10).users.count |
|
| 90 |
assert_select 'user[id=8]' |
|
| 91 |
end |
|
| 92 |
end |
|
| 93 |
end |
|
| 94 |
|
|
| 95 |
should "include memberships if requested" do |
|
| 96 |
get '/groups/10.xml?include=memberships', {}, credentials('admin')
|
|
| 97 |
assert_response :success |
|
| 98 |
assert_equal 'application/xml', response.content_type |
|
| 99 |
|
|
| 100 |
assert_select 'group' do |
|
| 101 |
assert_select 'memberships' |
|
| 102 |
end |
|
| 103 |
end |
|
| 104 |
end |
|
| 105 |
end |
|
| 106 |
|
|
| 107 |
context "POST /groups" do |
|
| 108 |
context "with valid parameters" do |
|
| 109 |
context ".xml" do |
|
| 110 |
should "create groups" do |
|
| 111 |
assert_difference('Group.count') do
|
|
| 112 |
post '/groups.xml', {:group => {:name => 'Test', :user_ids => [2, 3]}}, credentials('admin')
|
|
| 113 |
assert_response :created |
|
| 114 |
assert_equal 'application/xml', response.content_type |
|
| 115 |
end |
|
| 116 |
|
|
| 117 |
group = Group.order('id DESC').first
|
|
| 118 |
assert_equal 'Test', group.name |
|
| 119 |
assert_equal [2, 3], group.users.map(&:id).sort |
|
| 120 |
|
|
| 121 |
assert_select 'group' do |
|
| 122 |
assert_select 'name', :text => 'Test' |
|
| 123 |
end |
|
| 124 |
end |
|
| 125 |
end |
|
| 126 |
end |
|
| 127 |
|
|
| 128 |
context "with invalid parameters" do |
|
| 129 |
context ".xml" do |
|
| 130 |
should "return errors" do |
|
| 131 |
assert_no_difference('Group.count') do
|
|
| 132 |
post '/groups.xml', {:group => {:name => ''}}, credentials('admin')
|
|
| 133 |
end |
|
| 134 |
assert_response :unprocessable_entity |
|
| 135 |
assert_equal 'application/xml', response.content_type |
|
| 136 |
|
|
| 137 |
assert_select 'errors' do |
|
| 138 |
assert_select 'error', :text => /Name can't be blank/ |
|
| 139 |
end |
|
| 140 |
end |
|
| 141 |
end |
|
| 142 |
end |
|
| 143 |
end |
|
| 144 |
|
|
| 145 |
context "PUT /groups/:id" do |
|
| 146 |
context "with valid parameters" do |
|
| 147 |
context ".xml" do |
|
| 148 |
should "update the group" do |
|
| 149 |
put '/groups/10.xml', {:group => {:name => 'New name', :user_ids => [2, 3]}}, credentials('admin')
|
|
| 150 |
assert_response :ok |
|
| 151 |
assert_equal '', @response.body |
|
| 152 |
|
|
| 153 |
group = Group.find(10) |
|
| 154 |
assert_equal 'New name', group.name |
|
| 155 |
assert_equal [2, 3], group.users.map(&:id).sort |
|
| 156 |
end |
|
| 157 |
end |
|
| 158 |
end |
|
| 159 |
|
|
| 160 |
context "with invalid parameters" do |
|
| 161 |
context ".xml" do |
|
| 162 |
should "return errors" do |
|
| 163 |
put '/groups/10.xml', {:group => {:name => ''}}, credentials('admin')
|
|
| 164 |
assert_response :unprocessable_entity |
|
| 165 |
assert_equal 'application/xml', response.content_type |
|
| 166 |
|
|
| 167 |
assert_select 'errors' do |
|
| 168 |
assert_select 'error', :text => /Name can't be blank/ |
|
| 169 |
end |
|
| 170 |
end |
|
| 171 |
end |
|
| 172 |
end |
|
| 173 |
end |
|
| 174 |
|
|
| 175 |
context "DELETE /groups/:id" do |
|
| 176 |
context ".xml" do |
|
| 177 |
should "delete the group" do |
|
| 178 |
assert_difference 'Group.count', -1 do |
|
| 179 |
delete '/groups/10.xml', {}, credentials('admin')
|
|
| 180 |
assert_response :ok |
|
| 181 |
assert_equal '', @response.body |
|
| 182 |
end |
|
| 183 |
end |
|
| 184 |
end |
|
| 185 |
end |
|
| 186 |
|
|
| 187 |
context "POST /groups/:id/users" do |
|
| 188 |
context ".xml" do |
|
| 189 |
should "add user to the group" do |
|
| 190 |
assert_difference 'Group.find(10).users.count' do |
|
| 191 |
post '/groups/10/users.xml', {:user_id => 5}, credentials('admin')
|
|
| 192 |
assert_response :ok |
|
| 193 |
assert_equal '', @response.body |
|
| 194 |
end |
|
| 195 |
assert_include User.find(5), Group.find(10).users |
|
| 196 |
end |
|
| 197 |
end |
|
| 198 |
end |
|
| 199 |
|
|
| 200 |
context "DELETE /groups/:id/users/:user_id" do |
|
| 201 |
context ".xml" do |
|
| 202 |
should "remove user from the group" do |
|
| 203 |
assert_difference 'Group.find(10).users.count', -1 do |
|
| 204 |
delete '/groups/10/users/8.xml', {}, credentials('admin')
|
|
| 205 |
assert_response :ok |
|
| 206 |
assert_equal '', @response.body |
|
| 207 |
end |
|
| 208 |
assert_not_include User.find(8), Group.find(10).users |
|
| 209 |
end |
|
| 210 |
end |
|
| 211 |
end |
|
| 212 |
end |
|
| .svn/pristine/2d/2de3a87e3fe026c2ba56948a00c54c7c54909e85.svn-base | ||
|---|---|---|
| 1 |
/* redMine - project management software |
|
| 2 |
Copyright (C) 2006-2008 Jean-Philippe Lang */ |
|
| 3 |
|
|
| 4 |
function checkAll (id, checked) {
|
|
| 5 |
var els = Element.descendants(id); |
|
| 6 |
for (var i = 0; i < els.length; i++) {
|
|
| 7 |
if (els[i].disabled==false) {
|
|
| 8 |
els[i].checked = checked; |
|
| 9 |
} |
|
| 10 |
} |
|
| 11 |
} |
|
| 12 |
|
|
| 13 |
function toggleCheckboxesBySelector(selector) {
|
|
| 14 |
boxes = $$(selector); |
|
| 15 |
var all_checked = true; |
|
| 16 |
for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } }
|
|
| 17 |
for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; }
|
|
| 18 |
} |
|
| 19 |
|
|
| 20 |
function setCheckboxesBySelector(checked, selector) {
|
|
| 21 |
var boxes = $$(selector); |
|
| 22 |
boxes.each(function(ele) {
|
|
| 23 |
ele.checked = checked; |
|
| 24 |
}); |
|
| 25 |
} |
|
| 26 |
|
|
| 27 |
function showAndScrollTo(id, focus) {
|
|
| 28 |
Element.show(id); |
|
| 29 |
if (focus!=null) { Form.Element.focus(focus); }
|
|
| 30 |
Element.scrollTo(id); |
|
| 31 |
} |
|
| 32 |
|
|
| 33 |
function toggleRowGroup(el) {
|
|
| 34 |
var tr = Element.up(el, 'tr'); |
|
| 35 |
var n = Element.next(tr); |
|
| 36 |
tr.toggleClassName('open');
|
|
| 37 |
while (n != undefined && !n.hasClassName('group')) {
|
|
| 38 |
Element.toggle(n); |
|
| 39 |
n = Element.next(n); |
|
| 40 |
} |
|
| 41 |
} |
|
| 42 |
|
|
| 43 |
function collapseAllRowGroups(el) {
|
|
| 44 |
var tbody = Element.up(el, 'tbody'); |
|
| 45 |
tbody.childElements('tr').each(function(tr) {
|
|
| 46 |
if (tr.hasClassName('group')) {
|
|
| 47 |
tr.removeClassName('open');
|
|
| 48 |
} else {
|
|
| 49 |
tr.hide(); |
|
| 50 |
} |
|
| 51 |
}) |
|
| 52 |
} |
|
| 53 |
|
|
| 54 |
function expandAllRowGroups(el) {
|
|
| 55 |
var tbody = Element.up(el, 'tbody'); |
|
| 56 |
tbody.childElements('tr').each(function(tr) {
|
|
| 57 |
if (tr.hasClassName('group')) {
|
|
| 58 |
tr.addClassName('open');
|
|
| 59 |
} else {
|
|
| 60 |
tr.show(); |
|
| 61 |
} |
|
| 62 |
}) |
|
| 63 |
} |
|
| 64 |
|
|
| 65 |
function toggleAllRowGroups(el) {
|
|
| 66 |
var tr = Element.up(el, 'tr'); |
|
| 67 |
if (tr.hasClassName('open')) {
|
|
| 68 |
collapseAllRowGroups(el); |
|
| 69 |
} else {
|
|
| 70 |
expandAllRowGroups(el); |
|
| 71 |
} |
|
| 72 |
} |
|
| 73 |
|
|
| 74 |
function toggleFieldset(el) {
|
|
| 75 |
var fieldset = Element.up(el, 'fieldset'); |
|
| 76 |
fieldset.toggleClassName('collapsed');
|
|
| 77 |
Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2});
|
|
| 78 |
} |
|
| 79 |
|
|
| 80 |
function hideFieldset(el) {
|
|
| 81 |
var fieldset = Element.up(el, 'fieldset'); |
|
| 82 |
fieldset.toggleClassName('collapsed');
|
|
| 83 |
fieldset.down('div').hide();
|
|
| 84 |
} |
|
| 85 |
|
|
| 86 |
var fileFieldCount = 1; |
|
| 87 |
|
|
| 88 |
function addFileField() {
|
|
| 89 |
var fields = $('attachments_fields');
|
|
| 90 |
if (fields.childElements().length >= 10) return false; |
|
| 91 |
fileFieldCount++; |
|
| 92 |
var s = new Element('span');
|
|
| 93 |
s.update(fields.down('span').innerHTML);
|
|
| 94 |
s.down('input.file').name = "attachments[" + fileFieldCount + "][file]";
|
|
| 95 |
s.down('input.description').name = "attachments[" + fileFieldCount + "][description]";
|
|
| 96 |
fields.appendChild(s); |
|
| 97 |
} |
|
| 98 |
|
|
| 99 |
function removeFileField(el) {
|
|
| 100 |
var fields = $('attachments_fields');
|
|
| 101 |
var s = Element.up(el, 'span'); |
|
| 102 |
if (fields.childElements().length > 1) {
|
|
| 103 |
s.remove(); |
|
| 104 |
} else {
|
|
| 105 |
s.update(s.innerHTML); |
|
| 106 |
} |
|
| 107 |
} |
|
| 108 |
|
|
| 109 |
function checkFileSize(el, maxSize, message) {
|
|
| 110 |
var files = el.files; |
|
| 111 |
if (files) {
|
|
| 112 |
for (var i=0; i<files.length; i++) {
|
|
| 113 |
if (files[i].size > maxSize) {
|
|
| 114 |
alert(message); |
|
| 115 |
el.value = ""; |
|
| 116 |
} |
|
| 117 |
} |
|
| 118 |
} |
|
| 119 |
} |
|
| 120 |
|
|
| 121 |
function showTab(name) {
|
|
| 122 |
var f = $$('div#content .tab-content');
|
|
| 123 |
for(var i=0; i<f.length; i++){
|
|
| 124 |
Element.hide(f[i]); |
|
| 125 |
} |
|
| 126 |
var f = $$('div.tabs a');
|
|
| 127 |
for(var i=0; i<f.length; i++){
|
|
| 128 |
Element.removeClassName(f[i], "selected"); |
|
| 129 |
} |
|
| 130 |
Element.show('tab-content-' + name);
|
|
| 131 |
Element.addClassName('tab-' + name, "selected");
|
|
| 132 |
return false; |
|
| 133 |
} |
|
| 134 |
|
|
| 135 |
function moveTabRight(el) {
|
|
| 136 |
var lis = Element.up(el, 'div.tabs').down('ul').childElements();
|
|
| 137 |
var tabsWidth = 0; |
|
| 138 |
var i; |
|
| 139 |
for (i=0; i<lis.length; i++) {
|
|
| 140 |
if (lis[i].visible()) {
|
|
| 141 |
tabsWidth += lis[i].getWidth() + 6; |
|
| 142 |
} |
|
| 143 |
} |
|
| 144 |
if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) {
|
|
| 145 |
return; |
|
| 146 |
} |
|
| 147 |
i=0; |
|
| 148 |
while (i<lis.length && !lis[i].visible()) {
|
|
| 149 |
i++; |
|
| 150 |
} |
|
| 151 |
lis[i].hide(); |
|
| 152 |
} |
|
| 153 |
|
|
| 154 |
function moveTabLeft(el) {
|
|
| 155 |
var lis = Element.up(el, 'div.tabs').down('ul').childElements();
|
|
| 156 |
var i = 0; |
|
| 157 |
while (i<lis.length && !lis[i].visible()) {
|
|
| 158 |
i++; |
|
| 159 |
} |
|
| 160 |
if (i>0) {
|
|
| 161 |
lis[i-1].show(); |
|
| 162 |
} |
|
| 163 |
} |
|
| 164 |
|
|
| 165 |
function displayTabsButtons() {
|
|
| 166 |
var lis; |
|
| 167 |
var tabsWidth = 0; |
|
| 168 |
var i; |
|
| 169 |
$$('div.tabs').each(function(el) {
|
|
| 170 |
lis = el.down('ul').childElements();
|
|
| 171 |
for (i=0; i<lis.length; i++) {
|
|
| 172 |
if (lis[i].visible()) {
|
|
| 173 |
tabsWidth += lis[i].getWidth() + 6; |
|
| 174 |
} |
|
| 175 |
} |
|
| 176 |
if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) {
|
|
| 177 |
el.down('div.tabs-buttons').hide();
|
|
| 178 |
} else {
|
|
| 179 |
el.down('div.tabs-buttons').show();
|
|
| 180 |
} |
|
| 181 |
}); |
|
| 182 |
} |
|
| 183 |
|
|
| 184 |
function setPredecessorFieldsVisibility() {
|
|
| 185 |
relationType = $('relation_relation_type');
|
|
| 186 |
if (relationType && (relationType.value == "precedes" || relationType.value == "follows")) {
|
|
| 187 |
Element.show('predecessor_fields');
|
|
| 188 |
} else {
|
|
| 189 |
Element.hide('predecessor_fields');
|
|
| 190 |
} |
|
| 191 |
} |
|
| 192 |
|
|
| 193 |
function promptToRemote(text, param, url) {
|
|
| 194 |
value = prompt(text + ':'); |
|
| 195 |
if (value) {
|
|
| 196 |
new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
|
|
| 197 |
return false; |
|
| 198 |
} |
|
| 199 |
} |
|
| 200 |
|
|
| 201 |
function showModal(id, width) {
|
|
| 202 |
el = $(id); |
|
| 203 |
if (el == undefined || el.visible()) {return;}
|
|
| 204 |
var h = $$('body')[0].getHeight();
|
|
| 205 |
var d = document.createElement("div");
|
|
| 206 |
d.id = 'modalbg'; |
|
| 207 |
$('main').appendChild(d);
|
|
| 208 |
$('modalbg').setStyle({ width: '100%', height: h + 'px' });
|
|
| 209 |
$('modalbg').show();
|
|
| 210 |
|
|
| 211 |
var pageWidth = document.viewport.getWidth(); |
|
| 212 |
el.setStyle({'width': width});
|
|
| 213 |
el.setStyle({'left': (((pageWidth - el.getWidth())/2 *100) / pageWidth) + '%'});
|
|
| 214 |
el.addClassName('modal');
|
|
| 215 |
el.show(); |
|
| 216 |
|
|
| 217 |
var submit = el.down("input[type=submit]");
|
|
| 218 |
if (submit) {
|
|
| 219 |
submit.focus(); |
|
| 220 |
} |
|
| 221 |
} |
|
| 222 |
|
|
| 223 |
function hideModal(el) {
|
|
| 224 |
var modal = Element.up(el, 'div.modal'); |
|
| 225 |
if (modal) {
|
|
| 226 |
modal.hide(); |
|
| 227 |
} |
|
| 228 |
var bg = $('modalbg');
|
|
| 229 |
if (bg) {
|
|
| 230 |
bg.remove(); |
|
| 231 |
} |
|
| 232 |
} |
|
| 233 |
|
|
| 234 |
function collapseScmEntry(id) {
|
|
| 235 |
var els = document.getElementsByClassName(id, 'browser'); |
|
| 236 |
for (var i = 0; i < els.length; i++) {
|
|
| 237 |
if (els[i].hasClassName('open')) {
|
|
| 238 |
collapseScmEntry(els[i].id); |
|
| 239 |
} |
|
| 240 |
Element.hide(els[i]); |
|
| 241 |
} |
|
| 242 |
$(id).removeClassName('open');
|
|
| 243 |
} |
|
| 244 |
|
|
| 245 |
function expandScmEntry(id) {
|
|
| 246 |
var els = document.getElementsByClassName(id, 'browser'); |
|
| 247 |
for (var i = 0; i < els.length; i++) {
|
|
| 248 |
Element.show(els[i]); |
|
| 249 |
if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) {
|
|
| 250 |
expandScmEntry(els[i].id); |
|
| 251 |
} |
|
| 252 |
} |
|
| 253 |
$(id).addClassName('open');
|
|
| 254 |
} |
|
| 255 |
|
|
| 256 |
function scmEntryClick(id) {
|
|
| 257 |
el = $(id); |
|
| 258 |
if (el.hasClassName('open')) {
|
|
| 259 |
collapseScmEntry(id); |
|
| 260 |
el.addClassName('collapsed');
|
|
| 261 |
return false; |
|
| 262 |
} else if (el.hasClassName('loaded')) {
|
|
| 263 |
expandScmEntry(id); |
|
| 264 |
el.removeClassName('collapsed');
|
|
| 265 |
return false; |
|
| 266 |
} |
|
| 267 |
if (el.hasClassName('loading')) {
|
|
| 268 |
return false; |
|
| 269 |
} |
|
| 270 |
el.addClassName('loading');
|
|
| 271 |
return true; |
|
| 272 |
} |
|
| 273 |
|
|
| 274 |
function scmEntryLoaded(id) {
|
|
| 275 |
Element.addClassName(id, 'open'); |
|
| 276 |
Element.addClassName(id, 'loaded'); |
|
| 277 |
Element.removeClassName(id, 'loading'); |
|
| 278 |
} |
|
| 279 |
|
|
| 280 |
function randomKey(size) {
|
|
| 281 |
var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
|
|
| 282 |
var key = ''; |
|
| 283 |
for (i = 0; i < size; i++) {
|
|
| 284 |
key += chars[Math.floor(Math.random() * chars.length)]; |
|
| 285 |
} |
|
| 286 |
return key; |
|
| 287 |
} |
|
| 288 |
|
|
| 289 |
function observeParentIssueField(url) {
|
|
| 290 |
new Ajax.Autocompleter('issue_parent_issue_id',
|
|
| 291 |
'parent_issue_candidates', |
|
| 292 |
url, |
|
| 293 |
{ minChars: 3,
|
|
| 294 |
frequency: 0.5, |
|
| 295 |
paramName: 'q', |
|
| 296 |
method: 'get', |
|
| 297 |
updateElement: function(value) {
|
|
| 298 |
document.getElementById('issue_parent_issue_id').value = value.id;
|
|
| 299 |
}}); |
|
| 300 |
} |
|
| 301 |
|
|
| 302 |
function observeRelatedIssueField(url) {
|
|
| 303 |
new Ajax.Autocompleter('relation_issue_to_id',
|
|
| 304 |
'related_issue_candidates', |
|
| 305 |
url, |
|
| 306 |
{ minChars: 3,
|
|
| 307 |
frequency: 0.5, |
|
| 308 |
paramName: 'q', |
|
| 309 |
method: 'get', |
|
| 310 |
updateElement: function(value) {
|
|
| 311 |
document.getElementById('relation_issue_to_id').value = value.id;
|
|
| 312 |
}, |
|
| 313 |
parameters: 'scope=all' |
|
| 314 |
}); |
|
| 315 |
} |
|
| 316 |
|
|
| 317 |
function setVisible(id, visible) {
|
|
| 318 |
var el = $(id); |
|
| 319 |
if (el) {if (visible) {el.show();} else {el.hide();}}
|
|
| 320 |
} |
|
| 321 |
|
|
| 322 |
function observeProjectModules() {
|
|
| 323 |
var f = function() {
|
|
| 324 |
/* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */ |
|
| 325 |
var c = ($('project_enabled_module_names_issue_tracking').checked == true);
|
|
| 326 |
setVisible('project_trackers', c);
|
|
| 327 |
setVisible('project_issue_custom_fields', c);
|
|
| 328 |
}; |
|
| 329 |
|
|
| 330 |
Event.observe(window, 'load', f); |
|
| 331 |
Event.observe('project_enabled_module_names_issue_tracking', 'change', f);
|
|
| 332 |
} |
|
| 333 |
|
|
| 334 |
/* |
|
| 335 |
* Class used to warn user when leaving a page with unsaved textarea |
|
| 336 |
* Author: mathias.fischer@berlinonline.de |
|
| 337 |
*/ |
|
| 338 |
|
|
| 339 |
var WarnLeavingUnsaved = Class.create({
|
|
| 340 |
observedForms: false, |
|
| 341 |
observedElements: false, |
|
| 342 |
changedForms: false, |
|
| 343 |
message: null, |
|
| 344 |
|
|
| 345 |
initialize: function(message){
|
|
| 346 |
this.observedForms = $$('form');
|
|
| 347 |
this.observedElements = $$('textarea');
|
|
| 348 |
this.message = message; |
|
| 349 |
|
|
| 350 |
this.observedElements.each(this.observeChange.bind(this)); |
|
| 351 |
this.observedForms.each(this.submitAction.bind(this)); |
|
| 352 |
|
|
| 353 |
window.onbeforeunload = this.unload.bind(this); |
|
| 354 |
}, |
|
| 355 |
|
|
| 356 |
unload: function(){
|
|
| 357 |
this.observedElements.each(function(el) {el.blur();})
|
|
| 358 |
if(this.changedForms) |
|
| 359 |
return this.message; |
|
| 360 |
}, |
|
| 361 |
|
|
| 362 |
setChanged: function(){
|
|
| 363 |
this.changedForms = true; |
|
| 364 |
}, |
|
| 365 |
|
|
| 366 |
setUnchanged: function(){
|
|
| 367 |
this.changedForms = false; |
|
| 368 |
}, |
|
| 369 |
|
|
| 370 |
observeChange: function(element){
|
|
| 371 |
element.observe('change',this.setChanged.bindAsEventListener(this));
|
|
| 372 |
}, |
|
| 373 |
|
|
| 374 |
submitAction: function(element){
|
|
| 375 |
element.observe('submit',this.setUnchanged.bindAsEventListener(this));
|
|
| 376 |
} |
|
| 377 |
}); |
|
| 378 |
|
|
| 379 |
/* |
|
| 380 |
* 1 - registers a callback which copies the csrf token into the |
|
| 381 |
* X-CSRF-Token header with each ajax request. Necessary to |
|
| 382 |
* work with rails applications which have fixed |
|
| 383 |
* CVE-2011-0447 |
|
| 384 |
* 2 - shows and hides ajax indicator |
|
| 385 |
*/ |
|
| 386 |
Ajax.Responders.register({
|
|
| 387 |
onCreate: function(request){
|
|
| 388 |
var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
|
|
| 389 |
|
|
| 390 |
if (csrf_meta_tag) {
|
|
| 391 |
var header = 'X-CSRF-Token', |
|
| 392 |
token = csrf_meta_tag.readAttribute('content');
|
|
| 393 |
|
|
| 394 |
if (!request.options.requestHeaders) {
|
|
| 395 |
request.options.requestHeaders = {};
|
|
| 396 |
} |
|
| 397 |
request.options.requestHeaders[header] = token; |
|
| 398 |
} |
|
| 399 |
|
|
| 400 |
if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
|
|
| 401 |
Element.show('ajax-indicator');
|
|
| 402 |
} |
|
| 403 |
}, |
|
| 404 |
onComplete: function(){
|
|
| 405 |
if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
|
|
| 406 |
Element.hide('ajax-indicator');
|
|
| 407 |
} |
|
| 408 |
} |
|
| 409 |
}); |
|
| 410 |
|
|
| 411 |
function hideOnLoad() {
|
|
| 412 |
$$('.hol').each(function(el) {
|
|
| 413 |
el.hide(); |
|
| 414 |
}); |
|
| 415 |
} |
|
| 416 |
|
|
| 417 |
Event.observe(window, 'load', hideOnLoad); |
|
Also available in: Unified diff