Merge pull request #7444 from opf/feature/30285-30286-Create-new-admin-menu-nodes

[30285][30286] Rework of admin menu structure
pull/7453/head
Henriette Dinger 5 years ago committed by GitHub
commit 0a338708f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/assets/stylesheets/content/_index.sass
  2. 24
      app/assets/stylesheets/content/menus/_menu_blocks.sass
  3. 4
      app/controllers/admin_controller.rb
  4. 25
      app/controllers/authentication_controller.rb
  5. 26
      app/controllers/settings_controller.rb
  6. 57
      app/controllers/users_settings_controller.rb
  7. 57
      app/controllers/work_packages/settings_controller.rb
  8. 3
      app/helpers/settings_helper.rb
  9. 51
      app/services/settings/update_service.rb
  10. 41
      app/views/admin/index.html.erb
  11. 2
      app/views/admin/plugins.html.erb
  12. 107
      app/views/attribute_help_texts/_tab.html.erb
  13. 85
      app/views/attribute_help_texts/index.html.erb
  14. 4
      app/views/authentication/authentication_settings.html.erb
  15. 5
      app/views/users/users_settings.html.erb
  16. 0
      app/views/work_packages/settings/_enterprise_feature_hint.html.erb
  17. 9
      app/views/work_packages/settings/work_package_tracking.html.erb
  18. 91
      config/initializers/menus.rb
  19. 2
      config/locales/en.yml
  20. 17
      config/routes.rb
  21. 3
      lib/redmine/menu_manager/menu_helper.rb
  22. 1
      modules/avatars/config/locales/en.yml
  23. 3
      modules/avatars/lib/open_project/avatars/engine.rb
  24. 3
      modules/backlogs/app/views/shared/_settings.html.erb
  25. 3
      modules/backlogs/lib/open_project/backlogs/engine.rb
  26. 5
      modules/costs/lib/open_project/costs/engine.rb
  27. 2
      modules/ldap_groups/config/locales/en.yml
  28. 3
      modules/ldap_groups/lib/open_project/ldap_groups/engine.rb
  29. 5
      modules/openid_connect/lib/open_project/openid_connect/engine.rb
  30. 2
      modules/pdf_export/lib/open_project/pdf_export/engine.rb
  31. 5
      modules/two_factor_authentication/lib/open_project/two_factor_authentication/engine.rb
  32. 88
      spec/controllers/admin_controller_spec.rb
  33. 6
      spec/features/auth/consent_auth_stage_spec.rb
  34. 4
      spec/features/menu_items/admin_menu_item_spec.rb
  35. 6
      spec/features/users/delete_spec.rb
  36. 5
      spec/features/users/password_change_spec.rb
  37. 2
      spec/views/authentication/authentication_settings.html.erb_spec.rb
  38. 102
      spec_legacy/functional/admin_controller_spec.rb

@ -73,4 +73,5 @@
@import content/version @import content/version
@import content/menus/_project_autocompletion @import content/menus/_project_autocompletion
@import content/menus/_menu_blocks
@import content/editor/index @import content/editor/index

@ -0,0 +1,24 @@
.menu-blocks--container
display: grid
grid-template: repeat(auto-fit, 200px) / repeat(auto-fit, 200px)
grid-auto-rows: 200px
grid-column-gap: 30px
grid-row-gap: 30px
.menu-block
border-radius: 3px
display: grid
grid-template: 110px 1fr / 1fr
grid-row-gap: 5px
justify-items: center
background: #cccccc30
&:hover
outline: 1px solid grey
text-decoration: none
.menu-block--icon
font-size: 50px
align-self: end
&:before
padding-left: 10px

@ -34,9 +34,11 @@ class AdminController < ApplicationController
menu_item :plugins, only: [:plugins] menu_item :plugins, only: [:plugins]
menu_item :info, only: [:info] menu_item :info, only: [:info]
menu_item :admin_overview, only: [:index]
def index def index
redirect_to controller: 'users', action: 'index' @menu_nodes = Redmine::MenuManager.items(:admin_menu).children
@menu_nodes.shift
end end
def projects def projects

@ -30,6 +30,8 @@
class AuthenticationController < ApplicationController class AuthenticationController < ApplicationController
before_action :disable_api before_action :disable_api
before_action :require_login before_action :require_login
layout 'admin'
menu_item :authentication_settings
accept_key_auth :index accept_key_auth :index
@ -38,4 +40,27 @@ class AuthenticationController < ApplicationController
format.html format.html
end end
end end
def edit
if params[:settings]
Settings::UpdateService
.new(user: current_user)
.call(settings: permitted_params.settings.to_h)
flash[:notice] = l(:notice_successful_update)
redirect_to action: 'authentication_settings'
end
end
def authentication_settings
render 'authentication_settings'
end
def default_breadcrumb
t(:label_authentication)
end
def show_local_breadcrumb
true
end
end end

@ -29,10 +29,20 @@
class SettingsController < ApplicationController class SettingsController < ApplicationController
layout 'admin' layout 'admin'
menu_item :settings
before_action :require_admin before_action :require_admin
current_menu_item [:index, :edit] do
:settings
end
current_menu_item :plugin do |controller|
plugin = Redmine::Plugin.find(controller.params[:id])
plugin.settings[:menu_item] || :settings
rescue Redmine::PluginNotFound
:settings
end
def index def index
edit edit
render action: 'edit' render action: 'edit'
@ -41,17 +51,9 @@ class SettingsController < ApplicationController
def edit def edit
@notifiables = Redmine::Notifiable.all @notifiables = Redmine::Notifiable.all
if request.post? && params[:settings] if request.post? && params[:settings]
permitted_params.settings.to_h.each do |name, value| Settings::UpdateService
if value.is_a?(Array) .new(user: current_user)
# remove blank values in array settings .call(settings: permitted_params.settings.to_h)
value.delete_if(&:blank?)
elsif value.is_a?(Hash)
value.delete_if { |_, v| v.blank? }
else
value = value.strip
end
Setting[name] = value
end
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_to action: 'edit', tab: params[:tab] redirect_to action: 'edit', tab: params[:tab]

@ -0,0 +1,57 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
class UsersSettingsController < ::ApplicationController
layout 'admin'
menu_item :user_settings
def index
render 'users/users_settings'
end
def edit
if params[:settings]
Settings::UpdateService
.new(user: current_user)
.call(settings: permitted_params.settings.to_h)
flash[:notice] = l(:notice_successful_update)
redirect_to action: 'index'
end
end
def default_breadcrumb
t(:label_user_settings)
end
def show_local_breadcrumb
true
end
end

@ -0,0 +1,57 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
class WorkPackages::SettingsController < ::ApplicationController
layout 'admin'
menu_item :work_packages_setting
def index
render 'work_packages/settings/work_package_tracking'
end
def edit
if params[:settings]
Settings::UpdateService
.new(user: current_user)
.call(settings: permitted_params.settings.to_h)
flash[:notice] = l(:notice_successful_update)
redirect_to action: 'index'
end
end
def default_breadcrumb
t(:label_work_package_tracking)
end
def show_local_breadcrumb
true
end
end

@ -36,10 +36,7 @@ module SettingsHelper
[ [
{ name: 'general', partial: 'settings/general', label: :label_general }, { name: 'general', partial: 'settings/general', label: :label_general },
{ name: 'display', partial: 'settings/display', label: :label_display }, { name: 'display', partial: 'settings/display', label: :label_display },
{ name: 'authentication', partial: 'settings/authentication', label: :label_authentication },
{ name: 'users', partial: 'settings/users', label: :label_user_plural },
{ name: 'projects', partial: 'settings/projects', label: :label_project_plural }, { name: 'projects', partial: 'settings/projects', label: :label_project_plural },
{ name: 'work_packages', partial: 'settings/work_packages', label: :label_work_package_tracking },
{ name: 'notifications', partial: 'settings/notifications', label: Proc.new { User.human_attribute_name(:mail_notification) } }, { name: 'notifications', partial: 'settings/notifications', label: Proc.new { User.human_attribute_name(:mail_notification) } },
{ name: 'mail_handler', partial: 'settings/mail_handler', label: :label_incoming_emails }, { name: 'mail_handler', partial: 'settings/mail_handler', label: :label_incoming_emails },
{ name: 'repositories', partial: 'settings/repositories', label: :label_repository_plural } { name: 'repositories', partial: 'settings/repositories', label: :label_repository_plural }

@ -0,0 +1,51 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2019 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class Settings::UpdateService < ::BaseServices::Update
attr_accessor :user
def initialize(user)
self.user = user
end
def call(settings:)
settings.each do |name, value|
if value.is_a?(Array)
# remove blank values in array settings
value.delete_if(&:blank?)
elsif value.is_a?(Hash)
value.delete_if { |_, v| v.blank? }
else
value = value.strip
end
Setting[name] = value
end
end
end

@ -0,0 +1,41 @@
<%#-- copyright
OpenProject is a project management system.
Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2017 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See docs/COPYRIGHT.rdoc for more details.
++#%>
<% html_title(l(:label_administration), l(:label_overview)) -%>
<%= toolbar title: t(:label_overview) %>
<div class="menu-blocks--container">
<% @menu_nodes.each do |menu_node| -%>
<%= link_to menu_node.url, { class: 'menu-block' } do %>
<%= op_icon('menu-block--icon ' + menu_node.icon) %>
<span class="menu-block--title"> <%= menu_node.caption %> </span>
<% end %>
<% end %>
</div>

@ -36,7 +36,6 @@ See docs/COPYRIGHT.rdoc for more details.
<col> <col>
<col> <col>
<col> <col>
<col>
</colgroup> </colgroup>
<tbody> <tbody>
<% @plugins.each do |plugin| %> <% @plugins.each do |plugin| %>
@ -53,7 +52,6 @@ See docs/COPYRIGHT.rdoc for more details.
<%= h plugin.version %> <%= h plugin.version %>
<% end %> <% end %>
</td> </td>
<td class="configure"><%= link_to(l(:button_configure), controller: '/settings', action: 'plugin', id: plugin.id) if plugin.configurable? %></td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>

@ -1,107 +0,0 @@
<%#-- copyright
OpenProject is a project management system.
Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2017 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See docs/COPYRIGHT.rdoc for more details.
++#%>
<% entries = @texts_by_type[tab[:name]] || [] %>
<% if entries.any? %>
<div class="generic-table--container">
<div class="generic-table--results-container">
<table class="generic-table">
<colgroup>
<col highlight-col>
<col highlight-col>
<col highlight-col>
<col>
</colgroup>
<thead>
<tr>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= AttributeHelpText.human_attribute_name(:attribute_name) %>
</span>
</div>
</div>
</th>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= AttributeHelpText.human_attribute_name(:help_text) %>
</span>
</div>
</div>
</th>
<th>
<div class="generic-table--empty-header"></div>
</th>
</tr>
</thead>
<tbody>
<% entries.each do |attribute_help_text| -%>
<tr class="attribute-help-text--entry">
<td>
<%= link_to h(attribute_help_text.attribute_caption),
edit_attribute_help_text_path(attribute_help_text) %>
</td>
<td>
<attribute-help-text
data-help-text-id="<%= attribute_help_text.id %>"
data-attribute="<%= attribute_help_text.attribute_name %>"
data-attribute-scope="'<%= attribute_help_text.attribute_scope %>'"
data-additional-label="<%= t(:'attribute_help_texts.show_preview') %>">
</attribute-help-text>
</td>
<td class="buttons">
<%= link_to(
op_icon('icon icon-delete'),
(attribute_help_text_path(attribute_help_text)),
method: :delete,
data: { confirm: I18n.t(:text_are_you_sure) },
title: t(:button_delete)) %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<% else %>
<%= no_results_box %>
<% end %>
<div class="generic-table--action-buttons">
<%= link_to new_attribute_help_text_path(name: tab[:name]),
{ class: 'attribute-help-texts--create-button button -alt-highlight',
aria: {label: t(:'attribute_help_texts.add_new')},
title: t(:'attribute_help_texts.add_new')} do %>
<%= op_icon('button--icon icon-add') %>
<span class="button--text"><%= t('activerecord.models.attribute_help_text') %></span>
<% end %>
</div>

@ -34,9 +34,84 @@ See docs/COPYRIGHT.rdoc for more details.
</div> </div>
</div> </div>
<%= render_tabs [
{ name: 'WorkPackage', partial: 'attribute_help_texts/tab', label: :label_work_package },
]
%>
<% html_title(t(:label_administration), t(:'attribute_help_texts.label_plural')) -%> <% html_title(t(:label_administration), t(:'attribute_help_texts.label_plural')) -%>
<% name = 'WorkPackage' %>
<% entries = @texts_by_type[name] || [] %>
<% if entries.any? %>
<div class="generic-table--container">
<div class="generic-table--results-container">
<table class="generic-table">
<colgroup>
<col highlight-col>
<col highlight-col>
<col highlight-col>
<col>
</colgroup>
<thead>
<tr>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= AttributeHelpText.human_attribute_name(:attribute_name) %>
</span>
</div>
</div>
</th>
<th>
<div class="generic-table--sort-header-outer">
<div class="generic-table--sort-header">
<span>
<%= AttributeHelpText.human_attribute_name(:help_text) %>
</span>
</div>
</div>
</th>
<th>
<div class="generic-table--empty-header"></div>
</th>
</tr>
</thead>
<tbody>
<% entries.each do |attribute_help_text| -%>
<tr class="attribute-help-text--entry">
<td>
<%= link_to h(attribute_help_text.attribute_caption),
edit_attribute_help_text_path(attribute_help_text) %>
</td>
<td>
<attribute-help-text
data-help-text-id="<%= attribute_help_text.id %>"
data-attribute="<%= attribute_help_text.attribute_name %>"
data-attribute-scope="'<%= attribute_help_text.attribute_scope %>'"
data-additional-label="<%= t(:'attribute_help_texts.show_preview') %>">
</attribute-help-text>
</td>
<td class="buttons">
<%= link_to(
op_icon('icon icon-delete'),
(attribute_help_text_path(attribute_help_text)),
method: :delete,
data: { confirm: I18n.t(:text_are_you_sure) },
title: t(:button_delete)) %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<% else %>
<%= no_results_box %>
<% end %>
<div class="generic-table--action-buttons">
<%= link_to new_attribute_help_text_path(name: name),
{ class: 'attribute-help-texts--create-button button -alt-highlight',
aria: {label: t(:'attribute_help_texts.add_new')},
title: t(:'attribute_help_texts.add_new')} do %>
<%= op_icon('button--icon icon-add') %>
<span class="button--text"><%= t('activerecord.models.attribute_help_text') %></span>
<% end %>
</div>

@ -26,9 +26,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See docs/COPYRIGHT.rdoc for more details. See docs/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<% html_title l(:label_administration), l(:label_authentication) -%>
<%= styled_form_tag({ action: 'edit', tab: 'authentication' }) do %> <%= toolbar title: l(:label_authentication) %>
<%= styled_form_tag({ action: :edit }) do %>
<section class="form--section"> <section class="form--section">
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= I18n.t(:general, scope: [:settings]) %></legend> <legend class="form--fieldset-legend"><%= I18n.t(:general, scope: [:settings]) %></legend>

@ -26,8 +26,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See docs/COPYRIGHT.rdoc for more details. See docs/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<% html_title l(:label_administration), l(:label_user_settings) -%>
<%= styled_form_tag({ action: 'edit', tab: 'users' }, class: 'admin-settings--form') do %> <%= toolbar title: l(:label_user_settings) %>
<%= styled_form_tag({ action: 'edit' }, class: 'admin-settings--form') do %>
<fieldset class="form--fieldset"> <fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= t(:'settings.user.default_preferences')%></legend> <legend class="form--fieldset-legend"><%= t(:'settings.user.default_preferences')%></legend>

@ -26,7 +26,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See docs/COPYRIGHT.rdoc for more details. See docs/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<%= styled_form_tag({action: 'edit', tab: 'work_packages'}) do %>
<% html_title l(:label_administration), l(:label_work_package_tracking) -%>
<%= toolbar title: l(:label_work_package_tracking) %>
<%= styled_form_tag({action: 'edit'}) do %>
<section class="form--section"> <section class="form--section">
<div class="form--field"><%= setting_check_box :cross_project_work_package_relations %></div> <div class="form--field"><%= setting_check_box :cross_project_work_package_relations %></div>
<div class="form--field"><%= setting_check_box :work_package_group_assignment %></div> <div class="form--field"><%= setting_check_box :work_package_group_assignment %></div>
@ -44,7 +49,7 @@ See docs/COPYRIGHT.rdoc for more details.
options[:disabled] = 'disabled' unless EnterpriseToken.allows_to?(:conditional_highlighting) options[:disabled] = 'disabled' unless EnterpriseToken.allows_to?(:conditional_highlighting)
} }
%> %>
<%= render partial: 'enterprise_feature_hint', <%= render partial: 'work_packages/settings/enterprise_feature_hint',
locals: { locals: {
ee_feature: :conditional_highlighting, ee_feature: :conditional_highlighting,
explanation: t('js.work_packages.table_configuration.upsale.attribute_highlighting'), explanation: t('js.work_packages.table_configuration.upsale.attribute_highlighting'),

@ -75,7 +75,7 @@ Redmine::MenuManager.map :account_menu do |menu|
{ controller: '/my', action: 'account' }, { controller: '/my', action: 'account' },
if: Proc.new { User.current.logged? } if: Proc.new { User.current.logged? }
menu.push :administration, menu.push :administration,
{ controller: '/users', action: 'index' }, { controller: '/admin', action: 'index' },
if: Proc.new { User.current.admin? } if: Proc.new { User.current.admin? }
menu.push :logout, menu.push :logout,
:signout_path, :signout_path,
@ -123,35 +123,67 @@ Redmine::MenuManager.map :my_menu do |menu|
end end
Redmine::MenuManager.map :admin_menu do |menu| Redmine::MenuManager.map :admin_menu do |menu|
menu.push :admin_overview,
{ controller: '/admin' },
caption: :label_overview,
icon: 'icon2 icon-home',
first: true
menu.push :users_and_permissions,
{ controller: '/users_settings' },
caption: :label_user_and_permission,
icon: 'icon2 icon-group'
menu.push :user_settings,
{ controller: '/users_settings' },
caption: :label_settings,
parent: :users_and_permissions
menu.push :users, menu.push :users,
{ controller: '/users' }, { controller: '/users' },
caption: :label_user_plural, caption: :label_user_plural,
icon: 'icon2 icon-user' parent: :users_and_permissions
menu.push :groups, menu.push :groups,
{ controller: '/groups' }, { controller: '/groups' },
caption: :label_group_plural, caption: :label_group_plural,
icon: 'icon2 icon-group' parent: :users_and_permissions
menu.push :roles, menu.push :roles,
{ controller: '/roles' }, { controller: '/roles' },
caption: :label_role_and_permissions, caption: :label_role_and_permissions,
icon: 'icon2 icon-settings' parent: :users_and_permissions
menu.push :user_avatars,
{ controller: '/settings', action: 'plugin', id: :openproject_avatars },
caption: :label_avatar_plural,
parent: :users_and_permissions
menu.push :admin_work_packages,
{ controller: '/work_packages/settings' },
caption: :label_work_package_plural,
icon: 'icon2 icon-view-timeline'
menu.push :work_packages_setting,
{ controller: '/work_packages/settings' },
caption: :label_settings,
parent: :admin_work_packages
menu.push :types, menu.push :types,
{ controller: '/types' }, { controller: '/types' },
caption: :label_work_package_types, caption: :label_type_plural,
icon: 'icon2 icon-types' parent: :admin_work_packages
menu.push :statuses, menu.push :statuses,
{ controller: '/statuses' }, { controller: '/statuses' },
caption: :label_work_package_status_plural, caption: :label_status,
icon: 'icon2 icon-flag', parent: :admin_work_packages,
html: { class: 'statuses' } html: { class: 'statuses' }
menu.push :workflows, menu.push :workflows,
{ controller: '/workflows', action: 'edit' }, { controller: '/workflows', action: 'edit' },
caption: Proc.new { Workflow.model_name.human }, caption: Proc.new { Workflow.model_name.human },
icon: 'icon2 icon-workflow' parent: :admin_work_packages
menu.push :custom_fields, menu.push :custom_fields,
{ controller: '/custom_fields' }, { controller: '/custom_fields' },
@ -162,12 +194,12 @@ Redmine::MenuManager.map :admin_menu do |menu|
menu.push :custom_actions, menu.push :custom_actions,
{ controller: '/custom_actions' }, { controller: '/custom_actions' },
caption: :'custom_actions.plural', caption: :'custom_actions.plural',
icon: 'icon2 icon-play' parent: :admin_work_packages
menu.push :attribute_help_texts, menu.push :attribute_help_texts,
{ controller: '/attribute_help_texts' }, { controller: '/attribute_help_texts' },
caption: :'attribute_help_texts.label_plural', caption: :'attribute_help_texts.label_plural',
icon: 'icon2 icon-help2', parent: :admin_work_packages,
if: Proc.new { if: Proc.new {
EnterpriseToken.allows_to?(:attribute_help_texts) EnterpriseToken.allows_to?(:attribute_help_texts)
} }
@ -181,16 +213,27 @@ Redmine::MenuManager.map :admin_menu do |menu|
caption: :label_system_settings, caption: :label_system_settings,
icon: 'icon2 icon-settings2' icon: 'icon2 icon-settings2'
menu.push :authentication,
{ controller: '/authentication', action: 'authentication_settings' },
caption: :label_authentication,
icon: 'icon2 icon-two-factor-authentication'
menu.push :authentication_settings,
{ controller: '/authentication', action: 'authentication_settings' },
caption: :label_settings,
parent: :authentication
menu.push :ldap_authentication, menu.push :ldap_authentication,
{ controller: '/ldap_auth_sources', action: 'index' }, { controller: '/ldap_auth_sources', action: 'index' },
parent: :authentication,
html: { class: 'server_authentication' }, html: { class: 'server_authentication' },
icon: 'icon2 icon-flag', last: true,
if: proc { !OpenProject::Configuration.disable_password_login? } if: proc { !OpenProject::Configuration.disable_password_login? }
menu.push :oauth_applications, menu.push :oauth_applications,
{ controller: '/oauth/applications', action: 'index' }, { controller: '/oauth/applications', action: 'index' },
html: { class: 'oauth_applications' }, parent: :authentication,
icon: 'icon2 icon-key' html: { class: 'oauth_applications' }
menu.push :announcements, menu.push :announcements,
{ controller: '/announcements', action: 'edit' }, { controller: '/announcements', action: 'edit' },
@ -223,6 +266,26 @@ Redmine::MenuManager.map :admin_menu do |menu|
caption: :label_enterprise_edition, caption: :label_enterprise_edition,
icon: 'icon2 icon-headset', icon: 'icon2 icon-headset',
if: proc { OpenProject::Configuration.ee_manager_visible? } if: proc { OpenProject::Configuration.ee_manager_visible? }
menu.push :admin_costs,
{ controller: '/settings', action: 'plugin', id: :openproject_costs },
caption: :label_cost_object_plural,
icon: 'icon2 icon-budget'
menu.push :costs_setting,
{ controller: '/settings', action: 'plugin', id: :openproject_costs },
caption: :label_settings,
parent: :admin_costs
menu.push :admin_backlogs,
{ controller: '/settings', action: 'plugin', id: :openproject_backlogs },
caption: :label_backlogs,
icon: 'icon2 icon-backlogs'
menu.push :backlogs_settings,
{ controller: '/settings', action: 'plugin', id: :openproject_backlogs },
caption: :label_settings,
parent: :admin_backlogs
end end
Redmine::MenuManager.map :project_menu do |menu| Redmine::MenuManager.map :project_menu do |menu|

@ -1608,6 +1608,7 @@ en:
label_used_by_types: "Used by types" label_used_by_types: "Used by types"
label_used_in_projects: "Used in projects" label_used_in_projects: "Used in projects"
label_user: "User" label_user: "User"
label_user_and_permission: "Users & Permissions"
label_user_named: "User %{name}" label_user_named: "User %{name}"
label_user_activity: "%{value}'s activity" label_user_activity: "%{value}'s activity"
label_user_anonymous: "Anonymous" label_user_anonymous: "Anonymous"
@ -1620,6 +1621,7 @@ en:
label_user_new: "New user" label_user_new: "New user"
label_user_plural: "Users" label_user_plural: "Users"
label_user_search: "Search for user" label_user_search: "Search for user"
label_user_settings: "User settings"
label_version_new: "New version" label_version_new: "New version"
label_version_plural: "Versions" label_version_plural: "Versions"
label_version_sharing_descendants: "With subprojects" label_version_sharing_descendants: "With subprojects"

@ -411,6 +411,11 @@ OpenProject::Application.routes.draw do
get '/bulk' => 'bulk#destroy' get '/bulk' => 'bulk#destroy'
end end
scope controller: 'work_packages/settings' do
get 'work_package_tracking' => 'work_packages/settings#index'
post 'work_package_tracking' => 'work_packages/settings#edit'
end
resources :work_packages, only: [:index] do resources :work_packages, only: [:index] do
get :column_data, on: :collection # TODO move to API get :column_data, on: :collection # TODO move to API
@ -464,6 +469,11 @@ OpenProject::Application.routes.draw do
end end
end end
scope controller: 'users_settings' do
get 'users_settings' => 'users_settings#index'
post 'users_settings' => 'users_settings#edit'
end
resources :forums, only: [] do resources :forums, only: [] do
resources :topics, controller: 'messages', except: [:index], shallow: true do resources :topics, controller: 'messages', except: [:index], shallow: true do
member do member do
@ -539,7 +549,12 @@ OpenProject::Application.routes.draw do
patch 'user_settings', action: 'user_settings' patch 'user_settings', action: 'user_settings'
end end
get 'authentication' => 'authentication#index'
scope controller: 'authentication' do
get 'authentication' => 'authentication#index'
get 'authentication_settings' => 'authentication#authentication_settings'
post 'authentication_settings' => 'authentication#edit'
end
resources :colors do resources :colors do
member do member do

@ -61,6 +61,7 @@ module Redmine::MenuManager::MenuHelper
links = [] links = []
menu_items = first_level_menu_items_for(menu, project) do |node| menu_items = first_level_menu_items_for(menu, project) do |node|
@menu = menu
links << render_menu_node(node, project) links << render_menu_node(node, project)
end end
@ -145,7 +146,7 @@ module Redmine::MenuManager::MenuHelper
content_tag :li, html_options do content_tag :li, html_options do
# Standard children # Standard children
standard_children_list = node.children.map { |child| standard_children_list = node.children.map { |child|
render_menu_node(child, project) render_menu_node(child, project) if visible_node?(@menu, child)
}.join.html_safe }.join.html_safe
# Unattached children # Unattached children

@ -1,6 +1,7 @@
# English strings go here # English strings go here
en: en:
label_avatar: "Avatar" label_avatar: "Avatar"
label_avatar_plural: "Avatars"
label_current_avatar: "Current Avatar" label_current_avatar: "Current Avatar"
label_choose_avatar: "Choose Avatar from file" label_choose_avatar: "Choose Avatar from file"
message_avatar_uploaded: "Avatar changed successfully." message_avatar_uploaded: "Avatar changed successfully."

@ -30,7 +30,8 @@ module OpenProject::Avatars
enable_gravatars: true, enable_gravatars: true,
enable_local_avatars: true enable_local_avatars: true
}, },
partial: 'settings/openproject_avatars' partial: 'settings/openproject_avatars',
menu_item: :user_avatars
}, },
bundled: true do bundled: true do

@ -66,7 +66,8 @@ See doc/COPYRIGHT.rdoc for more details.
}); });
</script> </script>
<% end %> <% end %>
<% html_title l(:label_administration), l(:label_plugins), 'Backlogs' %> <% html_title l(:label_administration), l(:label_plugins), l(:label_backlogs) %>
<div class="form--field"> <div class="form--field">
<%= styled_label_tag("settings[story_types]", l(:backlogs_story_type)) %> <%= styled_label_tag("settings[story_types]", l(:backlogs_story_type)) %>
<div class="form--field-container"> <div class="form--field-container">

@ -44,7 +44,8 @@ module OpenProject::Backlogs
'task_type' => nil, 'task_type' => nil,
'card_spec' => nil 'card_spec' => nil
}, },
partial: 'shared/settings' } partial: 'shared/settings',
menu_item: :backlogs_settings }
end end
include OpenProject::Plugins::ActsAsOpEngine include OpenProject::Plugins::ActsAsOpEngine

@ -30,7 +30,8 @@ module OpenProject::Costs
bundled: true, bundled: true,
settings: { settings: {
default: { 'costs_currency' => 'EUR','costs_currency_format' => '%n %u' }, default: { 'costs_currency' => 'EUR','costs_currency_format' => '%n %u' },
partial: 'settings/openproject_costs' partial: 'settings/openproject_costs',
menu_item: :costs_setting
}, },
name: 'OpenProject Costs' do name: 'OpenProject Costs' do
@ -66,7 +67,7 @@ module OpenProject::Costs
menu :admin_menu, menu :admin_menu,
:cost_types, :cost_types,
{ controller: '/cost_types', action: 'index' }, { controller: '/cost_types', action: 'index' },
icon: 'icon2 icon-cost-types', parent: :admin_costs,
caption: :label_cost_type_plural caption: :label_cost_type_plural
menu :project_menu, menu :project_menu,

@ -7,7 +7,7 @@ en:
models: models:
ldap_groups/synchronized_group: 'Synchronized LDAP group' ldap_groups/synchronized_group: 'Synchronized LDAP group'
ldap_groups: ldap_groups:
label_menu_item: 'Group synchronization' label_menu_item: 'LDAP group synchronization'
label_group_key: 'LDAP group filter key' label_group_key: 'LDAP group filter key'
label_group_key: 'LDAP group filter key' label_group_key: 'LDAP group filter key'
settings: settings:

@ -15,7 +15,8 @@ module OpenProject::LdapGroups
menu :admin_menu, menu :admin_menu,
:plugin_ldap_groups, :plugin_ldap_groups,
{ controller: '/ldap_groups/synchronized_groups', action: :index }, { controller: '/ldap_groups/synchronized_groups', action: :index },
parent: :ldap_authentication, parent: :authentication,
last: true,
caption: ->(*) { I18n.t('ldap_groups.label_menu_item') } caption: ->(*) { I18n.t('ldap_groups.label_menu_item') }
end end

@ -14,9 +14,8 @@ module OpenProject::OpenIDConnect
menu :admin_menu, menu :admin_menu,
:plugin_openid_connect, :plugin_openid_connect,
:openid_connect_providers_path, :openid_connect_providers_path,
after: :ldap_authentication, parent: :authentication,
caption: ->(*) { I18n.t('openid_connect.menu_title') }, caption: ->(*) { I18n.t('openid_connect.menu_title') }
icon: 'icon2 icon-openid'
end end
assets %w( assets %w(

@ -39,7 +39,7 @@ module OpenProject::PdfExport
:export_card_configurations, :export_card_configurations,
{ controller: '/export_card_configurations', action: 'index' }, { controller: '/export_card_configurations', action: 'index' },
caption: :label_export_card_configuration_plural, caption: :label_export_card_configuration_plural,
icon: 'icon2 icon-ticket-down' parent: :admin_backlogs
end end
end end
end end

@ -32,9 +32,8 @@ module OpenProject::TwoFactorAuthentication
:two_factor_authentication, :two_factor_authentication,
{ controller: '/two_factor_authentication/two_factor_settings', action: :show }, { controller: '/two_factor_authentication/two_factor_settings', action: :show },
caption: ->(*) { I18n.t('two_factor_authentication.label_two_factor_authentication') }, caption: ->(*) { I18n.t('two_factor_authentication.label_two_factor_authentication') },
after: :ldap_authentication, parent: :authentication,
if: ->(*) { ::OpenProject::TwoFactorAuthentication::TokenStrategyManager.configurable_by_ui? }, if: ->(*) { ::OpenProject::TwoFactorAuthentication::TokenStrategyManager.configurable_by_ui? }
icon: 'icon2 icon-two-factor-authentication'
end end
initializer 'two_factor_authentication.precompile_assets' do |app| initializer 'two_factor_authentication.precompile_assets' do |app|

@ -0,0 +1,88 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe AdminController, type: :controller do
let(:user) { FactoryBot.build :admin }
before do
allow(User).to receive(:current).and_return user
end
describe '#index' do
it 'renders index' do
get :index
expect(response).to be_successful
expect(response).to render_template 'index'
end
end
describe '#plugins' do
render_views
context 'with plugins' do
before do
Redmine::Plugin.register :foo do end
Redmine::Plugin.register :bar do end
end
it 'renders the plugins' do
get :plugins
expect(response).to be_successful
expect(response).to render_template 'plugins'
expect(response.body).to have_selector('td span', text: 'Foo')
expect(response.body).to have_selector('td span', text: 'Bar')
end
end
context 'without plugins' do
before do
Redmine::Plugin.clear
end
it 'renders even without plugins' do
get :plugins
expect(response).to be_successful
expect(response).to render_template 'plugins'
end
end
end
describe '#info' do
it 'renders info' do
get :info
expect(response).to be_successful
expect(response).to render_template 'info'
end
end
end

@ -134,12 +134,10 @@ describe 'Authentication Stages', type: :feature, js: true do
expect_logged_in expect_logged_in
# Update consent date # Update consent date
visit settings_path(tab: 'users') visit users_settings_path
find("#toggle_consent_time").set(true) find("#toggle_consent_time").set(true)
within '#tab-content-users' do click_on 'Save'
click_on 'Save'
end
expect(page).to have_selector('.flash.notice') expect(page).to have_selector('.flash.notice')
Setting.clear_cache Setting.clear_cache

@ -45,7 +45,7 @@ feature 'Admin menu items' do
expect(page).to have_selector('a', text: I18n.t('label_user_plural')) expect(page).to have_selector('a', text: I18n.t('label_user_plural'))
expect(page).to have_selector('a', text: I18n.t('label_role_plural')) expect(page).to have_selector('a', text: I18n.t('label_role_plural'))
expect(page).to have_selector('a', text: I18n.t('label_work_package_types')) expect(page).to have_selector('a', text: I18n.t('label_type_plural'))
end end
end end
@ -60,7 +60,7 @@ feature 'Admin menu items' do
expect(page).to have_selector('a', text: I18n.t('label_user_plural')) expect(page).to have_selector('a', text: I18n.t('label_user_plural'))
expect(page).not_to have_selector('a', text: I18n.t('label_role_plural')) expect(page).not_to have_selector('a', text: I18n.t('label_role_plural'))
expect(page).not_to have_selector('a', text: I18n.t('label_work_package_types')) expect(page).not_to have_selector('a', text: I18n.t('label_type_plural'))
end end
end end
end end

@ -107,14 +107,12 @@ describe 'user deletion: ', type: :feature, js: true do
Setting.users_deletable_by_admins = 0 Setting.users_deletable_by_admins = 0
Setting.users_deletable_by_self = 0 Setting.users_deletable_by_self = 0
visit settings_path(tab: 'users') visit users_settings_path
find(:css, "#settings_users_deletable_by_admins").set(true) find(:css, "#settings_users_deletable_by_admins").set(true)
find(:css, "#settings_users_deletable_by_self").set(true) find(:css, "#settings_users_deletable_by_self").set(true)
within '#tab-content-users' do click_on 'Save'
click_on 'Save'
end
expect(Setting.users_deletable_by_admins?).to eq true expect(Setting.users_deletable_by_admins?).to eq true
expect(Setting.users_deletable_by_self?).to eq true expect(Setting.users_deletable_by_self?).to eq true

@ -118,12 +118,9 @@ describe 'random password generation',
end end
it 'can configure and enforce password rules', js: true do it 'can configure and enforce password rules', js: true do
visit '/settings' visit authentication_settings_path
expect_angular_frontend_initialized expect_angular_frontend_initialized
# Go to authentication
find('#tab-authentication').click
# Enforce rules # Enforce rules
# 3 of 'lowercase, uppercase, special' # 3 of 'lowercase, uppercase, special'
find('.form--check-box[value=uppercase]').set true find('.form--check-box[value=uppercase]').set true

@ -28,7 +28,7 @@
require 'spec_helper' require 'spec_helper'
describe 'settings/_authentication', type: :view do describe 'authentication/authentication_settings', type: :view do
context 'with password login enabled' do context 'with password login enabled' do
before do before do
allow(OpenProject::Configuration).to receive(:disable_password_login?).and_return(false) allow(OpenProject::Configuration).to receive(:disable_password_login?).and_return(false)

@ -1,102 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require_relative '../legacy_spec_helper'
require 'admin_controller'
describe AdminController, type: :controller do
render_views
fixtures :all
before do
User.current = nil
request.session[:user_id] = 1 # admin
end
it 'should index' do
get :index
assert_select 'div', false,
attributes: { class: /nodata/ }
end
it 'should no plugins' do
Redmine::Plugin.clear
get :plugins
assert_response :success
assert_template 'plugins'
end
it 'should plugins' do
# Register a few plugins
Redmine::Plugin.register :foo do
name 'Foo plugin'
author 'John Smith'
description 'This is a test plugin'
version '0.0.1'
settings default: { 'sample_setting' => 'value', 'foo' => 'bar' }, partial: 'foo/settings'
end
Redmine::Plugin.register :bar do
end
get :plugins
assert_response :success
assert_template 'plugins'
assert_select 'td', child: { tag: 'span', content: 'Foo plugin' }
assert_select 'td', child: { tag: 'span', content: 'Bar' }
end
it 'should info' do
get :info
assert_response :success
assert_template 'info'
end
it 'should admin menu plugin extension' do
Redmine::MenuManager.map :admin_menu do |menu|
menu.push :test_admin_menu_plugin_extension,
{ controller: '/admin', action: 'plugins' },
caption: 'Test'
end
User.current = User.find(1)
get :plugins
assert_response :success
assert_select 'a',
attributes: { href: '/admin/plugins' },
content: 'Test'
Redmine::MenuManager.map :admin_menu do |menu|
menu.delete :test_admin_menu_plugin_extension
end
end
end
Loading…
Cancel
Save