diff --git a/app/assets/javascripts/hyrax/editor.es6 b/app/assets/javascripts/hyrax/editor.es6 new file mode 100644 index 000000000..7fceb82dd --- /dev/null +++ b/app/assets/javascripts/hyrax/editor.es6 @@ -0,0 +1,117 @@ +import RelationshipsControl from 'hyrax/relationships/control' +import SaveWorkControl from 'hyrax/save_work/save_work_control' +import AdminSetWidget from 'hyrax/editor/admin_set_widget' +import ControlledVocabulary from 'hyrax/editor/controlled_vocabulary' +import Autocomplete from 'hyrax/autocomplete' +import AuthoritySelect from 'hyrax/authority_select' + +export default class { + /** + * initialize the editor behaviors + * @param {jQuery} element - The form that has a data-param-key attribute + */ + constructor(element) { + this.element = element + this.paramKey = element.data('paramKey') // The work type + this.adminSetWidget = new AdminSetWidget(element.find('select[id$="_admin_set_id"]')) + this.sharingTabElement = $('#tab-share') + } + + init() { + this.autocomplete() + this.controlledVocabularies() + this.sharingTab() + this.relationshipsControl() + this.saveWorkControl() + this.saveWorkFixed() + this.authoritySelect() + this.formInProgress() + } + + // Immediate feedback after work creation, editing. + formInProgress() { + $('[data-behavior~=work-form]').on('submit', function(event){ + $('.card-footer.save-progress').removeAttr("hidden"); + }); + } + + // Used when you have a linked data field that can have terms from multiple + // authorities. + authoritySelect() { + $("[data-authority-select]").each(function() { + let authoritySelect = $(this).data().authoritySelect + let options = {selectBox: 'select.' + authoritySelect, + inputField: 'input.' + authoritySelect} + new AuthoritySelect(options); + }) + } + + // Autocomplete fields for the work edit form (based_near, subject, language, child works) + autocomplete() { + var autocomplete = new Autocomplete() + + $('[data-autocomplete]').each((function() { + var elem = $(this) + autocomplete.setup(elem, elem.data('autocomplete'), elem.data('autocompleteUrl')) + elem.parents('.multi_value.form-group').manage_fields({ + add: function(e, element) { + var elem = $(element) + // Don't mark an added element as readonly even if previous element was + // Enable before initializing, as otherwise LinkedData fields remain disabled + elem.attr('readonly', false) + autocomplete.setup(elem, elem.data('autocomplete'), elem.data('autocompleteUrl')) + } + }) + })) + } + + // initialize any controlled vocabulary widgets + controlledVocabularies() { + this.element.find('.controlled_vocabulary.form-group').each((_idx, controlled_field) => + new ControlledVocabulary(controlled_field, this.paramKey) + ) + } + + // Display the sharing tab if they select an admin set that permits sharing + sharingTab() { + if(this.adminSetWidget && !this.adminSetWidget.isEmpty()) { + this.adminSetWidget.on('change', () => this.sharingTabVisiblity(this.adminSetWidget.isSharing())) + this.sharingTabVisiblity(this.adminSetWidget.isSharing()) + } + } + + sharingTabVisiblity(visible) { + if (visible) + this.sharingTabElement.removeAttr("hidden") + else + this.sharingTabElement.attr("hidden","") + } + + relationshipsControl() { + let collections = this.element.find('[data-behavior="collection-relationships"]') + collections.each((_idx, element) => + new RelationshipsControl(element, + collections.data('members'), + collections.data('paramKey'), + 'member_of_collections_attributes', + 'tmpl-collection').init()) + + let works = this.element.find('[data-behavior="child-relationships"]') + works.each((_idx, element) => + new RelationshipsControl(element, + works.data('members'), + works.data('paramKey'), + 'work_members_attributes', + 'tmpl-child-work').init()) + } + // [hyc-override] Store saveWorkControl instance in a global variable to enable tinymce to call it + // This is necessary for requirement checks to update when rich text fields are edited + saveWorkControl() { + window.saveWorkControlInstance = new SaveWorkControl(this.element.find("#form-progress"), this.adminSetWidget) + } + + saveWorkFixed() { + // Fixedsticky will polyfill position:sticky + this.element.find('#savewidget').fixedsticky() + } +} diff --git a/app/renderers/hyrax/renderers/formatted_text_renderer.rb b/app/renderers/hyrax/renderers/formatted_text_renderer.rb new file mode 100644 index 000000000..fa2a0d7e6 --- /dev/null +++ b/app/renderers/hyrax/renderers/formatted_text_renderer.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true +module Hyrax + module Renderers + class FormattedTextRenderer < AttributeRenderer + private + def attribute_value_to_html(value) + sanitized_value = get_sanitized_string(value) + if microdata_value_attributes(field).present? + "#{sanitized_value}" + else + li_value(sanitized_value) + end + end + + # Sanitize the value, allowing only safe HTML tags and attributes + def get_sanitized_string(string) + # Define allowed tags and attributes + allowed_tags = %w[strong em b i u p br small mark sub sup a ul ol li dl dt dd div span h1 h2 h3 h4 h5 h6] + allowed_attributes = %w[href] + sanitize(string, tags: allowed_tags, attributes: allowed_attributes) + end + + # Same as attribute renderer override, but without escaping the value + def li_value(value) + field_value = find_language(value) || value + auto_link((field_value)) + end + end + end +end diff --git a/app/views/hyrax/articles/_attribute_rows.html.erb b/app/views/hyrax/articles/_attribute_rows.html.erb index c3b92907c..20709c87f 100755 --- a/app/views/hyrax/articles/_attribute_rows.html.erb +++ b/app/views/hyrax/articles/_attribute_rows.html.erb @@ -1,7 +1,7 @@ <%= presenter.attribute_to_html(:alternative_title, html_dl: true, label: "Alternate title") %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> <%= presenter.attribute_to_html(:translator_display, label: 'Translator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/artworks/_attribute_rows.html.erb b/app/views/hyrax/artworks/_attribute_rows.html.erb index f780fd564..ad78b0cc5 100644 --- a/app/views/hyrax/artworks/_attribute_rows.html.erb +++ b/app/views/hyrax/artworks/_attribute_rows.html.erb @@ -1,5 +1,5 @@ <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:description, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:note, html_dl: true) %> diff --git a/app/views/hyrax/data_sets/_attribute_rows.html.erb b/app/views/hyrax/data_sets/_attribute_rows.html.erb index a0f4bcd31..3911f4d91 100644 --- a/app/views/hyrax/data_sets/_attribute_rows.html.erb +++ b/app/views/hyrax/data_sets/_attribute_rows.html.erb @@ -1,8 +1,8 @@ <%= presenter.attribute_to_html(:last_modified_date, render_as: :date, html_dl: true) %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> <%= presenter.attribute_to_html(:contributor_display, label: 'Contributor', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> -<%= presenter.attribute_to_html(:methodology, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> +<%= presenter.attribute_to_html(:methodology, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/dissertations/_attribute_rows.html.erb b/app/views/hyrax/dissertations/_attribute_rows.html.erb index 474955e1a..64b464a8c 100755 --- a/app/views/hyrax/dissertations/_attribute_rows.html.erb +++ b/app/views/hyrax/dissertations/_attribute_rows.html.erb @@ -2,7 +2,7 @@ <%= presenter.attribute_to_html(:date_modified, label: 'Last Modified', render_as: :date, html_dl: true) %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> <%= presenter.attribute_to_html(:contributor_display, label: 'Contributor', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/generals/_attribute_rows.html.erb b/app/views/hyrax/generals/_attribute_rows.html.erb index 2114d9a4a..b13d27748 100755 --- a/app/views/hyrax/generals/_attribute_rows.html.erb +++ b/app/views/hyrax/generals/_attribute_rows.html.erb @@ -3,10 +3,10 @@ <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> <%= presenter.attribute_to_html(:contributor_display, label: 'Contributor', render_as: :person, html_dl: true) %> <%= presenter.attribute_to_html(:translator_display, label: 'Translator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:description, html_dl: true) %> <%= presenter.attribute_to_html(:table_of_contents, html_dl: true) %> -<%= presenter.attribute_to_html(:methodology, html_dl: true) %> +<%= presenter.attribute_to_html(:methodology, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/honors_theses/_attribute_rows.html.erb b/app/views/hyrax/honors_theses/_attribute_rows.html.erb index d918553f1..cc21e846c 100755 --- a/app/views/hyrax/honors_theses/_attribute_rows.html.erb +++ b/app/views/hyrax/honors_theses/_attribute_rows.html.erb @@ -1,7 +1,7 @@ <%= presenter.attribute_to_html(:alternative_title, html_dl: true, label: 'Alternate title') %> <%= presenter.attribute_to_html(:date_modified, label: 'Last Modified', render_as: :date, html_dl: true) %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/journals/_attribute_rows.html.erb b/app/views/hyrax/journals/_attribute_rows.html.erb index 9a722c7b2..f7627a247 100644 --- a/app/views/hyrax/journals/_attribute_rows.html.erb +++ b/app/views/hyrax/journals/_attribute_rows.html.erb @@ -1,6 +1,6 @@ <%= presenter.attribute_to_html(:alternative_title, html_dl: true, label: "Alternate title") %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/masters_papers/_attribute_rows.html.erb b/app/views/hyrax/masters_papers/_attribute_rows.html.erb index 0cda3091c..c424224f7 100755 --- a/app/views/hyrax/masters_papers/_attribute_rows.html.erb +++ b/app/views/hyrax/masters_papers/_attribute_rows.html.erb @@ -1,6 +1,6 @@ <%= presenter.attribute_to_html(:date_modified, label: 'Last Modified', render_as: :date, html_dl: true) %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: 'Date of publication') %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/multimeds/_attribute_rows.html.erb b/app/views/hyrax/multimeds/_attribute_rows.html.erb index afa24e684..0668915bb 100644 --- a/app/views/hyrax/multimeds/_attribute_rows.html.erb +++ b/app/views/hyrax/multimeds/_attribute_rows.html.erb @@ -1,6 +1,6 @@ <%= presenter.attribute_to_html(:date_modified, label: 'Last Modified', render_as: :date, html_dl: true) %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %> diff --git a/app/views/hyrax/scholarly_works/_attribute_rows.html.erb b/app/views/hyrax/scholarly_works/_attribute_rows.html.erb index c42c2307a..b3c7f3732 100644 --- a/app/views/hyrax/scholarly_works/_attribute_rows.html.erb +++ b/app/views/hyrax/scholarly_works/_attribute_rows.html.erb @@ -1,6 +1,6 @@ <%= presenter.attribute_to_html(:date_modified, label: 'Last Modified', render_as: :date, html_dl: true) %> <%= presenter.attribute_to_html(:creator_display, label: 'Creator', render_as: :person, html_dl: true) %> -<%= presenter.attribute_to_html(:abstract, html_dl: true) %> +<%= presenter.attribute_to_html(:abstract, render_as: :formatted_text, html_dl: true) %> <%= presenter.attribute_to_html(:description, html_dl: true) %> <%= presenter.attribute_to_html(:date_issued, html_dl: true, label: "Date of publication") %> <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %> diff --git a/app/views/records/edit_fields/_abstract.html.erb b/app/views/records/edit_fields/_abstract.html.erb index 3d486d035..c1f38c284 100644 --- a/app/views/records/edit_fields/_abstract.html.erb +++ b/app/views/records/edit_fields/_abstract.html.erb @@ -1,5 +1,7 @@ <% if f.object.multiple? key %> - <%= f.input :abstract, as: :multi_value, input_html: { rows: '14', type: 'textarea'}, required: f.object.required?(key) %> + <%= f.input :abstract, as: :multi_value, input_html: { rows: '14', type: 'textarea', class: 'tinymce'}, required: f.object.required?(key) %> <% else %> - <%= f.input :abstract, as: :text, input_html: { rows: '14' }, required: f.object.required?(key) %> -<% end %> \ No newline at end of file + <%= f.input :abstract, as: :text, input_html: { rows: '14', class: 'tinymce' }, required: f.object.required?(key) %> +<% end %> + +<%= tinymce :rich_text %> \ No newline at end of file diff --git a/app/views/records/edit_fields/_methodology.html.erb b/app/views/records/edit_fields/_methodology.html.erb index 41cbea54b..c87b5aaa2 100644 --- a/app/views/records/edit_fields/_methodology.html.erb +++ b/app/views/records/edit_fields/_methodology.html.erb @@ -1,5 +1,7 @@ <% if f.object.multiple? key %> - <%= f.input :methodology, as: :multi_value, input_html: { rows: '14', type: 'textarea'}, required: f.object.required?(key) %> + <%= f.input :methodology, as: :multi_value, input_html: { rows: '14', type: 'textarea', class: 'tinymce' }, required: f.object.required?(key) %> <% else %> - <%= f.input :methodology, as: :text, input_html: { rows: '14' }, required: f.object.required?(key) %> -<% end %> \ No newline at end of file + <%= f.input :methodology, as: :text, input_html: { rows: '14', class: 'tinymce' }, required: f.object.required?(key) %> +<% end %> + +<%= tinymce :rich_text %> \ No newline at end of file diff --git a/config/tinymce.yml b/config/tinymce.yml index ed2b4c001..bac21fa4d 100755 --- a/config/tinymce.yml +++ b/config/tinymce.yml @@ -9,5 +9,49 @@ content_block: - table - fullscreen - image +rich_text: + menubar: false + toolbar: + - "undo redo | bold italic underline | alignleft aligncenter alignright | link | numlist bullist outdent indent | blockquote | code" + plugins: + - "link lists code" + style_formats: + - title: "Inline" + items: + - title: "Bold" + inline: "b" + - title: "Italic" + inline: "i" + - title: "Underline" + inline: "u" + - title: "Strikethrough" + inline: "strike" + - title: "Blocks" + items: + - title: "Paragraph" + block: "p" + - title: "Blockquote" + block: "blockquote" + - title: "Alignments" + items: + - title: "Left" + block: "" + classes: "align-left" + - title: "Center" + block: "" + classes: "align-center" + - title: "Right" + block: "" + classes: "align-right" + setup: | + function (editor) { + editor.on('change', function () { + tinymce.triggerSave(); + if (window.saveWorkControlInstance) { + window.saveWorkControlInstance.validateMetadata(); + } + }); + } + custom: <<: *default diff --git a/spec/features/edit_sage_ingested_works_spec.rb b/spec/features/edit_sage_ingested_works_spec.rb index 4928fd254..896b4df4c 100644 --- a/spec/features/edit_sage_ingested_works_spec.rb +++ b/spec/features/edit_sage_ingested_works_spec.rb @@ -110,7 +110,10 @@ expect(page).to have_field('Creator #2', with: 'Zhang, Xi') expect(page).to have_field('Additional affiliation (Creator #1)', with: 'Department of Family and Community Medicine, University of California, San Francisco, CA, USA') expect(page).to have_field('ORCID (Creator #1)', with: 'https://orcid.org/0000-0001-6833-8372') - expect(page).to have_field('Abstract', with: /Efforts to increase education opportunities, provide insurance/) + # Assert content within an iframe to check the abstract. Tinymce rich text editors confuse Capybara assertions. + within_frame(find('iframe#article_abstract_ifr')) do + expect(page).to have_content('Efforts to increase education opportunities, provide insurance') + end # Javascript execution is inconsistent in the test environment, so rather than expanding the rest # of the form elements, checking for the remainder of the elements whether they are visible or not.