Merge branch 'dev' into fix/21569-cursor-offset-project-search-IE

pull/3543/head
Henriette Dinger 9 years ago
commit 897099dd77
  1. 2
      Gemfile
  2. 6
      Gemfile.lock
  3. 28
      app/assets/stylesheets/content/_forms.sass
  4. 3
      app/assets/stylesheets/content/_in_place_editing.sass
  5. 11
      app/assets/stylesheets/content/_notifications.lsg
  6. 3
      app/assets/stylesheets/content/_notifications.sass
  7. 30
      app/assets/stylesheets/content/_select2.scss
  8. 29
      app/assets/stylesheets/content/_tables.sass
  9. 16
      app/assets/stylesheets/layout/_top_menu.sass
  10. 6
      app/controllers/projects_controller.rb
  11. 5
      app/controllers/repositories_controller.rb
  12. 4
      app/helpers/repositories_helper.rb
  13. 2
      app/models/version.rb
  14. 6
      app/views/account/password_recovery.html.erb
  15. 35
      app/views/projects/destroy_info.html.erb
  16. 47
      app/views/repositories/destroy_info.html.erb
  17. 39
      app/views/work_packages/bulk/destroy.html.erb
  18. 23
      config/locales/en.yml
  19. 138
      doc/apiv3-documentation.apib
  20. 2
      features/work_packages/destroy.feature
  21. 5
      frontend/app/services/version-service.js
  22. 4
      frontend/app/work_packages/services/work-package-field-configuration-service.js
  23. 31
      spec/features/projects/delete_projects_spec.rb
  24. 8
      spec/features/repositories/repository_settings_spec.rb
  25. 43
      spec/models/version_spec.rb

@ -147,7 +147,7 @@ group :test do
gem 'rspec-legacy_formatters' gem 'rspec-legacy_formatters'
gem 'capybara', '~> 2.4.4' gem 'capybara', '~> 2.4.4'
gem 'capybara-screenshot', '~> 1.0.4' gem 'capybara-screenshot', '~> 1.0.4'
gem 'capybara-select2', github: 'finnlabs/capybara-select2' gem 'capybara-select2', github: 'goodwill/capybara-select2'
gem 'capybara-ng', '~> 0.2.1' gem 'capybara-ng', '~> 0.2.1'
gem 'selenium-webdriver', '~> 2.47.1' gem 'selenium-webdriver', '~> 2.47.1'
gem 'timecop', '~> 0.7.1' gem 'timecop', '~> 0.7.1'

@ -7,8 +7,8 @@ GIT
activerecord (>= 3.0.0) activerecord (>= 3.0.0)
GIT GIT
remote: git://github.com/finnlabs/capybara-select2.git remote: git://github.com/goodwill/capybara-select2.git
revision: 4088099ecc8a03c0c3155827a89a1a51057b0e38 revision: 585192e4bb0db8d52e761ab68f08c17294806447
specs: specs:
capybara-select2 (1.0.1) capybara-select2 (1.0.1)
capybara capybara
@ -30,7 +30,7 @@ GIT
GIT GIT
remote: https://github.com/opf/openproject-translations.git remote: https://github.com/opf/openproject-translations.git
revision: a2a7d246c6212c0867970ee44c505f97e5c96ee3 revision: d7c6e76ff6c3a56e42102a209900c2ad85af458f
branch: dev branch: dev
specs: specs:
openproject-translations (5.0.0.pre.alpha) openproject-translations (5.0.0.pre.alpha)

@ -66,21 +66,21 @@ $form--field-types: (text-field, text-area, select, check-box, radio-button, ran
.form--section .form--section
padding-top: 0px padding-top: 0px
margin-bottom: 0px
& > *
padding-left: 1rem
.form--section-title .form--section-title
background-color: $content-form-danger-zone-bg-color background-color: $content-form-danger-zone-bg-color
color: $content-form-danger-zone-font-color !important color: $content-form-danger-zone-font-color !important
padding: 0.5rem !important padding: 1rem !important
margin: 0 0 1rem 0 margin: 0 0 1rem 0
em em
font-style: italics font-style: italic
p
padding-left: 0.5rem
&.danger-zone--warning p.danger-zone--warning
font-weight: bold font-weight: bold
color: $content-form-danger-zone-bg-color color: $content-form-danger-zone-bg-color
@ -96,7 +96,7 @@ $form--field-types: (text-field, text-area, select, check-box, radio-button, ran
input input
flex-basis: 50% flex-basis: 50%
margin: 0 0.5rem 0 0.5rem margin: 0 0.5rem 0 0
.button.-highlight .button.-highlight
background: $content-form-danger-zone-bg-color background: $content-form-danger-zone-bg-color
@ -493,11 +493,6 @@ input[readonly].-clickable
//prevent radio-buttons from being cut at the border //prevent radio-buttons from being cut at the border
padding: 0 1px padding: 0 1px
.form--radio-button
// Be on par with Foundation's margin-bottom on labels.
.form &
margin-bottom: 0.5rem
.form--grouping .form--grouping
@include grid-block($wrap: true) @include grid-block($wrap: true)
align-items: center align-items: center
@ -512,7 +507,7 @@ input[readonly].-clickable
@include grid-content(2) @include grid-content(2)
@include grid-visible-overflow @include grid-visible-overflow
@extend %label-style @extend %label-style
padding: 0 1rem 0 0 padding: 0 1rem 0.5rem 0
font-size: $form-label-fontsize font-size: $form-label-fontsize
line-height: $base-line-height line-height: $base-line-height
color: $form-label-color color: $form-label-color
@ -620,3 +615,8 @@ input[readonly].-clickable
li, div li, div
padding: 0 padding: 0
.form--text-field-container
input[type=text]
margin-bottom: 0

@ -161,6 +161,9 @@
&:last-of-type &:last-of-type
margin-bottom: 0 margin-bottom: 0
.user-field-user-link
display: inline
.inplace-edit--preview .inplace-edit--preview
border: 1px solid $inplace-edit--border-color border: 1px solid $inplace-edit--border-color
padding: 0.375rem padding: 0.375rem

@ -46,17 +46,6 @@
</div> </div>
``` ```
## Severe warning
```
<div class="notification-box -warning -severe">
<a href="#" title="close" class="notification-box--close icon-context icon-close2"></a>
<div class="notification-box--content">
<p>This is a warning with severe consequences. You should not ignore it.</p>
</div>
</div>
```
## Success ## Success
``` ```

@ -153,9 +153,6 @@ $nm-upload-box-padding: rem-calc(15) rem-calc(25)
&::before &::before
@include messages-icon @include messages-icon
@extend %nm-icon-warning @extend %nm-icon-warning
&.-severe
background-color: $nm-color-error-background
border-color: $nm-color-error-border
&.-info::before &.-info::before
@include messages-icon @include messages-icon

@ -121,7 +121,7 @@ $se2-input-height: rem-calc(34px);
$se2-line-height: 30px; $se2-line-height: 30px;
$se2-button-width: 28px; $se2-button-width: 28px;
$se2-arrow-button-width: 18px; $se2-arrow-button-width: 18px;
$se2-results-max-height: 500px; $se2-results-max-height: 200px;
$se-multiple-input-line-height: 19px; $se-multiple-input-line-height: 19px;
$se-multiple-tags-line-height: 19px; $se-multiple-tags-line-height: 19px;
@ -192,14 +192,27 @@ $se2-width: 100%;
.select2-drop { .select2-drop {
.select2-search { .select2-search {
&:before {
content: "\e0a1";
font-family: "openproject-icon-font";
color: #E0E0E0;
position: absolute;
top: 6px;
right: 16px;
}
.select2-input { .select2-input {
//possible background tweaks background-image: none !important;
padding-right: 32px;
height: $se2-input-height !important;
} }
} }
.select2-results { .select2-results {
max-height: $se2-results-max-height; max-height: $se2-results-max-height;
overflow: auto; overflow: auto;
margin: 4px 9px 9px 0;
padding: 0 0 0 9px;
.select2-highlighted { .select2-highlighted {
background-color: $se2-theme-selectable-color-highlighted; background-color: $se2-theme-selectable-color-highlighted;
@ -208,8 +221,19 @@ $se2-width: 100%;
} }
} }
.select2-drop:not(.project-search-results) {
input[type="text"].select2-input { input[type="text"].select2-input {
height: $se2-input-height; height: $se2-input-height !important;
}
.select2-search {
margin: 10px 0 6px 0;
padding: 0 9px;
input {
margin-bottom: 0;
}
}
} }
.columns-modal-content { .columns-modal-content {

@ -26,6 +26,14 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
$project-table--start-indentation: 0.5em
$project-table--child-indentation: 1.1em
$project-table--icon-distance: 5px
@mixin calc-indentation($indentation)
// This does not work for big font-sizes
padding-left: calc(#{$indentation} * #{$project-table--child-indentation} + #{$project-table--start-indentation} - #{$project-table--icon-distance})
table table
td td
padding: 3px 6px padding: 3px 6px
@ -124,27 +132,26 @@ tr
td.name a td.name a
white-space: nowrap white-space: nowrap
&.idnt td.project--hierarchy span &.idnt td.project--hierarchy span
padding-left: 16px
&.icon-context:before &.icon-context:before
padding: 9px 5px 0 10px padding: 9px 5px 0 0px
&.idnt-1 td.project--hierarchy &.idnt-1 td.project--hierarchy
padding-left: 0.5em @include calc-indentation(0)
&.idnt-2 td.project--hierarchy &.idnt-2 td.project--hierarchy
padding-left: 2em @include calc-indentation(1)
&.idnt-3 td.project--hierarchy &.idnt-3 td.project--hierarchy
padding-left: 3.5em @include calc-indentation(2)
&.idnt-4 td.project--hierarchy &.idnt-4 td.project--hierarchy
padding-left: 5em @include calc-indentation(3)
&.idnt-5 td.project--hierarchy &.idnt-5 td.project--hierarchy
padding-left: 6.5em @include calc-indentation(4)
&.idnt-6 td.project--hierarchy &.idnt-6 td.project--hierarchy
padding-left: 8em @include calc-indentation(5)
&.idnt-7 td.project--hierarchy &.idnt-7 td.project--hierarchy
padding-left: 9.5em @include calc-indentation(6)
&.idnt-8 td.project--hierarchy &.idnt-8 td.project--hierarchy
padding-left: 11em @include calc-indentation(7)
&.idnt-9 td.project--hierarchy &.idnt-9 td.project--hierarchy
padding-left: 12.5em @include calc-indentation(8)
&.issue &.issue
white-space: nowrap white-space: nowrap

@ -225,17 +225,13 @@ input.top-menu-search--input
background: $header-drop-down-projects-search-bg-color background: $header-drop-down-projects-search-bg-color
margin: 0 0 10px 0 margin: 0 0 10px 0
.select2-search:before &:before
content: "\e0a1" top: 21px
font-family: "openproject-icon-font" right: 26px
color: #E0E0E0
position: absolute
top: 24px
right: 20px
.select2-search input .select2-search input.select2-input
margin: 10px 0px margin: 10px 10px
padding: 9px 10px 9px 10px padding: 9px 32px 9px 10px !important
border: none border: none
border-radius: 25px border-radius: 25px
width: 92% width: 92%

@ -215,16 +215,10 @@ class ProjectsController < ApplicationController
def destroy def destroy
@project_to_destroy = @project @project_to_destroy = @project
if params[:confirm]
@project_to_destroy.destroy @project_to_destroy.destroy
respond_to do |format| respond_to do |format|
format.html do redirect_to controller: '/admin', action: 'projects' end format.html do redirect_to controller: '/admin', action: 'projects' end
end end
else
flash[:error] = l(:notice_project_not_deleted)
redirect_to confirm_destroy_project_path(@project)
return
end
hide_project_in_layout hide_project_in_layout
end end

@ -39,6 +39,7 @@ end
class RepositoriesController < ApplicationController class RepositoriesController < ApplicationController
include PaginationHelper include PaginationHelper
include RepositoriesHelper
menu_item :repository menu_item :repository
menu_item :settings, only: [:edit, :destroy_info] menu_item :settings, only: [:edit, :destroy_info]
@ -303,10 +304,6 @@ class RepositoriesController < ApplicationController
end end
end end
def settings_repository_tab_path
settings_project_path(@project, tab: 'repository')
end
def find_repository def find_repository
@repository = @project.repository @repository = @project.repository
(render_404; return false) unless @repository (render_404; return false) unless @repository

@ -28,6 +28,10 @@
#++ #++
module RepositoriesHelper module RepositoriesHelper
def settings_repository_tab_path
settings_project_path(@project, tab: 'repository')
end
def format_revision(revision) def format_revision(revision)
if revision.respond_to? :format_identifier if revision.respond_to? :format_identifier
revision.format_identifier revision.format_identifier

@ -205,7 +205,7 @@ class Version < ActiveRecord::Base
# tie the comparison to the presentation as sorting is mostly # tie the comparison to the presentation as sorting is mostly
# used within sorted tables. # used within sorted tables.
# Thus, when the representation changes, the sorting will change as well. # Thus, when the representation changes, the sorting will change as well.
to_s_with_project <=> version.to_s_with_project to_s_with_project.downcase <=> version.to_s_with_project.downcase
end end
# Returns the sharings that +user+ can set the version to # Returns the sharings that +user+ can set the version to

@ -33,8 +33,8 @@ See doc/COPYRIGHT.rdoc for more details.
<section class="form--section"> <section class="form--section">
<div class="form--field -required"> <div class="form--field -required">
<%= styled_label_tag 'new_password', User.human_attribute_name(:new_password) %> <%= styled_label_tag 'new_password', User.human_attribute_name(:new_password) %>
<div class="form--field--container"> <div class="form--field-container">
<%= styled_password_field_tag 'new_password', nil, :size => 25 %> <%= styled_password_field_tag 'new_password', nil, :size => 25, container_class: '-middle' %>
</div> </div>
<div class="form--field-instructions"> <div class="form--field-instructions">
<%= password_complexity_requirements %> <%= password_complexity_requirements %>
@ -44,7 +44,7 @@ See doc/COPYRIGHT.rdoc for more details.
<%= styled_label_tag 'new_password_confirmation', <%= styled_label_tag 'new_password_confirmation',
User.human_attribute_name(:new_password_confirmation) %> User.human_attribute_name(:new_password_confirmation) %>
<div class="form--field-container"> <div class="form--field-container">
<%= styled_password_field_tag 'new_password_confirmation', nil, :size => 25 %> <%= styled_password_field_tag 'new_password_confirmation', nil, :size => 25, container_class: '-middle' %>
</div> </div>
</div> </div>
</section> </section>

@ -26,21 +26,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.rdoc for more details. See doc/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<%= toolbar title: l(:label_confirmation) %> <%= styled_form_tag(project_path(@project_to_destroy),
<div class="notification-box -warning -severe"> class: 'danger-zone',
<div class="notification-box--content"> method: :delete) do %>
<p><strong><%= l(:text_project_destroy_title) %></strong><br /> <section class="form--section">
<%= l(:text_project_destroy_confirmation, identifier: @project_to_destroy.identifier)%> <h3 class="form--section-title">
<%= l('project.destroy.title', name: "<em>#{h(@project_to_destroy)}</em>").html_safe %>
</h3>
<p>
<%= l('project.destroy.confirmation', identifier: @project_to_destroy.identifier)%>
<% if @project_to_destroy.descendants.any? %> <% if @project_to_destroy.descendants.any? %>
<br /> <br />
<%= l(:text_subprojects_destroy_warning, content_tag('strong', h(@project_to_destroy.descendants.collect{|p| p.to_s}.join(', ')))).html_safe %> <% descendants = h(@project_to_destroy.descendants.collect{|p| p.to_s}.join(', ')) %>
<%= l('project.destroy.subprojects_confirmation', value: "<strong>#{h(descendants)}</strong>").html_safe %>
<% end %> <% end %>
</p> </p>
<p class="danger-zone--warning">
<span class="icon icon-attention2"></span>
<span><%= l('project.destroy.info')%></span>
</p>
<p> <p>
<%= styled_form_tag(project_path(@project_to_destroy), method: :delete) do %> <%= l('project.destroy.project_verification', name: "<em class=\"danger-zone--expected-value\">#{h(@project_to_destroy)}</em>").html_safe %>
<label><%= check_box_tag 'confirm', 1 %> <%= l(:general_text_Yes) %></label>
<%= submit_tag l(:button_delete), class: 'button' %>
<% end %>
</p> </p>
<div class="danger-zone--verification">
<input type="text"></input>
<%= styled_button_tag title: l(:button_delete), class: '-highlight', disabled: true do
concat content_tag :i, '', class: 'button--icon icon-delete'
concat content_tag :span, l(:button_delete), class: 'button--text'
end %>
</div> </div>
</div> </section>
<% end %>

@ -26,28 +26,57 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.rdoc for more details. See doc/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<%= toolbar title: l(:label_confirmation) %>
<div class="notification-box -warning <%= @repository.managed? ? '-severe' : '' %>">
<div class="notification-box--content">
<p><strong><%= l('repositories.destroy.title') %></strong><br /></p>
<p>
<% if @repository.managed? %> <% if @repository.managed? %>
<%= l('repositories.destroy.managed_text', project_name: @project.identifier) %> <%= styled_form_tag(project_repository_path(@project), method: :delete, class: 'danger-zone') do %>
<section class="form--section">
<h3 class="form--section-title">
<%= l('repositories.destroy.title', name: "<em>#{h(@repository.root_url)}</em>").html_safe %>
</h3>
<p>
<%= l('repositories.destroy.confirmation', project_name: @project.identifier) %>
<br/> <br/>
<%= l('repositories.destroy.managed_path_note', path: @repository.root_url) %> <%= l('repositories.destroy.managed_path_note', path: @repository.root_url) %>
</p>
<p class="danger-zone--warning">
<span class="icon icon-attention2"></span>
<span><%= l('repositories.destroy.info') %></span>
</p>
<div class="danger-zone--verification">
<%= styled_button_tag project_repository_path(@project),
method: :delete,
title: l(:button_delete),
disabled: false,
class: '-highlight' do %>
<i class="button--icon icon-delete"></i>
<span class="button--text"><%= l(:button_delete) %></span>
<% end %>
</div>
</section>
<% end %>
<% else %> <% else %>
<div class="notification-box -warning">
<div class="notification-box--content">
<p><strong><%= l('repositories.destroy.title', name: h(@repository.root_url)) %></strong><br /></p>
<p>
<%= simple_format l('repositories.destroy.linked_text', <%= simple_format l('repositories.destroy.linked_text',
project_name: @project.identifier, url: @repository.url) %> project_name: @project.identifier, url: @repository.url) %>
<% end %>
</p> </p>
<p> <p>
<%= link_to project_repository_path(@project), <%= link_to project_repository_path(@project),
:method => :delete, title: l(:button_delete),
:class => 'button' do %> method: :delete,
class: 'button' do %>
<i class="button--icon icon-delete"></i> <i class="button--icon icon-delete"></i>
<span class="button--text"><%= l(:button_delete) %></span> <span class="button--text"><%= l(:button_delete) %></span>
<% end %> <% end %>
<%= link_to settings_repository_tab_path,
title: l(:button_cancel),
class: 'button' do %>
<i class="button--icon icon-close"></i>
<span class="button--text"><%= l(:button_cancel) %></span>
<% end %>
</p> </p>
</div> </div>
</div> </div>
<% end %>

@ -26,17 +26,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.rdoc for more details. See doc/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<h2><%= l(:label_confirmation) %></h2>
<%= error_messages_for work_packages.first %> <%= error_messages_for work_packages.first %>
<%= styled_form_tag work_packages_bulk_path, :method => :delete do %> <%= styled_form_tag work_packages_bulk_path, method: :delete, class: 'form danger-zone' do %>
<% work_packages.each do |work_package| %> <% work_packages.each do |work_package| %>
<%= hidden_field_tag 'ids[]', work_package.id %> <%= hidden_field_tag 'ids[]', work_package.id %>
<% end %> <% end %>
<div class="form--section"> <section class="form--section">
<h3 class="form--section-title">
<%= l('work_package.destroy.title') %>
</h3>
<%= work_package_associations_to_address(associated) %> <%= work_package_associations_to_address(associated) %>
@ -45,29 +45,42 @@ See doc/COPYRIGHT.rdoc for more details.
</p> </p>
<%= fields_for :to_do do |f| %> <%= fields_for :to_do do |f| %>
<div class="form--field"> <div class="form--field -trailing-label">
<%= styled_label_tag :to_do_action_destroy, l(:text_destroy) %> <%= styled_label_tag :to_do_action_destroy, l(:text_destroy) %>
<div class="form--field-container"> <div class="form--field-container">
<%= styled_radio_button_tag 'to_do[action]', 'destroy' %> <%= styled_radio_button_tag 'to_do[action]', 'destroy' %>
</div> </div>
</div> </div>
<div class="form--field"> <div class="form--field -trailing-label">
<%=styled_label_tag 'to_do_action_nullify', l(:text_assign_to_project) %> <%=styled_label_tag 'to_do_action_nullify', l(:text_assign_to_project) %>
<div class="form--field-container"> <div class="form--field-container">
<%= styled_radio_button_tag 'to_do[action]' , 'nullify' %> <%= styled_radio_button_tag 'to_do[action]' , 'nullify' %>
</div> </div>
</div> </div>
<div class="form--field"> <div class="grid-block">
<div class="grid-block small-3">
<div class="form--field -trailing-label">
<%= styled_label_tag 'to_do_action_reassign', l(:text_reassign) %> <%= styled_label_tag 'to_do_action_reassign', l(:text_reassign) %>
<div class="form--field-container"> <div class="form--field-container">
<%= styled_radio_button_tag 'to_do[action]', 'reassign', :onclick => 'if(jQuery("#to_do_action_reassign").prop("checked")) { jQuery("#to_do_reassign_to_id").focus(); }' %> <%= styled_radio_button_tag 'to_do[action]', 'reassign', :onclick => 'if(jQuery("#to_do_action_reassign").prop("checked")) { jQuery("#to_do_reassign_to_id").focus(); }' %>
<%= styled_label_tag 'to_do_reassign_to_id', l(:text_reassign_to) %>
<%= f.text_field 'reassign_to_id', :value => params[:reassign_to_id], :size => 6, :onfocus => 'jQuery("#to_do_action_reassign").prop("checked", true);' %>
</div> </div>
</div> </div>
<% end %>
</div> </div>
<div class="grid-block">
<%= styled_submit_tag l(:button_apply), class: '-highlight' %> <div class="form--field">
<div class="form--text-field-container -xslim">
<%= styled_label_tag 'to_do_reassign_to_id', l(:text_reassign_to), class: 'hidden-for-sighted' %>
<%= f.text_field 'reassign_to_id', :placeholder => WorkPackage.human_attribute_name(:id), :value => params[:reassign_to_id], :size => 6, :onfocus => 'jQuery("#to_do_action_reassign").prop("checked", true);' %>
</div>
</div>
</div>
</div>
<% end %>
<div class="danger-zone--verification">
<%= styled_button_tag title: l(:button_delete), class: '-highlight' do
concat content_tag :i, '', class: 'button--icon icon-delete'
concat content_tag :span, l(:button_delete), class: 'button--text'
end %>
</section>
<% end %> <% end %>

@ -1320,6 +1320,14 @@ en:
permission_view_wiki_edits: "View wiki history" permission_view_wiki_edits: "View wiki history"
permission_view_wiki_pages: "View wiki" permission_view_wiki_pages: "View wiki"
project:
destroy:
confirmation: "If you continue, the project %{identifier} and all related data will be permanently destroyed."
info: "Deleting the project is an irreversible action."
project_verification: "Enter the project's name %{name} to verify the deletion."
subprojects_confirmation: "Its subproject(s): %{value} will also be deleted."
title: "Do you really want to delete the project %{name}?"
project_module_activity: "Activity" project_module_activity: "Activity"
project_module_boards: "Forums" project_module_boards: "Forums"
project_module_calendar: "Calendar" project_module_calendar: "Calendar"
@ -1365,10 +1373,11 @@ en:
create_successful: "The repository has been registered." create_successful: "The repository has been registered."
delete_sucessful: "The repository has been deleted." delete_sucessful: "The repository has been deleted."
destroy: destroy:
title: "Are you absolutely sure?" confirmation: "If you continue, this will permanently delete the managed repository of %{project_name}."
managed_text: "If you continue, this will permanently delete the managed repository of %{project_name}. This action cannot be reverted." info: "Deleting the repository is an irreversible action."
managed_path_note: "The following directory will be erased: %{path}"
linked_text: "You're about to remove the linked repository %{url} from the project %{project_name}.\nNote: This will NOT delete the contents of this repository, as it is not managed by OpenProject." linked_text: "You're about to remove the linked repository %{url} from the project %{project_name}.\nNote: This will NOT delete the contents of this repository, as it is not managed by OpenProject."
managed_path_note: "The following directory will be erased: %{path}"
title: "Do you really want to delete the repository %{name}?"
errors: errors:
build_failed: "Unable to create the repository with the selected configuration. %{reason}" build_failed: "Unable to create the repository with the selected configuration. %{reason}"
empty_repository: "The repository exists, but is empty. It does not contain any revisions yet." empty_repository: "The repository exists, but is empty. It does not contain any revisions yet."
@ -1587,16 +1596,13 @@ en:
text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?" text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
text_plugin_assets_writable: "Plugin assets directory writable" text_plugin_assets_writable: "Plugin assets directory writable"
text_powered_by: "Powered by %{link}" text_powered_by: "Powered by %{link}"
text_project_destroy_title: "Do you really want to delete this project?"
text_project_destroy_confirmation: "If you continue, the project %{identifier} and all related data will be permanently destroyed. Please check the box below if you want to continue."
text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter."
text_reassign: "Reassign to" text_reassign: "Reassign to work package:"
text_reassign_to: "work package:" text_reassign_to: "work package:"
text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_info: "eg. ^[A-Z0-9]+$"
text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped."
text_select_mail_notifications: "Select actions for which email notifications should be sent." text_select_mail_notifications: "Select actions for which email notifications should be sent."
text_status_changed_by_changeset: "Applied in changeset %{value}." text_status_changed_by_changeset: "Applied in changeset %{value}."
text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted."
text_time_logged_by_changeset: "Applied in changeset %{value}." text_time_logged_by_changeset: "Applied in changeset %{value}."
text_tip_work_package_begin_day: "work package beginning this day" text_tip_work_package_begin_day: "work package beginning this day"
text_tip_work_package_begin_end_day: "work package beginning and ending this day" text_tip_work_package_begin_end_day: "work package beginning and ending this day"
@ -1877,6 +1883,9 @@ en:
work_package: work_package:
updated_automatically_by_child_changes: | updated_automatically_by_child_changes: |
_Updated automatically by changing values within child work package %{child}_ _Updated automatically by changing values within child work package %{child}_
destroy:
info: "Deleting the work package is an irreversible action."
title: "Do you really want to delete the work package?"
nothing_to_preview: "Nothing to preview" nothing_to_preview: "Nothing to preview"

@ -545,13 +545,15 @@ a client should be able to discover further resources in the API.
*Note: Currently there is no list action for projects available.* *Note: Currently there is no list action for projects available.*
*A client will therefore have to know links to projects and can't (yet) discover them.* *A client will therefore have to know links to projects and can't (yet) discover them.*
| Link | Description | Type | Nullable | Supported operations | | Link | Description | Type | Nullable | Supported operations | Condition |
|:-------------------:| ------------------------------------------------ | ------------- | -------- | -------------------- | |:-------------------:| ------------------------------------------------ | --------------- | -------- | -------------------- | --------- |
| configuration | The configuration of this OpenProject instance | Configuration | | READ | | configuration | The configuration of this OpenProject instance | Configuration | | READ | |
| priorities | List of available priorities | Collection | | READ | | user | The user currently logged-in | User | | READ | logged in |
| statuses | List of available work package statuses | Collection | | READ | | userPreferences | The preferences of the logged-in user | UserPreference | | READ | logged in |
| types | List of available work package types | Collection | | READ | | priorities | List of available priorities | Collection | | READ | |
| workPackages | List of all work packages | Collection | | READ | | statuses | List of available work package statuses | Collection | | READ | |
| types | List of available work package types | Collection | | READ | |
| workPackages | List of all work packages | Collection | | READ | |
## Local Properties ## Local Properties
@ -570,6 +572,13 @@ a client should be able to discover further resources in the API.
"configuration" : { "configuration" : {
"href" : "/api/v3/configuration" "href" : "/api/v3/configuration"
}, },
"user": {
"href": "/api/v3/users/1",
"title": "John Sheppard - admin"
};
"userPreferences" : {
"href" : "/api/v3/my_preferences"
},
"priorities" : { "priorities" : {
"href" : "/api/v3/priorities" "href" : "/api/v3/priorities"
}, },
@ -3153,6 +3162,121 @@ Permanently deletes the specified user account.
"message": "The specified user does not exist." "message": "The specified user does not exist."
} }
# Group UserPreferences
## Linked Properties
| Link | Description | Type | Constraints | Supported operations |
|:---------:|----------------------------------------------------------| -------------- | --------------------- | -------------------- |
| self | This UserPreferences | UserPreferences| not null | READ |
| user | The user that this preference belongs to | User | not null | READ |
## Local Properties
| Property | Description | Type | Constraints | Supported operations |
|:----------------------:| -----------------------------------------------------------| ---------- | ----------- | -------------------- |
| hideMail | Hide mail address from other users | Boolean | | READ / WRITE |
| timeZone | Current selected time zone | String | | READ / WRITE |
| theme | Current selected theme | String | | READ / WRITE |
| commentSortDescending | Sort comments in descending order | Boolean | | READ / WRITE |
| warnOnLeavingUnsaved | Issue warning when leaving a page with unsaved text | Boolean | | READ / WRITE |
| accessibilityMode | Enable accessibility mode | Boolean | | READ / WRITE |
## UserPreferences [/api/v3/my_preferences]
+ Model
+ Body
{
"_type" : "UserPreferences",
"_links" : {
"self" : {
"href" : "/api/v3/my_preferences",
},
"user": {
"href": "/api/v3/users/1",
"title": "John Sheppard"
}
},
"hideMail" : false,
"timeZone" : "Europe/Berlin",
"theme" : "OpenProject",
"commentSortDescending" : true,
"warnOnLeavingUnsaved" : true,
"accessibilityMode" : false
}
## Show my preferences [GET]
+ Response 200 (application/hal+json)
[UserPreferences][]
+ Response 401 (application/hal+json)
Returned if no user is currently authenticated
+ Body
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:Unauthenticated",
"message": "You need to be authenticated to access this resource."
}
## Update UserPreferences [PATCH]
When calling this endpoint the client provides a single object, containing the properties that it wants to change, in the body.
+ Request (application/json)
{
"accessibilityMode": true,
"timeZone": "Europe/Paris"
}
+ Response 200 (application/hal+json)
[UserPreferences][]
+ Response 400 (application/hal+json)
Occurs when the client did not send a valid JSON object in the request body.
+ Body
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:InvalidRequestBody",
"message": "The request body was not a single JSON object."
}
+ Response 401 (application/hal+json)
Returned if no user is currently authenticated
+ Body
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:Unauthenticated",
"message": "You need to be authenticated to access this resource."
}
+ Response 422 (application/hal+json)
Returned if the update contains invalid properties.
Reasons are:
* Specifying an invalid type
* Using an unknown time zone
+ Body
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation",
"message": "Time zone is not set to one of the allowed values."
}
# Group Versions # Group Versions
## Linked Properties ## Linked Properties

@ -57,7 +57,7 @@ Feature: Deleting work packages
When I choose "Reassign" When I choose "Reassign"
And I fill in the id of work package "wp2" into "work package" And I fill in the id of work package "wp2" into "work package"
And I submit the form by the "Apply" button And I submit the form by the "Delete" button
Then I should be on the work packages index page of the project called "ecookbook" Then I should be on the work packages index page of the project called "ecookbook"

@ -30,7 +30,6 @@ module.exports = function($http, PathHelper) {
var VersionService = { var VersionService = {
// TODO: check if code invoked anywere
getVersions: function(projectIdentifier) { getVersions: function(projectIdentifier) {
var url; var url;
@ -46,7 +45,9 @@ module.exports = function($http, PathHelper) {
doQuery: function(url, params) { doQuery: function(url, params) {
return $http.get(url, { params: params }) return $http.get(url, { params: params })
.then(function(response){ .then(function(response){
return _.sortBy(response.data.versions, 'name'); return _.sortBy(response.data.versions, function(version) {
return version.name.toLowerCase();
});
}); });
} }
}; };

@ -32,7 +32,9 @@ module.exports = function() {
switch(field) { switch(field) {
case 'version': case 'version':
sorting = 'title'; sorting = function(field) {
return field.title.toLowerCase();
};
break; break;
default: default:
sorting = null; sorting = null;

@ -29,11 +29,10 @@
require 'spec_helper' require 'spec_helper'
require 'features/projects/projects_page' require 'features/projects/projects_page'
describe 'Delete project', type: :feature do describe 'Delete project', type: :feature, js: true do
let(:current_user) { FactoryGirl.create (:admin) } let(:current_user) { FactoryGirl.create (:admin) }
let(:project) { FactoryGirl.create(:project) } let(:project) { FactoryGirl.create(:project) }
let(:projects_page) { ProjectsPage.new(project) } let(:projects_page) { ProjectsPage.new(project) }
let(:delete_button) { find('input[type="submit"]') }
before do before do
allow(User).to receive(:current).and_return current_user allow(User).to receive(:current).and_return current_user
@ -41,23 +40,23 @@ describe 'Delete project', type: :feature do
projects_page.visit_confirm_destroy projects_page.visit_confirm_destroy
end end
it { expect(find('input#confirm')).not_to be_nil } describe 'disable delete w/o confirm' do
it { expect(page).to have_css('.danger-zone .button[disabled]') }
describe 'click delete w/o confirm' do
before do delete_button.click end
it { expect(find('.error', text: I18n.t(:notice_project_not_deleted))).not_to be_nil }
end end
describe 'click delete with confirm' do describe 'disable delete with wrong input' do
let(:confirm_checkbox) { find('input#confirm') } let(:input) { find('.danger-zone input') }
it do
before do input.set 'Not the project name'
confirm_checkbox.set true expect(page).to have_css('.danger-zone .button[disabled]')
end
delete_button.click
end end
it { expect(find('h2', text: 'Projects')).not_to be_nil } describe 'enable delete with correct input' do
let(:input) { find('.danger-zone input') }
it do
input.set project.name
expect(page).to have_css('.danger-zone .button:not([disabled])')
end
end end
end end

@ -62,9 +62,13 @@ describe 'Repository Settings', type: :feature, js: true do
find('a.icon-delete', text: I18n.t(:button_delete)).click find('a.icon-delete', text: I18n.t(:button_delete)).click
# Confirm the notification warning # Confirm the notification warning
warning = (type == 'managed') ? '-warning.-severe' : '-warning' if type == 'managed'
expect(page).to have_selector(".notification-box.#{warning}") expect(page).to have_selector('form.danger-zone')
find('.danger-zone .button').click
else
expect(page).to have_selector('.notification-box.-warning')
find('a', text: I18n.t(:button_delete)).click find('a', text: I18n.t(:button_delete)).click
end
vendor = find('select[name="scm_vendor"]') vendor = find('select[name="scm_vendor"]')
expect(vendor).not_to be_nil expect(vendor).not_to be_nil

@ -127,6 +127,49 @@ describe Version, type: :model do
expect(version1 <=> version2).to eql 1 expect(version1 <=> version2).to eql 1
end end
it 'is 0 if name and project are equal except for case' do
version1.project.name = version2.project.name.upcase
version1.name = version2.name.upcase
expect(version1 <=> version2).to be 0
end
it "is -1 if the project name is alphabetically before the other's project name ignoring case" do
version1.name = 'BBBB'
version1.project.name = 'aaaa'
version2.name = 'AAAA'
version2.project.name = 'BBBB'
expect(version1 <=> version2).to eql -1
end
it "is 1 if the project name is alphabetically after the other's project name ignoring case" do
version1.name = 'AAAA'
version1.project.name = 'BBBB'
version2.name = 'BBBB'
version2.project.name = 'aaaa'
expect(version1 <=> version2).to eql 1
end
it "is -1 if the project name is equal
and the version's name is alphabetically before the other's name ignoring case" do
version1.project.name = version2.project.name
version1.name = 'aaaa'
version2.name = 'BBBB'
expect(version1 <=> version2).to eql -1
end
it "is 1 if the project name is equal
and the version's name is alphabetically after the other's name ignoring case" do
version1.project.name = version2.project.name
version1.name = 'BBBB'
version2.name = 'aaaa'
expect(version1 <=> version2).to eql 1
end
end end
context '#projects' do context '#projects' do

Loading…
Cancel
Save