diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 023d573..3fce5a9 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -119,7 +119,7 @@ mandatory_params &&= false end if params[:user][:org_id].blank? && params[:user][:other_organisation].blank? - message += _('Please select an organisation, or select Other.') + message += _('Please select an organisation from the list, or enter your organisation\'s name.') mandatory_params &&= false end if mandatory_params # has the user entered all the details diff --git a/app/views/devise/registrations/_external_identifier_shibboleth.html.erb b/app/views/devise/registrations/_external_identifier_shibboleth.html.erb index 8e7283b..0d09d9e 100644 --- a/app/views/devise/registrations/_external_identifier_shibboleth.html.erb +++ b/app/views/devise/registrations/_external_identifier_shibboleth.html.erb @@ -1,19 +1,19 @@ <% if id.nil? || id.identifier == '' %> - <%= link_to "#{_("Link account with #{scheme.description} ID")}", + <%= link_to "#{_("Link your institutional credentials")}", Rails.application.routes.url_helpers.send( "user_#{scheme.name.downcase}_omniauth_authorize_path" ), 'data-toggle': "tooltip", - title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") + title: _('Link your institutional credentials to access your account with them.') %> <% else %> <% if scheme.user_landing_url.nil? %> <%= _("Your account has been linked to #{scheme.description}.") %> <% else %> - <%= link_to "#{_("Your account has been linked to #{scheme.description}.")}", "#{scheme.user_landing_url}/#{id.identifier}", target: '_blank', 'data-toggle': "tooltip", - title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> + <%= link_to "#{_("Your account has been linked to your institution.")}", "#{scheme.user_landing_url}/#{id.identifier}", target: '_blank', 'data-toggle': "tooltip", + title: _('Your account has been linked to your institution. You can now login with that method.') %> <% end %> - <% title = _("Unlink your account from #{scheme.description}. You can link again at any time.") %> + <% title = _("Unlink your account from your institution. You can link again at any time.") %> <%= link_to ''.html_safe, destroy_user_identifier_path(id), method: :delete, title: title, 'aria-label': title, 'data-toggle': "tooltip", - data: {confirm: _("Are you sure you want to unlink #{scheme.description} ID?")} %> + id: 'unlink-shibboleth', data: {confirm: _("Are you sure you want to unlink your institutional credentials?")} %> <% end %> diff --git a/app/views/devise/registrations/_personal_details.html.erb b/app/views/devise/registrations/_personal_details.html.erb index c928f7f..842d2e9 100644 --- a/app/views/devise/registrations/_personal_details.html.erb +++ b/app/views/devise/registrations/_personal_details.html.erb @@ -28,7 +28,10 @@ <%= f.password_field(:password, class: "form-control", "aria-required": true) %> - <%= render partial: "shared/org_dropdown", locals: {f: f, default_org: @default_org, orgs: @orgs, allow_other_orgs: true} %> + <% org_admin = (current_user.can_org_admin? && !current_user.can_super_admin?) %> +
> + <%= render partial: "shared/my_org", locals: {f: f, default_org: @default_org, orgs: @orgs, allow_other_orgs: true} %> +
<% if MANY_LANGUAGES %>
@@ -43,18 +46,23 @@ <% @identifier_schemes.each do |scheme| %>
<% if scheme.name == 'shibboleth' %> + <% if current_user.org.present? %> + <% end %> <% else %> - <%= label_tag(:scheme_name, scheme.name.capitalize, class: 'control-label') %> + <%= label_tag(:scheme_name, scheme.name.capitalize, class: 'control-label') %> <% end %> -
- <%= render partial: "external_identifier_#{scheme.name}", - locals: { scheme: scheme, - id: current_user.identifier_for(scheme)} %> -
+ + <% if scheme.name != 'shibboleth' || (scheme.name == 'shibboleth' && current_user.org.present?) %> +
+ <%= render partial: "external_identifier_#{scheme.name}", + locals: { scheme: scheme, + id: current_user.identifier_for(scheme)} %> +
+ <% end %>
<% end %> diff --git a/app/views/shared/_create_account_form.html.erb b/app/views/shared/_create_account_form.html.erb index 181362f..55e7e7a 100644 --- a/app/views/shared/_create_account_form.html.erb +++ b/app/views/shared/_create_account_form.html.erb @@ -14,20 +14,11 @@
- <%= f.label _('Organisation'), for: :user_org_name, class: "control-label" %> - <%= render partial: "shared/accessible_combobox", - locals: {name: 'user[org_name]', - id: 'user_org_name', - default_selection: @default_org, - models: Org.where("parent_id IS NULL").order("sort_name ASC, name ASC"), - attribute: 'name', - classes: 'form-control'} %> - - <% other_organisations = Org.where("parent_id IS ? AND is_other = ?", nil, true).pluck(:id) %> - <%= f.text_field :other_organisation, autocomplete: "off", class: "form-control", 'data-toggle': "tooltip", title: _('Please enter the name of your organisation.'), style: 'display: none;' %> - <%= _("My organisation isn't listed.") %> + <%= render partial: "shared/my_org", + locals: {f: f, default_org: @default_org, + orgs: Org.where("parent_id IS NULL").order("sort_name ASC, name ASC"), allow_other_orgs: true} %>
- +
<%= f.label(:password, _('Password'), class: "control-label") %> <%= f.password_field(:password, class: "form-control", "aria-required": true) %> diff --git a/app/views/shared/_my_org.html.erb b/app/views/shared/_my_org.html.erb new file mode 100644 index 0000000..83acbac --- /dev/null +++ b/app/views/shared/_my_org.html.erb @@ -0,0 +1,18 @@ +<%= f.label :org_name, _('Organisation'), class: 'control-label' %> +<%= render partial: "shared/accessible_combobox", + locals: {name: "user[org_name]", + id: "user_org_name", + default_selection: default_org, + models: orgs, + attribute: 'name'} %> + +<% if allow_other_orgs %> + <%= f.text_field :other_organisation, autocomplete: "off", class: "form-control", + placeholder: _('Please enter the name of your organisation') %> +<% end %> + +<% if allow_other_orgs %> +
+ <%= _('My organisation isn\'t listed.') %> +
+<% end %> diff --git a/app/views/shared/_org_dropdown.html.erb b/app/views/shared/_org_dropdown.html.erb deleted file mode 100644 index f186af4..0000000 --- a/app/views/shared/_org_dropdown.html.erb +++ /dev/null @@ -1,25 +0,0 @@ -<% others = allow_other_orgs ||= false %> -<% left_size = (others ? 5 : 8) %> - -
- <%= f.label :org_name, _('Organisation'), class: 'control-label' %> - <%= render partial: "shared/accessible_combobox", - locals: {name: "user[org_name]", - id: "user_org_name", - default_selection: default_org, - models: orgs, - attribute: 'name'} %> - - <% if others %> - <%= f.text_field :other_organisation, autocomplete: "off", class: "form-control hide", - placeholder: _('Please enter the name of your organisation') %> - <% end %> -
- -<% if others %> -
- -
-<% end %> diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js index bb948af..de9dba9 100644 --- a/lib/assets/javascripts/application.js +++ b/lib/assets/javascripts/application.js @@ -33,6 +33,7 @@ import './views/questions/show'; import './views/question_options/index'; import './views/shared/create_account_form'; +import './views/shared/my_org'; import './views/shared/sign_in_form'; import './views/templates/edit'; import './views/templates/show'; diff --git a/lib/assets/javascripts/constants.js b/lib/assets/javascripts/constants.js index 376f2e5..f2d030d 100644 --- a/lib/assets/javascripts/constants.js +++ b/lib/assets/javascripts/constants.js @@ -20,5 +20,9 @@ export const PLAN_VISIBILITY_WHEN_NOT_TEST = 'Private'; export const PLAN_VISIBILITY_WHEN_NOT_TEST_TOOLTIP = 'Private: restricted to me and people I invite.'; +export const DISABLE_ORG_COMBO_MESSAGE = 'You must unlink your account before changing your organisation.'; +export const OTHER_ORG_HIDE_COMBO_MESSAGE = 'My organisation isn\'t listed.'; +export const OTHER_ORG_SHOW_COMBO_MESSAGE = 'Select the organisation from a list.'; + export const SHIBBOLETH_DISCOVERY_SERVICE_HIDE_LIST = 'Hide list.'; export const SHIBBOLETH_DISCOVERY_SERVICE_SHOW_LIST = 'See the full list of partner institutions.'; diff --git a/lib/assets/javascripts/utils/autoComplete.js b/lib/assets/javascripts/utils/autoComplete.js index 50c3658..9391f7b 100644 --- a/lib/assets/javascripts/utils/autoComplete.js +++ b/lib/assets/javascripts/utils/autoComplete.js @@ -1,4 +1,6 @@ import debounce from '../utils/debounce'; +import { isObject } from '../utils/isType'; +import { isValidText } from '../utils/isValidInputType'; /* * Looks up the id for the text selected by the user in the jquery autocomplete combobox and @@ -10,10 +12,10 @@ const crosswalk = $(`#${$(el).attr('id')}_crosswalk`); const idField = $(el).attr('id').replace(/_name/, '_id'); - if (crosswalk && idField) { + if (isObject(crosswalk) && isObject($(idField))) { const json = JSON.parse(`${$(crosswalk).val().replace(/\\"/g, '"').replace(/\\'/g, '\'')}`); - const selection = json[$(el).val()]; - $(`#${idField}`).val(selection === undefined ? '' : selection).change(); + const selection = (json[$(el).val()] === undefined ? '' : json[$(el).val()]); + $(el).parent().siblings(`#${idField}`).val(selection); } }; @@ -23,10 +25,12 @@ */ const toggleClearButton = (el) => { const clearButton = $(el).parent().find('.combobox-clear-button'); - if ($(el).val().trim().length <= 0) { - $(clearButton).addClass('hidden'); - } else { - $(clearButton).removeClass('hidden'); + if (isObject(clearButton)) { + if (isValidText($(el).val())) { + $(clearButton).removeClass('hidden'); + } else { + $(clearButton).addClass('hidden'); + } } }; @@ -36,20 +40,25 @@ */ export default () => { $('.js-combobox').each((idx, el) => { + // Swap out the 'X' with a fontawesome icon + $(el).siblings('.combobox-clear-button').text('') + .addClass('fa') + .addClass('fa-times-circle'); + const debounced = debounce((e) => { toggleClearButton(e); updateIdField(e); - }, 300); + }, 100); // When the value in the combobox changes update the hidden id field - $(el).on('keyup, blur', (e) => { + $(el).on('keyup blur', (e) => { debounced($(e.currentTarget)); }); // Clear the text and hide the button when the user clicks the clear button $(el).parent().find('.combobox-clear-button').on('click', () => { - $(el).val('').focus(); - debounced($(el)); + $(el).val(''); + debounced(el); }); // add a Bootstrap 'hide' class to the invisible help text @@ -57,8 +66,5 @@ // Show/hide the clear button on page load toggleClearButton(el); - - // Update the hidden ID field on initialize - updateIdField(el); }); }; diff --git a/lib/assets/javascripts/views/devise/registrations/edit.js b/lib/assets/javascripts/views/devise/registrations/edit.js index a03e07e..3b6b122 100644 --- a/lib/assets/javascripts/views/devise/registrations/edit.js +++ b/lib/assets/javascripts/views/devise/registrations/edit.js @@ -1,5 +1,5 @@ import ariatiseForm from '../../../utils/ariatiseForm'; - +import { DISABLE_ORG_COMBO_MESSAGE } from '../../../constants'; import { addMatchingPasswordValidator, togglisePasswords } from '../../../utils/passwordHelper'; $(() => { @@ -8,4 +8,13 @@ ariatiseForm({ selector: '#preferences_registration_form' }); addMatchingPasswordValidator({ selector: '#password_details_registration_form' }); togglisePasswords({ selector: '#password_details_registration_form' }); + + // Disable organisation autocomplete if the user has linked their account to Shibboleth + if ($('.identifier-scheme #unlink-shibboleth').length > 0) { + $('#org-controls #user_org_name').attr('disabled', true) + .attr('data-toggle', 'tooltip') + .attr('title', DISABLE_ORG_COMBO_MESSAGE); + $('#org-controls .combobox-clear-button').hide(); + $('#other_org_toggle a').hide(); + } }); diff --git a/lib/assets/javascripts/views/shared/my_org.js b/lib/assets/javascripts/views/shared/my_org.js new file mode 100644 index 0000000..30d745a --- /dev/null +++ b/lib/assets/javascripts/views/shared/my_org.js @@ -0,0 +1,39 @@ +import { OTHER_ORG_HIDE_COMBO_MESSAGE, OTHER_ORG_SHOW_COMBO_MESSAGE } from '../../constants'; +import { isValidNumber, isValidText } from '../../utils/isValidInputType'; + +$(() => { + const combo = $('.combobox-container'); + const id = $('input#user_org_id'); + const text = $('input#user_other_organisation'); + const link = $('#other_org_toggle a'); + + // Toggle between the autocomplete dropdown box and the other org textbox + const toggleCombobox = (show) => { + if (show) { + $(text).hide(); + $(combo).fadeIn(); + $(link).text(OTHER_ORG_HIDE_COMBO_MESSAGE); + } else { + $(combo).hide(); + $(text).fadeIn(); + $(link).text(OTHER_ORG_SHOW_COMBO_MESSAGE); + } + }; + + // Toggle between the combobox and textbox when the link is clicked + $(link).click((e) => { + e.preventDefault(); + if ($(combo).css('display') === 'none') { + $(text).val('').hide(); + toggleCombobox(true); + } else { + $(combo).find('.combobox-clear-button').click(); + toggleCombobox(false); + } + }); + + // Display the appropriate input type on page load + if ($(id).length > 0 && $(text).length > 0) { + toggleCombobox(isValidNumber($(id).val()) || !isValidText($(text).val())); + } +}); diff --git a/lib/assets/package-lock.json b/lib/assets/package-lock.json index fdd4011..b95b6c5 100644 --- a/lib/assets/package-lock.json +++ b/lib/assets/package-lock.json @@ -7845,9 +7845,9 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/timeago/-/timeago-1.6.1.tgz", "integrity": "sha512-mvXlF+JKeclH6AxvBDcwLvDFJyfpjC9Qs3zs6O97u4WBoHYlnnx/QyE7h5INRkGnT53rS8BGUTRhIr661VqTZA==", - "requires": { + "requires": { "jquery": "3.2.1" - } + } }, "timers-browserify": { "version": "2.0.4", diff --git a/lib/assets/stylesheets/overrides.scss b/lib/assets/stylesheets/overrides.scss index 4773088..d113fe5 100644 --- a/lib/assets/stylesheets/overrides.scss +++ b/lib/assets/stylesheets/overrides.scss @@ -218,3 +218,64 @@ } } +/* JQuery Autocomplete Styling */ +/* ---------------------------------------------------- */ +.invisible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +.combobox-container { + position: relative; +} + +.combobox-suggestions { + position: absolute; + left: 0; + width: 100%; + background: $white; + z-index: 99; +} +.combobox-suggestion { + color: #grey; + border-bottom: 1px solid $grey; + border-left: 1px solid $grey; + border-right: 1px solid $grey; + padding: 5px 10px 5px 10px; + cursor: pointer; + text-align: left; +} +.combobox-suggestion:first-child { + border-top: 1px solid $grey; +} +.combobox-suggestion:hover, +.combobox-suggestion:focus { + color: $white; + background-color: $grey; +} + +.combobox-clear-button, .combobox-clear-button:hover, .combobox-clear-button:focus { + cursor: pointer; + display: inline; + position: absolute; + top: 0; + right: 0; + margin-top: 2px; + margin-right: -35px; + border: none; + background: transparent; + color: $grey; + padding-top: 3px; + font-size: 16pt; +} + +/* http://geektnt.com/how-to-remove-x-from-search-input-field-on-chrome-and-ie.html */ +.js-combobox[type=text]::-ms-clear { display: none; width: 0; height: 0; } +.js-combobox[type=text]::-ms-reveal { display: none; width: 0; height: 0; } +