diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index 4b79893..43e0c55 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -9,21 +9,22 @@ access_level = params[:role][:access_level].to_i set_access_level(access_level) + message = '' if params[:user].present? if @role.plan.owner.present? && @role.plan.owner.email == params[:user] flash[:notice] = _('Cannot share plan with %{email} since that email matches with the owner of the plan.') % {email: params[:user]} else - if Role.find_by(plan: @role.plan, user: User.find_by(email: params[:user])) # role already exists + user = User.where_case_insensitive('email',params[:user]).first + if Role.find_by(plan: @role.plan, user: user) # role already exists flash[:notice] = _('Plan is already shared with %{email}.') % {email: params[:user]} - else - message = _('Plan shared with %{email}.') % {email: params[:user]} - user = User.find_by(email: params[:user]) + else if user.nil? registered = false User.invite!(email: params[:user]) - message = _('Invitation to %{email} issued successfully.') % {email: params[:user]} + message = _('Invitation to %{email} issued successfully. \n') % {email: params[:user]} user = User.find_by(email: params[:user]) end + message += _('Plan shared with %{email}.') % {email: user.email} @role.user = user if @role.save if registered then UserMailer.sharing_notification(@role, current_user).deliver_now end diff --git a/app/models/settings/template.rb b/app/models/settings/template.rb index 59ac3de..a9fd6f8 100644 --- a/app/models/settings/template.rb +++ b/app/models/settings/template.rb @@ -1,11 +1,11 @@ module Settings class Template < RailsSettings::SettingObject - + #attr_accessible :var, :target, :target_id, :target_type VALID_FONT_FACES = [ - 'Arial, Helvetica, Sans-Serif', - '"Times New Roman", Times, Serif' + '"Times New Roman", Times, Serif', + 'Arial, Helvetica, Sans-Serif' ] VALID_FONT_SIZE_RANGE = (8..14) @@ -17,13 +17,13 @@ DEFAULT_SETTINGS = { formatting: { margin: { # in millimeters - top: 20, - bottom: 20, - left: 20, - right: 20 + top: 10, + bottom: 10, + left: 10, + right: 10 }, font_face: VALID_FONT_FACES.first, - font_size: 12 # pt + font_size: 10 # pt }, max_pages: 3, fields: { diff --git a/app/models/user.rb b/app/models/user.rb index d4e0543..79f5be7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -276,5 +276,13 @@ def deliver_invitation(options = {}) super(options.merge(subject: _('A Data Management Plan in %{application_name} has been shared with you') % {application_name: Rails.configuration.branding[:application][:name]})) end + ## + # Case insensitive search over User model + # @param field [string] The name of the field being queried + # @param val [string] The string to search for, case insensitive + # @return [ActiveRecord::Relation] The result of the search + def self.where_case_insensitive(field, val) + User.where("lower(#{field}) = ?", val.downcase) + end end diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index cbf6238..b37fa86 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -12,7 +12,6 @@
- - <%= render :partial => 'shared/register_form', locals: {extended: true} %> + <%= render :partial => 'shared/create_account_form', locals: {extended: false} %>
diff --git a/app/views/layouts/_es5_scripts.html.erb b/app/views/layouts/_es5_scripts.html.erb index a16e37f..6a59ea3 100644 --- a/app/views/layouts/_es5_scripts.html.erb +++ b/app/views/layouts/_es5_scripts.html.erb @@ -9,7 +9,6 @@ <%= javascript_include_tag 'jquery.tablesorter.min.js' %> <%= javascript_include_tag 'jquery.tablesorter.widgets.min.js' %> <%= javascript_include_tag 'jquery.timeago.js' %> - <%= javascript_include_tag 'tinymce.min.js' %> <%= javascript_include_tag 'bootstrap.min.js' %> @@ -45,7 +44,6 @@ <%= javascript_include_tag 'views/plans/export_configure.js' %> <%= javascript_include_tag 'views/plans/index.js' %> <%= javascript_include_tag 'views/plans/new.js' %> - <%= javascript_include_tag 'views/plans/show.js' %> <%= javascript_include_tag 'views/registrations/sign_in_sign_up.js' %> <%#= javascript_include_tag 'views/shared/accessible_combobox.js' %> <%#= javascript_include_tag 'views/shared/accessible_submit_button.js' %> diff --git a/app/views/plans/_edit_details.html.erb b/app/views/plans/_edit_details.html.erb index 7380802..4da60a0 100644 --- a/app/views/plans/_edit_details.html.erb +++ b/app/views/plans/_edit_details.html.erb @@ -1,132 +1,208 @@ -<%= form_for plan, html: {method: :put, class: "roadmap-form"} do |f| %> -
-
-
-
- <%= f.label "#{_('Project Title')}", for: :title, class: 'required' %> - <%= f.text_field :title, class: "input-large left-indent required", 'data-toggle': "tooltip", - 'data-content': _('If applying for funding, state the name exactly as in the grant proposal.') %> - -
- <%= f.hidden_field :visibility %> - /> - -
-
- -
- <%= f.label "#{_('Funder')}", for: :funder_name %> - <%= f.text_field :funder_name, class: "input-large left-indent" %> -
- -
- <%= f.label :grant_number %> - <%= f.text_field :grant_number, class: 'input-small left-indent', 'data-toggle': "tooltip", - 'data-content': _('Grant reference number if applicable [POST-AWARD DMPs ONLY]') %> -
- -
- <%= f.label _('Project Abstract'), for: :description %> - <%= f.text_area :description, class: "left-indent" %> - - -
- -
- <%= f.label _('ID'), for: :identifier %> - <%= f.text_field :identifier, class: 'input-small left-indent', 'data-toggle': "tooltip", - 'data-content': _('A pertinent ID as determined by the funder and/or institution.') %> -
- -
- -
- -
- -
- <%= f.label _('Name'), for: :principal_investigator, 'aria-label': _('Principal Investigator Name') %> - <%= f.text_field :principal_investigator, class: 'input-small left-indent' %> -
- -
- <%= f.label _('ORCID iD'), for: :principal_investigator_identifier, 'aria-label': _('Principal Investigator ORCID iD') %> - <%= f.text_field :principal_investigator_identifier, class: 'input-small left-indent' %> -
- -
- <%= f.label _('Email'), for: :principal_investigator_email, 'aria-label': _('Principal Investigator Email') %> - <%= f.email_field :principal_investigator_email, class: 'input-small left-indent' %> - -
- -
-
- <%= f.label(:data_contact, raw("#{_('Data Contact Person')}")) %> -
-
- /> - -
-
- <%= f.label _('Name'), for: :data_contact, 'aria-label': _('Data Contact Name') %> - <%= f.text_field :data_contact, class: 'input-small left-indent' %> -
-
- <%= f.label _('Phone'), for: :data_contact_phone, 'aria-label': _('Data Contact Phone') %> - <%= f.telephone_field :data_contact_phone, class: 'input-small left-indent' %> -
-
- <%= f.label _('Email'), for: :data_contact_email, 'aria-label': _('Data Contact Email') %> - <%= f.email_field :data_contact_email, class: 'input-small left-indent' %> - -
- -
-
-
- <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'save-details-button', - val: _('Save'), - disabled_initially: true, - classes: 'small-input-button left-indent', - tooltip: _('You must provide a project title and any email addresses must be valid.')} %> -
-
-
- -
-
-

<%= _('Plan Guidance Configuration') %>

-

<%= _('To help you write your plan, %{application_name} can show you guidance from a variety of organisations.') % {application_name: Rails.configuration.branding[:application][:name]} %>

-

<%= _('Select up to 6 organisations to see their guidance.') %>

- -
    - <%= render partial: "guidance_choices", - locals: {choices: @important_ggs, form: f, - current_selections: @selected_guidance_groups} %> -
- - <%= _('See guidance from additional organisations') %> - - -
-
+
+
+ <%= form_for plan, html: {method: :put, class: 'form-horizontal edit_plan' } do |f| %> +
+
+ <%= f.label(:title, _('Project Title'), class: 'control-label') %> +
+
+ <%= f.text_field( + :title, + class: 'form-control', + "aria-required": true) %> +
+
+ +
+
+
+ <%= f.hidden_field :visibility %> +
+ <%= f.label(:is_test, _('mock project for testing, practice, or educational purposes'), class: 'control-label') %> +
+
+ <%= check_box_tag(:is_test,1, @plan.is_test?) %> +
+
+
+
+ <%= f.label(:funder_name, _('Funder'), class: 'control-label') %> +
+
+ <%= f.text_field( + :funder_name, + class: 'form-control', + "aria-required": false) %> +
+
+
+
+ <%= f.label(:grant_number, _('Grant Number'), class: 'control-label') %> +
+
+ <%= f.text_field( + :grant_number, + class: 'form-control', + "aria-required": false) %> +
+
+ +
+
+
+
+ <%= f.label(:description, _('Project Abstract'), class: 'control-label') %> +
+
+ <%= f.text_area( + :description, + class: 'form-control tinymce', + "aria-required": false) %> +
+
+ +
+
+
+
+ <%= f.label(:identifier, _('ID'), class: 'control-label') %> +
+
+ <%= f.text_field( + :identifier, + class: 'form-control', + "aria-required": false) %> +
+
+ +
+
+
+

<%= _('Principal Investigator') %>

+
+
+ <%= f.label(:principal_investigator, _('Name'), class: 'control-label') %> +
+
+ <%= f.text_field( + :principal_investigator, + class: 'form-control', + "aria-required": false) %> +
+
+
+
+ <%= f.label(:principal_investigator_identifier, _('ORCID iD'), class: 'control-label') %> +
+
+ <%= f.text_field( + :principal_investigator_identifier, + class: 'form-control', + "aria-required": false) %> +
+
+
+
+ <%= f.label(:principal_investigator_email, _('Email'), class: 'control-label') %> +
+
+ <%= f.email_field( + :principal_investigator_email, + class: 'form-control', + "aria-required": false, + "data-validation": "email") %> +
+
+
+

<%= _('Data Contact Person') %>

+
+
+ <%= f.label(:data_contact, _('Name'), class: 'control-label') %> +
+
+ <%= f.text_field( + :data_contact, + class: 'form-control', + "aria-required": false) %> +
+
+
+
+ <%= f.label(:data_contact_phone, _('Phone'), class: 'control-label') %> +
+
+ <%= f.text_field( + :data_contact_phone, + class: 'form-control', + "aria-required": false) %> +
+
+
+
+ <%= f.label(:data_contact_email, _('Email'), class: 'control-label') %> +
+
+ <%= f.email_field( + :data_contact_email, + class: 'form-control', + "aria-required": false, + "data-validation": "email") %> +
+
+ <%= f.button(_('Submit'), class: "btn btn-default", type: "submit") %> + <% end %>
-<% end %> \ No newline at end of file +
+

<%= _('Plan Guidance Configuration') %>

+

<%= _('To help you write your plan, %{application_name} can show you guidance from a variety of organisations.') % + {application_name: Rails.configuration.branding[:application][:name]} %> +

+

<%= _('Select up to 6 organisations to see their guidance.') %>

+ +
+
\ No newline at end of file diff --git a/app/views/plans/_show_details.html.erb b/app/views/plans/_show_details.html.erb index 0919c84..8c9d78d 100644 --- a/app/views/plans/_show_details.html.erb +++ b/app/views/plans/_show_details.html.erb @@ -1,4 +1,4 @@ -
+
<%= _('Project Title') %>
<%= plan.title %>
<%= _('Funder') %>
@@ -7,26 +7,26 @@
<%= plan.grant_number %>
<%= _('Project Abstract') %>
<%= raw plan.description %>
-
<%= _('Id') %>
+
<%= _('ID') %>
<%= plan.identifier %>

-
-
<%= _('Principal Investigator') %>
-
-
<%= _('Name') %>
-
<%= plan.principal_investigator %>
-
<%= _('ORCID') %>
-
<%= plan.principal_investigator_identifier %>
-
<%= _('Email') %>
-
<%= plan.principal_investigator_email %>
+

<%= _('Principal Investigator') %>

+
+
<%= _('Name') %>
+
<%= plan.principal_investigator %>
+
<%= _('ORCID iD') %>
+
<%= plan.principal_investigator_identifier %>
+
<%= _('Email') %>
+
<%= plan.principal_investigator_email %>

-
-
<%= _('Data Contact Person') %>
-
+

<%= _('Data Contact Person') %>

+
<%= _('Name') %>
<%= plan.data_contact %>
+
<%= _('Phone') %>
+
<%= plan.data_contact_phone %>
<%= _('Email') %>
<%= plan.data_contact_email %>
diff --git a/app/views/plans/show.html.erb b/app/views/plans/show.html.erb index 85c18ec..8736900 100644 --- a/app/views/plans/show.html.erb +++ b/app/views/plans/show.html.erb @@ -7,39 +7,10 @@
-
- - - - -
-
- <% if @plan.editable_by?(current_user) %> - <%= render partial: 'edit_details', locals: {plan: @plan, visibility: @visibility} %> - <% else %> - <%= render partial: 'show_details', locals: {plan: @plan, visibility: @visibility} %> - <% end %> -
-
-
+ <% if @plan.editable_by?(current_user) %> + <%= render partial: 'edit_details', layout: 'shared/plan_navigation', locals: {plan: @plan, visibility: @visibility} %> + <% else %> + <%= render partial: 'show_details', locals: {plan: @plan, visibility: @visibility} %> + <% end %>
\ No newline at end of file diff --git a/app/views/shared/_plan_navigation.html.erb b/app/views/shared/_plan_navigation.html.erb index 717a0be..1c65b33 100644 --- a/app/views/shared/_plan_navigation.html.erb +++ b/app/views/shared/_plan_navigation.html.erb @@ -1,22 +1,21 @@ -
<%= yield %> diff --git a/db/migrate/20170702012742_ensure_indexes_in_place.rb b/db/migrate/20170702012742_ensure_indexes_in_place.rb index 6bd9410..367ce5f 100644 --- a/db/migrate/20170702012742_ensure_indexes_in_place.rb +++ b/db/migrate/20170702012742_ensure_indexes_in_place.rb @@ -1,8 +1,12 @@ class EnsureIndexesInPlace < ActiveRecord::Migration def change #users_perms + remove_foreign_key :users_perms, :perms + remove_foreign_key :users_perms, :users remove_index :users_perms, name: 'index_users_perms_on_user_id_and_perm_id' add_index :users_perms, :user_id + add_foreign_key :users_perms, :perms + add_foreign_key :users_perms, :users #user_identifiers add_index :user_identifiers, :user_id #roles @@ -27,14 +31,22 @@ #annotations add_index :annotations, :question_id #question_themes + remove_foreign_key :questions_themes, :questions + remove_foreign_key :questions_themes, :themes remove_index :questions_themes, name: 'question_theme_index' remove_index :questions_themes, name: 'theme_question_index' add_index :questions_themes, :question_id + add_foreign_key :questions_themes, :questions + add_foreign_key :questions_themes, :themes #question_options add_index :question_options, :question_id #answers_question_options + remove_foreign_key :answers_question_options, :answers + remove_foreign_key :answers_question_options, :question_options remove_index :answers_question_options, name: 'answer_question_option_index' remove_index :answers_question_options, name: 'question_option_answer_index' add_index :answers_question_options, :answer_id + add_foreign_key :answers_question_options, :answers + add_foreign_key :answers_question_options, :question_options end end diff --git a/db/seeds.rb b/db/seeds.rb index 6c4aded..fabb9a8 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -8,7 +8,7 @@ {name: 'orcid', description: 'ORCID', active: true, logo_url:'http://orcid.org/sites/default/files/images/orcid_16x16.png', user_landing_url:'https://orcid.org' }, - {name: 'shibboleth', description: 'your institutional credentials', active: true, + {name: 'shibboleth', description: 'Your institutional credentials', active: true, }, ] identifier_schemes.map{ |is| IdentifierScheme.create!(is) if IdentifierScheme.find_by(name: is[:name]).nil? } @@ -16,13 +16,13 @@ # Question Formats # ------------------------------------------------------- question_formats = [ - {title: "Text area", option_based: false}, - {title: "Text field", option_based: false}, - {title: "Radio buttons", option_based: true}, - {title: "Check box", option_based: true}, - {title: "Dropdown", option_based: true}, - {title: "Multi select box", option_based: true}, - {title: "Date", option_based: false} + {title: "Text area", option_based: false, formattype: 0}, + {title: "Text field", option_based: false, formattype: 1}, + {title: "Radio buttons", option_based: true, formattype: 2}, + {title: "Check box", option_based: true, formattype: 3}, + {title: "Dropdown", option_based: true, formattype: 4}, + {title: "Multi select box", option_based: true, formattype: 5}, + {title: "Date", option_based: true, formattype: 6} ] question_formats.map{ |qf| QuestionFormat.create!(qf) if QuestionFormat.find_by(title: qf[:title]).nil? } @@ -154,7 +154,9 @@ # ------------------------------------------------------- token_permission_types = [ {token_type: 'guidances', text_description: 'allows a user access to the guidances api endpoint'}, - {token_type: 'plans', text_description: 'allows a user access to the plans api endpoint'} + {token_type: 'plans', text_description: 'allows a user access to the plans api endpoint'}, + {token_type: 'templates', text_description: 'allows a user access to the templates api endpoint'}, + {token_type: 'statistics', text_description: 'allows a user access to the statistics api endpoint'} ] token_permission_types.map{ |tpt| TokenPermissionType.create!(tpt) if TokenPermissionType.find_by(token_type: tpt[:token_type]).nil? } diff --git a/lib/assets/.eslintrc.json b/lib/assets/.eslintrc.json index 95727a0..b2243c8 100644 --- a/lib/assets/.eslintrc.json +++ b/lib/assets/.eslintrc.json @@ -1,7 +1,7 @@ { "extends": "airbnb-base", "env": { - "jquery": true, - "jasmine": true + "jasmine": true, + "jquery": true } } diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js index 401088c..9328f23 100644 --- a/lib/assets/javascripts/application.js +++ b/lib/assets/javascripts/application.js @@ -2,4 +2,5 @@ import './views/devise/invitations/edit'; import './views/devise/passwords/edit'; import './views/devise/passwords/new'; +import './views/plans/edit_details'; import './views/plans/share'; diff --git a/lib/assets/javascripts/utils/tinymce.js b/lib/assets/javascripts/utils/tinymce.js new file mode 100644 index 0000000..d0b7e37 --- /dev/null +++ b/lib/assets/javascripts/utils/tinymce.js @@ -0,0 +1,108 @@ +// Import TinyMCE +import tinymce from 'tinymce/tinymce'; +// Import TinyMCE theme +import 'tinymce/themes/modern/theme'; +// Plugins +import 'tinymce/plugins/table'; +import 'tinymce/plugins/autoresize'; +import 'tinymce/plugins/link'; +import 'tinymce/plugins/paste'; +import 'tinymce/plugins/advlist'; +// Other dependencies +import { isObject, isString } from './isType'; + +// Configuration extracted from https://www.tinymce.com/docs/advanced/usage-with-module-loaders/ +require.context( + 'file-loader?name=./javascripts/[path][name].[ext]&context=node_modules/tinymce!tinymce/skins', + true, + /.*/, +); + +export const defaultOptions = { + selector: '.tinymce', + statusbar: false, + menubar: false, + toolbar: 'bold italic | bullist numlist | link | table', + plugins: 'table autoresize link paste advlist', + advlist_bullet_styles: 'circle,disc,square', // Only disc bullets display on htmltoword + target_list: false, + autoresize_min_height: 130, + autoresize_bottom_margin: 10, + extended_valid_elements: 'iframe[tooltip] , a[href|target=_blank]', + paste_auto_cleanup_on_paste: true, + paste_remove_styles: true, + paste_retain_style_properties: 'none', + paste_convert_middot_lists: true, + paste_remove_styles_if_webkit: true, + paste_remove_spans: true, + paste_strip_class_attributes: 'all', + table_default_attributes: { + border: 1, + }, +}; + +export const Tinymce = { + /* + Initialises a tinymce editor given the object passed. If a non-valid object is passed, + the defaultOptions object is used instead + @param options - An object with tinyMCE properties + */ + init(options = {}) { + if (isObject(options)) { + tinymce.init($.extend(true, defaultOptions, options)); + } else { + tinymce.init(defaultOptions); + } + }, + /* + Finds any tinyMCE editor whose target element/textarea has the className passed + @param className - A string representing the class name of the tinyMCE editor + target element/textarea to look for + @return An Array of tinymce.Editor objects + */ + findEditorsByClassName(className) { + if (isString(className)) { + return tinymce.editors.reduce((acc, e) => { + if ($(e.getElement()).hasClass(className)) { + return acc.concat([e]); + } + return acc; + }, []); + } + return []; + }, + /* + Finds a tinyMCE editor whose target element/textarea has the id passed + @param id - A string representing the id of the tinyMCE editor target + element/textarea to look for + @return tinymce.Editor object, otherwise undefined + */ + findEditorById(id) { + if (isString(id)) { + return tinymce.editors.find(el => el.id === id); + } + return undefined; + }, + /* + Destroy every editor instance whose target element/textarea has the className passed. This + method executes for each editor the method defined at tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy). + @param className - A string representing the class name of the tinyMCE editor + target element/textarea to look for + @return undefined + */ + destroyEditorsByClassName(className) { + const editors = this.findEditorsByClassName(className); + editors.forEach(ed => ed.destroy(false)); + }, + /* + Destroy an editor instance whose target element/textarea has HTML id passed. This method + executes tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy) for a successfull id found. + @return undefined + */ + destroyEditorById(id) { + const editor = this.findEditorById(id); + if (editor) { + editor.destroy(false); + } + }, +}; diff --git a/lib/assets/javascripts/utils/tinymceSpec.js b/lib/assets/javascripts/utils/tinymceSpec.js new file mode 100644 index 0000000..2c6439f --- /dev/null +++ b/lib/assets/javascripts/utils/tinymceSpec.js @@ -0,0 +1,50 @@ +import { Tinymce } from './tinymce'; + +beforeEach(() => { + $('body').append(''); + $('body').append(''); + Tinymce.init({ selector: '.test' }); +}); + +describe('findEditorsByClassName test suite', () => { + it('expect two editors with class name test', () => expect(Tinymce.findEditorsByClassName('test').length).toBe(2)); + it('expect zero editors with class name whatever', () => expect(Tinymce.findEditorsByClassName('whatever').length).toBe(0)); +}); + +describe('findEditorById test suite', () => { + it('expect editor with id test1 to be defined', () => expect(Tinymce.findEditorById('test1')).toBeDefined()); + it('expect editor with id test2 to be defined', () => expect(Tinymce.findEditorById('test2')).toBeDefined()); + it('expect editor with id whatever to be undefined', () => expect(Tinymce.findEditorById('whatever')).not.toBeDefined()); +}); + +describe('destroyEditorsByClassName test suite', () => { + it('expect remaining two editors', () => { + Tinymce.destroyEditorsByClassName('whatever'); + expect(Tinymce.findEditorsByClassName('test').length).toBe(2); + }); + it('expect remaining zero editors', () => { + Tinymce.destroyEditorsByClassName('test'); + expect(Tinymce.findEditorsByClassName('test').length).toBe(0); + }); +}); + +describe('destroyEditorsById test suite', () => { + it('expect remaining two editors', () => { + Tinymce.destroyEditorById('test3'); + expect(Tinymce.findEditorsByClassName('test').length).toBe(2); + }); + it('expect remaining one editor', () => { + Tinymce.destroyEditorById('test1'); + expect(Tinymce.findEditorsByClassName('test').length).toBe(1); + }); + it('expect remaining zero editors', () => { + Tinymce.destroyEditorById('test1'); + Tinymce.destroyEditorById('test2'); + expect(Tinymce.findEditorsByClassName('test').length).toBe(0); + }); +}); + +afterEach(() => { + $('body').html(''); +}); + diff --git a/lib/assets/javascripts/views/plans/edit_details.js b/lib/assets/javascripts/views/plans/edit_details.js new file mode 100644 index 0000000..85514c0 --- /dev/null +++ b/lib/assets/javascripts/views/plans/edit_details.js @@ -0,0 +1,14 @@ +import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip'; +import 'bootstrap-sass/assets/javascripts/bootstrap/popover'; +import { Tinymce } from '../../utils/tinymce'; +import ariatiseForm from '../../utils/ariatiseForm'; + +$(() => { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="popover"]').popover(); + Tinymce.init(); + $('#is_test').click(() => { + $('#plan_visibility').val($(this).is(':checked') ? 'is_test' : 'privately_visible'); + }); + ariatiseForm({ selector: '.edit_plan' }); +}); diff --git a/lib/assets/karma.conf.js b/lib/assets/karma.conf.js index f94733c..a48ab6e 100644 --- a/lib/assets/karma.conf.js +++ b/lib/assets/karma.conf.js @@ -11,7 +11,6 @@ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['fixture', 'jquery-3.2.1', 'jasmine-jquery', 'jasmine'], - // list of files / patterns to load in the browser files: [ './node_modules/phantomjs-polyfill/bind-polyfill.js', diff --git a/lib/assets/webpack.config.js b/lib/assets/webpack.config.js index 0011906..71f1ec7 100644 --- a/lib/assets/webpack.config.js +++ b/lib/assets/webpack.config.js @@ -15,7 +15,7 @@ context: __dirname, entry: { - vendor: ['jquery', 'tinymce/tinymce', 'tinymce/themes/modern/theme'], + vendor: ['jquery'], application: ['./javascripts/application.js', './stylesheets/application.scss'], }, @@ -47,7 +47,7 @@ }), }, { - test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/, + test: /(?:fonts\/bootstrap\/.*)|(?:font-awesome\/fonts\/.*)(?:\.woff2?$|\.ttf$|\.eot$|\.svg$)/, use: [ { loader: 'file-loader', @@ -64,14 +64,14 @@ plugins: [ extractSass, + new webpack.ProvidePlugin({ // Load jquery module automatically instead of import everywhere + jQuery: 'jquery', + $: 'jquery', + }), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: Infinity, }), - new webpack.ProvidePlugin({ // Load jquery module automatically instead of import everywhere - $: 'jquery', - jQuery: 'jquery', - }), new CopyWebPackPlugin([ // Copies every file under images or videos { from: './images/**/*', to: `${destPath}/` }, { from: './videos/**/*', to: `${destPath}/` }, diff --git a/lib/tasks/bugfix.rake b/lib/tasks/bugfix.rake new file mode 100644 index 0000000..5fc6bf1 --- /dev/null +++ b/lib/tasks/bugfix.rake @@ -0,0 +1,49 @@ +namespace :bugfix do + + desc "Bug fixes for version v0.3.3" + task v0_3_3: :environment do + Rake::Task['bugfix:fix_question_formats'].execute + Rake::Task['bugfix:add_missing_token_permission_types'].execute + end + + desc "Add the missing formattype to the question_formats table" + task fix_question_formats: :environment do + QuestionFormat.all.each do |qf| + case qf.title.downcase + when 'text area' + qf.formattype = :textarea + when 'text field' + qf.formattype = :textfield + when 'radio buttons' + qf.formattype = :radiobuttons + when 'check box' + qf.formattype = :checkbox + when 'dropdown' + qf.formattype = :dropdown + when 'multi select box' + qf.formattype = :multiselectbox + when 'date' + qf.formattype = :date + end + + qf.save! + end + + if QuestionFormat.find_by(formattype: :date).nil? + QuestionFormat.create!({title: "Date", option_based: true, formattype: 6}) + end + end + + desc "Add the missing token_permission_types" + task add_missing_token_permission_types: :environment do + if TokenPermissionType.find_by(token_type: 'templates').nil? + TokenPermissionType.create!({token_type: 'templates', + text_description: 'allows a user access to the templates api endpoint'}) + end + if TokenPermissionType.find_by(token_type: 'statistics').nil? + TokenPermissionType.create!({token_type: 'statistics', + text_description: 'allows a user access to the statistics api endpoint'}) + end + end + +end \ No newline at end of file diff --git a/test/functional/roles_controller_test.rb b/test/functional/roles_controller_test.rb index 6a61b79..7d2f9f0 100644 --- a/test/functional/roles_controller_test.rb +++ b/test/functional/roles_controller_test.rb @@ -7,10 +7,10 @@ setup do scaffold_plan scaffold_org_admin(@plan.template.org) - + # This should NOT be unnecessary! Owner should have full access @plan.roles << Role.create(user: @user, plan: @plan, access: 15) - + end # TODO: Cleanup routes for this one. The controller currently only responds to create, update, destroy @@ -21,19 +21,19 @@ # role PATCH /roles/:id roles#update # PUT /roles/:id roles#update # DELETE /roles/:id roles#destroy - + # POST /roles (roles_path) # ---------------------------------------------------------- test "create a new role" do params = {plan_id: @plan.id, access_level: 4} - + # Should redirect user to the root path if they are not logged in! post roles_path, {role: params} assert_unauthorized_redirect_to_root_path sign_in @user - + # Known user @invitee = User.where.not(id: [@plan.owner.id, @user.id]).first post roles_path, {user: @invitee.email, role: params} @@ -50,15 +50,15 @@ assert_redirected_to share_plan_path(@plan) assert_equal @invitee.id, Role.last.user_id, "expected no record to have been created!" assert assigns(:role) - + # Unknown user post roles_path, {user: 'unknown_user@org.org', role: params} - assert_equal _('Invitation to unknown_user@org.org issued successfully.'), flash[:notice] + assert_equal _('Invitation to unknown_user@org.org issued successfully. \nPlan shared with unknown_user@org.org.'), flash[:notice] assert_response :redirect assert_redirected_to share_plan_path(@plan) assert_equal User.find_by(email:'unknown_user@org.org').id, Role.last.user_id, "expected the record to have been created!" assert assigns(:role) - + # Invite owner @invitee = User.find_by(id: @plan.owner.id) post roles_path, {user: @invitee.email, role: params} @@ -67,32 +67,32 @@ assert_redirected_to share_plan_path(@plan) assert_not_equal @invitee.id, Role.last.user_id, "expected no record to have been created!" assert assigns(:role) - + # Missing email post roles_path, {role: {plan_id: @plan.id, access_level: 4}} assert_equal _('Please enter an email address'), flash[:notice] assert_response :redirect assert_redirected_to share_plan_path(@plan) assert assigns(:role) - end - + end + # PUT /role/:id (role_path) # ---------------------------------------------------------- test "update the role" do @invitee = User.last role = Role.create(user: @invitee, plan: @plan, access: 1) params = {access_level: 2} - + # Should redirect user to the root path if they are not logged in! put role_path(role), {role: params} assert_unauthorized_redirect_to_root_path - + sign_in @user # Valid save put role_path(role, format: :json), {role: params} assert_equal 13, role.reload.access, "expected the record to have been updated" - + # TODO: Role should require a user, plan and an access level :/ # Invalid save # put role_path(role), {role: {user: nil}} @@ -101,26 +101,26 @@ # assert_redirected_to share_plan_path(@plan) # assert assigns(:role) end - + # DELETE /role/:id (role_path) # ---------------------------------------------------------- test "delete the section" do @invitee = User.last role = Role.create(user: @invitee, plan: @plan, access: 1) - + # Should redirect user to the root path if they are not logged in! delete role_path(role) assert_unauthorized_redirect_to_root_path - + sign_in @user - + delete role_path(role) assert_equal _('Access removed'), flash[:notice] assert_response :redirect assert_redirected_to share_plan_path(@plan) - assert_raise ActiveRecord::RecordNotFound do + assert_raise ActiveRecord::RecordNotFound do Role.find(role.id).nil? end end - + end