Correct group filter && and apply filters to userAutocompleter to search only for members who are not yet in the group

pull/7521/head
Henriette Dinger 5 years ago
parent 04b291f127
commit 9c2a920955
  1. 8
      app/controllers/groups_controller.rb
  2. 24
      app/models/queries/filters/shared/group_filter.rb
  3. 20
      app/models/user.rb
  4. 69
      app/views/groups/_users.html.erb
  5. 34
      frontend/src/app/modules/common/autocomplete/user-autocompleter.component.ts

@ -69,6 +69,8 @@ class GroupsController < ApplicationController
# GET /groups/1/edit
def edit
@group = Group.includes(:members, :users).find(params[:id])
set_filters_for_user_autocompleter
end
# POST /groups
@ -178,6 +180,12 @@ class GroupsController < ApplicationController
@group = Group.find(params[:id])
end
def set_filters_for_user_autocompleter
@autocompleter_filters = []
@autocompleter_filters.push({ selector: 'status', operator: '=', values: ['active'] })
@autocompleter_filters.push({ selector: 'group', operator: '!', values: [@group.id] })
end
def default_breadcrumb
if action_name == 'index'
t('label_group_plural')

@ -53,13 +53,29 @@ module Queries::Filters::Shared::GroupFilter
I18n.t('query_fields.member_of_group')
end
def joins
:groups
def where
case operator
when '='
"users.id IN (#{group_subselect})"
when '!'
"users.id NOT IN (#{group_subselect})"
when '*'
"users.id IN (#{any_group_subselect})"
when '!*'
"users.id NOT IN (#{any_group_subselect})"
end
end
def where
operator_strategy.sql_for_field(values, 'groups_users', 'id')
private
def group_subselect
User.in_group(values).select(:id).to_sql
end
def any_group_subselect
User.within_group([]).select(:id).to_sql
end
end
module ClassMethods

@ -140,13 +140,25 @@ class User < Principal
before_destroy :reassign_associated
scope :in_group, ->(group) {
group_id = group.is_a?(Group) ? group.id : group.to_i
where(["#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}group_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id])
within_group(group)
}
scope :not_in_group, ->(group) {
group_id = group.is_a?(Group) ? group.id : group.to_i
where(["#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}group_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id])
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) }
scope :newest, -> { not_builtin.order(created_on: :desc) }

@ -28,44 +28,41 @@ See docs/COPYRIGHT.rdoc for more details.
++#%>
<%= activate_angular_js do %>
<div class="grid-block">
<div class="grid-content">
<% if @group.users.any? %>
<div class="generic-table--container">
<div id="group_users_table" class="generic-table--results-container">
<div class="grid-block -visible-overflow">
<div class="grid-content medium-6">
<% if @group.users.any? %>
<div class="generic-table--container">
<div id="group_users_table" class="generic-table--results-container">
<%= render partial: 'groups/users_table' %>
</div>
</div>
<% else %>
<%= no_results_box %>
<% end %>
</div>
<div class="grid-content">
<% users = User
.not_builtin
.active
.not_in_group(@group)
.limit(1) %>
<% if users.any? %>
<%= styled_form_tag(members_of_group_path(@group), method: :post) do %>
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%=l(:label_user_new)%></legend>
<div class="form--field -vertical">
<% user_ids = [] %>
<% @group.users.each do |user| %>
<% user_ids.push(user.id) %>
<% end %>
<%= hidden_field_tag :user_ids, user_ids %>
<user-autocompleter data-update-input="user_ids"></user-autocompleter>
</div>
<div>
<%= styled_button_tag l(:button_add),
class: '-highlight -with-icon icon-checkmark' %>
</div>
</fieldset>
</div>
<% else %>
<%= no_results_box %>
<% end %>
</div>
<div class="grid-content medium-6 -visible-overflow">
<% users = User
.not_builtin
.active
.not_in_group(@group)
.limit(1) %>
<% if users.any? %>
<%= styled_form_tag(members_of_group_path(@group), method: :post) do %>
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%=l(:label_user_new)%></legend>
<div class="form--field -vertical">
<%= hidden_field_tag :user_ids, nil %>
<user-autocompleter data-update-input="user_ids"
data-additional-filter="<%= @autocompleter_filters&.to_json %>">
</user-autocompleter>
</div>
<div>
<%= styled_button_tag l(:button_add),
class: '-highlight -with-icon icon-checkmark' %>
</div>
</fieldset>
<% end %>
<% end %>
<% end %>
</div>
</div>
</div>
<% end %>

@ -30,7 +30,7 @@ import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} f
import {DynamicBootstrapper} from "core-app/globals/dynamic-bootstrapper";
import {HalResourceService} from "core-app/modules/hal/services/hal-resource.service";
import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service";
import {ApiV3FilterBuilder} from "core-components/api/api-v3/api-v3-filter-builder";
import {ApiV3FilterBuilder, FilterOperator} from "core-components/api/api-v3/api-v3-filter-builder";
import {NgSelectComponent} from "@ng-select/ng-select/dist";
import {I18nService} from "core-app/modules/common/i18n/i18n.service";
@ -70,6 +70,7 @@ export class UserAutocompleterComponent implements OnInit {
private updateInputField:HTMLInputElement|undefined;
public options:any[];
public filters:ApiV3FilterBuilder = new ApiV3FilterBuilder();
constructor(protected elementRef:ElementRef,
protected halResourceService:HalResourceService,
@ -80,6 +81,7 @@ export class UserAutocompleterComponent implements OnInit {
ngOnInit() {
const input = this.elementRef.nativeElement.dataset['updateInput'];
const allowEmpty = this.elementRef.nativeElement.dataset['allowEmpty'];
if (input) {
this.updateInputField = document.getElementsByName(input)[0] as HTMLInputElement|undefined;
this.setInitialSelection();
@ -89,13 +91,21 @@ export class UserAutocompleterComponent implements OnInit {
this.allowEmpty = true;
}
this.setAvailableUsers(this.url, '');
let filterInput = this.elementRef.nativeElement.dataset['additionalFilter'];
if (filterInput) {
JSON.parse(filterInput).forEach((filter:{selector:string; operator:FilterOperator, values:string[]}) => {
this.filters.add(filter['selector'], filter['operator'], filter['values']);
})
}
this.setAvailableUsers(this.url, this.filters);
}
public onModelChange(user:any) {
if (user) {
this.onChange.emit(user);
this.setAvailableUsers(this.url, '');
this.setAvailableUsers(this.url, this.filters);
if (this.clearAfterSelection) {
this.ngSelectComponent.clearItem(user);
@ -108,24 +118,26 @@ export class UserAutocompleterComponent implements OnInit {
}
public onSearch($event:any) {
let urlQuery:any;
let newFilters = this.filters;
if($event) {
let filters = new ApiV3FilterBuilder();
filters.add('name', '~', [$event]);
urlQuery = { filters: filters.toJson() };
newFilters.add('name', '~', [$event]);
}
this.setAvailableUsers(this.url, urlQuery);
this.setAvailableUsers(this.url, newFilters);
}
public onFocus(){
// For dynamic changes of the available users
// we have to reload them on focus (e.g. watchers)
this.setAvailableUsers(this.url, '');
this.setAvailableUsers(this.url, this.filters);
}
private setAvailableUsers(url:string, filters:any) {
this.halResourceService.get(url, filters)
private setAvailableUsers(url:string, filters:ApiV3FilterBuilder) {
let params = {
filters: filters.toJson()
};
this.halResourceService.get(url, params)
.subscribe(res => {
this.options = res.elements.map((el:any) => {
return {name: el.name, id: el.id, href: el.href, avatar: el.avatar};

Loading…
Cancel
Save