diff --git a/app/controllers/concerns/hyku/works_controller_behavior.rb b/app/controllers/concerns/hyku/works_controller_behavior.rb new file mode 100644 index 000000000..96b6f4e53 --- /dev/null +++ b/app/controllers/concerns/hyku/works_controller_behavior.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +# OVERRIDE: Hyrax v5.0.0rc2 to add inject_show_theme_views - Hyku theming +# and correct hostname of manifests +# and to add Hyrax IIIF AV +module Hyku + # include this module after including Hyrax::WorksControllerBehavior to override + # Hyrax::WorksControllerBehavior methods with the ones defined here + module WorksControllerBehavior + extend ActiveSupport::Concern + + # Adds behaviors for hyrax-iiif_av plugin and provides #manifest and #iiif_manifest_builder + include Hyrax::IiifAv::ControllerBehavior + + included do + # add around action to load theme show page views + around_action :inject_show_theme_views, except: :delete + end + + def json_manifest + iiif_manifest_builder.manifest_for(presenter: iiif_manifest_presenter) + end + + private + + def iiif_manifest_presenter + Hyrax::IiifManifestPresenter.new(search_result_document(id: params[:id])).tap do |p| + p.hostname = request.hostname + p.ability = current_ability + end + end + + def format_error_messages(errors) + errors.messages.map do |field, messages| + field_name = field.to_s.humanize + messages.map { |message| "#{field_name} #{message.sub(/^./, &:downcase)}" } + end.flatten.join("\n") + end + + # Creating a form object that can re-render most of the submitted parameters. + # Required for ActiveFedora::Base objects only. + def rebuild_form(original_input_params_for_form) + build_form + @form = Hyrax::Forms::FailedSubmissionFormWrapper + .new(form: @form, + input_params: original_input_params_for_form) + end + + def after_update_error(errors) + respond_to do |wants| + wants.html do + flash[:error] = format_error_messages(errors) + build_form unless @form.is_a? Hyrax::ChangeSet + render 'edit', status: :unprocessable_entity + end + wants.json { render_json_response(response_type: :unprocessable_entity, options: { errors: }) } + end + end + + def available_admin_sets + # only returns admin sets in which the user can deposit + admin_set_results = Hyrax::AdminSetService.new(self).search_results(:deposit) + + # get all the templates at once, reducing query load + templates = Hyrax::PermissionTemplate.where(source_id: admin_set_results.map(&:id)).to_a + + admin_sets = admin_set_results.map do |admin_set_doc| + template = templates.find { |temp| temp.source_id == admin_set_doc.id.to_s } + + ## OVERRIDE Hyrax v5.0.0rc2 + # Removes a short-circuit that allowed users with manage access to + # the given permission_template to always be able to edit a record's sharing + # (i.e. the "Sharing" tab in forms). + # + # We remove this because there is currently a bug in Hyrax where, if the + # workflow does not allow access grants, changes to a record's sharing + # are not being persisted, leading to a confusing UX. + # @see https://github.com/samvera/hyrax/issues/5904 + # + # TEMPORARY: This override should be removed when the bug is resolved in + # upstream Hyrax and brought into this project. + # + # determine if sharing tab should be visible + sharing = !!template&.active_workflow&.allows_access_grant? + + Hyrax::AdminSetSelectionPresenter::OptionsEntry + .new(admin_set: admin_set_doc, permission_template: template, permit_sharing: sharing) + end + + Hyrax::AdminSetSelectionPresenter.new(admin_sets:) + end + + # added to prepend the show theme views into the view_paths + def inject_show_theme_views + if show_page_theme && show_page_theme != 'default_show' + original_paths = view_paths + Hyku::Application.theme_view_path_roots.each do |root| + show_theme_view_path = File.join(root, 'app', 'views', "themes", show_page_theme.to_s) + prepend_view_path(show_theme_view_path) + end + yield + # rubocop:disable Lint/UselessAssignment, Layout/SpaceAroundOperators, Style/RedundantParentheses + # Do NOT change this line. This is calling the Rails view_paths=(paths) method and not a variable assignment. + view_paths=(original_paths) + # rubocop:enable Lint/UselessAssignment, Layout/SpaceAroundOperators, Style/RedundantParentheses + else + yield + end + end + end +end diff --git a/app/controllers/concerns/hyrax/admin/users_controller_behavior.rb b/app/controllers/concerns/hyrax/admin/users_controller_behavior.rb index 8b15f9e23..d58ad7ce0 100644 --- a/app/controllers/concerns/hyrax/admin/users_controller_behavior.rb +++ b/app/controllers/concerns/hyrax/admin/users_controller_behavior.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -# OVERRIDE Hyrax v3.4.2 Authorize User access and set up roles dropdown +# OVERRIDE Hyrax v5.0.0rc2 Authorize User access and set up roles dropdown module Hyrax module Admin module UsersControllerBehavior extend ActiveSupport::Concern include Blacklight::SearchContext included do - # OVERRIDE Hyrax v3.4.2 Replace :ensure_admin! with :ensure_access! to leverage User abilities. + # OVERRIDE Hyrax v5.0.0rc2 Replace :ensure_admin! with :ensure_access! to leverage User abilities. # @see app/models/concerns/hyrax/ability/user_ability.rb before_action :ensure_access! with_themed_layout 'dashboard' @@ -19,7 +19,7 @@ def index add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path add_breadcrumb t(:'hyrax.admin.users.index.title'), hyrax.admin_users_path @presenter = Hyrax::Admin::UsersPresenter.new - # OVERRIDE Hyrax v3.4.2 Sets up a list of role names to be used by a dropdown input that + # OVERRIDE Hyrax v5.0.0rc2 Sets up a list of role names to be used by a dropdown input that # allows users to be invited with a specific role. @invite_roles_options = if current_ability.admin? ::RolesService::DEFAULT_ROLES @@ -30,7 +30,7 @@ def index private - # OVERRIDE Hyrax v3.4.2 Replace :ensure_admin! with :ensure_access! to leverage User abilities. + # OVERRIDE Hyrax v5.0.0rc2 Replace :ensure_admin! with :ensure_access! to leverage User abilities. # @see app/models/concerns/hyrax/ability/user_ability.rb def ensure_access! authorize! :read, ::User diff --git a/app/controllers/concerns/hyrax/works_controller_behavior.rb b/app/controllers/concerns/hyrax/works_controller_behavior.rb deleted file mode 100644 index f7ea802a0..000000000 --- a/app/controllers/concerns/hyrax/works_controller_behavior.rb +++ /dev/null @@ -1,526 +0,0 @@ -# frozen_string_literal: true - -# OVERRIDE: Hyrax 3.4.0 to add inject_show_theme_views - Hyku theming and correct hostname of manifests -# OVERRIDE: Hyrax 3.4.0 to add Hyrax IIIF AV -require 'iiif_manifest' - -# rubocop:disable Metrics/ModuleLength -# rubocop:disable Metrics/LineLength -module Hyrax - module WorksControllerBehavior - extend ActiveSupport::Concern - include Blacklight::Base - include Blacklight::AccessControls::Catalog - - # Adds behaviors for hyrax-iiif_av plugin and provides #manifest and #iiif_manifest_builder - include Hyrax::IiifAv::ControllerBehavior - - included do - with_themed_layout :decide_layout - copy_blacklight_config_from(::CatalogController) - - class_attribute :_curation_concern_type, :show_presenter, :work_form_service, :search_builder_class - class_attribute :iiif_manifest_builder, instance_accessor: false - self.show_presenter = Hyku::WorkShowPresenter - self.work_form_service = Hyrax::WorkFormService - self.search_builder_class = WorkSearchBuilder - # Set to nil for the #iiif_manifest_builder (provided by Hyrax::IiifAv) to work - self.iiif_manifest_builder = nil - - attr_accessor :curation_concern - helper_method :curation_concern, :contextual_path - - rescue_from WorkflowAuthorizationException, with: :render_unavailable - # add around action to load theme show page views - around_action :inject_show_theme_views, except: :delete # rubocop:disable Rails/LexicallyScopedActionFilter - end - - class_methods do - def curation_concern_type=(curation_concern_type) - load_and_authorize_resource class: curation_concern_type, - instance_name: :curation_concern, - except: %i[show file_manager inspect_work manifest] - - # Load the fedora resource to get the etag. - # No need to authorize for the file manager, because it does authorization via the presenter. - load_resource class: curation_concern_type, instance_name: :curation_concern, only: :file_manager - - self._curation_concern_type = curation_concern_type - # We don't want the breadcrumb action to occur until after the concern has - # been loaded and authorized - before_action :save_permissions, only: :update # rubocop:disable Rails/LexicallyScopedActionFilter - end - - def curation_concern_type - _curation_concern_type - end - - def cancan_resource_class - Hyrax::ControllerResource - end - end - - def new - @admin_set_options = available_admin_sets - # TODO: move these lines to the work form builder in Hyrax - curation_concern.depositor = current_user.user_key - curation_concern.admin_set_id = admin_set_id_for_new - build_form - end - - def create - case curation_concern - when ActiveFedora::Base - original_input_params_for_form = params[hash_key_for_curation_concern].deep_dup - actor.create(actor_environment) ? after_create_response : after_create_error(curation_concern.errors, original_input_params_for_form) - else - create_valkyrie_work - end - end - - # Finds a solr document matching the id and sets @presenter - # @raise CanCan::AccessDenied if the document is not found or the user doesn't have access to it. - def show - @user_collections = user_collections - - respond_to do |wants| - wants.html { presenter && parent_presenter } - wants.json do - # load @curation_concern manually because it's skipped for html - @curation_concern = Hyrax.query_service.find_by(alternate_identifier: params[:id]) - curation_concern # This is here for authorization checks (we could add authorize! but let's use the same method for CanCanCan) - render :show, status: :ok - end - additional_response_formats(wants) - wants.ttl { render body: presenter.export_as_ttl, mime_type: Mime[:ttl] } - wants.jsonld { render body: presenter.export_as_jsonld, mime_type: Mime[:jsonld] } - wants.nt { render body: presenter.export_as_nt, mime_type: Mime[:nt] } - end - end - # rubocop:enable Metrics/AbcSize - - def edit - @admin_set_options = available_admin_sets - build_form - end - - def update - case curation_concern - when ActiveFedora::Base - actor.update(actor_environment) ? after_update_response : after_update_error(curation_concern.errors) - else - update_valkyrie_work - end - end - - def destroy - case curation_concern - when ActiveFedora::Base - title = curation_concern.to_s - env = Actors::Environment.new(curation_concern, current_ability, {}) - return unless actor.destroy(env) - Hyrax.config.callback.run(:after_destroy, curation_concern.id, current_user, warn: false) - else - transactions['work_resource.destroy'] - .with_step_args('work_resource.delete' => { user: current_user }) - .call(curation_concern) - .value! - - title = Array(curation_concern.title).first - end - - after_destroy_response(title) - end - - def file_manager - @form = Forms::FileManagerForm.new(curation_concern, current_ability) - end - - def inspect_work - raise Hydra::AccessDenied unless current_ability.admin? - presenter - end - - def json_manifest - iiif_manifest_builder.manifest_for(presenter: iiif_manifest_presenter) - end - - private - - def iiif_manifest_presenter - IiifManifestPresenter.new(search_result_document(id: params[:id])).tap do |p| - p.hostname = request.hostname - p.ability = current_ability - end - end - - def user_collections - collections_service.search_results(:deposit) - end - - def collections_service - Hyrax::CollectionsService.new(self) - end - - def admin_set_id_for_new - Hyrax::AdminSetCreateService.find_or_create_default_admin_set.id.to_s - end - - def build_form - @form = work_form_service.build(curation_concern, current_ability, self) - end - - def actor - @actor ||= Hyrax::CurationConcern.actor - end - - ## - # @return [#errors] - def create_valkyrie_work - form = build_form - return after_create_error(form_err_msg(form)) unless form.validate(params[hash_key_for_curation_concern]) - - result = - transactions['change_set.create_work'] - .with_step_args( - 'work_resource.add_to_parent' => { parent_id: params[:parent_id], user: current_user }, - 'work_resource.add_file_sets' => { uploaded_files:, file_set_params: params[hash_key_for_curation_concern][:file_set] }, - 'change_set.set_user_as_depositor' => { user: current_user }, - 'work_resource.change_depositor' => { user: ::User.find_by_user_key(form.on_behalf_of) } - ) - .call(form) - @curation_concern = result.value_or { return after_create_error(transaction_err_msg(result)) } - after_create_response - end - - def update_valkyrie_work - form = build_form - return after_update_error(form_err_msg(form)) unless form.validate(params[hash_key_for_curation_concern]) - result = - transactions['change_set.update_work'] - .with_step_args('work_resource.add_file_sets' => { uploaded_files:, file_set_params: params[hash_key_for_curation_concern][:file_set] }, - 'work_resource.update_work_members' => { work_members_attributes: }) - .call(form) - @curation_concern = result.value_or { return after_update_error(transaction_err_msg(result)) } - after_update_response - end - - def work_members_attributes - params[hash_key_for_curation_concern][:work_members_attributes]&.permit!&.to_h - end - - def form_err_msg(form) - form.errors.messages.values.flatten.to_sentence - end - - def transaction_err_msg(result) - result.failure.first - end - - def presenter - @presenter ||= show_presenter.new(search_result_document(id: params[:id]), current_ability, request) - end - - def parent_presenter - return @parent_presenter unless params[:parent_id] - - @parent_presenter ||= - show_presenter.new(search_result_document(id: params[:parent_id]), current_ability, request) - end - - # Include 'hyrax/base' in the search path for views, while prefering - # our local paths. Thus we are unable to just override `self.local_prefixes` - def _prefixes - @_prefixes ||= super + ['hyrax/base'] - end - - def actor_environment - Actors::Environment.new(curation_concern, current_ability, attributes_for_actor) - end - - def hash_key_for_curation_concern - _curation_concern_type.model_name.param_key - end - - def contextual_path(presenter, parent_presenter) - ::Hyrax::ContextualPath.new(presenter, parent_presenter).show - end - - # @deprecated - def curation_concern_from_search_results - Deprecation.warn("'##{__method__}' will be removed in Hyrax 4.0. " \ - "Instead, use '#search_result_document'.") - search_params = params.deep_dup - search_params.delete :page - search_result_document(search_params) - end - - ## - # Only returns unsuppressed documents the user has read access to - # - # @api public - # - # @param search_params [ActionController::Parameters] this should - # include an :id key, but based on implementation and use of the - # WorkSearchBuilder, it need not. - # - # @return [SolrDocument] - # - # @raise [WorkflowAuthorizationException] when the object is not - # found via the search builder's search logic BUT the object is - # suppressed AND the user can read it (Yeah, it's confusing but - # after a lot of debugging that's the logic) - # - # @raise [CanCan::AccessDenied] when the object is not found via - # the search builder's search logic BUT the object is not - # supressed OR not readable by the user (Yeah.) - # - # @note This is Jeremy, I have suspicions about the first line of - # this comment (eg, "Only return unsuppressed..."). The - # reason is that I've encounter situations in the specs - # where the document_list is empty but if I then query Solr - # for the object by ID, I get a document that is NOT - # suppressed AND can be read. In other words, I believe - # there is more going on in the search_results method - # (e.g. a filter is being applied that is beyond what the - # comment indicates) - # - # @see #document_not_found! - def search_result_document(search_params) - _, document_list = search_results(search_params) - return document_list.first unless document_list.empty? - document_not_found! - end - - def document_not_found! - doc = ::SolrDocument.find(params[:id]) - raise WorkflowAuthorizationException if doc.suppressed? && current_ability.can?(:read, doc) - raise CanCan::AccessDenied.new(nil, :show) - end - - def render_unavailable - message = I18n.t("hyrax.workflow.unauthorized") - respond_to do |wants| - wants.html do - unavailable_presenter - flash[:notice] = message - render 'unavailable', status: :unauthorized - end - wants.json { render plain: message, status: :unauthorized } - additional_response_formats(wants) - wants.ttl { render plain: message, status: :unauthorized } - wants.jsonld { render plain: message, status: :unauthorized } - wants.nt { render plain: message, status: :unauthorized } - end - end - - def unavailable_presenter - @presenter ||= show_presenter.new(::SolrDocument.find(params[:id]), current_ability, request) - end - - def decide_layout - layout = case action_name - when 'show' - '1_column' - else - 'dashboard' - end - File.join(theme, layout) - end - - ## - # @todo should the controller know so much about browse_everything? - # hopefully this can be refactored to be more reusable. - # - # Add uploaded_files to the parameters received by the actor. - # rubocop:disable Metrics/MethodLength - def attributes_for_actor - raw_params = params[hash_key_for_curation_concern] - attributes = if raw_params - work_form_service.form_class(curation_concern).model_attributes(raw_params) - else - {} - end - - # If they selected a BrowseEverything file, but then clicked the - # remove button, it will still show up in `selected_files`, but - # it will no longer be in uploaded_files. By checking the - # intersection, we get the files they added via BrowseEverything - # that they have not removed from the upload widget. - uploaded_files = params.fetch(:uploaded_files, []) - selected_files = params.fetch(:selected_files, {}).values - browse_everything_urls = uploaded_files & - selected_files.pluck(:url) - - # we need the hash of files with url and file_name - browse_everything_files = selected_files - .select { |v| uploaded_files.include?(v[:url]) } - attributes[:remote_files] = browse_everything_files - # Strip out any BrowseEverthing files from the regular uploads. - attributes[:uploaded_files] = uploaded_files - - browse_everything_urls - attributes - end - # rubocop:enable Metrics/MethodLength - - def after_create_response - respond_to do |wants| - wants.html do - # Calling `#t` in a controller context does not mark _html keys as html_safe - flash[:notice] = view_context.t('hyrax.works.create.after_create_html', application_name: view_context.application_name) - - redirect_to [main_app, curation_concern] - end - wants.json { render :show, status: :created, location: polymorphic_path([main_app, curation_concern]) } - end - end - - def format_error_messages(errors) - errors.messages.map do |field, messages| - field_name = field.to_s.humanize - messages.map { |message| "#{field_name} #{message.sub(/^./, &:downcase)}" } - end.flatten.join("\n") - end - - def after_create_error(errors, original_input_params_for_form = nil) - respond_to do |wants| - wants.html do - flash[:error] = format_error_messages(errors) - rebuild_form(original_input_params_for_form) if original_input_params_for_form.present? - render 'new', status: :unprocessable_entity - end - wants.json { render_json_response(response_type: :unprocessable_entity, options: { errors: }) } - end - end - - # Creating a form object that can re-render most of the submitted parameters. - # Required for ActiveFedora::Base objects only. - def rebuild_form(original_input_params_for_form) - build_form - @form = Hyrax::Forms::FailedSubmissionFormWrapper - .new(form: @form, - input_params: original_input_params_for_form) - end - - def after_update_response - return redirect_to hyrax.confirm_access_permission_path(curation_concern) if permissions_changed? && concern_has_file_sets? - - respond_to do |wants| - wants.html { redirect_to [main_app, curation_concern], notice: "Work \"#{curation_concern}\" successfully updated." } - wants.json { render :show, status: :ok, location: polymorphic_path([main_app, curation_concern]) } - end - end - - def after_update_error(errors) - respond_to do |wants| - wants.html do - flash[:error] = format_error_messages(errors) - build_form unless @form.is_a? Hyrax::ChangeSet - render 'edit', status: :unprocessable_entity - end - wants.json { render_json_response(response_type: :unprocessable_entity, options: { errors: }) } - end - end - - def after_destroy_response(title) - respond_to do |wants| - wants.html { redirect_to hyrax.my_works_path, notice: "Deleted #{title}" } - wants.json { render_json_response(response_type: :deleted, message: "Deleted #{curation_concern.id}") } - end - end - - def additional_response_formats(format) - format.endnote do - send_data(presenter.solr_document.export_as_endnote, - type: "application/x-endnote-refer", - filename: presenter.solr_document.endnote_filename) - end - end - - def save_permissions - @saved_permissions = - case curation_concern - when ActiveFedora::Base - curation_concern.permissions.map(&:to_hash) - else - Hyrax::AccessControl.for(resource: curation_concern).permissions - end - end - - def permissions_changed? - @saved_permissions != - case curation_concern - when ActiveFedora::Base - curation_concern.permissions.map(&:to_hash) - else - Hyrax::AccessControl.for(resource: curation_concern).permissions - end - end - - def concern_has_file_sets? - case curation_concern - when ActiveFedora::Common - curation_concern.file_sets.present? - else - Hyrax.custom_queries.find_child_file_set_ids(resource: curation_concern).any? - end - end - - def uploaded_files - UploadedFile.find(params.fetch(:uploaded_files, [])) - end - - def available_admin_sets - # only returns admin sets in which the user can deposit - admin_set_results = Hyrax::AdminSetService.new(self).search_results(:deposit) - - # get all the templates at once, reducing query load - templates = PermissionTemplate.where(source_id: admin_set_results.map(&:id)).to_a - - admin_sets = admin_set_results.map do |admin_set_doc| - template = templates.find { |temp| temp.source_id == admin_set_doc.id.to_s } - - ## OVERRIDE Hyrax v3.4.2 - # Removes a short-circuit that allowed users with manage access to - # the given permission_template to always be able to edit a record's sharing - # (i.e. the "Sharing" tab in forms). - # - # We remove this because there is currently a bug in Hyrax where, if the - # workflow does not allow access grants, changes to a record's sharing - # are not being persisted, leading to a confusing UX. - # @see https://github.com/samvera/hyrax/issues/5904 - # - # TEMPORARY: This override should be removed when the bug is resolved in - # upstream Hyrax and brought into this project. - # - # determine if sharing tab should be visible - sharing = !!template&.active_workflow&.allows_access_grant? # rubocop:disable Style/DoubleNegation - - AdminSetSelectionPresenter::OptionsEntry - .new(admin_set: admin_set_doc, permission_template: template, permit_sharing: sharing) - end - - AdminSetSelectionPresenter.new(admin_sets:) - end - - # added to prepend the show theme views into the view_paths - def inject_show_theme_views - if show_page_theme && show_page_theme != 'default_show' - original_paths = view_paths - Hyku::Application.theme_view_path_roots.each do |root| - show_theme_view_path = File.join(root, 'app', 'views', "themes", show_page_theme.to_s) - prepend_view_path(show_theme_view_path) - end - yield - # rubocop:disable Lint/UselessAssignment, Layout/SpaceAroundOperators, Style/RedundantParentheses - # Do NOT change this line. This is calling the Rails view_paths=(paths) method and not a variable assignment. - view_paths=(original_paths) - # rubocop:enable Lint/UselessAssignment, Layout/SpaceAroundOperators, Style/RedundantParentheses - else - yield - end - end - end -end -# rubocop:enable Metrics/ModuleLength -# rubocop:enable Metrics/LineLength diff --git a/app/controllers/hyrax/admin/appearances_controller.rb b/app/controllers/hyrax/admin/appearances_controller_decorator.rb similarity index 83% rename from app/controllers/hyrax/admin/appearances_controller.rb rename to app/controllers/hyrax/admin/appearances_controller_decorator.rb index dbd0abf41..287c27df1 100644 --- a/app/controllers/hyrax/admin/appearances_controller.rb +++ b/app/controllers/hyrax/admin/appearances_controller_decorator.rb @@ -1,15 +1,10 @@ # frozen_string_literal: true -# OVERRIDE Hyrax 3.4 to add selectable themes +# OVERRIDE Hyrax v5.0.0rc2 to add selectable themes module Hyrax module Admin - class AppearancesController < ApplicationController - before_action :require_permissions - with_themed_layout 'dashboard' - class_attribute :form_class - self.form_class = Hyrax::Forms::Admin::Appearance - + module AppearancesControllerDecorator def show # TODO: make selected font the font that show in select box # TODO add body and headline font to the import url @@ -44,14 +39,6 @@ def update private - def update_params - params.require(:admin_appearance).permit(form_class.permitted_params) - end - - def require_permissions - authorize! :update, :appearance - end - def add_breadcrumbs add_breadcrumb t(:'hyrax.controls.home'), root_path add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path @@ -88,3 +75,5 @@ def load_search_themes end end end + +Hyrax::Admin::AppearancesController.prepend(Hyrax::Admin::AppearancesControllerDecorator) diff --git a/app/controllers/hyrax/admin/workflow_roles_controller.rb b/app/controllers/hyrax/admin/workflow_roles_controller.rb deleted file mode 100644 index a82fabcad..000000000 --- a/app/controllers/hyrax/admin/workflow_roles_controller.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -# OVERRIDE Hyrax v3.4.2 Expand to allow adding groups to workflow roles -module Hyrax - module Admin - class WorkflowRolesController < ApplicationController - before_action :require_permissions - with_themed_layout 'dashboard' - - def index - add_breadcrumb t(:'hyrax.controls.home'), root_path - add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path - add_breadcrumb t(:'hyrax.admin.workflow_roles.header'), hyrax.admin_workflow_roles_path - @presenter = WorkflowRolesPresenter.new - end - - def destroy - responsibility = Sipity::WorkflowResponsibility.find(params[:id]) - authorize! :destroy, responsibility - responsibility.destroy - redirect_to admin_workflow_roles_path - end - - # OVERRIDE: Add WorkflowResponsibilityGroupForm - def create - authorize! :create, Sipity::WorkflowResponsibility - - # Determine which form it is, user or group - form_params = params[:sipity_workflow_responsibility] - form = if form_params[:user_id].present? - Forms::WorkflowResponsibilityForm.new(form_params) - else - Forms::WorkflowResponsibilityGroupForm.new(form_params) - end - - begin - form.save! - rescue ActiveRecord::RecordNotUnique - logger.info "Not unique *****\n\n\n" - end - redirect_to admin_workflow_roles_path - end - - private - - def require_permissions - authorize! :read, :admin_dashboard - end - end - end -end diff --git a/app/controllers/hyrax/contact_form_controller.rb b/app/controllers/hyrax/contact_form_controller_decorator.rb similarity index 76% rename from app/controllers/hyrax/contact_form_controller.rb rename to app/controllers/hyrax/contact_form_controller_decorator.rb index 46bf0e59f..63d7bef19 100644 --- a/app/controllers/hyrax/contact_form_controller.rb +++ b/app/controllers/hyrax/contact_form_controller_decorator.rb @@ -1,26 +1,31 @@ # frozen_string_literal: true -# OVERRIDE: Hyrax v5.0.0 +# OVERRIDE: Hyrax v5.0.0rc2 # - adds inject_theme_views method for theming # - adds homepage presenter for access to feature flippers # - adds access to content blocks in the show method # - adds @featured_collection_list to new method # - adds captcha - module Hyrax - class ContactFormController < ApplicationController + module ContactFormControllerDecorator + extend ActiveSupport::Concern + # OVERRIDE: Add for theming # Adds Hydra behaviors into the application controller include Blacklight::SearchContext include Blacklight::AccessControls::Catalog - before_action :build_contact_form - layout 'homepage' - # OVERRIDE: Adding inject theme views method for theming - around_action :inject_theme_views - class_attribute :model_class - self.model_class = Hyrax::ContactForm - before_action :setup_negative_captcha, only: %i[new create] + prepended do + # OVERRIDE: Adding inject theme views method for theming + around_action :inject_theme_views + before_action :setup_negative_captcha, only: %i[new create] + + # OVERRIDE: Add for theming + class_attribute :presenter_class + self.presenter_class = Hyrax::HomepagePresenter + + helper Hyrax::ContentBlockHelper + end # OVERRIDE: Add for theming # The search builder for finding recent documents @@ -29,12 +34,6 @@ def search_builder_class Hyrax::HomepageSearchBuilder end - # OVERRIDE: Add for theming - class_attribute :presenter_class - self.presenter_class = Hyrax::HomepagePresenter - - helper Hyrax::ContentBlockHelper - def new # OVERRIDE: Add for theming @presenter = presenter_class.new(current_ability, collections) @@ -68,28 +67,8 @@ def create end # rubocop:enable Metrics/AbcSize, Metrics/MethodLength - def handle_create_exception(exception) - logger.error("Contact form failed to send: #{exception.inspect}") - flash.now[:error] = 'Sorry, this message was not delivered.' - render :new - end - - # Override this method if you want to perform additional operations - # when a email is successfully sent, such as sending a confirmation - # response to the user. - def after_deliver; end - private - def build_contact_form - @contact_form = model_class.new(contact_form_params) - end - - def contact_form_params - return {} unless params.key?(:contact_form) - params.require(:contact_form).permit(:contact_method, :category, :name, :email, :subject, :message) - end - # OVERRIDE: return collections for theming # Return 6 collections, sorts by title def collections(rows: 6) @@ -134,3 +113,5 @@ def setup_negative_captcha end end end + +Hyrax::ContactFormController.prepend(Hyrax::ContactFormControllerDecorator) diff --git a/app/controllers/hyrax/content_blocks_controller_decorator.rb b/app/controllers/hyrax/content_blocks_controller_decorator.rb index afaa2201f..bcca43e36 100644 --- a/app/controllers/hyrax/content_blocks_controller_decorator.rb +++ b/app/controllers/hyrax/content_blocks_controller_decorator.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true -# OVERRIDE Hyrax v3.4.0 to add home_text to permitted_params - Adding themes +# OVERRIDE Hyrax v5.0.0rc2 to add home_text to permitted_params - Adding themes + module Hyrax module ContentBlocksControllerDecorator - # override hyrax v2.9.0 added the home_text content block to permitted_params - Adding Themes + private + def permitted_params params.require(:content_block).permit(:marketing, :announcement, diff --git a/app/controllers/hyrax/dashboard/collections_controller.rb b/app/controllers/hyrax/dashboard/collections_controller.rb deleted file mode 100644 index 95e25f557..000000000 --- a/app/controllers/hyrax/dashboard/collections_controller.rb +++ /dev/null @@ -1,749 +0,0 @@ -# frozen_string_literal: true - -# OVERRIDE Hyrax v3.4.2 -# - Fix file upload in logo and banner -# - Use work titles for collection thumbnail select & to add an option to reset to the default thumbnail -module Hyrax - module Dashboard - ## Shows a list of all collections to the admins - class CollectionsController < Hyrax::My::CollectionsController - include Blacklight::AccessControls::Catalog - include Blacklight::Base - - configure_blacklight do |config| - config.search_builder_class = Hyrax::Dashboard::CollectionsSearchBuilder - end - - include BreadcrumbsForCollections - with_themed_layout 'dashboard' - - before_action :filter_docs_with_read_access!, except: %i[show edit] - before_action :remove_select_something_first_flash, except: :show - - include Hyrax::Collections::AcceptsBatches - - # include the render_check_all view helper method - helper Hyrax::BatchEditsHelper - # include the display_trophy_link view helper method - helper Hyrax::TrophyHelper - - # Catch permission errors - rescue_from Hydra::AccessDenied, CanCan::AccessDenied, with: :deny_collection_access - - # actions: index, create, new, edit, show, update, destroy, permissions, citation - before_action :authenticate_user!, except: [:index] - - class_attribute :presenter_class, - :form_class, - :single_item_search_builder_class, - :membership_service_class - - self.presenter_class = Hyrax::CollectionPresenter - - self.form_class = Hyrax::Forms::CollectionForm - - # The search builder to find the collection - self.single_item_search_builder_class = SingleCollectionSearchBuilder - # The search builder to find the collections' members - self.membership_service_class = Collections::CollectionMemberSearchService - - load_and_authorize_resource except: [:index, :create], - instance_name: :collection, - class: Hyrax.config.collection_model - - def deny_collection_access(exception) - if exception.action == :edit - redirect_to(url_for(action: 'show'), alert: 'You do not have sufficient privileges to edit this document') - elsif current_user&.persisted? - redirect_to root_url, alert: exception.message - else - session['user_return_to'] = request.url - redirect_to main_app.new_user_session_url, alert: exception.message - end - end - - def new - # Coming from the UI, a collection type id should always be present. Coming from the API, if a collection type id is not specified, - # use the default collection type (provides backward compatibility with versions < Hyrax 2.1.0) - collection_type_id = params[:collection_type_id].presence || default_collection_type.id - @collection.collection_type_gid = CollectionType.find(collection_type_id).to_global_id - add_breadcrumb t(:'hyrax.controls.home'), root_path - add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path - add_breadcrumb t('.header', type_title: collection_type.title), request.path - @collection.try(:apply_depositor_metadata, current_user.user_key) - form - end - - def show - # unused assignment to be removed in 4.0.0 - @banner_file = presenter.banner_file if collection_type.brandable? - - presenter - query_collection_members - end - - def edit - form - collection_type - # Gets original filename of an uploaded thumbnail. See #update - return unless ::SolrDocument.find(@collection.id).thumbnail_path.include?("uploaded_collection_thumbnails" && uploaded_thumbnail?) - @thumbnail_filename = File.basename(uploaded_thumbnail_files.reject { |f| File.basename(f).include? @collection.id }.first) - end - - def uploaded_thumbnail? - uploaded_thumbnail_files.any? - end - - def uploaded_thumbnail_files - Dir["#{UploadedCollectionThumbnailPathService.upload_dir(@collection)}/*"] - end - - def after_create - Deprecation.warn("Method `#after_create` will be removed in Hyrax 4.0.") - after_create_response # call private method for processing - end - - def after_create_error - Deprecation.warn("Method `#after_create_error` will be removed in Hyrax 4.0.") - after_create_errors("") # call private method for processing - end - - def create - # Manual load and authorize necessary because Cancan will pass in all - # form attributes. When `permissions_attributes` are present the - # collection is saved without a value for `has_model.` - @collection = Hyrax.config.collection_class.new - authorize! :create, @collection - - case @collection - when ActiveFedora::Base - create_active_fedora_collection - else - create_valkyrie_collection - end - end - - def after_update - Deprecation.warn("Method `#after_update` will be removed in Hyrax 4.0.") - after_update_response # call private method for processing - end - - def after_update_error - Deprecation.warn("Method `#after_update_error` will be removed in Hyrax 4.0.") - after_update_errors(@collection.errors) # call private method for processing - end - - def update - # OVERRIDE: ensure user is allowed to change visibility - authorize! :manage_discovery, @collection if collection_params[:visibility].present? && @collection.visibility != collection_params[:visibility] - - case @collection - when ActiveFedora::Base - update_active_fedora_collection - else - update_valkyrie_collection - end - end - - def process_branding - process_banner_input - process_logo_input - # TODO: does this still work? - process_uploaded_thumbnail(params[:collection][:thumbnail_upload]) if params[:collection][:thumbnail_upload] - end - - # Deletes any previous thumbnails. The thumbnail indexer (see services/hyrax/indexes_thumbnails) - # checks if an uploaded thumbnail exists in the public folder before indexing the thumbnail path. - def delete_uploaded_thumbnail - FileUtils.rm_rf(uploaded_thumbnail_files) - @collection.update_index - - respond_to do |format| - format.html - format.js # renders delete_uploaded_thumbnail.js.erb, which updates _current_thumbnail.html.erb - end - end - - def after_destroy(_id) - # leaving id to avoid changing the method's parameters prior to release - respond_to do |format| - format.html do - redirect_to hyrax.my_collections_path, - notice: t('hyrax.dashboard.my.action.collection_delete_success') - end - format.json { head :no_content, location: hyrax.my_collections_path } - end - end - - def after_destroy_error(id) - respond_to do |format| - format.html do - flash[:notice] = t('hyrax.dashboard.my.action.collection_delete_fail') - render :edit, status: :unprocessable_entity - end - format.json { render json: { id: }, status: :unprocessable_entity, location: dashboard_collection_path(@collection) } - end - end - - def destroy - case @collection - when Valkyrie::Resource - valkyrie_destroy - else - if @collection.destroy - after_destroy(params[:id]) - else - after_destroy_error(params[:id]) - end - end - rescue StandardError => err - Rails.logger.error(err) - after_destroy_error(params[:id]) - end - - def collection - action_name == 'show' ? @presenter : @collection - end - - # Renders a JSON response with a list of files in this collection - # This is used by the edit form to populate the thumbnail_id dropdown - # OVERRIDE: Hyrax 2.9 to use work titles for collection thumbnail select & to add an option to reset to the default thumbnail - # rubocop:disable Metrics/MethodLength, Metrics/AbcSize - def files - params[:q] = '' unless params[:q] - builder = Hyrax::CollectionMemberSearchBuilder.new(scope: self, collection:, search_includes_models: :works) - # get the default work image because we do not want to show any works in this - # dropdown that only have the default work image. this indicates that they have - # no files attached, and will throw an error if selected. - default_work_thumbnail_path = Site.instance.default_work_image&.url.presence || ActionController::Base.helpers.image_path('default.png') - work_with_no_files_thumbnail_path = ActionController::Base.helpers.image_path('work.png') - response = repository.search(builder.where(params[:q]).query) - # only return the works that have files, because these will be the only ones with a viable thumbnail - result = response.documents.reject do |document| - document["thumbnail_path_ss"].blank? || document["thumbnail_path_ss"].include?(default_work_thumbnail_path) || - document["thumbnail_path_ss"].include?(work_with_no_files_thumbnail_path) - # rubocop:disable Style/MultilineBlockChain - end.map do |document| - # rubocop:enable Style/MultilineBlockChain - { id: document["thumbnail_path_ss"].split('/').last.gsub(/\?.*/, ''), text: document["title_tesim"].first } - end - reset_thumbnail_option = { - id: '', - text: 'Default thumbnail' - } - result << reset_thumbnail_option - render json: result - end - # rubocop:enable Metrics/MethodLength, Metrics/AbcSize - - private - - def create_active_fedora_collection - # Coming from the UI, a collection type gid should always be present. Coming from the API, if a collection type gid is not specified, - # use the default collection type (provides backward compatibility with versions < Hyrax 2.1.0) - @collection.collection_type_gid = params[:collection_type_gid].presence || default_collection_type.to_global_id - @collection.attributes = collection_params.except(:members, :parent_id, :collection_type_gid) - @collection.apply_depositor_metadata(current_user.user_key) - @collection.visibility = Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE unless @collection.discoverable? - if @collection.save - after_create_response - else - after_create_errors(@collection.errors) - end - end - - def create_valkyrie_collection - return after_create_errors(form_err_msg(form)) unless form.validate(collection_params) - - result = - transactions['change_set.create_collection'] - .with_step_args( - 'change_set.set_user_as_depositor' => { user: current_user }, - 'change_set.add_to_collections' => { collection_ids: Array(params[:parent_id]) }, - 'collection_resource.apply_collection_type_permissions' => { user: current_user } - ) - .call(form) - - @collection = result.value_or { return after_create_errors(result.failure.first) } - after_create_response - end - - def update_active_fedora_collection - process_member_changes - process_branding - - @collection.visibility = Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE unless @collection.discoverable? - # we don't have to reindex the full graph when updating collection - @collection.try(:reindex_extent=, Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX) - if @collection.update(collection_params.except(:members)) - after_update_response - else - after_update_errors(@collection.errors) - end - end - - def update_valkyrie_collection - return after_update_errors(form_err_msg(form)) unless form.validate(collection_params) - - result = transactions['change_set.update_collection'] - .with_step_args( - 'collection_resource.save_collection_banner' => { update_banner_file_ids: params["banner_files"], - banner_unchanged_indicator: params["banner_unchanged"] }, - 'collection_resource.save_collection_logo' => { update_logo_file_ids: params["logo_files"], - alttext_values: params["alttext"], - linkurl_values: params["linkurl"] } - ) - .call(form) - @collection = result.value_or { return after_update_errors(result.failure.first) } - - process_member_changes - after_update_response - end - - def valkyrie_destroy - if transactions['collection_resource.destroy'].call(@collection).successful? - after_destroy(params[:id]) - else - after_destroy_error(params[:id]) - end - end - - def form_err_msg(form) - errmsg = [] - form.errors.messages.each do |fld, err| - errmsg << "#{fld} #{err.to_sentence}" - end - errmsg.to_sentence - end - - def default_collection_type - Hyrax::CollectionType.find_or_create_default_collection_type - end - - def default_collection_type_gid - default_collection_type.to_global_id.to_s - end - - def collection_type - @collection_type ||= CollectionType.find_by_gid!(collection.collection_type_gid) - end - - def link_parent_collection(parent_id) - child = collection.respond_to?(:valkyrie_resource) ? collection.valkyrie_resource : collection - Hyrax::Collections::CollectionMemberService.add_member(collection_id: parent_id, - new_member: child, - user: current_user) - end - - def uploaded_files(uploaded_file_ids) - return [] if uploaded_file_ids.empty? - UploadedFile.find(uploaded_file_ids) - end - - def update_referer - return edit_dashboard_collection_path(@collection) + (params[:referer_anchor] || '') if params[:stay_on_edit] - dashboard_collection_path(@collection) - end - - # branding specific methods - def process_banner_input - return update_existing_banner if params["banner_unchanged"] == "true" - remove_banner - uploaded_file_ids = params["banner_files"] - banner_text = params["banner_text"]&.first - add_new_banner(uploaded_file_ids, banner_text) if uploaded_file_ids - end - - def update_existing_banner - banner_info = CollectionBrandingInfo.where(collection_id: @collection.id.to_s).where(role: "banner") - banner_info.first.alt_text = params["banner_text"].first - banner_info.first.save(banner_info.first.local_path, false) - end - - def add_new_banner(uploaded_file_ids, banner_text) - f = uploaded_files(uploaded_file_ids).first - ## OVERRIDE Hyrax 3.4.0 - process locations - file_location = process_file_location(f) - - banner_info = CollectionBrandingInfo.new( - collection_id: @collection.id, - filename: File.split(f.file_url).last, - role: "banner", - alt_txt: banner_text, - target_url: "" - ) - ## OVERRIDE Hyrax 3.4.0 - process locations - banner_info.save file_location - end - - def remove_banner - banner_info = CollectionBrandingInfo.where(collection_id: @collection.id.to_s).where(role: "banner") - banner_info&.delete_all - end - - def update_logo_info(uploaded_file_id, alttext, linkurl) - logo_info = CollectionBrandingInfo.where(collection_id: @collection.id.to_s).where(role: "logo").where(local_path: uploaded_file_id.to_s) - logo_info.first.alt_text = alttext - logo_info.first.target_url = linkurl - logo_info.first.local_path = uploaded_file_id - logo_info.first.save(uploaded_file_id, false) - end - - def create_logo_info(uploaded_file_id, alttext, linkurl) - file = uploaded_files(uploaded_file_id) - file_location = process_file_location(file) # OVERRIDE Hyrax 3.4.0 to clean file location - - logo_info = CollectionBrandingInfo.new( - collection_id: @collection.id, - filename: File.split(file.file_url).last, - role: "logo", - alt_txt: alttext, - target_url: linkurl - ) - logo_info.save file_location - logo_info - end - - def remove_redundant_files(public_files) - # remove any public ones that were not included in the selection. - logos_info = CollectionBrandingInfo.where(collection_id: @collection.id.to_s).where(role: "logo") - logos_info.each do |logo_info| - logo_info.delete(logo_info.local_path) unless public_files.include? logo_info.local_path - logo_info.destroy unless public_files.include? logo_info.local_path - end - end - - def process_logo_records(uploaded_file_ids) - public_files = [] - uploaded_file_ids.each_with_index do |ufi, i| - # If the user has chosen a new logo, the ufi will be an integer - # If the logo was previously chosen, the ufi will be a path - # If it is a path, update the rec, else create a new rec - if !ufi.match(/\D/).nil? - update_logo_info(ufi, params["alttext"][i], verify_linkurl(params["linkurl"][i])) - public_files << ufi - else # brand new one, insert in the database - logo_info = create_logo_info(ufi, params["alttext"][i], verify_linkurl(params["linkurl"][i])) - public_files << logo_info.local_path - end - end - public_files - end - - def process_logo_input - uploaded_file_ids = params["logo_files"] - public_files = [] - - if uploaded_file_ids.nil? - remove_redundant_files public_files - return - end - - public_files = process_logo_records uploaded_file_ids - remove_redundant_files public_files - end - - # run a solr query to get the collections the user has access to edit - # @return [Array] a list of the user's collections - def find_collections_for_form - Hyrax::CollectionsService.new(self).search_results(:edit) - end - - def remove_select_something_first_flash - flash.delete(:notice) if flash.notice == 'Select something first' - end - - def presenter - @presenter ||= presenter_class.new(curation_concern, current_ability) - end - - def curation_concern - # Query Solr for the collection. - # run the solr query to find the collection members - response, _docs = single_item_search_service.search_results - curation_concern = response.documents.first - raise CanCan::AccessDenied unless curation_concern - curation_concern - end - - def single_item_search_service - Hyrax::SearchService.new(config: blacklight_config, user_params: params.except(:q, :page), scope: self, search_builder_class: single_item_search_builder_class) - end - - # Instantiates the search builder that builds a query for a single item - # this is useful in the show view. - def single_item_search_builder - search_service.search_builder - end - deprecation_deprecate :single_item_search_builder - - def collection_params - if Hyrax.config.collection_class < ActiveFedora::Base - @participants = extract_old_style_permission_attributes(params[:collection]) - form_class.model_attributes(params[:collection]) - else - params.permit(collection: {})[:collection] - .merge(params.permit(:collection_type_gid) - .with_defaults(collection_type_gid: default_collection_type_gid)) - .merge(member_of_collection_ids: Array(params[:parent_id])) - end - end - - def extract_old_style_permission_attributes(attributes) - # TODO: REMOVE in 3.0 - part of deprecation of permission attributes - permissions = attributes.delete("permissions_attributes") - return [] unless permissions - Deprecation.warn(self, "Passing in permissions_attributes parameter with a new collection is deprecated and support will be removed from Hyrax 3.0. " \ - "Use Hyrax::PermissionTemplate instead to grant Manage, Deposit, or View access.") - participants = [] - permissions.each do |p| - access = access(p) - participants << { agent_type: agent_type(p), agent_id: p["name"], access: } if access - end - participants - end - - def agent_type(permission) - # TODO: REMOVE in 3.0 - part of deprecation of permission attributes - return 'group' if permission["type"] == 'group' - 'user' - end - - def access(permission) - # TODO: REMOVE in 3.0 - part of deprecation of permission attributes - return Hyrax::PermissionTemplateAccess::MANAGE if permission["access"] == 'edit' - return Hyrax::PermissionTemplateAccess::VIEW if permission["access"] == 'read' - end - - def process_member_changes - case params[:collection][:members] - when 'add' then add_members_to_collection - when 'remove' then remove_members_from_collection - when 'move' then move_members_between_collections - end - end - - def add_members_to_collection(collection = nil, collection_id: nil) - collection_id ||= (collection.try(:id) || @collection.id) - - Hyrax::Collections::CollectionMemberService - .add_members_by_ids(collection_id:, - new_member_ids: batch, - user: current_user) - end - - def remove_members_from_collection - Hyrax::Collections::CollectionMemberService - .remove_members_by_ids(collection_id: @collection.id, - member_ids: batch, - user: current_user) - end - - def move_members_between_collections - remove_members_from_collection - add_members_to_collection(collection_id: params[:destination_collection_id]) - - destination_title = - Hyrax.query_service.find_by(id: params[:destination_collection_id]).title.first || - params[:destination_collection_id] - flash[:notice] = "Successfully moved #{batch.count} files to #{destination_title} Collection." - rescue StandardError => err - Rails.logger.error(err) - destination_title = - Hyrax.query_service.find_by(id: params[:destination_collection_id]).title.first || - destination_id - flash[:error] = "An error occured. Files were not moved to #{destination_title} Collection." - end - - # Include 'catalog' and 'hyrax/base' in the search path for views, while prefering - # our local paths. Thus we are unable to just override `self.local_prefixes` - def _prefixes - @_prefixes ||= super + ['catalog', 'hyrax/base'] - end - - def ensure_admin! - # Even though the user can view this collection, they may not be able to view - # it on the admin page. - authorize! :read, :admin_dashboard - end - - def search_action_url(*args) - hyrax.dashboard_collections_url(*args) - end - - def form - @form ||= - case @collection - when Valkyrie::Resource - form = Hyrax::Forms::ResourceForm.for(@collection) - form.prepopulate! - form - else - form_class.new(@collection, current_ability, repository) - end - end - - def set_default_permissions - additional_grants = @participants # Grants converted from older versions (< Hyrax 2.1.0) where share was edit or read access instead of managers, depositors, and viewers - Collections::PermissionsCreateService.create_default(collection: @collection, creating_user: current_user, grants: additional_grants) - end - - def query_collection_members - member_works - member_subcollections if collection_type.nestable? - parent_collections if collection_type.nestable? && action_name == 'show' - end - - # Instantiate the membership query service - def collection_member_service - @collection_member_service ||= membership_service_class.new(scope: self, collection:, params: params_for_query) - end - - def member_works - @response = collection_member_service.available_member_works - @member_docs = @response.documents - @members_count = @response.total - end - - def member_subcollections - results = collection_member_service.available_member_subcollections - @subcollection_solr_response = results - @subcollection_docs = results.documents - @subcollection_count = @presenter.nil? ? 0 : @subcollection_count = @presenter.subcollection_count = results.total - end - - def parent_collections - page = params[:parent_collection_page].to_i - query = Hyrax::Collections::NestedCollectionQueryService - collection.parent_collections = query.parent_collections(child: collection_object, scope: self, page:) - end - - def collection_object - @collection - end - - # You can override this method if you need to provide additional - # inputs to the search builder. For example: - # search_field: 'all_fields' - # @return the inputs required for the collection member search builder - def params_for_query - params.merge(q: params[:cq]) - end - - # Only accept HTTP|HTTPS urls; - # @return the url - def verify_linkurl(linkurl) - url = Loofah.scrub_fragment(linkurl, :prune).to_s - url if valid_url?(url) - end - - def valid_url?(url) - (url =~ URI.regexp(['http', 'https'])) - end - - def after_create_response - if @collection.is_a?(ActiveFedora::Base) - form - set_default_permissions - # if we are creating the new collection as a subcollection (via the nested collections controller), - # we pass the parent_id through a hidden field in the form and link the two after the create. - link_parent_collection(params[:parent_id]) unless params[:parent_id].nil? - end - respond_to do |format| - Hyrax::SolrService.commit - format.html { redirect_to edit_dashboard_collection_path(@collection), notice: t('hyrax.dashboard.my.action.collection_create_success') } - format.json { render json: @collection, status: :created, location: dashboard_collection_path(@collection) } - end - add_members_to_collection unless batch.empty? - end - - def after_create_errors_for_active_fedora(errors) - form - respond_to do |format| - format.html do - flash[:error] = errors.to_s - render action: 'new' - end - format.json { render json: @collection.errors, status: :unprocessable_entity } - end - end - - def after_create_errors(errors) # for valkyrie - return after_create_errors_for_active_fedora(errors) if @collection.is_a? ActiveFedora::Base - respond_to do |wants| - wants.html do - flash[:error] = errors.to_s - render 'new', status: :unprocessable_entity - end - wants.json do - render_json_response(response_type: :unprocessable_entity, options: { errors: }) - end - end - end - - def after_update_response - respond_to do |format| - format.html { redirect_to update_referer, notice: t('hyrax.dashboard.my.action.collection_update_success') } - format.json { render json: @collection, status: :updated, location: dashboard_collection_path(@collection) } - end - end - - def after_update_errors_for_active_fedora(errors) - form - respond_to do |format| - format.html do - flash[:error] = errors.to_s - render action: 'edit' - end - format.json { render json: @collection.errors, status: :unprocessable_entity } - end - end - - def after_update_errors(errors) # for valkyrie - return after_update_errors_for_active_fedora(errors) if @collection.is_a? ActiveFedora::Base - respond_to do |wants| - wants.html do - flash[:error] = errors.to_s - render 'edit', status: :unprocessable_entity - end - wants.json { render_json_response(response_type: :unprocessable_entity, options: { errors: }) } - end - end - - # rubocop:disable Metrics/MethodLength - def process_uploaded_thumbnail(uploaded_file) - dir_name = UploadedCollectionThumbnailPathService.upload_dir(@collection) - saved_file = Rails.root.join(dir_name, uploaded_file.original_filename) - # Create directory if it doesn't already exist - if File.directory?(dir_name) # clear contents - delete_uploaded_thumbnail - else - FileUtils.mkdir_p(dir_name) - end - File.open(saved_file, 'wb') do |file| - file.write(uploaded_file.read) - end - image = MiniMagick::Image.open(saved_file) - # Save two versions of the image: one for homepage feature cards and one for regular thumbnail - image.resize('500x900').format("jpg").write("#{dir_name}/#{@collection.id}_card.jpg") - image.resize('150x300').format("jpg").write("#{dir_name}/#{@collection.id}_thumbnail.jpg") - File.chmod(0o664, "#{dir_name}/#{@collection.id}_thumbnail.jpg") - File.chmod(0o664, "#{dir_name}/#{@collection.id}_card.jpg") - end - # rubocop:enable Metrics/MethodLength - - ## OVERRIDE Hyrax 3.4.0 handle file locations - def process_file_location(f) - if /^http/.match?(f.file_url) - f.file.download!(f.file_url) - f.file_url - elsif %r{^\/}.match?(f.file_url) - f.file.path - else - f.file_url - end - end - ## END OVERRIDE - end - end -end diff --git a/app/controllers/hyrax/dashboard/collections_controller_decorator.rb b/app/controllers/hyrax/dashboard/collections_controller_decorator.rb new file mode 100644 index 000000000..dcc1fb94b --- /dev/null +++ b/app/controllers/hyrax/dashboard/collections_controller_decorator.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +# OVERRIDE Hyrax v5.0.0rc2 +# - Fix file upload in logo and banner +# - Use work titles for collection thumbnail select & to add an option to reset to the default thumbnail +module Hyrax + module Dashboard + ## Shows a list of all collections to the admins + # rubocop:disable Metrics/ModuleLength + module CollectionsControllerDecorator + def edit + form + collection_type + # Gets original filename of an uploaded thumbnail. See #update + return unless ::SolrDocument.find(@collection.id).thumbnail_path.include?("uploaded_collection_thumbnails") && uploaded_thumbnail? + @thumbnail_filename = File.basename(uploaded_thumbnail_files.reject { |f| File.basename(f).include? @collection.id }.first) + end + + def uploaded_thumbnail? + uploaded_thumbnail_files.any? + end + + def uploaded_thumbnail_files + Dir["#{UploadedCollectionThumbnailPathService.upload_dir(@collection)}/*"] + end + + def update + # OVERRIDE: ensure user is allowed to change visibility + authorize! :manage_discovery, @collection if collection_params[:visibility].present? && @collection.visibility != collection_params[:visibility] + + super + end + + def process_branding + super + + # TODO: does this still work? + process_uploaded_thumbnail(params[:collection][:thumbnail_upload]) if params[:collection][:thumbnail_upload] + end + + # Deletes any previous thumbnails. The thumbnail indexer (see services/hyrax/indexes_thumbnails) + # checks if an uploaded thumbnail exists in the public folder before indexing the thumbnail path. + def delete_uploaded_thumbnail + FileUtils.rm_rf(uploaded_thumbnail_files) + @collection.update_index + + respond_to do |format| + format.html + format.js # renders delete_uploaded_thumbnail.js.erb, which updates _current_thumbnail.html.erb + end + end + + # Renders a JSON response with a list of files in this collection + # This is used by the edit form to populate the thumbnail_id dropdown + # OVERRIDE: Hyrax 2.9 to use work titles for collection thumbnail select & to add an option to reset to the default thumbnail + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + def files + params[:q] = '' unless params[:q] + builder = Hyrax::CollectionMemberSearchBuilder.new(scope: self, collection:, search_includes_models: :works) + # get the default work image because we do not want to show any works in this + # dropdown that only have the default work image. this indicates that they have + # no files attached, and will throw an error if selected. + default_work_thumbnail_path = Site.instance.default_work_image&.url.presence || ActionController::Base.helpers.image_path('default.png') + work_with_no_files_thumbnail_path = ActionController::Base.helpers.image_path('work.png') + response = repository.search(builder.where(params[:q]).query) + # only return the works that have files, because these will be the only ones with a viable thumbnail + result = response.documents.reject do |document| + document["thumbnail_path_ss"].blank? || document["thumbnail_path_ss"].include?(default_work_thumbnail_path) || + document["thumbnail_path_ss"].include?(work_with_no_files_thumbnail_path) + # rubocop:disable Style/MultilineBlockChain + end.map do |document| + # rubocop:enable Style/MultilineBlockChain + { id: document["thumbnail_path_ss"].split('/').last.gsub(/\?.*/, ''), text: document["title_tesim"].first } + end + reset_thumbnail_option = { + id: '', + text: 'Default thumbnail' + } + result << reset_thumbnail_option + render json: result + end + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize + + private + + # branding specific methods + def process_banner_input + return update_existing_banner if params["banner_unchanged"] == "true" + remove_banner + uploaded_file_ids = params["banner_files"] + banner_text = params["banner_text"]&.first + add_new_banner(uploaded_file_ids, banner_text) if uploaded_file_ids + end + + def update_existing_banner + banner_info = CollectionBrandingInfo.where(collection_id: @collection.id.to_s).where(role: "banner") + banner_info.first.alt_text = params["banner_text"].first + banner_info.first.save(banner_info.first.local_path, false) + end + + def add_new_banner(uploaded_file_ids, banner_text) + f = uploaded_files(uploaded_file_ids).first + ## OVERRIDE Hyrax v5.0.0rc2 - process locations + file_location = process_file_location(f) + + banner_info = CollectionBrandingInfo.new( + collection_id: @collection.id, + filename: File.split(f.file_url).last, + role: "banner", + alt_txt: banner_text, + target_url: "" + ) + ## OVERRIDE Hyrax v5.0.0rc2 - process locations + banner_info.save file_location + end + + def create_logo_info(uploaded_file_id, alttext, linkurl) + file = uploaded_files(uploaded_file_id) + file_location = process_file_location(file) # OVERRIDE Hyrax v5.0.0rc2 to clean file location + + logo_info = CollectionBrandingInfo.new( + collection_id: @collection.id, + filename: File.split(file.file_url).last, + role: "logo", + alt_txt: alttext, + target_url: linkurl + ) + logo_info.save file_location + logo_info + end + + def collection_params + if Hyrax.config.collection_class < ActiveFedora::Base + @participants = extract_old_style_permission_attributes(params[:collection]) + form_class.model_attributes(params[:collection]) + else + params.permit(collection: {})[:collection] + .merge(params.permit(:collection_type_gid) + .with_defaults(collection_type_gid: default_collection_type_gid)) + .merge(member_of_collection_ids: Array(params[:parent_id])) + end + end + + # rubocop:disable Metrics/MethodLength + def process_uploaded_thumbnail(uploaded_file) + dir_name = UploadedCollectionThumbnailPathService.upload_dir(@collection) + saved_file = Rails.root.join(dir_name, uploaded_file.original_filename) + # Create directory if it doesn't already exist + if File.directory?(dir_name) # clear contents + delete_uploaded_thumbnail + else + FileUtils.mkdir_p(dir_name) + end + File.open(saved_file, 'wb') do |file| + file.write(uploaded_file.read) + end + image = MiniMagick::Image.open(saved_file) + # Save two versions of the image: one for homepage feature cards and one for regular thumbnail + image.resize('500x900').format("jpg").write("#{dir_name}/#{@collection.id}_card.jpg") + image.resize('150x300').format("jpg").write("#{dir_name}/#{@collection.id}_thumbnail.jpg") + File.chmod(0o664, "#{dir_name}/#{@collection.id}_thumbnail.jpg") + File.chmod(0o664, "#{dir_name}/#{@collection.id}_card.jpg") + end + # rubocop:enable Metrics/MethodLength + + ## OVERRIDE Hyrax v5.0.0rc2 handle file locations + def process_file_location(f) + if /^http/.match?(f.file_url) + f.file.download!(f.file_url) + f.file_url + elsif %r{^\/}.match?(f.file_url) + f.file.path + else + f.file_url + end + end + ## END OVERRIDE + end + end +end + +Hyrax::Dashboard::CollectionsController.prepend(Hyrax::Dashboard::CollectionsControllerDecorator) diff --git a/app/controllers/hyrax/generic_works_controller.rb b/app/controllers/hyrax/generic_works_controller.rb index 243dfcbfd..9831ea89a 100644 --- a/app/controllers/hyrax/generic_works_controller.rb +++ b/app/controllers/hyrax/generic_works_controller.rb @@ -7,6 +7,7 @@ module Hyrax class GenericWorksController < ApplicationController # Adds Hyrax behaviors to the controller. include Hyrax::WorksControllerBehavior + include Hyku::WorksControllerBehavior include Hyrax::BreadcrumbsForWorks self.curation_concern_type = ::GenericWork diff --git a/app/controllers/hyrax/images_controller.rb b/app/controllers/hyrax/images_controller.rb index e37798964..ec45442aa 100644 --- a/app/controllers/hyrax/images_controller.rb +++ b/app/controllers/hyrax/images_controller.rb @@ -7,6 +7,7 @@ module Hyrax class ImagesController < ApplicationController # Adds Hyrax behaviors to the controller. include Hyrax::WorksControllerBehavior + include Hyku::WorksControllerBehavior include Hyrax::BreadcrumbsForWorks self.curation_concern_type = ::Image diff --git a/app/controllers/hyrax/my/works_controller_decorator.rb b/app/controllers/hyrax/my/works_controller_decorator.rb deleted file mode 100644 index df68ce1ca..000000000 --- a/app/controllers/hyrax/my/works_controller_decorator.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -## -# OVERRIDE Hyrax 3.5.0; when Hyrax hits v4.0.0 we can remove this. -# @see https://github.com/samvera/hyrax/pull/5972 -module Hyrax - module My - module WorksControllerDecorator - def collections_service - cloned = clone - cloned.params = {} - Hyrax::CollectionsService.new(cloned) - end - end - end -end - -Hyrax::My::WorksController.prepend(Hyrax::My::WorksControllerDecorator) diff --git a/app/controllers/hyrax/pages_controller.rb b/app/controllers/hyrax/pages_controller_decorator.rb similarity index 56% rename from app/controllers/hyrax/pages_controller.rb rename to app/controllers/hyrax/pages_controller_decorator.rb index ed12e044d..3f470a4db 100644 --- a/app/controllers/hyrax/pages_controller.rb +++ b/app/controllers/hyrax/pages_controller_decorator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# OVERRIDE: Hyrax v5.0.0 +# OVERRIDE: Hyrax v5.0.0rc2 # - add inject_theme_views method for theming # - add homepage presenter for access to feature flippers # - add access to content blocks in the show method @@ -8,17 +8,22 @@ module Hyrax # Shows the about and help page - class PagesController < ApplicationController - load_and_authorize_resource class: ContentBlock, except: :show - layout :pages_layout + module PagesControllerDecorator + extend ActiveSupport::Concern # OVERRIDE: Add for theming # Adds Hydra behaviors into the application controller include Blacklight::SearchContext include Blacklight::AccessControls::Catalog - # OVERRIDE: Adding inject theme views method for theming - around_action :inject_theme_views + prepended do + # OVERRIDE: Adding inject theme views method for theming + around_action :inject_theme_views + + # OVERRIDE: Hyrax v5.0.0rc2 Add for theming + class_attribute :presenter_class + self.presenter_class = Hyrax::HomepagePresenter + end # OVERRIDE: Add for theming # The search builder for finding recent documents @@ -27,14 +32,8 @@ def search_builder_class Hyrax::HomepageSearchBuilder end - # OVERRIDE: Hyrax v3.4.0 Add for theming - class_attribute :presenter_class - self.presenter_class = Hyrax::HomepagePresenter - - helper Hyrax::ContentBlockHelper - def show - @page = ContentBlock.for(params[:key]) + super # OVERRIDE: Additional for theming @presenter = presenter_class.new(current_ability, collections) @@ -46,45 +45,8 @@ def show @announcement_text = ContentBlock.for(:announcement) end - def edit - add_breadcrumb t(:'hyrax.controls.home'), root_path - add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path - add_breadcrumb t(:'hyrax.admin.sidebar.configuration'), '#' - add_breadcrumb t(:'hyrax.admin.sidebar.pages'), hyrax.edit_pages_path - end - - def update - respond_to do |format| - if @page.update(value: update_value_from_params) - redirect_path = "#{hyrax.edit_pages_path}##{params[:content_block].keys.first}" - format.html { redirect_to redirect_path, notice: t(:'hyrax.pages.updated') } - else - format.html { render :edit } - end - end - end - private - def permitted_params - params.require(:content_block).permit(:about, - :agreement, - :help, - :terms) - end - - # When a request comes to the controller, it will be for one and - # only one of the content blocks. Params always looks like: - # {'about_page' => 'Here is an awesome about page!'} - # So reach into permitted params and pull out the first value. - def update_value_from_params - permitted_params.values.first - end - - def pages_layout - action_name == 'show' ? 'homepage' : 'hyrax/dashboard' - end - # OVERRIDE: return collections for theming # Return 6 collections, sorts by title def collections(rows: 6) @@ -115,3 +77,5 @@ def inject_theme_views end end end + +Hyrax::PagesController.prepend(Hyrax::PagesControllerDecorator) diff --git a/app/forms/hyrax/forms/workflow_responsibility_form_decorator.rb b/app/forms/hyrax/forms/workflow_responsibility_form_decorator.rb new file mode 100644 index 000000000..10fc33a32 --- /dev/null +++ b/app/forms/hyrax/forms/workflow_responsibility_form_decorator.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# OVERRIDE Hyrax v5.0.0rc2 Expand to allow adding groups to workflow roles + +module Hyrax + module Forms + module WorkflowResponsibilityFormDecorator + module ClassMethods + # Determine which form it is, user or group + def new(params = {}) + if params[:user_id].present? + super + else + Forms::WorkflowResponsibilityGroupForm.new(params) + end + end + end + end + end +end + +Hyrax::Forms::WorkflowResponsibilityForm.singleton_class + .send(:prepend, Hyrax::Forms::WorkflowResponsibilityFormDecorator::ClassMethods) diff --git a/spec/forms/hyrax/forms/workflow_responsibility_form_decorator_spec.rb b/spec/forms/hyrax/forms/workflow_responsibility_form_decorator_spec.rb new file mode 100644 index 000000000..eb072adc2 --- /dev/null +++ b/spec/forms/hyrax/forms/workflow_responsibility_form_decorator_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +RSpec.describe Hyrax::Forms::WorkflowResponsibilityForm, type: :decorator do + describe ".new" do + context "when user_id is present" do + let(:user) { create(:user) } + + it "returns a WorkflowResponsibilityForm" do + expect(described_class.new(user_id: user.id)).to be_a described_class + end + end + + context "when user_id is not present" do + let(:group) { create(:group) } + + it "returns a WorkflowResponsibilityGroupForm" do + expect(described_class.new(group_id: group.id)).to be_a Hyrax::Forms::WorkflowResponsibilityGroupForm + end + end + end +end