Commit 8e8f5b31 authored by Taico Aerts's avatar Taico Aerts
Browse files

Merge branch 'development' into 'master'

Project Forum Release v2.8.6 - 25-08-2022

See merge request !843
parents 3121e4ab 182294e5
......@@ -81,7 +81,9 @@ class CompaniesController < OfferersController
def join
@company = Company.find(params[:company])
if @company.allow_to_join
if @company.company_user_email_suffixes.any? { |c| current_user.email.end_with?("@#{c.suffix}") }
current_user.add_role :employee, @company
elsif @company.allow_to_join
if current_user.role_invitations.where(resource: @company, name: :employee).none?
current_user.add_role :unconfirmed_employee, @company
else
......
......@@ -11,11 +11,13 @@ module ParamsConcerns
# The full parameter keys including those for nested attributes
# that are accepted via a controller for a company.
def company_params_keys
BASE
BASE + [
company_user_email_suffixes_attributes: ParamsConcerns::CompanyUserEmailSuffixes::BASE + %i[id _destroy]
]
end
def company_update_params_keys
BASE + [
company_params_keys + [
{ image_attributes: ParamsConcerns::Images::BASE + %i[id _destroy] }
]
end
......
module ParamsConcerns
module CompanyUserEmailSuffixes
extend ActiveSupport::Concern
BASE = %i[suffix].freeze
end
end
......@@ -11,6 +11,9 @@ class Company < ApplicationRecord
has_many :role_invitations, as: :resource, dependent: :destroy
has_many :company_user_email_suffixes, inverse_of: :company, dependent: :destroy
accepts_nested_attributes_for :company_user_email_suffixes, allow_destroy: true
enum affiliation: { external: 0, internal: 1 }
# ===========================================================================
......
class CompanyUserEmailSuffix < ApplicationRecord
belongs_to :company, inverse_of: :company_user_email_suffixes
validates :suffix, presence: true, allow_blank: false,
format: { with: /\A([a-z0-9-]+\.)+[a-z]{2,}\z/, message: 'not a valid domain name' }
validate :suffix_not_blacklisted
def suffix_not_blacklisted
if BLACKLISTED_DOMAINS.include? suffix
errors.add(:suffix, 'domain not allowed')
end
end
BLACKLISTED_DOMAINS = File.readlines('config/company_user_email_suffix_blacklist.txt').map { |s| s.strip.freeze }.to_set
end
......@@ -136,9 +136,12 @@ class User < ApplicationRecord
validates :first_name, presence: true
validates :last_name, presence: true
after_save :auto_associate_import_entries!, :convert_role_invitations,
:assign_to_course_specific_roles,
if: -> { saved_change_to_email }
after_create :auto_associate_import_entries!, :convert_role_invitations,
:assign_to_course_specific_roles
after_update :auto_associate_import_entries!, :convert_role_invitations,
:assign_to_course_specific_roles,
if: -> { saved_change_to_email }
# -----------------------------------------------------------------------------------------------
# Scopes
......
......@@ -41,6 +41,30 @@
<tr>
<td><%= f.check_box :unconfirmed_employees_can_offer %></td>
</tr>
<tr>
<td>
<div class="form-group row">
<div class="col-sm-2">
<%= f.label :company_user_email_suffixes %>
</div>
<div class="col-sm-10">
<div class="list-group cocoon">
<%= f.fields_for :company_user_email_suffixes do |ff| %>
<%= render 'companies/company_user_email_suffix_fields', f: ff %>
<% end %>
<%= link_to_add_association(
glyphicon_text('plus-sign', 'Add verified email suffix'),
f,
:company_user_email_suffixes,
partial: 'companies/company_user_email_suffix_fields',
class: 'list-group-item list-group-item--button add-btn',
'data-association-insertion-node' => 'this'
) %>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div class="list-group cocoon" data-limit="1">
......
......@@ -42,13 +42,17 @@
<td><%= link_to_new_window(@company.website_url) %></td>
</tr>
<tr>
<td><strong><%= 'Allow users to join the company without an invitation' %></strong></td>
<td><strong><%= Company.human_attribute_name :allow_to_join %></strong></td>
<td><%= check_box :allow_to_join, 'allow_to_join_checkbox', {checked: @company.allow_to_join, disabled: true} %></td>
</tr>
<tr>
<td><strong><%= 'Allow unconfirmed employees to offer projects' %></strong></td>
<td><strong><%= Company.human_attribute_name :unconfirmed_employees_can_offer %></strong></td>
<td><%= check_box :unconfirmed_employees_can_offer, 'unconfirmed_employees_can_offer_checkbox', {checked: @company.unconfirmed_employees_can_offer, disabled: true} %></td>
</tr>
<tr>
<td><strong><%= Company.human_attribute_name :company_user_email_suffixes %></strong></td>
<td><%= @company.company_user_email_suffixes.map(&:suffix).to_sentence %></td>
</tr>
<tr>
<td><strong><%= Company.human_attribute_name :image %></strong></td>
<td>
......
......@@ -63,8 +63,6 @@
<% resource.user_course_specific_roles.each do |ucsr| %>
<% next if ucsr.course_specific_role_id != csr.id %>
<div class="nested-fields list-group-item">
<%= link_to_if_rights ucsr.user, :name, admin_user_path(ucsr.user) %>
<%# Display removal option if the user can edit the resource %>
<% if can? :edit, resource %>
<%= modal_with_link('Remove role assignment', "remove-course-specific-role-assignment-#{ucsr.id}",
......@@ -81,6 +79,17 @@
<% end %>
<% end %>
<% end %>
<span class="list-group-item-heading">
<%= link_to_if_rights ucsr.user, :name, admin_user_path(ucsr.user) %>
</span>
<p class="list-group-item-text text-muted txt-italic"><%= ucsr.unregistered_email || ucsr.user.email %></p>
<% if ucsr.user_id && ucsr.unregistered_name %>
<p class="list-group-item-text c-gray txt-italic">
Name entered by client:
<span class="text-muted"><%= ucsr.unregistered_name %></span>
</p>
<% end %>
</div>
<% end %>
......
......@@ -154,7 +154,7 @@
let selectizeElement = $('#<%= selectize_id %>').selectize()[0].selectize
selectizeElement.addOption({
text: user.name,
text: JSON.stringify([user.name, user.email, user.affiliation]),
value: userId,
optgroup: user.affiliation === 'student' ? 'TU Delft Students' : 'TU Delft Employees'
})
......
......@@ -13,12 +13,22 @@
<script type="text/javascript">
$(document).on('turbolinks:load', function () {
let renderFunction = function (data, escape) {
let u = JSON.parse(data.text);
return '<div class="option">' +
'<span>' + escape(u[0]) + '</span><br>' +
'<span class="text-muted">' + escape(u[1]) + ' (' + escape(u[2]) + ')' + '</span>' +
'</div>';
};
let dropdown = $('#user_selection_dropdown_<%= unique_id %>').selectize({
sortField: 'text',
render: {
'option_create': function (data, escape) {
return '<div class="option create"><%= icon :fas, 'house-user' %> Find other TU Delft users</div>'
}
return '<div class="option create"><%= icon :fas, 'house-user' %> Find other TU Delft users</div>';
},
item: renderFunction,
option: renderFunction
},
create: function (input) {
// show ldap modal
......@@ -45,7 +55,15 @@
external
end
}, :to_s,
:id, :name,
:id, ->(u) { [u.name, u.email,
(
if u.role_staff? || u.role_admin?
'employee'
elsif u.role_student?
'student'
else
'external'
end)] },
{ prompt: true },
{ required: true, placeholder: 'Select a user', id: "user_selection_dropdown_#{ unique_id}" } %>
......
<div class="nested-fields list-group-item">
<div class="row">
<div class="col-sm-10">
<%= f.hidden_field :id %>
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">@</span>
<%= f.text_field :suffix, skip_label: true, placeholder: 'example.com', wrapper: false,
pattern: '^([a-z0-9-]+\.)+[a-z]{2,}$' %>
</div>
</div>
<div class="col-sm-2 text-right">
<%= cocoon_remove_link f %>
</div>
</div>
</div>
......@@ -7,9 +7,11 @@
- redirect_back [Boolean] whether the form should send a referrer header
(to redirect to the previous page instead of the company index)
- no_cancel [Boolean] whether the cancel button should be hidden
- no_horizontal [Boolean] whether the form should always be fully vertical
%>
<% redirect_back |= false %>
<% no_cancel |= false %>
<% no_horizontal |= false %>
<%= bootstrap_form_for company do |f| %>
<% if redirect_back %>
......@@ -25,8 +27,29 @@
<%= f.text_field :postal_code %>
<%= f.text_field :country, required: true %>
<%= f.url_field :website_url, placeholder: 'https://yourcompany.nl' %>
<%= f.check_box :allow_to_join, checked: company.allow_to_join, label: 'Allow users to join the company without an invitation' %>
<%= f.check_box :unconfirmed_employees_can_offer, checked: company.unconfirmed_employees_can_offer, label: "Allow unconfirmed employees to offer #{projects_name}" %>
<div class="row">
<div class="<%= no_horizontal ? 'col-md-12' : 'col-md-6' %>">
<%= f.check_box :allow_to_join, checked: company.allow_to_join, label: 'Allow users to join the company without an invitation' %>
<%= f.check_box :unconfirmed_employees_can_offer, checked: company.unconfirmed_employees_can_offer, label: "Allow unconfirmed employees to offer #{projects_name}" %>
</div>
<div class="<%= no_horizontal ? 'col-md-12' : 'col-md-6' %>">
<%= f.label :company_user_email_suffixes %>
<p class="txt-italic"><%= t 'companies.form.company_user_email_suffixes.explanation' %></p>
<div class="list-group cocoon">
<%= f.fields_for :company_user_email_suffixes do |ff| %>
<%= render 'companies/company_user_email_suffix_fields', f: ff %>
<% end %>
<%= link_to_add_association(
glyphicon_text('plus-sign', 'Add verified email suffix'),
f,
:company_user_email_suffixes,
partial: 'companies/company_user_email_suffix_fields',
class: 'list-group-item list-group-item--button add-btn',
'data-association-insertion-node' => 'this'
) %>
</div>
</div>
</div>
<div class="list-group cocoon" data-limit="1">
<%= f.fields_for :image do |ff| %>
<%= render 'general/image_fields', f: ff %>
......@@ -41,6 +64,6 @@
force_non_association_create: true
) %>
</div>
<%= f.submit value: "Create #{company_name.capitalize}", class: 'btn btn-primary' %>
<%= f.submit value: "#{f.object.new_record? ? 'Create' : 'Update'} #{company_name.capitalize}", class: 'btn btn-primary' %>
<%= link_to 'Cancel', url_for(company), class: 'btn btn-default' unless no_cancel %>
<% end %>
......@@ -2,7 +2,7 @@
<% modal_body do %>
<div class="panel panel-body pre-scrollable" style="min-height: 50vh">
<%= render 'companies/form', company: Company.new, company_name: current_user.company_name,
redirect_back: true, no_cancel: true %>
redirect_back: true, no_cancel: true, no_horizontal: true %>
</div>
<% end %>
<% modal_footer do %>
......
......@@ -19,6 +19,34 @@
<%= modal("Join a #{current_user.company_name.capitalize}", 'join-company') do %>
<% modal_body do %>
<% companies_email_join = Company.without_role(%i[registrar employee unconfirmed_employee], current_user)
.includes(:company_user_email_suffixes)
.where(company_user_email_suffixes: { suffix: current_user.email.split('@').last }) %>
<% if companies_email_join.any? %>
<div class="panel panel-body">
<p>
Based on your email address, you may join the join the following
<%= current_user.company_name.pluralize(companies_email_join.size) %> directly.
</p>
<ul class="list-group">
<% companies_email_join.each do |c| %>
<div class="list-group-item">
<div class="row">
<div class="col-sm-10">
<strong><%= c.name %></strong>
</div>
<div class="col-sm-2">
<%= bootstrap_form_tag url: join_companies_url, method: :post do |f| %>
<%= f.hidden_field :company, value: c.id %>
<%= f.submit "Join", class: 'btn btn-primary' %>
<% end %>
</div>
</div>
</div>
<% end %>
</ul>
</div>
<% end %>
<div class="panel panel-body">
<p>
You can join a <%= current_user.company_name %> by searching for its name,
......
......@@ -38,8 +38,12 @@
<% has_assignments = true %>
<div class="nested-fields list-group-item">
<%# If the role has been entered by a client, do not show the real name, but the name that the client has entered %>
<%= ucsr.unregistered_name || ucsr.user.name %>
(<%= ucsr.real_user ? ucsr.user.email : ucsr.unregistered_email %>)
<span class="list-group-item-heading">
<%= ucsr.unregistered_name || ucsr.user.name %>
</span>
<p class="list-group-item-text c-gray txt-italic">
<%= ucsr.real_user ? ucsr.user.email : ucsr.unregistered_email %>
</p>
</div>
<% end %>
<% if f %>
......
......@@ -85,6 +85,39 @@
<% end %>
</div>
</div>
<div class="panel panel-default mtx">
<div class="panel-heading">
<div class="row">
<div class="col-xs-8">Settings</div>
<% if can? :edit, @specific_offerer %>
<div class="col-xs-4 align text-right">
<%= glyphicon_link_to :pencil, 'Edit', edit_company_path(@specific_offerer), class: 'btn btn-default btn-xs' %>
</div>
<% end %>
</div>
</div>
<div class="panel-body">
<div class="row">
<label class="col-sm-6 control-label"><%= Company.human_attribute_name :allow_to_join %></label>
<div class="col-sm-6">
<%= check_box :allow_to_join, 'allow_to_join_checkbox',
{checked: @specific_offerer.allow_to_join, disabled: true} %>
</div>
</div>
<div class="row">
<label class="col-sm-6 control-label"><%= Company.human_attribute_name :unconfirmed_employees_can_offer %></label>
<div class="col-sm-6">
<%= check_box :unconfirmed_employees_can_offer, 'unconfirmed_employees_can_offer_checkbox',
{ checked: @specific_offerer.unconfirmed_employees_can_offer, disabled: true } %>
</div>
</div>
<div class="row">
<label class="col-sm-6 control-label"><%= Company.human_attribute_name :company_user_email_suffixes %></label>
<div class="col-sm-6"><%= @specific_offerer.company_user_email_suffixes.map(&:suffix).to_sentence %></div>
</div>
</div>
</div>
<% end %>
</div>
</div>
This diff is collapsed.
......@@ -74,7 +74,7 @@ end
task :restart_passenger do
on roles(:app) do
with passenger_instance_registry_dir: deploy_path.join('passenger') do
execute :'passenger-config', 'restart-app', deploy_path, '--ignore-app-not-running'
sudo :'passenger-config', 'restart-app', deploy_path, '--ignore-app-not-running'
end
end
end
......
......@@ -29,5 +29,6 @@ en:
updated_at: Updated
projects_count: Number of Projects
image: Logo
company_user_email_suffixes: Verified email suffixes
allow_to_join: Allow users to join the company without an invitation
unconfirmed_employees_can_offer: Allow unconfirmed employees to offer projects
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment