diff --git a/Gemfile b/Gemfile index 33403f3c0b..b9833e15b6 100644 --- a/Gemfile +++ b/Gemfile @@ -147,7 +147,7 @@ group :test do gem 'rspec-legacy_formatters' gem 'capybara', '~> 2.4.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 'selenium-webdriver', '~> 2.47.1' gem 'timecop', '~> 0.7.1' diff --git a/Gemfile.lock b/Gemfile.lock index 1d383c2821..30df1854b3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,8 +7,8 @@ GIT activerecord (>= 3.0.0) GIT - remote: git://github.com/finnlabs/capybara-select2.git - revision: 4088099ecc8a03c0c3155827a89a1a51057b0e38 + remote: git://github.com/goodwill/capybara-select2.git + revision: 585192e4bb0db8d52e761ab68f08c17294806447 specs: capybara-select2 (1.0.1) capybara @@ -30,7 +30,7 @@ GIT GIT remote: https://github.com/opf/openproject-translations.git - revision: a2a7d246c6212c0867970ee44c505f97e5c96ee3 + revision: d7c6e76ff6c3a56e42102a209900c2ad85af458f branch: dev specs: openproject-translations (5.0.0.pre.alpha) diff --git a/app/assets/stylesheets/content/_forms.sass b/app/assets/stylesheets/content/_forms.sass index 4a8a6f9528..d7aec7c913 100644 --- a/app/assets/stylesheets/content/_forms.sass +++ b/app/assets/stylesheets/content/_forms.sass @@ -66,37 +66,37 @@ $form--field-types: (text-field, text-area, select, check-box, radio-button, ran .form--section padding-top: 0px + margin-bottom: 0px + + & > * + padding-left: 1rem .form--section-title background-color: $content-form-danger-zone-bg-color color: $content-form-danger-zone-font-color !important - padding: 0.5rem !important + padding: 1rem !important margin: 0 0 1rem 0 em - font-style: italics - - - p - padding-left: 0.5rem + font-style: italic - &.danger-zone--warning - font-weight: bold - color: $content-form-danger-zone-bg-color + p.danger-zone--warning + font-weight: bold + color: $content-form-danger-zone-bg-color - span.icon, - span.icon-context - vertical-align: middle - &:before - padding-left: 0px - color: $content-form-danger-zone-bg-color + span.icon, + span.icon-context + vertical-align: middle + &:before + padding-left: 0px + color: $content-form-danger-zone-bg-color .danger-zone--verification display: flex input flex-basis: 50% - margin: 0 0.5rem 0 0.5rem + margin: 0 0.5rem 0 0 .button.-highlight background: $content-form-danger-zone-bg-color @@ -493,11 +493,6 @@ input[readonly].-clickable //prevent radio-buttons from being cut at the border padding: 0 1px -.form--radio-button - // Be on par with Foundation's margin-bottom on labels. - .form & - margin-bottom: 0.5rem - .form--grouping @include grid-block($wrap: true) align-items: center @@ -512,7 +507,7 @@ input[readonly].-clickable @include grid-content(2) @include grid-visible-overflow @extend %label-style - padding: 0 1rem 0 0 + padding: 0 1rem 0.5rem 0 font-size: $form-label-fontsize line-height: $base-line-height color: $form-label-color @@ -620,3 +615,8 @@ input[readonly].-clickable li, div padding: 0 + + +.form--text-field-container + input[type=text] + margin-bottom: 0 diff --git a/app/assets/stylesheets/content/_in_place_editing.sass b/app/assets/stylesheets/content/_in_place_editing.sass index 5fce25c469..1acf0aa969 100644 --- a/app/assets/stylesheets/content/_in_place_editing.sass +++ b/app/assets/stylesheets/content/_in_place_editing.sass @@ -161,6 +161,9 @@ &:last-of-type margin-bottom: 0 + .user-field-user-link + display: inline + .inplace-edit--preview border: 1px solid $inplace-edit--border-color padding: 0.375rem diff --git a/app/assets/stylesheets/content/_notifications.lsg b/app/assets/stylesheets/content/_notifications.lsg index afb774f9c2..8b919c17b9 100644 --- a/app/assets/stylesheets/content/_notifications.lsg +++ b/app/assets/stylesheets/content/_notifications.lsg @@ -46,17 +46,6 @@ ``` -## Severe warning - -``` -
- -
-

This is a warning with severe consequences. You should not ignore it.

-
-
-``` - ## Success ``` diff --git a/app/assets/stylesheets/content/_notifications.sass b/app/assets/stylesheets/content/_notifications.sass index 72c5842b24..a26c292f43 100644 --- a/app/assets/stylesheets/content/_notifications.sass +++ b/app/assets/stylesheets/content/_notifications.sass @@ -153,9 +153,6 @@ $nm-upload-box-padding: rem-calc(15) rem-calc(25) &::before @include messages-icon @extend %nm-icon-warning - &.-severe - background-color: $nm-color-error-background - border-color: $nm-color-error-border &.-info::before @include messages-icon diff --git a/app/assets/stylesheets/content/_select2.scss b/app/assets/stylesheets/content/_select2.scss index 6544c893cc..3eecbd50a0 100644 --- a/app/assets/stylesheets/content/_select2.scss +++ b/app/assets/stylesheets/content/_select2.scss @@ -121,7 +121,7 @@ $se2-input-height: rem-calc(34px); $se2-line-height: 30px; $se2-button-width: 28px; $se2-arrow-button-width: 18px; -$se2-results-max-height: 500px; +$se2-results-max-height: 200px; $se-multiple-input-line-height: 19px; $se-multiple-tags-line-height: 19px; @@ -192,14 +192,27 @@ $se2-width: 100%; .select2-drop { .select2-search { + &:before { + content: "\e0a1"; + font-family: "openproject-icon-font"; + color: #E0E0E0; + position: absolute; + top: 6px; + right: 16px; + } + .select2-input { - //possible background tweaks + background-image: none !important; + padding-right: 32px; + height: $se2-input-height !important; } } .select2-results { max-height: $se2-results-max-height; overflow: auto; + margin: 4px 9px 9px 0; + padding: 0 0 0 9px; .select2-highlighted { background-color: $se2-theme-selectable-color-highlighted; @@ -208,8 +221,19 @@ $se2-width: 100%; } } -input[type="text"].select2-input { - height: $se2-input-height; +.select2-drop:not(.project-search-results) { + input[type="text"].select2-input { + height: $se2-input-height !important; + } + + .select2-search { + margin: 10px 0 6px 0; + padding: 0 9px; + + input { + margin-bottom: 0; + } + } } .columns-modal-content { diff --git a/app/assets/stylesheets/content/_tables.sass b/app/assets/stylesheets/content/_tables.sass index 5c89e011e2..ab5b8f83b5 100644 --- a/app/assets/stylesheets/content/_tables.sass +++ b/app/assets/stylesheets/content/_tables.sass @@ -26,6 +26,14 @@ // 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 td padding: 3px 6px @@ -124,27 +132,26 @@ tr td.name a white-space: nowrap &.idnt td.project--hierarchy span - padding-left: 16px &.icon-context:before - padding: 9px 5px 0 10px + padding: 9px 5px 0 0px &.idnt-1 td.project--hierarchy - padding-left: 0.5em + @include calc-indentation(0) &.idnt-2 td.project--hierarchy - padding-left: 2em + @include calc-indentation(1) &.idnt-3 td.project--hierarchy - padding-left: 3.5em + @include calc-indentation(2) &.idnt-4 td.project--hierarchy - padding-left: 5em + @include calc-indentation(3) &.idnt-5 td.project--hierarchy - padding-left: 6.5em + @include calc-indentation(4) &.idnt-6 td.project--hierarchy - padding-left: 8em + @include calc-indentation(5) &.idnt-7 td.project--hierarchy - padding-left: 9.5em + @include calc-indentation(6) &.idnt-8 td.project--hierarchy - padding-left: 11em + @include calc-indentation(7) &.idnt-9 td.project--hierarchy - padding-left: 12.5em + @include calc-indentation(8) &.issue white-space: nowrap diff --git a/app/assets/stylesheets/layout/_top_menu.sass b/app/assets/stylesheets/layout/_top_menu.sass index a0e39cede4..dfae117492 100644 --- a/app/assets/stylesheets/layout/_top_menu.sass +++ b/app/assets/stylesheets/layout/_top_menu.sass @@ -225,17 +225,13 @@ input.top-menu-search--input background: $header-drop-down-projects-search-bg-color margin: 0 0 10px 0 - .select2-search:before - content: "\e0a1" - font-family: "openproject-icon-font" - color: #E0E0E0 - position: absolute - top: 24px - right: 20px + &:before + top: 21px + right: 26px - .select2-search input - margin: 10px 0px - padding: 9px 10px 9px 10px + .select2-search input.select2-input + margin: 10px 10px + padding: 9px 32px 9px 10px !important border: none border-radius: 25px width: 92% diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 9171fb2177..abf0360988 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -215,15 +215,9 @@ class ProjectsController < ApplicationController def destroy @project_to_destroy = @project - if params[:confirm] - @project_to_destroy.destroy - respond_to do |format| - format.html do redirect_to controller: '/admin', action: 'projects' end - end - else - flash[:error] = l(:notice_project_not_deleted) - redirect_to confirm_destroy_project_path(@project) - return + @project_to_destroy.destroy + respond_to do |format| + format.html do redirect_to controller: '/admin', action: 'projects' end end hide_project_in_layout diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 348aca3974..d602ca90b6 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -39,6 +39,7 @@ end class RepositoriesController < ApplicationController include PaginationHelper + include RepositoriesHelper menu_item :repository menu_item :settings, only: [:edit, :destroy_info] @@ -303,10 +304,6 @@ class RepositoriesController < ApplicationController end end - def settings_repository_tab_path - settings_project_path(@project, tab: 'repository') - end - def find_repository @repository = @project.repository (render_404; return false) unless @repository diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index d0729b3cdf..747301127c 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -28,6 +28,10 @@ #++ module RepositoriesHelper + def settings_repository_tab_path + settings_project_path(@project, tab: 'repository') + end + def format_revision(revision) if revision.respond_to? :format_identifier revision.format_identifier diff --git a/app/models/version.rb b/app/models/version.rb index 832e0a040a..9ed5febebc 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -205,7 +205,7 @@ class Version < ActiveRecord::Base # tie the comparison to the presentation as sorting is mostly # used within sorted tables. # 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 # Returns the sharings that +user+ can set the version to diff --git a/app/views/account/password_recovery.html.erb b/app/views/account/password_recovery.html.erb index 20d192c206..c67d2b70df 100644 --- a/app/views/account/password_recovery.html.erb +++ b/app/views/account/password_recovery.html.erb @@ -33,8 +33,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= styled_label_tag 'new_password', User.human_attribute_name(:new_password) %> -
- <%= styled_password_field_tag 'new_password', nil, :size => 25 %> +
+ <%= styled_password_field_tag 'new_password', nil, :size => 25, container_class: '-middle' %>
<%= password_complexity_requirements %> @@ -44,7 +44,7 @@ See doc/COPYRIGHT.rdoc for more details. <%= styled_label_tag 'new_password_confirmation', User.human_attribute_name(:new_password_confirmation) %>
- <%= styled_password_field_tag 'new_password_confirmation', nil, :size => 25 %> + <%= styled_password_field_tag 'new_password_confirmation', nil, :size => 25, container_class: '-middle' %>
diff --git a/app/views/projects/destroy_info.html.erb b/app/views/projects/destroy_info.html.erb index e163f86aaf..bb1591a659 100644 --- a/app/views/projects/destroy_info.html.erb +++ b/app/views/projects/destroy_info.html.erb @@ -26,21 +26,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See doc/COPYRIGHT.rdoc for more details. ++#%> -<%= toolbar title: l(:label_confirmation) %> -
-
-

<%= l(:text_project_destroy_title) %>
- <%= l(:text_project_destroy_confirmation, identifier: @project_to_destroy.identifier)%> +<%= styled_form_tag(project_path(@project_to_destroy), + class: 'danger-zone', + method: :delete) do %> +

+

+ <%= l('project.destroy.title', name: "#{h(@project_to_destroy)}").html_safe %> +

+

+ <%= l('project.destroy.confirmation', identifier: @project_to_destroy.identifier)%> <% if @project_to_destroy.descendants.any? %>
- <%= 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: "#{h(descendants)}").html_safe %> <% end %>

+

+ + <%= l('project.destroy.info')%> +

- <%= styled_form_tag(project_path(@project_to_destroy), method: :delete) do %> - - <%= submit_tag l(:button_delete), class: 'button' %> - <% end %> + <%= l('project.destroy.project_verification', name: "#{h(@project_to_destroy)}").html_safe %>

-
-
+
+ + <%= 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 %> +
+ +<% end %> diff --git a/app/views/repositories/destroy_info.html.erb b/app/views/repositories/destroy_info.html.erb index bdec0a349b..07f648af9a 100644 --- a/app/views/repositories/destroy_info.html.erb +++ b/app/views/repositories/destroy_info.html.erb @@ -26,28 +26,57 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See doc/COPYRIGHT.rdoc for more details. ++#%> -<%= toolbar title: l(:label_confirmation) %> -
-
-

<%= l('repositories.destroy.title') %>

+<% if @repository.managed? %> +<%= styled_form_tag(project_repository_path(@project), method: :delete, class: 'danger-zone') do %> +
+

+ <%= l('repositories.destroy.title', name: "#{h(@repository.root_url)}").html_safe %> +

- <% if @repository.managed? %> - <%= l('repositories.destroy.managed_text', project_name: @project.identifier) %> -
- <%= l('repositories.destroy.managed_path_note', path: @repository.root_url) %> - <% else %> - <%= simple_format l('repositories.destroy.linked_text', - project_name: @project.identifier, url: @repository.url) %> + <%= l('repositories.destroy.confirmation', project_name: @project.identifier) %> +
+ <%= l('repositories.destroy.managed_path_note', path: @repository.root_url) %> +

+

+ + <%= l('repositories.destroy.info') %> +

+
+ <%= styled_button_tag project_repository_path(@project), + method: :delete, + title: l(:button_delete), + disabled: false, + class: '-highlight' do %> + + <%= l(:button_delete) %> <% end %> +
+
+<% end %> +<% else %> +
+
+

<%= l('repositories.destroy.title', name: h(@repository.root_url)) %>

+

+ <%= simple_format l('repositories.destroy.linked_text', + project_name: @project.identifier, url: @repository.url) %>

<%= link_to project_repository_path(@project), - :method => :delete, - :class => 'button' do %> + title: l(:button_delete), + method: :delete, + class: 'button' do %> <%= l(:button_delete) %> <% end %> + <%= link_to settings_repository_tab_path, + title: l(:button_cancel), + class: 'button' do %> + + <%= l(:button_cancel) %> + <% end %>

+<% end %> diff --git a/app/views/work_packages/bulk/destroy.html.erb b/app/views/work_packages/bulk/destroy.html.erb index b3aa0a38a1..5561d91e5a 100644 --- a/app/views/work_packages/bulk/destroy.html.erb +++ b/app/views/work_packages/bulk/destroy.html.erb @@ -26,17 +26,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See doc/COPYRIGHT.rdoc for more details. ++#%> - -

<%= l(:label_confirmation) %>

- <%= 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| %> <%= hidden_field_tag 'ids[]', work_package.id %> <% end %> -
+
+

+ <%= l('work_package.destroy.title') %> +

<%= work_package_associations_to_address(associated) %> @@ -45,29 +45,42 @@ See doc/COPYRIGHT.rdoc for more details.

<%= fields_for :to_do do |f| %> -
+
<%= styled_label_tag :to_do_action_destroy, l(:text_destroy) %>
<%= styled_radio_button_tag 'to_do[action]', 'destroy' %>
-
+
<%=styled_label_tag 'to_do_action_nullify', l(:text_assign_to_project) %>
<%= styled_radio_button_tag 'to_do[action]' , 'nullify' %>
-
- <%= styled_label_tag 'to_do_action_reassign', l(:text_reassign) %> -
- <%= 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);' %> +
+
+
+ <%= styled_label_tag 'to_do_action_reassign', l(:text_reassign) %> +
+ <%= 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), 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);' %> +
+
<% end %> -
- - <%= styled_submit_tag l(:button_apply), class: '-highlight' %> +
+ <%= 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 %> +
<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 2c3113aa46..67bb19f304 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1320,6 +1320,14 @@ en: permission_view_wiki_edits: "View wiki history" 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_boards: "Forums" project_module_calendar: "Calendar" @@ -1365,10 +1373,11 @@ en: create_successful: "The repository has been registered." delete_sucessful: "The repository has been deleted." destroy: - title: "Are you absolutely sure?" - managed_text: "If you continue, this will permanently delete the managed repository of %{project_name}. This action cannot be reverted." - managed_path_note: "The following directory will be erased: %{path}" + confirmation: "If you continue, this will permanently delete the managed repository of %{project_name}." + info: "Deleting the repository is an irreversible action." 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: 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." @@ -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_plugin_assets_writable: "Plugin assets directory writable" 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_reassign: "Reassign to" + text_reassign: "Reassign to work package:" text_reassign_to: "work package:" 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_select_mail_notifications: "Select actions for which email notifications should be sent." 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_tip_work_package_begin_day: "work package beginning this day" text_tip_work_package_begin_end_day: "work package beginning and ending this day" @@ -1877,6 +1883,9 @@ en: work_package: updated_automatically_by_child_changes: | _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" diff --git a/doc/apiv3-documentation.apib b/doc/apiv3-documentation.apib index 35b952ecdc..eaecd68741 100644 --- a/doc/apiv3-documentation.apib +++ b/doc/apiv3-documentation.apib @@ -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.* *A client will therefore have to know links to projects and can't (yet) discover them.* -| Link | Description | Type | Nullable | Supported operations | -|:-------------------:| ------------------------------------------------ | ------------- | -------- | -------------------- | -| configuration | The configuration of this OpenProject instance | Configuration | | READ | -| priorities | List of available priorities | 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 | +| Link | Description | Type | Nullable | Supported operations | Condition | +|:-------------------:| ------------------------------------------------ | --------------- | -------- | -------------------- | --------- | +| configuration | The configuration of this OpenProject instance | Configuration | | READ | | +| user | The user currently logged-in | User | | READ | logged in | +| userPreferences | The preferences of the logged-in user | UserPreference | | READ | logged in | +| priorities | List of available priorities | 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 @@ -570,6 +572,13 @@ a client should be able to discover further resources in the API. "configuration" : { "href" : "/api/v3/configuration" }, + "user": { + "href": "/api/v3/users/1", + "title": "John Sheppard - admin" + }; + "userPreferences" : { + "href" : "/api/v3/my_preferences" + }, "priorities" : { "href" : "/api/v3/priorities" }, @@ -3153,6 +3162,121 @@ Permanently deletes the specified user account. "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 ## Linked Properties diff --git a/features/work_packages/destroy.feature b/features/work_packages/destroy.feature index 3fbd8d92e0..42f69c8568 100644 --- a/features/work_packages/destroy.feature +++ b/features/work_packages/destroy.feature @@ -57,7 +57,7 @@ Feature: Deleting work packages When I choose "Reassign" 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" diff --git a/frontend/app/services/version-service.js b/frontend/app/services/version-service.js index 592844b4c2..45685cd007 100644 --- a/frontend/app/services/version-service.js +++ b/frontend/app/services/version-service.js @@ -30,7 +30,6 @@ module.exports = function($http, PathHelper) { var VersionService = { - // TODO: check if code invoked anywere getVersions: function(projectIdentifier) { var url; @@ -46,7 +45,9 @@ module.exports = function($http, PathHelper) { doQuery: function(url, params) { return $http.get(url, { params: params }) .then(function(response){ - return _.sortBy(response.data.versions, 'name'); + return _.sortBy(response.data.versions, function(version) { + return version.name.toLowerCase(); + }); }); } }; diff --git a/frontend/app/work_packages/services/work-package-field-configuration-service.js b/frontend/app/work_packages/services/work-package-field-configuration-service.js index 271fbe528f..2b9bda5a96 100644 --- a/frontend/app/work_packages/services/work-package-field-configuration-service.js +++ b/frontend/app/work_packages/services/work-package-field-configuration-service.js @@ -32,7 +32,9 @@ module.exports = function() { switch(field) { case 'version': - sorting = 'title'; + sorting = function(field) { + return field.title.toLowerCase(); + }; break; default: sorting = null; diff --git a/spec/features/projects/delete_projects_spec.rb b/spec/features/projects/delete_projects_spec.rb index 005eed7f67..d8fae08c50 100644 --- a/spec/features/projects/delete_projects_spec.rb +++ b/spec/features/projects/delete_projects_spec.rb @@ -29,11 +29,10 @@ require 'spec_helper' 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(:project) { FactoryGirl.create(:project) } let(:projects_page) { ProjectsPage.new(project) } - let(:delete_button) { find('input[type="submit"]') } before do allow(User).to receive(:current).and_return current_user @@ -41,23 +40,23 @@ describe 'Delete project', type: :feature do projects_page.visit_confirm_destroy end - it { expect(find('input#confirm')).not_to be_nil } - - 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 } + describe 'disable delete w/o confirm' do + it { expect(page).to have_css('.danger-zone .button[disabled]') } end - describe 'click delete with confirm' do - let(:confirm_checkbox) { find('input#confirm') } - - before do - confirm_checkbox.set true - - delete_button.click + describe 'disable delete with wrong input' do + let(:input) { find('.danger-zone input') } + it do + input.set 'Not the project name' + expect(page).to have_css('.danger-zone .button[disabled]') 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 diff --git a/spec/features/repositories/repository_settings_spec.rb b/spec/features/repositories/repository_settings_spec.rb index dcacb6cb67..923f2d0e2a 100644 --- a/spec/features/repositories/repository_settings_spec.rb +++ b/spec/features/repositories/repository_settings_spec.rb @@ -62,9 +62,13 @@ describe 'Repository Settings', type: :feature, js: true do find('a.icon-delete', text: I18n.t(:button_delete)).click # Confirm the notification warning - warning = (type == 'managed') ? '-warning.-severe' : '-warning' - expect(page).to have_selector(".notification-box.#{warning}") - find('a', text: I18n.t(:button_delete)).click + if type == 'managed' + 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 + end vendor = find('select[name="scm_vendor"]') expect(vendor).not_to be_nil diff --git a/spec/models/version_spec.rb b/spec/models/version_spec.rb index 887c7ac558..988e859585 100644 --- a/spec/models/version_spec.rb +++ b/spec/models/version_spec.rb @@ -127,6 +127,49 @@ describe Version, type: :model do expect(version1 <=> version2).to eql 1 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 context '#projects' do