Merge branch 'release/5.0' into dev

pull/4355/head
Oliver Günther 9 years ago
commit 3848c8aef0
  1. 3
      .rubocop.yml
  2. 2
      Gemfile
  3. 6
      Gemfile.lock
  4. 2
      app/assets/javascripts/tab_handling.js
  5. 2
      app/assets/stylesheets/content/_accounts_mobile.sass
  6. 2
      app/assets/stylesheets/content/_forms_mobile.sass
  7. 2
      app/assets/stylesheets/content/_notifications_mobile.sass
  8. 2
      app/assets/stylesheets/content/_widget_box.sass
  9. 2
      app/assets/stylesheets/content/_wiki.sass
  10. 2
      app/assets/stylesheets/layout/_base_mobile.sass
  11. 2
      app/assets/stylesheets/layout/_drop_down_mobile.sass
  12. 2
      app/assets/stylesheets/layout/_toolbar.sass
  13. 2
      app/assets/stylesheets/layout/_top_menu_mobile.sass
  14. 102
      app/assets/stylesheets/layout/_work_package_mobile.sass
  15. 2
      app/assets/stylesheets/specific/homescreen.sass
  16. 8
      app/controllers/concerns/omniauth_login.rb
  17. 2
      app/controllers/repositories_controller.rb
  18. 2
      app/helpers/application_helper.rb
  19. 2
      app/seeders/composite_seeder.rb
  20. 4
      app/views/homescreen/blocks/_community.html.erb
  21. 4
      app/views/members/index.html.erb
  22. 2
      app/views/repositories/settings/_repository_committers_link.html.erb
  23. 3
      app/views/repositories/settings/_vendor_attribute_groups.html.erb
  24. 34
      doc/operation_guides/docker/installation-guide.md
  25. 47
      spec/controllers/repositories_controller_spec.rb
  26. BIN
      spec/fixtures/repositories/subversion_repository.dump.gz
  27. BIN
      spec/fixtures/repositories/subversion_repository.tar.gz
  28. 24
      spec/lib/open_project/scm/adapters/subversion_adapter_spec.rb
  29. 8
      spec/models/repository/subversion_spec.rb
  30. 8
      spec/routing/repositories_routing_spec.rb
  31. 8
      spec_legacy/unit/repository_subversion_spec.rb

@ -207,9 +207,6 @@ StringLiterals:
VariableInterpolation: VariableInterpolation:
Enabled: false Enabled: false
TrailingComma:
Enabled: false
TrivialAccessors: TrivialAccessors:
Enabled: false Enabled: false

@ -182,7 +182,7 @@ group :ldap do
end end
group :development do group :development do
gem 'letter_opener', '~> 1.3.0' gem 'letter_opener'
gem 'thin' gem 'thin'
gem 'faker' gem 'faker'
gem 'quiet_assets' gem 'quiet_assets'

@ -181,7 +181,7 @@ GEM
cliver (0.3.2) cliver (0.3.2)
cocaine (0.5.7) cocaine (0.5.7)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
codecov (0.0.8) codecov (0.1.4)
json json
simplecov simplecov
url url
@ -318,7 +318,7 @@ GEM
mini_portile2 (2.0.0) mini_portile2 (2.0.0)
minitest (5.8.0) minitest (5.8.0)
mixlib-shellout (2.1.0) mixlib-shellout (2.1.0)
multi_json (1.11.2) multi_json (1.11.3)
multi_test (0.1.2) multi_test (0.1.2)
multi_xml (0.5.5) multi_xml (0.5.5)
mysql2 (0.3.20) mysql2 (0.3.20)
@ -601,7 +601,7 @@ DEPENDENCIES
jruby-openssl jruby-openssl
json_spec json_spec
launchy (~> 2.3.0) launchy (~> 2.3.0)
letter_opener (~> 1.3.0) letter_opener
multi_json (~> 1.11.0) multi_json (~> 1.11.0)
mysql2 (~> 0.3.20) mysql2 (~> 0.3.20)
net-ldap (~> 0.8.0) net-ldap (~> 0.8.0)

@ -36,7 +36,7 @@
// Responsible to hide the old selected tab and show the content // Responsible to hide the old selected tab and show the content
// of the currently selected tab. // of the currently selected tab.
function showTab(name, url) { function showTab(name, url) {
jQuery('div#content .tabs .a .position-label').hide(); jQuery('div#content .tabs .position-label').hide();
jQuery('div#content .tabs #tab-' + name + ' .position-label').show(); jQuery('div#content .tabs #tab-' + name + ' .position-label').show();
jQuery('div#content .tab-content').hide(); jQuery('div#content .tab-content').hide();
jQuery('div.tabs a').removeClass('selected'); jQuery('div.tabs a').removeClass('selected');

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
@include breakpoint(960px down) @include breakpoint(680px down)
body.controller-account body.controller-account
#login-form, #login-form,
#content .login-auth-providers, #content .login-auth-providers,

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
@include breakpoint(960px down) @include breakpoint(680px down)
form form
.grid-block .grid-block
display: block display: block

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
@include breakpoint(960px down) @include breakpoint(680px down)
.notification-box--wrapper, .notification-box--wrapper,
.flash, .flash,
#errorExplanation #errorExplanation

@ -124,7 +124,7 @@ $widget-box--enumeration-width: 20px
//necessary for correct alignment even with long texts //necessary for correct alignment even with long texts
width: calc(100% - #{$widget-box--enumeration-width}) width: calc(100% - #{$widget-box--enumeration-width})
@include breakpoint(960px down) @include breakpoint(680px down)
.widget-boxes .widget-boxes
&.-flex &.-flex
.widget-box .widget-box

@ -196,7 +196,7 @@ blockquote
.toolbar-container ~ .wiki-content .toolbar-container ~ .wiki-content
margin-top: -64px margin-top: -64px
@include breakpoint(960px down) @include breakpoint(680px down)
.toolbar-container ~ .wiki-content .toolbar-container ~ .wiki-content
margin-top: 0 margin-top: 0

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
@include breakpoint(960px down) @include breakpoint(680px down)
html html
min-width: 0 !important min-width: 0 !important

@ -30,7 +30,7 @@
// https://github.com/plapier/jquery-dropdown // https://github.com/plapier/jquery-dropdown
// (dual MIT/GPL-Licensed) // (dual MIT/GPL-Licensed)
@include breakpoint(960px down) @include breakpoint(680px down)
.dropdown .dropdown-menu, .dropdown .dropdown-menu,
.toolbar .legacy-actions-more .toolbar .legacy-actions-more
LI > A, LI > A,

@ -272,7 +272,7 @@
* *
outline: none outline: none
@include breakpoint(960px down) @include breakpoint(680px down)
.toolbar-container .toolbar-items .toolbar-container .toolbar-items
background: #fff background: #fff
display: flex display: flex

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
@include breakpoint(960px down) @include breakpoint(680px down)
#logo #logo
background-color: transparent background-color: transparent

@ -26,41 +26,45 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
@include breakpoint(960px down) @include breakpoint(1248px down)
body.controller-work_packages body.controller-work_packages
&.action-show #content &.action-show
overflow: visible overflow: auto !important
overflow-y: scroll !important
.toolbar-container #main,
margin-bottom: 4px #content
min-height: 0 position: relative !important
.work-packages--show-view #main
padding: 0 padding-bottom: 0
#toolbar-items #content
background: #fff height: 100% !important
display: flex overflow: visible
margin-bottom: 20px
width: calc(100% + 5px)
> li .work-packages--list-table-area
-webkit-flex: 1 0 0 position: relative
flex: 1 0 0
&:first-child .work-packages--list
-webkit-flex: 3 0 0 padding-bottom: 55px
flex: 3 0 0
button .work-packages--page-container
width: 100% .toolbar > .work-packages--split-view
display: block
.wp-create-button .dropdown #content
left: 0 !important overflow: visible
.toolbar-container
margin-bottom: 4px
min-height: 0
.work-packages--show-view
padding: 0
padding-right: 20px
.action_menu_main .dropdown
left: auto !important
.work-packages--split-view .work-packages--split-view
display: block display: block
@ -171,10 +175,10 @@
.work-packages--page-container .work-packages--page-container
.toolbar .toolbar
padding: 0 padding-right: 20px
.title-container .title-container
max-width: 40% max-width: 90%
overflow: hidden overflow: hidden
span span
@ -185,11 +189,43 @@
text-overflow: ellipsis text-overflow: ellipsis
white-space: nowrap white-space: nowrap
> .work-packages--split-view
display: block
.work-packages--list-table-area
position: relative
.work-packages--list @include breakpoint (680px down)
padding-bottom: 55px body.controller-work_packages
.work-packages--page-container
.toolbar
padding-right: 0
&.action-show #content
.work-packages--show-view
padding-right: 0
.toolbar-items
background: #fff
display: flex
margin-bottom: 20px
width: calc(100% + 5px)
> li
-webkit-flex: 1 0 0
flex: 1 0 0
&:first-child
-webkit-flex: 3 0 0
flex: 3 0 0
button
width: 100%
.wp-create-button .dropdown
left: 0 !important
.action_menu_main .dropdown
left: auto !important
.work-packages--list-table-area
position: relative
.work-packages--list
padding-bottom: 55px

@ -62,7 +62,7 @@
font-size: 3rem font-size: 3rem
color: $homescreen-footer-icon-color color: $homescreen-footer-icon-color
@include breakpoint(960px down) @include breakpoint(680px down)
.homescreen--links .homescreen--links
padding: 20px padding: 20px
flex-wrap: wrap flex-wrap: wrap

@ -35,7 +35,13 @@ module Concerns::OmniauthLogin
included do included do
# disable CSRF protection since that should be covered by the omniauth strategy # disable CSRF protection since that should be covered by the omniauth strategy
skip_before_filter :verify_authenticity_token, only: [:omniauth_login] # the other filters are not applicable either since OmniAuth is doing authentication
# itself
[
:verify_authenticity_token, :user_setup,
:check_if_login_required, :check_session_lifetime
]
.each { |key| skip_before_filter key, only: [:omniauth_login] }
helper :omniauth helper :omniauth
end end

@ -349,7 +349,7 @@ class RepositoriesController < ApplicationController
# Prepare checkout instructions # Prepare checkout instructions
# available on all pages (even empty!) # available on all pages (even empty!)
@path = CGI.unescape(params[:path] || '') @path = params[:path] || ''
@instructions = ::Scm::CheckoutInstructionsService.new(@repository, path: @path) @instructions = ::Scm::CheckoutInstructionsService.new(@repository, path: @path)
# Asserts repository availability, or renders an appropriate error # Asserts repository availability, or renders an appropriate error

@ -366,7 +366,7 @@ module ApplicationHelper
end end
def to_path_param(path) def to_path_param(path)
CGI.escape(path.to_s) path.to_s
end end
def reorder_links(name, url, options = {}) def reorder_links(name, url, options = {})

@ -62,7 +62,7 @@ class CompositeSeeder < Seeder
def discovered_seeder_classes def discovered_seeder_classes
Seeder Seeder
.subclasses .subclasses
.reject { |cl| cl.namespace_name == namespace } .reject { |cl| cl.to_s.deconstantize == namespace }
.select { |cl| include_discovered_class? cl } .select { |cl| include_discovered_class? cl }
end end

@ -26,7 +26,7 @@
</a> </a>
</li> </li>
<li> <li>
<a href="https://www.openproject.com/enterprise-services/" <a href="https://www.openproject.org/professional-services/"
target="_blank" target="_blank"
title="<%= l(:label_professional_support) %>"> title="<%= l(:label_professional_support) %>">
<%= l(:label_professional_support) %> <%= l(:label_professional_support) %>
@ -40,7 +40,7 @@
</a> </a>
</li> </li>
<li> <li>
<a href="https://www.openproject.org/open-source/openproject-plugins/" <a href="https://www.openproject.org/open-source/plugins/"
target="_blank" target="_blank"
title="<%= l(:label_plugins) %>"> title="<%= l(:label_plugins) %>">
<%= l(:label_plugins) %> <%= l(:label_plugins) %>

@ -27,8 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<% html_title 'Members' %> <% html_title t(:label_member_plural) %>
<%= toolbar title: 'Members' do %> <%= toolbar title: t(:label_member_plural) do %>
<% if authorize_for(:members, :new) %> <% if authorize_for(:members, :new) %>
<a href="<%= new_project_member_path %>" id="add-member-button" title="Add Member" class="button -alt-highlight"> <a href="<%= new_project_member_path %>" id="add-member-button" title="Add Member" class="button -alt-highlight">
<i class="button--icon icon-add"></i> <i class="button--icon icon-add"></i>

@ -0,0 +1,2 @@
<%= link_to l(:label_user_plural), committers_project_repository_path(repository.project),
class: 'icon icon-user' %>

@ -12,8 +12,7 @@
</div> </div>
<% unless repository.new_record? %> <% unless repository.new_record? %>
<div class="attributes-group--header-control"> <div class="attributes-group--header-control">
<%= link_to l(:label_user_plural), committers_project_repository_path(repository.project), <%= render partial: '/repositories/settings/repository_committers_link', locals: { repository: repository } %>
class: 'icon icon-user' %>
<% if type === 'managed' %> <% if type === 'managed' %>
<%= link_to l(:button_delete), destroy_info_project_repository_path(repository.project), <%= link_to l(:button_delete), destroy_info_project_repository_path(repository.project),
method: :get, method: :get,

@ -88,6 +88,40 @@ Docker Engine, or via an environment file:
[configuration-doc]: https://github.com/opf/openproject/blob/dev/doc/CONFIGURATION.md [configuration-doc]: https://github.com/opf/openproject/blob/dev/doc/CONFIGURATION.md
### SMTP configuration
By default, the docker container will try to send emails via the local
`postfix` daemon. However emails sent this way are more than likely to fail or
end up in the spam inbox of your users. We recommend using an external SMTP
server to send your emails.
A good choice is [SendGrid](https://sendgrid.net), which offers a free plan
with up to 12000 emails per month. Just sign up on the website, and once your
account is provisioned, generate a new API key and copy it somewhere (it looks
like `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`).
You can also just use your SendGrid username and password, but this is less
secure.
You can then configure OpenProject with the following additonal environment
variables (with SendGrid, the `SMTP_USER_NAME` is always `apikey`. Just replace
`SMTP_PASSWORD` with the API key you've generated and you should be good to
go):
docker run -d \
-e EMAIL_DELIVERY_METHOD=smtp \
-e SMTP_ADDRESS=smtp.sendgrid.net \
-e SMTP_PORT=587 \
-e SMTP_DOMAIN=my.domain.com \
-e SMTP_AUTHENTICATION=login \
-e SMTP_ENABLE_STARTTLS_AUTO=true \
-e SMTP_USER_NAME="apikey" \
-e SMTP_PASSWORD="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" \
...
You can adjust those settings for other SMTP providers, such as GMail,
Mandrill, etc. Please refer to the documentation of the corresponding provider
to see what values should be used.
## FAQ ## FAQ
* Can I use SSL? * Can I use SSL?

@ -1,3 +1,4 @@
#-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF) # Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
@ -252,6 +253,52 @@ describe RepositoriesController, type: :controller do
end end
end end
shared_examples 'renders the repository title' do |active_breadcrumb|
it do
expect(response).to be_success
expect(response.body).to have_selector('.repository-breadcrumbs', text: active_breadcrumb)
end
end
describe 'show' do
render_views
let(:role) { FactoryGirl.create(:role, permissions: [:browse_repository]) }
before do
get :show, project_id: project.identifier, path: path
end
context 'with brackets' do
let(:path) { 'subversion_test/[folder_with_brackets]' }
it_behaves_like 'renders the repository title', '[folder_with_brackets]'
end
context 'with unicode' do
let(:path) { 'Föbar/äm/Sägepütz!%5D§' }
it_behaves_like 'renders the repository title', 'Sägepütz!%5D§'
end
end
describe 'changes' do
render_views
let(:role) { FactoryGirl.create(:role, permissions: [:browse_repository]) }
before do
get :changes, project_id: project.identifier, path: path
expect(response).to be_success
end
context 'with brackets' do
let(:path) { 'subversion_test/[folder_with_brackets]' }
it_behaves_like 'renders the repository title', '[folder_with_brackets]'
end
context 'with unicode' do
let(:path) { 'Föbar/äm' }
it_behaves_like 'renders the repository title', 'äm'
end
end
describe 'checkout path' do describe 'checkout path' do
render_views render_views

@ -187,30 +187,32 @@ describe OpenProject::Scm::Adapters::Subversion do
it 'builds the info object' do it 'builds the info object' do
info = adapter.info info = adapter.info
expect(info.root_url).to eq(url) expect(info.root_url).to eq(url)
expect(info.lastrev.identifier).to eq('12') expect(info.lastrev.identifier).to eq('13')
expect(info.lastrev.author).to eq('oliver') expect(info.lastrev.author).to eq('oliver')
expect(info.lastrev.time).to eq('2015-07-08T13:32:29.228572Z') expect(info.lastrev.time).to eq('2016-04-14T19:23:01.74469Z')
end end
end end
describe '.entries' do describe '.entries' do
it 'reads all entries from the current revision' do it 'reads all entries from the current revision' do
entries = adapter.entries entries = adapter.entries
expect(entries.length).to eq(1) expect(entries.length).to eq(2)
expect(entries[0].name).to eq('subversion_test') expect(entries[0].name).to eq('Föbar')
expect(entries[0].path).to eq('subversion_test') expect(entries[0].path).to eq('Föbar')
expect(entries[1].name).to eq('subversion_test')
expect(entries[1].path).to eq('subversion_test')
end end
it 'contains a reference to the last revision' do it 'contains a reference to the last revision' do
entries = adapter.entries entries = adapter.entries
expect(entries.length).to eq(1) expect(entries.length).to eq(2)
lastrev = entries[0].lastrev lastrev = entries[0].lastrev
expect(lastrev.identifier).to eq('12') expect(lastrev.identifier).to eq('13')
expect(lastrev.author).to eq('oliver') expect(lastrev.author).to eq('oliver')
expect(lastrev.message).to eq('') expect(lastrev.message).to eq('')
expect(lastrev.time).to eq('2015-07-08T13:32:29.228572Z') expect(lastrev.time).to eq('2016-04-14T19:23:01.74469Z')
end end
it 'reads all entries from the given revision' do it 'reads all entries from the given revision' do
@ -282,13 +284,13 @@ describe OpenProject::Scm::Adapters::Subversion do
describe '.revisions' do describe '.revisions' do
it 'returns all revisions by default' do it 'returns all revisions by default' do
revisions = adapter.revisions revisions = adapter.revisions
expect(revisions.length).to eq(12) expect(revisions.length).to eq(13)
expect(revisions[0].author).to eq('oliver') expect(revisions[0].author).to eq('oliver')
expect(revisions[0].message).to eq("Propedit\n") expect(revisions[0].message).to eq("UTF-8 Test")
revisions.each_with_index do |rev, i| revisions.each_with_index do |rev, i|
expect(rev.identifier).to eq((12 - i).to_s) expect(rev.identifier).to eq((13 - i).to_s)
end end
end end

@ -167,8 +167,8 @@ describe Repository::Subversion, type: :model do
instance.fetch_changesets instance.fetch_changesets
instance.reload instance.reload
expect(instance.changesets.count).to eq(12) expect(instance.changesets.count).to eq(13)
expect(instance.file_changes.count).to eq(21) expect(instance.file_changes.count).to eq(25)
expect(instance.changesets.find_by(revision: '1').comments).to eq('Initial import.') expect(instance.changesets.find_by(revision: '1').comments).to eq('Initial import.')
end end
@ -181,7 +181,7 @@ describe Repository::Subversion, type: :model do
expect(instance.changesets.count).to eq(5) expect(instance.changesets.count).to eq(5)
instance.fetch_changesets instance.fetch_changesets
expect(instance.changesets.count).to eq(12) expect(instance.changesets.count).to eq(13)
end end
it 'should latest changesets' do it 'should latest changesets' do
@ -296,7 +296,7 @@ describe Repository::Subversion, type: :model do
changeset = instance.find_changeset_by_name('1') changeset = instance.find_changeset_by_name('1')
expect(changeset.previous).to be_nil expect(changeset.previous).to be_nil
changeset = instance.find_changeset_by_name('12') changeset = instance.find_changeset_by_name('13')
expect(changeset.next).to be_nil expect(changeset.next).to be_nil
end end

@ -45,6 +45,14 @@ describe RepositoriesController, type: :routing do
path: 'path/to/file.c') path: 'path/to/file.c')
} }
it {
expect(get('/projects/testproject/repository/folder%20with%20spaces'))
.to route_to(controller: 'repositories',
action: 'show',
project_id: 'testproject',
path: 'folder with spaces')
}
it { it {
expect(get('/projects/testproject/repository/revisions/5')) expect(get('/projects/testproject/repository/revisions/5'))
.to route_to(controller: 'repositories', .to route_to(controller: 'repositories',

@ -45,8 +45,8 @@ describe Repository::Subversion, type: :model do
@repository.fetch_changesets @repository.fetch_changesets
@repository.reload @repository.reload
assert_equal 12, @repository.changesets.count assert_equal 13, @repository.changesets.count
assert_equal 21, @repository.file_changes.count assert_equal 25, @repository.file_changes.count
assert_equal 'Initial import.', @repository.changesets.find_by(revision: '1').comments assert_equal 'Initial import.', @repository.changesets.find_by(revision: '1').comments
end end
@ -58,7 +58,7 @@ describe Repository::Subversion, type: :model do
assert_equal 5, @repository.changesets.count assert_equal 5, @repository.changesets.count
@repository.fetch_changesets @repository.fetch_changesets
assert_equal 12, @repository.changesets.count assert_equal 13, @repository.changesets.count
end end
it 'should latest changesets' do it 'should latest changesets' do
@ -199,7 +199,7 @@ describe Repository::Subversion, type: :model do
it 'should next nil' do it 'should next nil' do
@repository.fetch_changesets @repository.fetch_changesets
@repository.reload @repository.reload
changeset = @repository.find_changeset_by_name('12') changeset = @repository.find_changeset_by_name('13')
assert_nil changeset.next assert_nil changeset.next
end end

Loading…
Cancel
Save