Placeholder user services and administration (#8944)

* Adding placeholder user contracts

* Adding create, update, and delete services for placeholder users

* WIP: Adding Placeholder User contract specs [ci skip]

* Extract contract validation into common helper

* Add common validation in BaseContract + common example for admin checks

* Introduce common ModelContract shared context for validations

* WIP: PlaceholderUser controller, i18n, and routes [ci skip]

* Placeholder users index page and query

- moved all group related scopes from User to Principal to make them also available in PlaceholderUser.

* end

* Create PlaceholderUser

* Feature spec for editing a placeholder user

* Manage PlaceholderUser memberships

The managment of memberships is pretty similar for User and PlaceholderUser. This commit extacts the similarities and uses them for both.

* General partial and show view for PlaceholderUser

* Delete obosolete partial

* Allow RequireAdminGuard to be used as a module function

* Fix I18n for confirmation text

* Smaller code improvements

* Fix: Syntax for accessing status enums was wrong.

* Use UpdateService for updating a placeholder user

* Add spec for PlaceholderUsersController

* First code improvements after code review.

- more improvements to come.

* Further code improvements after review

... still more to come

* Correct namespace of delete service

* Fix: Make placeholder user contract validate

* Remove :type attribute from base contract of User and PlaceholerUser

...and add it to the CreateContracts.
Also add type validations.

Further extract shared examples for placeholder user attribute
validation

* Refactor: Extract membership hook calls to helper

* Fix redirect paths for membership controllers

* Specs already present in shared exampels.

* Fix duplicates routes for users and placeholder users

* Fix user path

* Add attribute name and lastname

We don't need a writeable check as both are equally writable

* Replace more references to tab_edit_user_path

* Skip specs for PlaceholderUsers::DeletionService

We will tackle that service in a separate PR.

* Fix module usage of RequireAdminGuard

* Fix group filter for placeholder users

* Fix invalid reference to expect_valid

* Fix: Fix tabbed edit path for placeholder users

* Fix status filtering on users

* Linting

* Improve generalisation of individual principal filter cell

- Check for presence of groups and statuses in order
  to toggle visibility of their UI element.
- Remove groups from placeholder user controller and
  cell initialization and options

* Fix selector on groups assign

* Remove using_shared_fixtures

Co-authored-by: Oliver Günther <mail@oliverguenther.de>
This commit is contained in:
Wieland Lindenthal
2021-02-12 17:18:55 +01:00
committed by GitHub
parent 6895974095
commit a18954b2c9
98 changed files with 3438 additions and 470 deletions
@@ -0,0 +1,117 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 IndividualPrincipalBaseFilterCell < RailsCell
include UsersHelper
include ActionView::Helpers::FormOptionsHelper
class << self
def query(params)
q = base_query.new
apply_filters(params, q)
q
end
def filter(params)
query(params).results
end
def filtered?(params)
%i(name status group_id role_id).any? { |name| params[name].present? }
end
def filter_name(query, name)
if name.present?
query.where(:any_name_attribute, '~', name)
end
end
def filter_group(query, group_id)
if group_id.present?
query.where(:group, '=', group_id)
end
end
def filter_role(query, role_id)
if role_id.present?
query.where(:role_id, '=', role_id)
end
end
def filter_project(query, project_id)
if project_id.present?
query.where(:project_id, '=', project_id)
end
end
def base_query
raise NotImplementedError
end
protected
def apply_filters(params, query)
filter_project query, params[:project_id]
filter_name query, params[:name]
filter_group query, params[:group_id]
filter_role query, params[:role_id]
query
end
end
# INSTANCE METHODS:
def filter_path
raise NotImplementedError
end
def initially_visible?
true
end
def has_close_icon?
false
end
def has_statuses?
defined?(status)
end
def has_groups?
defined?(groups) && groups.present?
end
def params
model
end
end
@@ -0,0 +1,47 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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.
#++
module PlaceholderUsers
class PlaceholderUserFilterCell < IndividualPrincipalBaseFilterCell
options :roles, :clear_url, :project
class << self
def base_query
Queries::PlaceholderUsers::PlaceholderUserQuery
end
end
# INSTANCE METHODS:
def filter_path
placeholder_users_path
end
end
end
+58
View File
@@ -0,0 +1,58 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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.
#++
module PlaceholderUsers
class RowCell < ::RowCell
include AvatarHelper
include UsersHelper
def placeholder_user
model
end
def name
link_to h(placeholder_user.name), edit_placeholder_user_path(placeholder_user)
end
def button_links
[delete_link].compact
end
def delete_link
return nil unless User.current.admin?
link_to '',
placeholder_user_path(placeholder_user),
data: { confirm: I18n.t(:text_are_you_sure) },
class: 'icon icon-delete',
method: :delete
end
end
end
+58
View File
@@ -0,0 +1,58 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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.
#++
module PlaceholderUsers
class TableCell < ::TableCell
options :current_user # adds this option to those of the base class
columns :name, :created_at
def initial_sort
%i[id asc]
end
def headers
columns.map do |name|
[name.to_s, header_options(name)]
end
end
def header_options(name)
options = { caption: PlaceholderUser.human_attribute_name(name) }
options[:default_order] = 'desc' if desc_by_default.include? name
options
end
def desc_by_default
[:created_at]
end
end
end
+40 -59
View File
@@ -1,30 +1,37 @@
class UserFilterCell < RailsCell
include UsersHelper
include ActionView::Helpers::FormOptionsHelper
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 UserFilterCell < IndividualPrincipalBaseFilterCell
options :groups, :status, :roles, :clear_url, :project
class << self
def query(params)
q = base_query.new
filter_project q, params[:project_id]
filter_name q, params[:name]
filter_status q, status_param(params)
filter_group q, params[:group_id]
filter_role q, params[:role_id]
q
end
def filter(params)
query(params).results
end
def filtered?(params)
%i(name status group_id role_id).any? { |name| params[name].present? }
end
##
# Returns the selected status from the parameters
# or the default status to be filtered by (all)
@@ -33,12 +40,6 @@ class UserFilterCell < RailsCell
params[:status].presence || 'all'
end
def filter_name(query, name)
if name.present?
query.where(:any_name_attribute, '~', name)
end
end
def filter_status(query, status)
return unless status && status != 'all'
@@ -53,27 +54,19 @@ class UserFilterCell < RailsCell
end
end
def filter_group(query, group_id)
if group_id.present?
query.where(:group, '=', group_id)
end
end
def filter_role(query, role_id)
if role_id.present?
query.where(:role_id, '=', role_id)
end
end
def filter_project(query, project_id)
if project_id.present?
query.where(:project_id, '=', project_id)
end
end
def base_query
Queries::Users::UserQuery
end
protected
def apply_filters(params, query)
super(params, query)
filter_status query, status_param(params)
query
end
end
# INSTANCE METHODS:
@@ -82,18 +75,6 @@ class UserFilterCell < RailsCell
users_path
end
def initially_visible?
true
end
def has_close_icon?
false
end
def params
model
end
def user_status_options
users_status_options_for_select status, extra: extra_user_status_options
end
+30
View File
@@ -1,3 +1,33 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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.
#++
module Users
class UserFilterCell < ::UserFilterCell
def filter_role(query, role_id)
@@ -37,11 +37,13 @@ See docs/COPYRIGHT.rdoc for more details.
</a>
<% end %>
<ul class="simple-filters--filters">
<li class="simple-filters--filter">
<% if has_statuses? %>
<li class="simple-filters--filter">
<label class='simple-filters--filter-name' for='status'><%= User.human_attribute_name(:status) %>:</label>
<%= select_tag 'status', user_status_options, class: 'simple-filters--filter-value' %>
</li>
<% if groups.present? %>
</li>
<% end %>
<% if has_groups? %>
<li class="simple-filters--filter">
<label class='simple-filters--filter-name' for='group_id'><%= Group.model_name.human %>:</label>
<%= collection_select :group,
@@ -30,10 +30,9 @@
module AttributeHelpTexts
class BaseContract < ::ModelContract
include RequiresAdminGuard
include Attachments::ValidateReplacements
validate :validate_user_allowed_to_manage
def self.model
AttributeHelpText
end
@@ -41,9 +40,5 @@ module AttributeHelpTexts
attribute :type
attribute :attribute_name
attribute :help_text
def validate_user_allowed_to_manage
errors.add :base, :error_unauthorized unless user.admin?
end
end
end
@@ -32,12 +32,14 @@ module RequiresAdminGuard
extend ActiveSupport::Concern
included do
validate :validate_admin_only
validate { validate_admin_only(user, errors) }
end
def validate_admin_only
unless user.admin?
errors.add :base, :error_unauthorized
end
module_function
def validate_admin_only(user, errors)
unless user.admin? && user.active?
errors.add :base, :error_unauthorized
end
end
end
+1 -1
View File
@@ -56,7 +56,7 @@ class DeleteContract < ModelContract
case permission
when :admin
user.admin?
user.admin? && user.active?
when Proc
instance_exec(&permission)
else
@@ -0,0 +1,55 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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.
#++
module PlaceholderUsers
class BaseContract < ::ModelContract
# attribute_alias is broken in the sense
# that `model#changed` includes only the non-aliased name
# hence we need to put "lastname" as an attribute here
attribute :name
attribute :lastname
def self.model
PlaceholderUser
end
validate :user_allowed_to_modify
protected
##
# Placeholder users can only be updated by Admins
def user_allowed_to_modify
unless user.admin? && user.active?
errors.add :base, :error_unauthorized
end
end
end
end
@@ -0,0 +1,45 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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.
#++
module PlaceholderUsers
class CreateContract < BaseContract
attribute :type
validate :type_is_placeholder_user
private
def type_is_placeholder_user
unless model.type == PlaceholderUser.name
errors.add(:type, 'Type and class mismatch')
end
end
end
end
@@ -0,0 +1,59 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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.
#++
module PlaceholderUsers
class DeleteContract < ::DeleteContract
delete_permission -> {
self.class.deletion_allowed?(user)
}
##
# Checks if a given user may be deleted by another one.
#
# @param actor [User] User who wants to delete the given placeholder user.
def self.deletion_allowed?(actor)
actor.admin? && actor.active?
end
protected
##
# PlaceholderUsers can only be deleted by Admins
def user_allowed_to_modify
unless deletion_allowed?
errors.add :base, :error_unauthorized
end
end
def deletion_allowed?
self.class.deletion_allowed? user
end
end
end
@@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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.
#++
module PlaceholderUsers
class UpdateContract < BaseContract
end
end
+1 -1
View File
@@ -28,9 +28,9 @@
module Projects
class ArchiveContract < ModelContract
include RequiresAdminGuard
include Projects::Archiver
validate :validate_admin_only
validate :validate_no_foreign_wp_references
protected
-6
View File
@@ -28,12 +28,6 @@
module Projects
module Archiver
def validate_admin_only
unless user.admin?
errors.add :base, :error_unauthorized
end
end
# Check that there is no wp of a non descendant project that is assigned
# to one of the project or descendant versions
def validate_no_foreign_wp_references
+1 -1
View File
@@ -135,7 +135,7 @@ module Projects
def validate_changing_active
return unless model.active_changed?
validate_admin_only
RequiresAdminGuard.validate_admin_only(user, errors)
if model.active?
# switched to active -> unarchiving
+1 -1
View File
@@ -28,9 +28,9 @@
module Projects
class UnarchiveContract < ModelContract
include RequiresAdminGuard
include Projects::Archiver
validate :validate_admin_only
validate :validate_all_ancestors_active
protected
-3
View File
@@ -28,11 +28,8 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'model_contract'
module Users
class BaseContract < ::ModelContract
attribute :type
attribute :login
attribute :firstname
attribute :lastname
+9 -2
View File
@@ -28,10 +28,10 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'users/base_contract'
module Users
class CreateContract < BaseContract
attribute :type
attribute :status do
unless model.active? || model.invited?
# New users may only have these two statuses
@@ -41,6 +41,7 @@ module Users
validate :user_allowed_to_add
validate :authentication_defined
validate :type_is_user
private
@@ -59,5 +60,11 @@ module Users
errors.add :base, :error_unauthorized
end
end
def type_is_user
unless model.type == User.name
errors.add(:type, 'Type and class mismatch')
end
end
end
end
-2
View File
@@ -28,8 +28,6 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'users/base_contract'
module Users
class DeleteContract < ::DeleteContract
delete_permission -> {
-2
View File
@@ -28,8 +28,6 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'users/base_contract'
module Users
class UpdateContract < BaseContract
validate :user_allowed_to_update
+1 -1
View File
@@ -67,7 +67,7 @@ class MessagesController < ApplicationController
@message = Messages::SetAttributesService
.new(user: current_user,
model: Message.new,
contract_class: NoopContract)
contract_class: EmptyContract)
.call(forum: @forum)
.result
end
@@ -0,0 +1,47 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 PlaceholderUsers::MembershipsController < ApplicationController
include IndividualPrincipals::MembershipControllerMethods
layout 'admin'
before_action :require_admin
before_action :find_individual_principal
def find_individual_principal
@individual_principal = PlaceholderUser.find(params[:placeholder_user_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def redirected_to_tab(_membership)
'memberships'
end
end
@@ -0,0 +1,162 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 PlaceholderUsersController < ApplicationController
layout 'admin'
helper_method :gon
before_action :require_admin, except: [:show]
before_action :find_placeholder_user, only: %i[show
edit
update
destroy]
before_action :check_if_deletion_allowed, only: [:destroy]
def index
@placeholder_users = PlaceholderUsers::PlaceholderUserFilterCell.filter params
respond_to do |format|
format.html do
render layout: !request.xhr?
end
end
end
def show
# show projects based on current user visibility
@memberships = @placeholder_user.memberships
.visible(current_user)
respond_to do |format|
format.html { render layout: 'no_menu' }
end
end
def new
@placeholder_user = PlaceholderUsers::SetAttributesService
.new(user: User.current,
model: PlaceholderUser.new,
contract_class: EmptyContract)
.call({})
.result
end
def create
service = PlaceholderUsers::CreateService.new(user: User.current)
service_result = service.call(permitted_params.placeholder_user)
@placeholder_user = service_result.result
if service_result.success?
respond_to do |format|
format.html do
flash[:notice] = I18n.t(:notice_successful_create)
redirect_to(params[:continue] ? new_placeholder_user_path : edit_placeholder_user_path(@placeholder_user))
end
end
else
respond_to do |format|
format.html do
render action: :new
end
end
end
end
def edit
@membership ||= Member.new
@individual_principal = @placeholder_user
end
def update
service_result = PlaceholderUsers::UpdateService
.new(user: User.current,
model: @placeholder_user)
.call(permitted_params.placeholder_user)
if service_result.success?
respond_to do |format|
format.html do
flash[:notice] = I18n.t(:notice_successful_update)
redirect_back(fallback_location: edit_placeholder_user_path(@placeholder_user))
end
end
else
@membership ||= Member.new
respond_to do |format|
format.html do
render action: :edit
end
end
end
end
def destroy
Users::DeleteService.new(user: User.current,
model: @placeholder_user)
.call
flash[:notice] = I18n.t('account.deleted')
respond_to do |format|
format.html do
redirect_to placeholder_users_path
end
end
end
private
def find_placeholder_user
@placeholder_user = PlaceholderUser.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def check_if_deletion_allowed
render_404 unless PlaceholderUsers::DeleteService.deletion_allowed? @placeholder_user, User.current
end
protected
def default_breadcrumb
if action_name == 'index'
t('label_placeholder_user_plural')
else
ActionController::Base.helpers.link_to(t('label_placeholder_user_plural'),
placeholder_users_path)
end
end
def show_local_breadcrumb
current_user.admin?
end
end
@@ -29,56 +29,14 @@
#++
class Users::MembershipsController < ApplicationController
include IndividualPrincipals::MembershipControllerMethods
layout 'admin'
before_action :require_admin
before_action :find_user
before_action :find_individual_principal
def update
update_or_create(request.patch?, :notice_successful_update)
end
def create
update_or_create(request.post?, :notice_successful_create)
end
def destroy
@membership = @user.memberships.find(params[:id])
tab = redirected_to_tab(@membership)
if @membership.deletable? && request.delete?
@membership.destroy
@membership = nil
flash[:notice] = I18n.t(:notice_successful_delete)
end
redirect_to controller: '/users', action: 'edit', id: @user, tab: tab
end
private
def update_or_create(save_record, message)
@membership = params[:id].present? ? Member.find(params[:id]) : Member.new(principal: @user, project: nil)
result = ::Members::EditMembershipService
.new(@membership, save: save_record, current_user: current_user)
.call(attributes: permitted_params.membership)
if result.success?
flash[:notice] = I18n.t(message)
else
flash[:error] = result.errors.full_messages.join("\n")
end
redirect_to controller: '/users',
action: 'edit',
id: @user,
tab: redirected_to_tab(@membership)
end
def find_user
@user = User.find(params[:user_id])
def find_individual_principal
@individual_principal = User.find(params[:user_id])
rescue ActiveRecord::RecordNotFound
render_404
end
+1
View File
@@ -126,6 +126,7 @@ class UsersController < ApplicationController
def edit
@auth_sources = AuthSource.all
@membership ||= Member.new
@individual_principal = @user
end
def update
@@ -0,0 +1,40 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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.
#++
module IndividualPrincipalHooksHelper
def call_individual_principals_memberships_hook(individual_principal, suffix, context = {})
call_context = { individual_principal_key(individual_principal) => individual_principal }.merge(context)
call_hook("view_#{individual_principal.class.name.underscore.pluralize}_memberships_table_#{suffix}".to_sym, call_context)
end
def individual_principal_key(individual_principal)
individual_principal.class.name.underscore.to_sym
end
end
+7
View File
@@ -183,6 +183,10 @@ class PermittedParams
permitted_params.merge(custom_field_values(:user))
end
def placeholder_user
params.require(:placeholder_user).permit(*self.class.permitted_attributes[:placeholder_user])
end
def my_account_settings
user.merge(pref: pref)
end
@@ -525,6 +529,9 @@ class PermittedParams
:client_credentials_user_id,
{ scopes: [] }
],
placeholder_user: %i(
name
),
project_type: [
:name,
{ type_ids: [] }
+1
View File
@@ -33,6 +33,7 @@ class PlaceholderUser < Principal
validates_presence_of(:name)
validates_uniqueness_of(:name)
validates_length_of :name, maximum: 256
include ::Associations::Groupable
+25
View File
@@ -76,6 +76,31 @@ class Principal < ApplicationRecord
scope :not_in_project, ->(project) {
where.not(id: Member.of(project).select(:user_id))
}
scope :in_group, ->(group) {
within_group(group)
}
scope :not_in_group, ->(group) {
within_group(group, false)
}
scope :within_group, ->(group, positive = true) {
group_id = group.is_a?(Group) ? [group.id] : Array(group).map(&:to_i)
sql_condition = group_id.any? ? 'WHERE gu.group_id IN (?)' : ''
sql_not = positive ? '' : 'NOT'
sql_query = [
"#{User.table_name}.id #{sql_not} IN " \
"(SELECT gu.user_id FROM #{table_name_prefix}group_users#{table_name_suffix} gu #{sql_condition})"
]
if group_id.any?
sql_query.push group_id
end
where(sql_query)
}
before_create :set_default_empty_values
@@ -0,0 +1,50 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::IndividualPrincipals::Orders::GroupOrder < Queries::BaseOrder
self.model = Principal
def self.key
:group
end
private
def order
order_string = "groups_users.lastname"
order_string += " DESC" if direction == :desc
model.order(order_string)
end
def joins
:groups
end
end
@@ -0,0 +1,49 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::IndividualPrincipals::Orders::NameOrder < Queries::BaseOrder
self.model = Principal
def self.key
:name
end
private
def order
ordered = model.order_by_name
if direction == :desc
ordered = ordered.reverse_order
end
ordered
end
end
+49
View File
@@ -0,0 +1,49 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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.
#++
module Queries::PlaceholderUsers
Queries::Register.filter Queries::PlaceholderUsers::PlaceholderUserQuery,
Queries::PlaceholderUsers::Filters::NameFilter
Queries::Register.filter Queries::PlaceholderUsers::PlaceholderUserQuery,
Queries::PlaceholderUsers::Filters::AnyNameAttributeFilter
Queries::Register.filter Queries::PlaceholderUsers::PlaceholderUserQuery,
Queries::PlaceholderUsers::Filters::GroupFilter
Queries::Register.order Queries::PlaceholderUsers::PlaceholderUserQuery,
Queries::PlaceholderUsers::Orders::DefaultOrder
Queries::Register.order Queries::PlaceholderUsers::PlaceholderUserQuery,
Queries::PlaceholderUsers::Orders::NameOrder
Queries::Register.order Queries::PlaceholderUsers::PlaceholderUserQuery,
Queries::PlaceholderUsers::Orders::GroupOrder
end
@@ -0,0 +1,33 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::Filters::AnyNameAttributeFilter < Queries::PlaceholderUsers::Filters::NameFilter
include Queries::Filters::Shared::AnyUserNameAttributeFilter
end
@@ -0,0 +1,49 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::Filters::GroupFilter < Queries::PlaceholderUsers::Filters::PlaceholderUserFilter
include Queries::Filters::Shared::GroupFilter
self.model = PlaceholderUser
def human_name
PlaceholderUser.human_attribute_name(name)
end
private
def group_subselect
PlaceholderUser.in_group(values).select(:id).to_sql
end
def any_group_subselect
PlaceholderUser.within_group([]).select(:id).to_sql
end
end
@@ -0,0 +1,33 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::Filters::NameFilter < Queries::PlaceholderUsers::Filters::PlaceholderUserFilter
include Queries::Filters::Shared::UserNameFilter
end
@@ -0,0 +1,37 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::Filters::PlaceholderUserFilter < Queries::Filters::Base
self.model = PlaceholderUser
def human_name
User.human_attribute_name(name)
end
end
@@ -0,0 +1,37 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::Orders::DefaultOrder < Queries::BaseOrder
self.model = PlaceholderUser
def self.key
/\A(id|name)\z/
end
end
@@ -0,0 +1,33 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::Orders::GroupOrder < Queries::IndividualPrincipals::Orders::GroupOrder
self.model = PlaceholderUser
end
@@ -0,0 +1,33 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::Orders::NameOrder < Queries::IndividualPrincipals::Orders::NameOrder
self.model = PlaceholderUser
end
@@ -0,0 +1,35 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 Queries::PlaceholderUsers::PlaceholderUserQuery < Queries::BaseQuery
def self.model
PlaceholderUser
end
end
+1 -19
View File
@@ -28,24 +28,6 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
class Queries::Users::Orders::GroupOrder < Queries::BaseOrder
class Queries::Users::Orders::GroupOrder < Queries::IndividualPrincipals::Orders::GroupOrder
self.model = User
def self.key
:group
end
private
def order
order_string = "groups_users.lastname"
order_string += " DESC" if direction == :desc
model.order(order_string)
end
def joins
:groups
end
end
-20
View File
@@ -137,26 +137,6 @@ class User < Principal
before_destroy :delete_associated_private_queries
before_destroy :reassign_associated
scope :in_group, ->(group) {
within_group(group)
}
scope :not_in_group, ->(group) {
within_group(group, false)
}
scope :within_group, ->(group, positive = true) {
group_id = group.is_a?(Group) ? [group.id] : Array(group).map(&:to_i)
sql_condition = group_id.any? ? 'WHERE gu.group_id IN (?)' : ''
sql_not = positive ? '' : 'NOT'
sql_query = ["#{User.table_name}.id #{sql_not} IN (SELECT gu.user_id FROM #{table_name_prefix}group_users#{table_name_suffix} gu #{sql_condition})"]
if group_id.any?
sql_query.push group_id
end
where(sql_query)
}
scope :admin, -> { where(admin: true) }
def self.unique_attribute
@@ -0,0 +1,32 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 PlaceholderUsers::CreateService < ::BaseServices::Create
end
@@ -0,0 +1,32 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 PlaceholderUsers::DeleteService < ::BaseServices::Delete
end
@@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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.
#++
module PlaceholderUsers
class SetAttributesService < ::BaseServices::SetAttributes
end
end
@@ -0,0 +1,32 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 PlaceholderUsers::UpdateService < ::BaseServices::Update
end
@@ -6,7 +6,7 @@ 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-2013 Jean-Philippe Lang
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
@@ -31,14 +31,14 @@ See docs/COPYRIGHT.rdoc for more details.
<div class="grid-block">
<div class="grid-content">
<% if @user.memberships.any? %>
<% if @individual_principal.memberships.any? %>
<div class="generic-table--container">
<div class="generic-table--results-container">
<table class="generic-table memberships">
<colgroup>
<col highlight-col>
<col highlight-col>
<%= call_hook(:view_users_memberships_table_colgroup, user: @user )%>
<%= call_individual_principals_memberships_hook(@individual_principal, 'colgroup') %>
<col>
</colgroup>
<thead>
@@ -61,12 +61,12 @@ See docs/COPYRIGHT.rdoc for more details.
</div>
</div>
</th>
<%= call_hook(:view_users_memberships_table_header, user: @user )%>
<%= call_individual_principals_memberships_hook(@individual_principal, 'header') %>
<th><div class="generic-table--empty-header"></div></th>
</tr>
</thead>
<tbody>
<% @user.memberships.where.not(project: nil).each do |membership| %>
<% @individual_principal.memberships.where.not(project: nil).each do |membership| %>
<% next if membership.new_record? %>
<tr id="member-<%= membership.id %>" class="member">
<td class="project">
@@ -78,7 +78,7 @@ See docs/COPYRIGHT.rdoc for more details.
<%=h membership.roles.sort.collect(&:to_s).join(', ') %>
</span>
<%= labelled_tabular_form_for(:membership,
url: user_membership_path(user_id: @user, id: membership),
url: polymorphic_path([@individual_principal, :membership], id: membership),
html: { id: "member-#{membership.id}-roles-form",
class: "member-#{membership.id}--edit-toggle-item",
style: 'display:none;'},
@@ -98,20 +98,24 @@ See docs/COPYRIGHT.rdoc for more details.
</label>
<% end %>
</div>
<p><%= submit_tag t(:button_change), class: 'user-memberships--edit-submit-button button -highlight -small' %>
<p><%= submit_tag t(:button_change), class: 'memberships--edit-submit-button button -highlight -small' %>
<%= link_to_function t(:button_cancel),
"jQuery('.member-#{membership.id}--edit-toggle-item').toggle();",
class: 'button -small' %></p>
<% end %>
</td>
<%= call_hook(:view_users_memberships_table_row, user: @user, membership: membership, roles: roles, projects: projects )%>
<%= call_individual_principals_memberships_hook(@individual_principal,
'row',
membership: membership,
roles: roles,
projects: projects) %>
<td class="buttons">
<%= link_to_function icon_wrapper('icon icon-edit', t(:button_edit)),
"jQuery('.member-#{membership.id}--edit-toggle-item').toggle();",
class: "member-#{membership.id}--edit-toggle-item user-memberships--edit-button",
class: "member-#{membership.id}--edit-toggle-item memberships--edit-button",
title: t(:button_edit) %>
<%= link_to(icon_wrapper('icon icon-remove', t(:button_remove)),
user_membership_path(user_id: @user, id: membership),
polymorphic_path([@individual_principal, :membership], id: membership),
method: :delete,
title: t(:button_remove)) if membership.deletable? %>
</td>
@@ -129,11 +133,11 @@ See docs/COPYRIGHT.rdoc for more details.
<div class="grid-content">
<% if projects.any? %>
<%= labelled_tabular_form_for(:membership,
url: user_memberships_path(user_id: @user),
url: polymorphic_path([@individual_principal, :memberships]),
html: {id: "new_project_membership"}) do %>
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%=t(:label_project_new)%></legend>
<%= styled_select_tag 'membership[project_id]', options_for_membership_project_select(@user, projects) %>
<%= styled_select_tag 'membership[project_id]', options_for_membership_project_select(@individual_principal, projects) %>
<div class="form--field -vertical">
<%= styled_label_tag nil, "#{t(:label_role_plural)}:" %>
<div class="form--field-container -vertical">
@@ -0,0 +1,38 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2020 the OpenProject GmbH
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.
++#%>
<%= error_messages_for 'placeholder_user' %>
<!--[form:placeholder_user]-->
<section class="form--section">
<div class="form--field -required"><%= f.text_field :name, label: t('label_name'), required: true, container_class: '-middle' %></div>
<%= call_hook(:view_placeholder_users_form, placeholder_user: @placeholder_user, form: f) %>
</section>
<!--[eoform:placeholder_user]-->
@@ -0,0 +1,40 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2020 the OpenProject GmbH
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.
++#%>
<%= labelled_tabular_form_for @placeholder_user,
url: { controller: '/placeholder_users',
action: "update",
tab: nil },
html: { method: :put,
autocomplete: 'off' },
as: :placeholder_user do |f| %>
<%= render partial: 'form', locals: { f: f } %>
<%= styled_button_tag t(:button_save), class: '-highlight -with-icon icon-checkmark' %>
<% end %>
@@ -0,0 +1,38 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2021 the OpenProject GmbH
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-2013 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.
++#%>
<%= breadcrumb_toolbar(@placeholder_user.new_record? ? t(:label_placeholder_user_new) : @placeholder_user.name) do %>
<% unless @placeholder_user.new_record? %>
<li class="toolbar-item">
<%= link_to placeholder_user_path(@placeholder_user), class: 'button' do %>
<%= op_icon('button--icon icon-user') %>
<span class="button--text"><%= t(:label_profile) %></span>
<% end %>
</li>
<% end %>
<% end %>
+36
View File
@@ -0,0 +1,36 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2020 the OpenProject GmbH
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(t(:label_administration), "#{t(:label_edit)} #{PlaceholderUser.model_name.human} #{h(@placeholder_user.name)}") -%>
<% local_assigns[:additional_breadcrumb] = @placeholder_user.name %>
<%= render partial: 'toolbar', locals: { new_user: false, :@placeholder_user => @placeholder_user } %>
<%= render_extensible_tabs :placeholder_user, placeholder_user: @placeholder_user %>
@@ -0,0 +1,46 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2020 the OpenProject GmbH
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 t(:label_administration), t(:label_placeholder_user_plural) -%>
<%= toolbar title: t(:label_placeholder_user_plural), title_class: 'no-padding-bottom' do %>
<li class="toolbar-item">
<%= link_to new_placeholder_user_path,
{ class: 'button -alt-highlight',
aria: { label: t(:label_placeholder_user_new) },
title: t(:label_placeholder_user_new) } do %>
<%= op_icon('button--icon icon-add') %>
<span class="button--text"><%= t('activerecord.models.placeholder_user') %></span>
<% end %>
</li>
<%= call_hook(:placeholder_user_admin_action_menu) %>
<% end %>
<%= cell PlaceholderUsers::PlaceholderUserFilterCell, params %>
&nbsp;
<%= cell PlaceholderUsers::TableCell, @placeholder_users, project: @project, current_user: current_user %>
+44
View File
@@ -0,0 +1,44 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2020 the OpenProject GmbH
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 t(:label_administration), t("label_placeholder_user_new") %>
<% local_assigns[:additional_breadcrumb] = t(:label_placeholder_user_new) %>
<%= render partial: 'toolbar', locals: { new_user: true,
:@placeholder_user => @placeholder_user } %>
<%= labelled_tabular_form_for @placeholder_user,
url: { action: "create" },
html: { class: nil, autocomplete: 'off' },
as: :placeholder_user do |f| %>
<%= render partial: 'form', locals: { f: f, placeholder_user: @placeholder_user } %>
<p>
<%= styled_button_tag t(:button_create), class: '-highlight -with-icon icon-checkmark' %>
<%= styled_button_tag t(:button_create_and_continue), name: 'continue', class: '-highlight -with-icon icon-checkmark' %>
</p>
<% end %>
+66
View File
@@ -0,0 +1,66 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2021 the OpenProject GmbH
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-2013 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.
++#%>
<% content_for :header_tags do %>
<%= call_hook :placeholder_users_show_head %>
<% end %>
<% html_title t(:label_administration), t(:label_placeholder_user_plural) -%>
<%= toolbar title: "#{avatar @placeholder_user} #{h(@placeholder_user.name)}".html_safe do %>
<% if User.current.admin? %>
<li class="toolbar-item">
<%= link_to edit_placeholder_user_path(@placeholder_user), class: 'button', accesskey: accesskey(:edit) do %>
<%= op_icon('button--icon icon-edit') %>
<span class="button--text"><%= t(:button_edit) %></span>
<% end %>
</li>
<% end %>
<% end %>
<div class="grid-block medium-up-2">
<div class="grid-content">
<%= call_hook :view_account_left_top, placeholder_user: @placeholder_user %>
<%= call_hook :view_account_left_middle, placeholder_user: @placeholder_user %>
<% unless @memberships.empty? %>
<h3><%=t(:label_project_plural)%></h3>
<ul>
<% for membership in @memberships %>
<li>
<%= link_to_project(membership.project) %>
(<%=h membership.roles.sort.collect(&:to_s).join(', ') %>, <%= format_date(membership.created_at) %>)
</li>
<% end %>
</ul>
<% end %>
<%= call_hook :view_account_left_bottom, placeholder_user: @placeholder_user %>
</div>
</div>
<% html_title h(@placeholder_user.name) %>
+2 -2
View File
@@ -44,8 +44,8 @@ See docs/COPYRIGHT.rdoc for more details.
<li class="toolbar-item">
<%= link_to new_user_path,
{ class: 'button -alt-highlight',
aria: {label: t(:label_user_new)},
title: t(:label_user_new)} do %>
aria: { label: t(:label_user_new) },
title: t(:label_user_new) } do %>
<%= op_icon('button--icon icon-add') %>
<span class="button--text"><%= t('activerecord.models.user') %></span>
<% end %>
+5
View File
@@ -155,6 +155,11 @@ Redmine::MenuManager.map :admin_menu do |menu|
caption: :label_user_plural,
parent: :users_and_permissions
menu.push :placeholder_users,
{ controller: '/placeholder_users' },
caption: :label_placeholder_user_plural,
parent: :users_and_permissions
menu.push :groups,
{ controller: '/groups' },
if: Proc.new { User.current.admin? },
+5
View File
@@ -774,6 +774,7 @@ en:
status: "Work package status"
member: "Member"
news: "News"
placeholder_user: "Placeholder user"
project: "Project"
query: "Custom query"
role:
@@ -1535,6 +1536,7 @@ en:
label_my_account_data: "My account data"
label_my_projects: "My projects"
label_my_queries: "My custom queries"
label_name: "Name"
label_never: "Never"
label_new: "New"
label_new_features: "New features"
@@ -1584,6 +1586,9 @@ en:
label_permissions: "Permissions"
label_permissions_report: "Permissions report"
label_personalize_page: "Personalize this page"
label_placeholder_user: "Placeholder user"
label_placeholder_user_new: "New placeholder user"
label_placeholder_user_plural: "Placeholder users"
label_planning: "Planning"
label_please_login: "Please log in"
label_plugins: "Plugins"
+10 -2
View File
@@ -452,11 +452,11 @@ OpenProject::Application.routes.draw do
resources :activity, :activities, only: :index, controller: 'activities'
resources :users do
resources :users, except: :edit do
resources :memberships, controller: 'users/memberships', only: %i[update create destroy]
member do
match '/edit/:tab' => 'users#edit', via: :get, as: 'tab_edit'
get '/edit(/:tab)' => 'users#edit', as: 'edit'
match '/change_status/:change_action' => 'users#change_status_info', via: :get, as: 'change_status_info'
post :change_status
post :resend_invitation
@@ -464,6 +464,14 @@ OpenProject::Application.routes.draw do
end
end
resources :placeholder_users, except: :edit do
resources :memberships, controller: 'placeholder_users/memberships', only: %i[update create destroy]
member do
get '/edit(/:tab)' => 'placeholder_users#edit', as: 'edit'
end
end
# The show page of groups is public and thus moved out of the admin scope
resources :groups, only: %i[show], as: :show_group
@@ -0,0 +1,43 @@
module IndividualPrincipals
module MembershipControllerMethods
def update
update_or_create(request.patch?, :notice_successful_update)
end
def create
update_or_create(request.post?, :notice_successful_create)
end
def destroy
@membership = @individual_principal.memberships.find(params[:id])
tab = redirected_to_tab(@membership)
if @membership.deletable? && request.delete?
@membership.destroy
@membership = nil
flash[:notice] = I18n.t(:notice_successful_delete)
end
redirect_to edit_polymorphic_path(@individual_principal, tab: tab)
end
private
def update_or_create(save_record, message)
@membership = params[:id].present? ? Member.find(params[:id]) : Member.new(principal: @individual_principal, project: nil)
result = ::Members::EditMembershipService
.new(@membership, save: save_record, current_user: current_user)
.call(attributes: permitted_params.membership)
if result.success?
flash[:notice] = I18n.t(message)
else
flash[:error] = result.errors.full_messages.join("\n")
end
redirect_to edit_polymorphic_path(@individual_principal, tab: redirected_to_tab(@membership))
end
end
end
+24 -6
View File
@@ -34,7 +34,8 @@ module OpenProject
class << self
def tabs
@tabs ||= {
user: core_user_tabs
user: core_user_tabs,
placeholder_user: core_placeholder_user_tabs
}
end
@@ -60,32 +61,49 @@ module OpenProject
{
name: 'general',
partial: 'users/general',
path: ->(params) { tab_edit_user_path(params[:user], tab: :general) },
path: ->(params) { edit_user_path(params[:user], tab: :general) },
label: :label_general
},
{
name: 'memberships',
partial: 'users/memberships',
path: ->(params) { tab_edit_user_path(params[:user], tab: :memberships) },
partial: 'individual_principals/memberships',
path: ->(params) { edit_user_path(params[:user], tab: :memberships) },
label: :label_project_plural,
only_if: ->(*) { User.current.admin? }
},
{
name: 'groups',
partial: 'users/groups',
path: ->(params) { tab_edit_user_path(params[:user], tab: :groups) },
path: ->(params) { edit_user_path(params[:user], tab: :groups) },
label: :label_group_plural,
only_if: ->(*) { User.current.admin? && Group.any? }
},
{
name: 'global_roles',
partial: 'users/global_roles',
path: ->(params) { tab_edit_user_path(params[:user], tab: :global_roles) },
path: ->(params) { edit_user_path(params[:user], tab: :global_roles) },
label: :label_global_roles,
only_if: ->(*) { User.current.admin? }
}
]
end
def core_placeholder_user_tabs
[
{
name: 'general',
partial: 'placeholder_users/general',
path: ->(params) { edit_placeholder_user_path(params[:placeholder_user], tab: :general) },
label: :label_general
},
{
name: 'memberships',
partial: 'individual_principals/memberships',
path: ->(params) { edit_placeholder_user_path(params[:placeholder_user], tab: :memberships) },
label: :label_project_plural
}
]
end
end
end
end
@@ -51,7 +51,7 @@ module OpenProject::Avatars
add_tab_entry :user,
name: 'avatar',
partial: 'avatars/users/avatar_tab',
path: ->(params) { tab_edit_user_path(params[:user], tab: :avatar) },
path: ->(params) { edit_user_path(params[:user], tab: :avatar) },
label: :label_avatar,
only_if: ->(*) { User.current.admin? && ::OpenProject::Avatars::AvatarManager.avatars_enabled? }
+1 -1
View File
@@ -104,7 +104,7 @@ module Costs
add_tab_entry :user,
name: 'rates',
partial: 'users/rates',
path: ->(params) { tab_edit_user_path(params[:user], tab: :rates) },
path: ->(params) { edit_user_path(params[:user], tab: :rates) },
only_if: ->(*) { User.current.admin? },
label: :caption_rate_history
@@ -19,7 +19,7 @@ OpenProject::Application::routes.draw do
end
end
resources :users do
resources :users, only: [] do
member do
resources :two_factor_devices,
param: :device_id,
@@ -41,7 +41,7 @@ module OpenProject::TwoFactorAuthentication
add_tab_entry :user,
name: 'two_factor_authentication',
partial: 'users/two_factor_authentication',
path: ->(params) { tab_edit_user_path(params[:user], tab: :two_factor_authentication) },
path: ->(params) { edit_user_path(params[:user], tab: :two_factor_authentication) },
label: 'two_factor_authentication.label_two_factor_authentication',
only_if: ->(*) { User.current.admin? && OpenProject::TwoFactorAuthentication::TokenStrategyManager.enabled? }
@@ -27,27 +27,13 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe AttributeHelpTexts::BaseContract do
include_context 'ModelContract shared context'
let(:model) { FactoryBot.build_stubbed :work_package_help_text }
let(:contract) { described_class.new(model, current_user) }
subject { contract.validate }
context 'as admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'validates the contract' do
expect(subject).to eq true
end
end
context 'as regular user' do
let(:current_user) { FactoryBot.build_stubbed :user }
it 'returns an error on validation' do
expect(subject).to eq false
expect(contract.errors.symbols_for(:base))
.to match_array [:error_unauthorized]
end
end
it_behaves_like 'contract is valid for active admin users only'
end
@@ -29,27 +29,28 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe CustomActions::CuContract do
include_context 'ModelContract shared context'
let(:user) { FactoryBot.build_stubbed(:user) }
let(:action) do
FactoryBot.build_stubbed(:custom_action, actions:
[CustomActions::Actions::AssignedTo.new])
end
let(:instance) { described_class.new(action) }
let(:contract) { described_class.new(action) }
describe 'name' do
it 'is writable' do
action.name = 'blubs'
expect(instance.validate)
.to be_truthy
expect_contract_valid
end
it 'needs to be set' do
action.name = nil
expect(instance.validate)
.to be_falsey
expect_contract_invalid
end
end
@@ -57,8 +58,7 @@ describe CustomActions::CuContract do
it 'is writable' do
action.description = 'blubs'
expect(instance.validate)
.to be_truthy
expect_contract_valid
end
end
@@ -68,26 +68,19 @@ describe CustomActions::CuContract do
action.actions = [responsible_action]
expect(instance.validate)
.to be_truthy
expect_contract_valid
end
it 'needs to have one' do
action.actions = []
instance.validate
expect(instance.errors.symbols_for(:actions))
.to eql [:empty]
expect_contract_invalid actions: :empty
end
it 'requires a value if the action requires one' do
action.actions = [CustomActions::Actions::Status.new([])]
instance.validate
expect(instance.errors.symbols_for(:actions))
.to eql [:empty]
expect_contract_invalid actions: :empty
end
it 'allows only the allowed values' do
@@ -99,19 +92,13 @@ describe CustomActions::CuContract do
action.actions = [status_action]
instance.validate
expect(instance.errors.symbols_for(:actions))
.to eql [:inclusion]
expect_contract_invalid actions: :inclusion
end
it 'is not allowed to have an inexistent action' do
action.actions = [CustomActions::Actions::Inexistent.new]
instance.validate
expect(instance.errors.symbols_for(:actions))
.to eql [:does_not_exist]
expect_contract_invalid actions: :does_not_exist
end
end
@@ -119,7 +106,7 @@ describe CustomActions::CuContract do
it 'is writable' do
action.conditions = [double('some bogus condition', key: 'some', values: 'bogus', validate: true)]
expect(instance.validate)
expect(contract.validate)
.to be_truthy
end
@@ -132,19 +119,13 @@ describe CustomActions::CuContract do
action.conditions = [status_condition]
instance.validate
expect(instance.errors.symbols_for(:conditions))
.to eql [:inclusion]
expect_contract_invalid conditions: :inclusion
end
it 'is not allowed to have an inexistent condition' do
action.conditions = [CustomActions::Conditions::Inexistent.new]
instance.validate
expect(instance.errors.symbols_for(:conditions))
.to eql [:does_not_exist]
expect_contract_invalid conditions: :does_not_exist
end
end
end
@@ -29,28 +29,15 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe CustomFields::CreateContract do
include_context 'ModelContract shared context'
let(:cf) { FactoryBot.build :project_custom_field }
let(:contract) do
described_class.new(cf, current_user, options: { changed_by_system: [] })
end
describe 'as admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'validates the contract' do
expect(contract.validate).to eq(true)
end
end
describe 'as regular user' do
let(:current_user) { FactoryBot.build_stubbed :user }
it 'invalidates the contract' do
expect(contract.validate).to eq(false)
expect(contract.errors.symbols_for(:base))
.to match_array [:error_unauthorized]
end
end
it_behaves_like 'contract is valid for active admin users only'
end
@@ -29,28 +29,15 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe CustomFields::UpdateContract do
include_context 'ModelContract shared context'
let(:cf) { FactoryBot.build :project_custom_field }
let(:contract) do
described_class.new(cf, current_user, options: { changed_by_system: [] })
end
describe 'as admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'validates the contract' do
expect(contract.validate).to eq(true)
end
end
describe 'as regular user' do
let(:current_user) { FactoryBot.build_stubbed :user }
it 'invalidates the contract' do
expect(contract.validate).to eq(false)
expect(contract.errors.symbols_for(:base))
.to match_array [:error_unauthorized]
end
end
it_behaves_like 'contract is valid for active admin users only'
end
+7 -10
View File
@@ -28,8 +28,11 @@
require 'spec_helper'
require_relative './shared_contract_examples'
require 'contracts/shared/model_contract_shared_context'
describe Members::CreateContract do
include_context 'ModelContract shared context'
it_behaves_like 'member contract' do
let(:member) do
Member.new(project: member_project,
@@ -37,31 +40,25 @@ describe Members::CreateContract do
principal: member_principal)
end
subject(:contract) { described_class.new(member, current_user) }
let(:contract) { described_class.new(member, current_user) }
describe '#validation' do
context 'if the principal is nil' do
let(:member_principal) { nil }
it 'is invalid' do
expect_valid(false, principal: %i(blank))
end
it_behaves_like 'contract is invalid', principal: :blank
end
context 'if the principal is a builtin user' do
let(:member_principal) { FactoryBot.build_stubbed(:anonymous) }
it 'is invalid' do
expect_valid(false, principal: %i(unassignable))
end
it_behaves_like 'contract is invalid', principal: :unassignable
end
context 'if the principal is a locked user' do
let(:member_principal) { FactoryBot.build_stubbed(:locked_user) }
it 'is invalid' do
expect_valid(false, principal: %i(unassignable))
end
it_behaves_like 'contract is invalid', principal: :unassignable
end
end
end
+8 -10
View File
@@ -28,8 +28,12 @@
require 'spec_helper'
require_relative './shared_contract_examples'
require 'contracts/shared/model_contract_shared_context'
describe Members::UpdateContract do
include_context 'ModelContract shared context'
it_behaves_like 'member contract' do
let(:member) do
FactoryBot.build_stubbed(:member,
@@ -38,7 +42,7 @@ describe Members::UpdateContract do
principal: member_principal)
end
subject(:contract) { described_class.new(member, current_user) }
let(:contract) { described_class.new(member, current_user) }
describe 'validation' do
context 'if the principal is changed' do
@@ -46,9 +50,7 @@ describe Members::UpdateContract do
member.principal = FactoryBot.build_stubbed(:user)
end
it 'is invalid' do
expect_valid(false, user_id: %i(error_readonly))
end
it_behaves_like 'contract is invalid', user_id: :error_readonly
end
context 'if the project is changed' do
@@ -56,17 +58,13 @@ describe Members::UpdateContract do
member.project = FactoryBot.build_stubbed(:project)
end
it 'is invalid' do
expect_valid(false, project_id: %i(error_readonly))
end
it_behaves_like 'contract is invalid', project_id: :error_readonly
end
context 'if the principal is a locked user' do
let(:member_principal) { FactoryBot.build_stubbed(:locked_user) }
it 'is valid' do
expect_valid(true)
end
it_behaves_like 'contract is valid'
end
end
end
@@ -0,0 +1,42 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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'
require 'contracts/shared/model_contract_shared_context'
require_relative 'shared_contract_examples'
describe PlaceholderUsers::CreateContract do
include_context 'ModelContract shared context'
it_behaves_like 'placeholder user contract' do
let(:placeholder_user) { PlaceholderUser.new(name: placeholder_user_name) }
let(:contract) { described_class.new(placeholder_user, current_user) }
let(:current_user) { FactoryBot.build_stubbed(:admin) }
end
end
@@ -0,0 +1,41 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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'
require 'contracts/shared/model_contract_shared_context'
describe PlaceholderUsers::DeleteContract do
include_context 'ModelContract shared context'
let(:placeholder_user) { FactoryBot.build_stubbed(:placeholder_user) }
let(:contract) { described_class.new(placeholder_user, current_user) }
it_behaves_like 'contract is valid for active admin users only'
end
@@ -0,0 +1,73 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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'
shared_examples_for 'placeholder user contract' do
let(:placeholder_user_name) { 'UX Designer' }
it_behaves_like 'contract is valid for active admin users only'
context 'name' do
context 'is valid' do
it_behaves_like 'contract is valid'
end
context 'is not too long' do
let(:placeholder_user) { PlaceholderUser.new(name: 'X' * 257) }
it_behaves_like 'contract is invalid'
end
context 'is not empty' do
let(:placeholder_user) { PlaceholderUser.new(name: '') }
it_behaves_like 'contract is invalid'
end
context 'is unique' do
before do
PlaceholderUser.create(name: placeholder_user_name)
end
it_behaves_like 'contract is invalid'
end
end
describe 'type' do
context 'type and class mismatch' do
before do
placeholder_user.type = User.name
end
it_behaves_like 'contract is invalid'
end
end
end
@@ -0,0 +1,42 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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'
require 'contracts/shared/model_contract_shared_context'
require_relative 'shared_contract_examples'
describe PlaceholderUsers::UpdateContract do
include_context 'ModelContract shared context'
it_behaves_like 'placeholder user contract' do
let(:placeholder_user) { FactoryBot.build_stubbed(:placeholder_user, name: placeholder_user_name) }
let(:contract) { described_class.new(placeholder_user, current_user) }
let(:current_user) { FactoryBot.build_stubbed(:admin) }
end
end
@@ -29,34 +29,13 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe Projects::ArchiveContract do
include_context 'ModelContract shared context'
let(:project) { FactoryBot.build_stubbed(:project) }
let(:contract) { described_class.new(project, current_user) }
subject(:contract) { described_class.new(project, current_user) }
def expect_valid(valid, symbols = {})
expect(contract.validate).to eq(valid)
symbols.each do |key, arr|
expect(contract.errors.symbols_for(key)).to match_array arr
end
end
context 'when user is admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'is valid' do
expect_valid(true)
end
end
context 'when user is not admin' do
let(:current_user) { FactoryBot.build_stubbed :user }
let(:permissions) { [] }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
end
it_behaves_like 'contract is valid for active admin users only'
end
@@ -29,37 +29,13 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe Projects::DeleteContract do
include_context 'ModelContract shared context'
let(:project) { FactoryBot.build_stubbed(:project) }
let(:contract) { described_class.new(project, current_user) }
subject(:contract) { described_class.new(project, current_user) }
def expect_valid(valid, symbols = {})
expect(contract.validate).to eq(valid)
symbols.each do |key, arr|
expect(contract.errors.symbols_for(key)).to match_array arr
end
end
shared_examples 'is valid' do
end
context 'when user is admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'is valid' do
expect_valid(true)
end
end
context 'when user is not admin' do
let(:current_user) { FactoryBot.build_stubbed :user }
let(:permissions) { [] }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
end
it_behaves_like 'contract is valid for active admin users only'
end
@@ -28,14 +28,17 @@
require 'spec_helper'
require_relative './shared_contract_examples'
require 'contracts/shared/model_contract_shared_context'
describe Projects::InstantiateTemplateContract do
include_context 'ModelContract shared context'
let(:user) { FactoryBot.build_stubbed :user }
let(:project) { Project.new name: 'Foo Bar', identifier: 'foo' }
let(:template) { FactoryBot.build_stubbed :project }
let(:options) { { template_project_id: template.id } }
subject { described_class.new(project, user, options: options) }
let(:contract) { described_class.new(project, user, options: options) }
before do
allow(user)
@@ -52,19 +55,13 @@ describe Projects::InstantiateTemplateContract do
let(:allowed_to_copy) { true }
let(:allowed_to_add) { true }
it 'validates the contract' do
expect(subject.validate).to eq true
expect(subject.errors).to be_empty
end
it_behaves_like 'contract is valid'
context 'but may not add projects' do
let(:allowed_to_copy) { true }
let(:allowed_to_add) { false }
it 'fails the parent contract' do
expect(subject.validate).to eq false
expect(subject.errors[:base]).to include 'may not be accessed.'
end
it_behaves_like 'contract is invalid', base: :error_unauthorized
end
end
@@ -72,9 +69,6 @@ describe Projects::InstantiateTemplateContract do
let(:allowed_to_copy) { false }
let(:allowed_to_add) { true }
it 'fails the contract' do
expect(subject.validate).to eq false
expect(subject.errors[:base]).to include 'may not be accessed.'
end
it_behaves_like 'contract is invalid', base: :error_unauthorized
end
end
@@ -29,34 +29,13 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe Projects::UnarchiveContract do
include_context 'ModelContract shared context'
let(:project) { FactoryBot.build_stubbed(:project) }
let(:contract) { described_class.new(project, current_user) }
subject(:contract) { described_class.new(project, current_user) }
def expect_valid(valid, symbols = {})
expect(contract.validate).to eq(valid)
symbols.each do |key, arr|
expect(contract.errors.symbols_for(key)).to match_array arr
end
end
context 'when user is admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'is valid' do
expect_valid(true)
end
end
context 'when user is not admin' do
let(:current_user) { FactoryBot.build_stubbed :user }
let(:permissions) { [] }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
end
it_behaves_like 'contract is valid for active admin users only'
end
+10 -27
View File
@@ -29,8 +29,11 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe Queries::UpdateContract do
include_context 'ModelContract shared context'
let(:project) { FactoryBot.build_stubbed :project }
let(:query) do
FactoryBot.build_stubbed(:query, project: project, is_public: public, user: user)
@@ -44,21 +47,13 @@ describe Queries::UpdateContract do
end
end
end
subject(:contract) { described_class.new(query, current_user) }
let(:contract) { described_class.new(query, current_user) }
before do
# Assume project is always visible
allow(contract).to receive(:project_visible?).and_return true
end
def expect_valid(valid, symbols = {})
expect(contract.validate).to eq(valid)
symbols.each do |key, arr|
expect(contract.errors.symbols_for(key)).to match_array arr
end
end
describe 'private query' do
let(:public) { false }
@@ -68,17 +63,13 @@ describe Queries::UpdateContract do
context 'user has no permission to save' do
let(:permissions) { %i(edit_work_packages) }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
it_behaves_like 'contract user is unauthorized'
end
context 'user has permission to save' do
let(:permissions) { %i(save_queries) }
it 'is valid' do
expect_valid(true)
end
it_behaves_like 'contract is valid'
end
end
@@ -86,9 +77,7 @@ describe Queries::UpdateContract do
let(:user) { FactoryBot.build_stubbed :user }
let(:permissions) { %i(save_queries) }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
it_behaves_like 'contract user is unauthorized'
end
end
@@ -99,25 +88,19 @@ describe Queries::UpdateContract do
context 'user has no permission to save' do
let(:permissions) { %i(invalid_permission) }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
it_behaves_like 'contract user is unauthorized'
end
context 'user has no permission to manage public' do
let(:permissions) { %i(manage_public_queries) }
it 'is valid' do
expect_valid(true)
end
it_behaves_like 'contract is valid'
end
context 'user has permission to save only own' do
let(:permissions) { %i(save_queries) }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
it_behaves_like 'contract user is unauthorized'
end
end
end
@@ -0,0 +1,53 @@
shared_context 'ModelContract shared context' do
def expect_contract_valid
expect(contract.validate).to eq(true)
end
def expect_contract_invalid(errors = {})
expect(contract.validate).to eq(false)
errors.each do |key, error_symbols|
expect(contract.errors.symbols_for(key)).to match_array Array(error_symbols)
end
end
shared_examples 'contract is valid' do
it do
expect_contract_valid
end
end
shared_examples 'contract is invalid' do |errors = {}|
it do
expect_contract_invalid errors
end
end
shared_examples 'contract user is unauthorized' do
it do
expect_contract_invalid base: :error_unauthorized
end
end
shared_examples 'contract is valid for active admin users only' do
context 'when admin' do
let(:current_user) { FactoryBot.build_stubbed(:admin) }
context 'when admin active' do
it_behaves_like 'contract is valid'
end
context 'when admin not active' do
let(:current_user) { FactoryBot.build_stubbed(:admin, status: User.statuses[:locked]) }
it_behaves_like 'contract user is unauthorized'
end
end
context 'when not admin' do
let(:current_user) { FactoryBot.build_stubbed(:user) }
it_behaves_like 'contract user is unauthorized'
end
end
end
+13 -34
View File
@@ -29,30 +29,18 @@
#++
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
describe Users::CreateContract do
include_context 'ModelContract shared context'
let(:user) { FactoryBot.build_stubbed(:user) }
subject(:contract) { described_class.new(user, current_user) }
def expect_valid(valid, symbols = {})
expect(contract.validate).to eq(valid)
symbols.each do |key, arr|
expect(contract.errors.symbols_for(key)).to match_array arr
end
end
shared_examples 'is valid' do
it 'is valid' do
expect_valid(true)
end
end
let(:contract) { described_class.new(user, current_user) }
context 'when admin' do
let(:current_user) { FactoryBot.build_stubbed(:admin) }
it_behaves_like 'is valid'
it_behaves_like 'contract is valid'
describe 'requires a password set when active' do
before do
@@ -60,16 +48,14 @@ describe Users::CreateContract do
user.activate
end
it 'is invalid' do
expect_valid(false, password: %i(blank))
end
it_behaves_like 'contract is invalid', password: :blank
context 'when password is set' do
before do
user.password = user.password_confirmation = 'password!password!'
end
it_behaves_like 'is valid'
it_behaves_like 'contract is valid'
end
end
end
@@ -84,7 +70,7 @@ describe Users::CreateContract do
user.invite
end
it_behaves_like 'is valid'
it_behaves_like 'contract is valid'
end
describe 'cannot set the password' do
@@ -92,9 +78,7 @@ describe Users::CreateContract do
user.password = user.password_confirmation = 'password!password!'
end
it 'is invalid' do
expect_valid(false, password: %i(error_readonly))
end
it_behaves_like 'contract is invalid', password: :error_readonly
end
describe 'can set the auth_source' do
@@ -105,9 +89,7 @@ describe Users::CreateContract do
user.auth_source = auth_source
end
it 'is valid' do
expect_valid(true)
end
it_behaves_like 'contract is valid'
end
describe 'cannot set the identity url' do
@@ -115,17 +97,14 @@ describe Users::CreateContract do
user.identity_url = 'saml:123412foo'
end
it 'is invalid' do
expect_valid(false, identity_url: %i(error_readonly))
end
it_behaves_like 'contract is invalid', identity_url: :error_readonly
end
end
context 'when unauthorized user' do
let(:current_user) { FactoryBot.build_stubbed(:user) }
it 'is invalid' do
expect_valid(false, base: %i(error_unauthorized))
end
it_behaves_like 'contract user is unauthorized'
end
end
@@ -0,0 +1,66 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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'
require 'work_package'
describe PlaceholderUsers::MembershipsController, type: :controller do
shared_let(:admin) { FactoryBot.create :admin }
let(:placeholder_user) { FactoryBot.create(:placeholder_user) }
let(:anonymous) { FactoryBot.create(:anonymous) }
describe 'update memberships' do
let(:project) { FactoryBot.create(:project) }
let(:role) { FactoryBot.create(:role) }
it 'works' do
# i.e. it should successfully add a placeholder user to a project's members
as_logged_in_user admin do
post :create,
params: {
placeholder_user_id: placeholder_user.id,
membership: {
project_id: project.id,
role_ids: [role.id]
}
}
end
expect(response).to redirect_to(controller: '/placeholder_users',
action: 'edit',
id: placeholder_user.id,
tab: 'memberships')
is_member = placeholder_user.reload.memberships.any? { |m|
m.project_id == project.id && m.role_ids.include?(role.id)
}
expect(is_member).to eql(true)
end
end
end
@@ -0,0 +1,283 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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'
require 'work_package'
describe PlaceholderUsersController, type: :controller do
let(:current_user) { FactoryBot.build(:admin) }
let(:placeholder_user) { FactoryBot.create(:placeholder_user) }
shared_examples 'do not allow non-admins' do
let(:current_user) { FactoryBot.build(:user) }
it 'responds with unauthorized status' do
expect(response).to_not be_successful
expect(response.status).to eq 403
end
end
describe 'GET new' do
before do
as_logged_in_user(current_user) do
get :new
end
end
context 'as admin' do
it 'renders the new template' do
expect(response).to be_successful
expect(response).to render_template 'placeholder_users/new'
expect(assigns(:placeholder_user)).to be_present
end
end
context 'not as admin' do
let(:current_user) { FactoryBot.build(:user) }
it 'responds with unauthorized status' do
expect(response).to_not be_successful
expect(response.status).to eq 403
end
end
end
describe 'GET index' do
before do
as_logged_in_user(current_user) do
get :index
end
end
context 'as admin' do
it 'renders the index template' do
expect(response).to be_successful
expect(response).to render_template 'placeholder_users/index'
expect(assigns(:placeholder_users)).to be_empty
expect(assigns(:groups)).not_to be_present
end
end
context 'not as admin' do
let(:current_user) { FactoryBot.build(:user) }
it_behaves_like 'do not allow non-admins'
end
end
describe 'GET show' do
shared_examples 'renders the show template' do
it 'renders the show template' do
expect(response).to be_successful
expect(response).to render_template 'placeholder_users/show'
expect(assigns(:placeholder_user)).to be_present
expect(assigns(:memberships)).to be_empty
end
end
before do
as_logged_in_user(current_user) do
get :show, params: { id: placeholder_user.id }
end
end
context 'as admin' do
it_behaves_like 'renders the show template'
end
context 'not as admin' do
let(:current_user) { FactoryBot.build(:user) }
# normal users can also checkout the profile page of placeholder user.
it_behaves_like 'renders the show template'
end
end
describe 'GET edit' do
shared_examples 'renders the edit template' do
it 'renders the show template' do
expect(response).to be_successful
expect(response).to render_template "placeholder_users/edit"
expect(assigns(:placeholder_user)).to eql(placeholder_user)
expect(assigns(:membership)).to be_present
expect(assigns(:individual_principal)).to eql(placeholder_user)
end
end
before do
as_logged_in_user(current_user) do
get :edit, params: { id: placeholder_user.id }
end
end
context 'as admin' do
it_behaves_like 'renders the edit template'
end
context 'not as admin' do
let(:current_user) { FactoryBot.build(:user) }
# normal users can also checkout the profile page of placeholder user.
it_behaves_like 'do not allow non-admins'
end
end
describe 'POST create' do
let(:params) do
{
placeholder_user: {
name: 'UX Developer'
}
}
end
before do
as_logged_in_user(current_user) do
post :create, params: params
end
end
context 'as admin' do
it 'should be assigned their new values' do
user_from_db = PlaceholderUser.last
expect(user_from_db.name).to eq('UX Developer')
end
it 'should show a success notice' do
expect(flash[:notice]).to eql(I18n.t(:notice_successful_create))
end
it 'should not send an email' do
expect(ActionMailer::Base.deliveries.empty?).to be_truthy
end
context 'when user chose to directly create the next placeholder user' do
let(:params) do
{
placeholder_user: {
name: 'UX Developer'
},
continue: true
}
end
it 'should redirect to the new page' do
expect(response).to redirect_to(new_placeholder_user_url)
end
end
context 'when user chose to NOT directly create the next placeholder user' do
let(:params) do
{
placeholder_user: {
name: 'UX Developer'
}
}
end
it 'should redirect to the edit page' do
user_from_db = PlaceholderUser.last
expect(response).to redirect_to(edit_placeholder_user_url(user_from_db))
end
end
end
it_behaves_like 'do not allow non-admins'
context 'invalid params' do
let(:params) do
{
placeholder_user: {
name: 'x' * 300 # Name is too long
}
}
end
it 'should render the edit form with a validation error message' do
expect(assigns(:'placeholder_user').errors.messages[:name].first).to include('is too long')
expect(response).to render_template 'placeholder_users/new'
end
end
end
describe 'PUT update' do
let(:params) do
{
id: placeholder_user.id,
placeholder_user: {
name: 'UX Guru'
}
}
end
before do
as_logged_in_user(current_user) do
put :update, params: params
end
end
context 'as admin' do
it 'should redirect to the edit page' do
expect(response).to redirect_to(edit_placeholder_user_url(placeholder_user))
end
it 'should be assigned their new values' do
user_from_db = PlaceholderUser.find(placeholder_user.id)
expect(user_from_db.name).to eq('UX Guru')
end
it 'should not send an email' do
expect(ActionMailer::Base.deliveries.empty?).to be_truthy
end
end
it_behaves_like 'do not allow non-admins'
context 'invalid params' do
let(:params) do
{
id: placeholder_user.id,
placeholder_user: {
name: 'x' * 300 # Name is too long
}
}
end
it 'should render the edit form with a validation error message' do
expect(assigns(:'placeholder_user').errors.messages[:name].first).to include('is too long')
expect(response).to render_template 'placeholder_users/edit'
end
end
end
describe 'POST destroy' do
pending 'Admins can destroy placeholder users'
pending 'Non admins cannot destroy placeholder users'
end
end
@@ -0,0 +1,62 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 'create placeholder users', type: :feature, selenium: true do
let(:current_user) { FactoryBot.create :admin }
let(:new_placeholder_user_page) { Pages::NewPlaceholderUser.new }
before do
allow(User).to receive(:current).and_return current_user
end
shared_examples_for 'successful placeholder user creation' do
it 'creates the placeholder user' do
expect(page).to have_selector('.flash', text: 'Successful creation.')
new_placeholder_user = PlaceholderUser.order(Arel.sql('id DESC')).first
expect(current_path).to eql(edit_placeholder_user_path(new_placeholder_user.id))
end
end
context 'as admin' do
before do
visit new_placeholder_user_path
new_placeholder_user_page.fill_in! name: 'UX Designer'
perform_enqueued_jobs do
new_placeholder_user_page.submit!
end
end
it_behaves_like 'successful placeholder user creation'
end
end
@@ -0,0 +1,58 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 'edit placeholder users', type: :feature, js: true do
let(:current_user) { FactoryBot.create :admin }
let(:placeholder_user) { FactoryBot.create :placeholder_user, name: 'UX Developer' }
before do
login_as current_user
end
context 'as admin' do
before do
visit edit_placeholder_user_path(placeholder_user)
end
it 'can edit name' do
expect(page).to have_selector '#placeholder_user_name'
fill_in 'placeholder_user[name]', with: 'NewName', fill_options: { clear: :backspace }
click_on 'Save'
expect(page).to have_selector('.flash.notice', text: 'Successful update.')
placeholder_user.reload
expect(placeholder_user.name).to eq 'NewName'
end
end
end
@@ -0,0 +1,74 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 'index placeholder users', type: :feature do
let!(:current_user) { FactoryBot.create :admin }
let!(:anonymous) { FactoryBot.create :anonymous }
let!(:placeholder_user_1) do
FactoryBot.create(:placeholder_user,
name: 'One',
created_at: 3.minute.ago)
end
let!(:placeholder_user_2) do
FactoryBot.create(:placeholder_user,
name: 'Two',
created_at: 2.minute.ago)
end
let!(:placeholder_user_3) do
FactoryBot.create(:placeholder_user,
name: 'Three',
created_at: 1.minute.ago)
end
let(:index_page) { Pages::Admin::PlaceholderUsers::Index.new }
before do
login_as(current_user)
end
it 'shows the placeholder users and allows filtering and ordering' do
index_page.visit!
index_page.expect_not_listed(anonymous, current_user)
# Order is by id, asc
# so first ones created are on top.
index_page.expect_listed(placeholder_user_1, placeholder_user_2, placeholder_user_3)
index_page.order_by('Created on')
index_page.expect_listed(placeholder_user_3, placeholder_user_2, placeholder_user_1)
index_page.order_by('Created on')
index_page.expect_listed(placeholder_user_1, placeholder_user_2, placeholder_user_3)
index_page.filter_by_name(placeholder_user_3.name)
index_page.expect_listed(placeholder_user_3)
index_page.expect_not_listed(placeholder_user_1, placeholder_user_2)
end
end
@@ -0,0 +1,77 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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'
feature 'placeholder user memberships through placeholder user page', type: :feature, js: true do
let!(:project) { FactoryBot.create :project, name: 'Project 1', identifier: 'project1' }
let!(:project2) { FactoryBot.create :project, name: 'Project 2', identifier: 'project2' }
let(:admin) { FactoryBot.create :admin }
let(:placeholder_user) { FactoryBot.create :placeholder_user, name: 'UX Designer' }
let!(:manager) { FactoryBot.create :role, name: 'Manager' }
let!(:developer) { FactoryBot.create :role, name: 'Developer' }
let(:placeholder_user_page) { Pages::Admin::IndividualPrincipals::Edit.new(placeholder_user) }
before do
login_as(admin)
placeholder_user_page.visit!
placeholder_user_page.open_projects_tab!
end
scenario 'handles role modification flow' do
placeholder_user_page.add_to_project! project.name, as: 'Manager'
member = placeholder_user.memberships.where(project_id: project.id).first
placeholder_user_page.edit_roles!(member, %w(Manager Developer))
# Modify roles
placeholder_user_page.expect_project(project.name)
placeholder_user_page.expect_roles(project.name, %w(Manager Developer))
placeholder_user_page.expect_no_membership(project2.name)
# Remove all roles
placeholder_user_page.expect_project(project.name)
placeholder_user_page.edit_roles!(member, %w())
expect(page).to have_selector('.flash.error', text: 'Roles need to be assigned.')
# Remove the user from the project
placeholder_user_page.remove_from_project!(project.name)
placeholder_user_page.expect_no_membership(project.name)
# Re-add the user
placeholder_user_page.add_to_project! project.name, as: %w(Manager Developer)
placeholder_user_page.expect_project(project.name)
placeholder_user_page.expect_roles(project.name, %w(Manager Developer))
end
end
+3 -3
View File
@@ -37,9 +37,9 @@ describe 'index users', type: :feature do
end
describe 'with some sortable users' do
let!(:a_user) { FactoryBot.create :user, login: 'a_login', firstname: 'a_first', lastname: 'xxx_a' }
let!(:b_user) { FactoryBot.create :user, login: 'b_login', firstname: 'b_first', lastname: 'nnn_b' }
let!(:z_user) { FactoryBot.create :user, login: 'z_login', firstname: 'z_first', lastname: 'ccc_z' }
let!(:a_user) { FactoryBot.create :user, login: 'aa_login', firstname: 'aa_first', lastname: 'xxx_a' }
let!(:b_user) { FactoryBot.create :user, login: 'bb_login', firstname: 'bb_first', lastname: 'nnn_b' }
let!(:z_user) { FactoryBot.create :user, login: 'zz_login', firstname: 'zz_first', lastname: 'ccc_z' }
it 'sorts them correctly (Regression #35012)' do
index_page.visit!
+1 -1
View File
@@ -36,7 +36,7 @@ feature 'user memberships through user page', type: :feature, js: true do
let!(:manager) { FactoryBot.create :role, name: 'Manager' }
let!(:developer) { FactoryBot.create :role, name: 'Developer' }
let(:user_page) { Pages::Admin::Users::Edit.new(admin.id) }
let(:user_page) { Pages::Admin::IndividualPrincipals::Edit.new(admin) }
before do
login_as(admin)
@@ -0,0 +1,79 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 IndividualPrincipalHooksHelper, type: :helper do
let(:user) { FactoryBot.build(:user) }
let(:placeholder_user) { FactoryBot.build(:placeholder_user) }
describe '#individual_principal_key' do
it 'returns the class name in underscore format' do
expect(helper.individual_principal_key(user)).to eql(:user)
expect(helper.individual_principal_key(placeholder_user)).to eql(:placeholder_user)
end
end
describe '#call_individual_principals_memberships_hook' do
context 'with user and without context' do
before do
expect(helper).to receive(:call_hook).with(:view_users_memberships_table_foo,
user: user)
end
it 'call call_hook with the correct arguments' do
helper.call_individual_principals_memberships_hook(user, 'foo')
end
end
context 'with placeholder user and without context' do
before do
expect(helper).to receive(:call_hook).with(:view_placeholder_users_memberships_table_foo,
placeholder_user: placeholder_user)
end
it 'call call_hook with the correct arguments' do
helper.call_individual_principals_memberships_hook(placeholder_user, 'foo')
end
end
context 'with user and with context' do
before do
expect(helper).to receive(:call_hook).with(:view_users_memberships_table_foo,
user: user,
yay: 'yo')
end
it 'call call_hook with the correct arguments' do
helper.call_individual_principals_memberships_hook(user, 'foo', yay: 'yo')
end
end
end
end
+1 -1
View File
@@ -34,7 +34,7 @@ describe TabsHelper, type: :helper do
let(:given_tab) do
{ name: 'avatar',
partial: 'avatars/users/avatar_tab',
path: ->(params) { tab_edit_user_path(params[:user], tab: :avatar) },
path: ->(params) { edit_user_path(params[:user], tab: :avatar) },
label: :label_avatar }
end
@@ -0,0 +1,196 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 Queries::PlaceholderUsers::PlaceholderUserQuery, type: :model do
let(:instance) { described_class.new }
let(:base_scope) { PlaceholderUser.order(id: :desc) }
context 'without a filter' do
describe '#results' do
it 'is the same as getting all the users' do
expect(instance.results.to_sql).to eql base_scope.to_sql
end
end
end
context 'with a name filter' do
before do
instance.where('name', '~', ['a user'])
end
describe '#results' do
it 'is the same as handwriting the query' do
expected = base_scope
.merge(PlaceholderUser
.where(["LOWER(CONCAT(users.firstname, CONCAT(' ', users.lastname))) LIKE ?",
"%a user%"]))
expect(instance.results.to_sql).to eql expected.to_sql
end
end
describe '#valid?' do
it 'is true' do
expect(instance).to be_valid
end
it 'is invalid if the filter is invalid' do
instance.where('name', '=', [''])
expect(instance).to be_invalid
end
end
end
context 'with a group filter' do
let(:group_1) { FactoryBot.build_stubbed(:group) }
before do
allow(Group)
.to receive(:exists?)
.and_return(true)
allow(Group)
.to receive(:all)
.and_return([group_1])
instance.where('group', '=', [group_1.id])
end
describe '#results' do
it 'is the same as handwriting the query' do
expected = base_scope
.merge(PlaceholderUser
.where(["users.id IN (#{PlaceholderUser.in_group([group_1.id.to_s]).select(:id).to_sql})"]))
expect(instance.results.to_sql).to eql expected.to_sql
end
end
describe '#valid?' do
it 'is true' do
expect(instance).to be_valid
end
it 'is invalid if the filter is invalid' do
instance.where('group', '=', [''])
expect(instance).to be_invalid
end
end
end
context 'with a non existent filter' do
before do
instance.where('not_supposed_to_exist', '=', ['bogus'])
end
describe '#results' do
it 'returns a query not returning anything' do
expected = PlaceholderUser.where(Arel::Nodes::Equality.new(1, 0))
expect(instance.results.to_sql).to eql expected.to_sql
end
end
describe 'valid?' do
it 'is false' do
expect(instance).to be_invalid
end
it 'returns the error on the filter' do
instance.valid?
expect(instance.errors[:filters]).to eql ["Not supposed to exist does not exist."]
end
end
end
context 'with an id sortation' do
before do
instance.order(id: :asc)
end
describe '#results' do
it 'is the same as handwriting the query' do
expected = PlaceholderUser.merge(PlaceholderUser.order(id: :asc))
expect(instance.results.to_sql).to eql expected.to_sql
end
end
end
context 'with a name sortation' do
before do
instance.order(name: :desc)
end
describe '#results' do
it 'is the same as handwriting the query' do
expected = "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"type\" = 'PlaceholderUser' ORDER BY \"users\".\"lastname\" DESC, \"users\".\"id\" DESC"
expect(instance.results.to_sql).to eql expected
end
end
end
context 'with a group sortation' do
before do
instance.order(group: :desc)
end
describe '#results' do
it 'is the same as handwriting the query' do
expected = PlaceholderUser.merge(PlaceholderUser.joins(:groups).order("groups_users.lastname DESC")).order(id: :desc)
expect(instance.results.to_sql).to eql expected.to_sql
end
end
end
context 'with a non existing sortation' do
# this is a field protected from sortation
before do
instance.order(password: :desc)
end
describe '#results' do
it 'returns a query not returning anything' do
expected = PlaceholderUser.where(Arel::Nodes::Equality.new(1, 0))
expect(instance.results.to_sql).to eql expected.to_sql
end
end
describe 'valid?' do
it 'is false' do
expect(instance).to be_invalid
end
end
end
end
@@ -0,0 +1,100 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 ::PlaceholderUsers::DeleteService, type: :model do
skip 'Skipped as we will tackle the DeleteService in a separate PR' do
let(:input_user) { FactoryBot.build_stubbed(:user) }
let(:project) { FactoryBot.build_stubbed(:project) }
let(:instance) { described_class.new(input_user, actor) }
subject { instance.call }
shared_examples 'deletes the user' do
it do
expect(input_user).to receive(:lock!)
expect(DeleteUserJob).to receive(:perform_later).with(input_user)
expect(subject).to eq true
end
end
shared_examples 'does not delete the user' do
it do
expect(input_user).not_to receive(:lock!)
expect(DeleteUserJob).not_to receive(:perform_later)
expect(subject).to eq false
end
end
context 'if deletion by admins allowed', with_settings: { users_deletable_by_admins: true } do
context 'with admin user' do
let(:actor) { FactoryBot.build_stubbed(:admin) }
it_behaves_like 'deletes the user'
end
context 'with unprivileged system user' do
let(:actor) { User.system }
before do
allow(actor).to receive(:admin?).and_return false
end
it_behaves_like 'does not delete the user'
end
context 'with privileged system user' do
let(:actor) { User.system }
it 'performs deletion' do
actor.run_given do
expect(input_user).to receive(:lock!)
expect(DeleteUserJob).to receive(:perform_later).with(input_user)
expect(subject).to eq true
end
end
end
end
context 'if deletion by admins NOT allowed', with_settings: { users_deletable_by_admins: false } do
context 'with admin user' do
let(:actor) { FactoryBot.build_stubbed(:admin) }
it_behaves_like 'does not delete the user'
end
context 'with system user' do
let(:actor) { User.system }
it_behaves_like 'does not delete the user'
end
end
end
end
@@ -0,0 +1,119 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 'support/pages/page'
module Pages
module Admin
module IndividualPrincipals
class Edit < ::Pages::Page
attr_reader :id
attr_reader :individual_principal
def initialize(individual_principal)
@individual_principal = individual_principal
@id = individual_principal.id
end
def path
"/#{individual_principal.class.name.underscore}s/#{id}/edit"
end
def open_projects_tab!
within('.content--tabs') do
click_on 'Projects'
end
end
def add_to_project!(project_name, as:)
open_projects_tab!
select_project! project_name
Array(as).each { |role| check role }
click_on 'Add'
expect_project(project_name)
end
def remove_from_project!(name)
open_projects_tab!
find_project(name).find('a[data-method=delete]').click
end
def edit_roles!(membership, roles)
find("#member-#{membership.id} .memberships--edit-button").click
page.within("#member-#{membership.id}-roles-form") do
page.all('.form--check-box').each do |f|
begin
f.set false
rescue Selenium::WebDriver::Error::InvalidElementStateError
# Happens if an element is disabled
end
end
Array(roles).each { |role| page.check role }
page.find('.memberships--edit-submit-button').click
end
end
def expect_project(project_name)
expect(page).to have_selector('tr', text: project_name, wait: 10)
end
def expect_no_membership(project_name)
expect(page).to have_no_selector('tr', text: project_name)
end
def expect_roles(project_name, roles)
row = page.find('tr', text: project_name, wait: 10)
roles.each do |role|
expect(row).to have_selector('span', text: role)
end
end
def find_project(name)
find('tr', text: name)
end
def has_project?(name)
has_selector? 'tr', text: name
end
def select_project!(project_name)
select(project_name, from: 'membership_project_id')
end
def activate!
within '.toolbar-items' do
click_button 'Activate'
end
end
end
end
end
end
@@ -0,0 +1,89 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 'support/pages/page'
module Pages
module Admin
module PlaceholderUsers
class Index < ::Pages::Page
def path
"/placeholder_users"
end
def expect_listed(*placeholder_users)
rows = page.all 'td.name'
expect(rows.map(&:text)).to include(*placeholder_users.map(&:name))
end
def expect_not_listed(*users)
rows = page.all 'td.name'
expect(rows.map(&:text)).to_not include(*users.map(&:name))
end
def expect_non_listed
expect(page)
.to have_no_selector('tr.placeholder-user')
expect(page)
.to have_selector('tr.generic-table--empty-row', text: 'There is currently nothing to display.')
end
def filter_by_name(value)
fill_in 'Name', with: value
click_button 'Apply'
end
def clear_filters
click_link 'Clear'
end
def order_by(key)
within 'thead' do
click_link key
end
end
def click_placeholder_user_button(placeholder_user, text)
within_user_row(placeholder_user) do
click_link text
end
end
private
def within_placeholder_user_row(placeholder_user)
row = find('tr.placeholder_user', text: placeholder_user.name)
within row do
yield
end
end
end
end
end
end
@@ -0,0 +1,49 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# 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-2013 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 'support/pages/page'
module Pages
class NewPlaceholderUser < Page
def path
'/placeholder_users/new'
end
##
# Fills in the given user form fields.
def fill_in!(fields = {})
form = FormFiller.new fields
form.fill! 'Name', :name
end
def submit!
click_button 'Create'
end
end
end