concord-consortium/rigse

View on GitHub
rails/lib/materials/data_helpers.rb

Summary

Maintainability
F
5 days
Test Coverage
module Materials
  module DataHelpers
    # This module expects to be included into a controller, so that view_context resolves
    # to something that provides all the various view helpers.
    # Note that this module will be dealing only with ExternalActivity instances. In the past, it used to handle
    # Activity, Sequence and Interactive types too, but it's not a case anymore.
    private

    # The main difference between this sanitization method and one provided by Rails natively is
    # is that Sanitize module *always* returns valid HTML input.
    def safe_sanitize(html_fragment)
      html_fragment = "" if (html_fragment.nil?)
      Sanitize.fragment(html_fragment, Sanitize::Config::BASIC)
    end

    def materials_data( materials,
                        assigned_to_class       = nil,
                        include_related         = 0,
                        skip_lightbox_reloads   = false )
      data = []

      if assigned_to_class
        portal_clazz = Portal::Clazz.find(assigned_to_class)
        active_assigned_materials = portal_clazz.active_offerings.map { |offering| "#{offering.runnable_type}::#{offering.runnable_id}" }
      else
        active_assigned_materials = []
      end

      materials.each do |material|
        parent_data = nil
        material_count = nil

        if material.respond_to?(:offerings_count)
          material_count = material.offerings_count
        end

        if material.respond_to?(:parent) && material.parent
          material_count = material_count + material.parent.offerings_count

          parent_data = {
            id: material.parent.id,
            type: view_context.t(:investigation),
            name: material.parent.name
          }
        end

        has_activities = material.respond_to?(:activities) && !material.activities.nil?
        has_pretest = material.respond_to?(:has_pretest) && material.has_pretest

        if material.respond_to?(:has_teacher_edition)
          has_teacher_edition = material.has_teacher_edition
        else
          has_teacher_edition = false
        end

        if material.respond_to?(:saves_student_data)
          saves_student_data = material.saves_student_data
        else
          saves_student_data = true
        end

        user_data = nil
        if material.user && (!material.user.name.nil?)
          user_data = {
            id: material.user.id,
            name: material.user.name
          }
        end

        #
        # Find favorite data
        #
        is_favorite = false
        favorite_id = nil

        if  current_user                        &&
            !current_user.anonymous?            &&
            material.respond_to?(:favorites)

          favorites = material.favorites.where(:user_id => current_user.id)
          if favorites.count > 0
            favorite_id = favorites[0].id
            is_favorite = true

          end
        end

        #
        # Add subject_areas and grade_levels
        # from the ActsAsTaggableOn associated properties lists.
        #
        tags = {}
        tags['subject_areas']   = []
        tags['grade_levels']    = []
        tags['sensors']    = []

        tags.each do |key, value|
            list = material.send(key)
            list.each do |o|
                tags[key].push o.name
            end
        end

        project_ids = material.projects.map { |p| p.id }

        projects = material.projects.map { |p|
            url = nil
            if p.landing_page_slug
                url = project_page_url(p.landing_page_slug)
            end

            {
                id:                 p.id,
                name:               p.name,
                landing_page_url:   url,
                public:             p.public
            }
        }

        #
        # Check if we should search for related material
        #
        related_materials = []
        if include_related > 0

            cohort_ids  = []
            user_id     = nil

            if ! current_user.nil?
                user_id = current_user.id
                if  current_user.portal_teacher &&
                    current_user.portal_teacher.cohorts

                    cohort_ids = current_user.portal_teacher.cohorts.map {|c| c.id}
                end
            end

            search = Sunspot.search(Search::DefaultSearchableModels) do

                fulltext "*" do
                    boost(5.0) { with(:project_ids, project_ids) }
                    boost(4.0) { with(:subject_areas, tags['subject_areas']) }
                    boost(2.0) { with(:grade_levels, tags['grade_levels']) }
                end

                any_of do
                    with    :published,     true
                    with    :user_id,       user_id
                end

                if current_user
                    if ! current_user.has_role? ['admin']
                        any_of do
                            with    :cohort_ids,    nil
                            with    :cohort_ids,    cohort_ids
                        end
                    end
                else
                    with :cohort_ids, nil
                end

                if current_user.nil? || current_user.only_a_student?
                    with(:is_assessment_item, false)
                end

                with        :is_archived,   false
                with        :is_official,   true
                without     material
                order_by    :score, :desc
                paginate    page: 1, per_page: include_related
            end

            related = search.results
            related_materials = materials_data(related)
        end

        slug = material.name.respond_to?(:parameterize) ? material.name.parameterize : nil
        stem_resource_type = material.respond_to?(:lara_sequence?) ? (material.lara_sequence? ? 'sequence' : 'activity') : material.class.name.downcase


        #
        # Include associated standards
        #
        standard_statements =
            StandardStatement.where(
                material_type: material.class.name.underscore,
                material_id: material.id).order("uri ASC")

        standard_statements_json = []

        standard_statements.each do |statement|
            standard_statements_json.push( {
                type:               statement.doc,
                uri:                statement.uri,
                parents:            statement.parents,
                notation:           statement.statement_notation,
                education_level:    statement.education_level,
                description:        statement.description
            });
        end

        #
        # Add license info
        #
        license_info_json = nil
        if material.respond_to?(:license) && material.license
            license = material.license
            license_info_json = {
                name:           license.name,
                code:           license.code,
                deed:           license.deed,
                legal:          license.legal,
                image:          license.image,
                description:    license.description,
                number:         license.number
            }
        end

        #
        # Determine if we should enable social media sharing
        #
        enable_sharing = true
        if material.is_a?(ExternalActivity) && material.respond_to?(:enable_sharing)
            enable_sharing = material.enable_sharing
        end

        #
        # Create stem_resource_url
        #
        stem_resource_url = nil
        if material.is_a?(ExternalActivity)
            stem_resource_url = view_context.stem_resources_url(material.id.to_i, slug)
        elsif material.is_a?(Interactive) && material.respond_to?(:external_activity_id)
            stem_resource_url = view_context.stem_resources_url(material.external_activity_id.to_i, slug)
        end

        mat_data = {
          id: material.id,
          name: material.name,
          # long_description_for_current_user returns long_description_for_teacher, long_description, or short_description.
          long_description_for_current_user: safe_sanitize(material.long_description_for_user(current_visitor)),
          # Raw db attribute, no fallback behavior.
          long_description: safe_sanitize(material.long_description),
          # Raw db attribute, no fallback behavior.
          long_description_for_teacher: safe_sanitize(material.long_description_for_teacher),
          # Raw db attribute, no fallback behavior.
          short_description: safe_sanitize(material.short_description),
          keywords: material.keywords,
          material_type: material.material_type,
          class_name: material.class.name,
          class_name_underscored: material.class.name.underscore,
          icon: {
            url: (material.respond_to?(:icon_image) ? material.icon_image : nil),
          },
          material_properties: material.material_property_list,
          is_official: material.is_official,
          is_archived: material.archived?,

          is_favorite: is_favorite,
          favorite_id: favorite_id,

          subject_areas:    tags['subject_areas'],
          grade_levels:     tags['grade_levels'],
          projects:         projects,

          publication_status: material.publication_status,

          #
          # Links for assigning materials to classes and collections
          #
          links: links_for_material(material, skip_lightbox_reloads),

          external_url: material.is_a?(ExternalActivity) ? material.url : nil,
          preview_url: view_context.run_url_for(material, view_context.preview_params(current_user, material.teacher_only? ? {:teacher_mode => true} : {})),
          edit_url: (material.is_a?(ExternalActivity) && policy(material).matedit?) ? view_context.matedit_external_activity_url(material, iFrame: true) : nil,
          unarchive_url: (material.is_a?(ExternalActivity) && policy(material).unarchive?) ? view_context.unarchive_external_activity_url(material) : nil,
          archive_url:   (material.is_a?(ExternalActivity) && policy(material).archive?) ? view_context.archive_external_activity_url(material) : nil,
          copy_url: external_copyable(material) ? view_context.copy_external_activity_url(material) : nil,
          assign_to_class_url: current_visitor.portal_teacher && material.respond_to?(:offerings) ? "javascript:PortalComponents.renderAssignToClassModal({material_id: #{material.id}, material_type: '#{material.class.to_s}', lightbox_material_text: '#{t('material').pluralize.capitalize}', skip_reload: true})" : nil,
          assign_to_collection_url: current_visitor.has_role?('admin') && material.respond_to?(:materials_collections) ? collections_external_activity_path(material) : nil,
          assigned_classes: assigned_clazz_names(material),
          class_count: material_count,
          sensors: tags['sensors'],
          has_teacher_edition: has_teacher_edition,
          has_activities: has_activities,
          has_pretest: has_pretest,
          saves_student_data: saves_student_data,
          activities: has_activities ? material.activities.map{ |a| {id: a.id, name: a.name} } : [],
          lara_activity_or_sequence: material.respond_to?(:lara_activity_or_sequence?) ? material.lara_activity_or_sequence? : false,
          parent: parent_data,
          user: user_data,
          assigned: active_assigned_materials.include?("#{material.class.name}::#{material.id}"),
          credits: material.respond_to?(:credits) ? material.credits : nil,
          created_at: material.created_at,

          license_info: license_info_json,

          related_materials: related_materials,

          standard_statements: standard_statements_json,

          enable_sharing: enable_sharing,

          slug: slug,

          stem_resource_url: stem_resource_url
        }

        data.push mat_data
      end
      data
    end

    def external_copyable(material)
      if !(material.is_a? ExternalActivity) || material.author_url.blank?
        return false
      end

      client = Client.where("site_url LIKE :ext_act_host", ext_act_host: "%#{URI.parse(material.author_url).host}%").first
      if client.nil?
        return false;
      end

      tool = Tool.where("tool_id LIKE :client_site_url", client_site_url: "%#{URI.parse(client.site_url).host}").first
      if tool.nil? || tool.remote_duplicate_url.blank?
        return false;
      end

      return current_visitor.has_role?('admin','manager') ||
             (!material.is_locked && current_visitor.has_role?('author')) ||
             material.author_email == current_visitor.email ||
             (!material.is_locked && material.teacher_copyable && current_visitor.portal_teacher.present?)
    end

    def links_for_material( material,
                            skip_lightbox_reloads = false )
      external = false
      if material.is_a? ExternalActivity
        browse_url = browse_external_activity_url(material)
        external = true
      elsif material.is_a? Interactive
        browse_url = interactive_url(material)
      end

      links = {
        browse: {
          url: browse_url
        }
      }
      if material.archived?
        if policy(material).unarchive?
          return  {
              unarchive: {
                url: unarchive_external_activity_url(material),
                text: t("matedit.unarchive"),
                ccConfirm: t("matedit.unarchive_confirm", {name: material.name})
              }
          }
        else
          return {}
        end
      end

      preview_params = !current_user.nil? && current_user.portal_teacher ? {logging: true} : {}
      links[:preview] = {
        url: view_context.run_url_for(material, preview_params),
        text: 'Preview',
        target: '_blank'
      }

      if external && material.author_url.present?
        edit_in_iframe = !current_visitor.has_role?('author') && !current_visitor.has_role?('admin')
        if policy(material).matedit?
          links[:external_edit] = {
            url: matedit_external_activity_url(material, iFrame: edit_in_iframe),
            text: "Edit",
            target: '_blank'
          }
          links[:external_lara_edit] = {
            url: matedit_external_activity_url(material, iFrame: edit_in_iframe),
            text: "Edit",
            target: '_blank'
          }
        end
        if external_copyable(material)
          links[:external_copy] = {
            url: copy_external_activity_url(material),
            text: "Copy",
            target: '_blank'
          }
        end
        if current_visitor.has_role?('admin')
          links[:external_edit_iframe] = {
            url: matedit_external_activity_url(material, iFrame: true),
            text: "(edit in iframe)",
            target: '_blank',
            className: ''
          }
        end
      end

      if material.respond_to?(:print_url) && material.print_url.present?
        links[:print_url] = {
            text: "Print",
            target: "_blank",
            url: material.print_url
        }
      end

      if material.respond_to?(:teacher_resources_url) && !material.teacher_resources_url.blank?
        if current_visitor.portal_teacher || current_visitor.has_role?('admin','manager')
          links[:teacher_resources] = {
            text: "Teacher Resources",
            target: "_blank",
            url: material.teacher_resources_url
          }
        end
      end

      if material.respond_to?(:teacher_guide_url) && !material.teacher_guide_url.blank?
        if current_visitor.portal_teacher || current_visitor.has_role?('admin','manager')
          links[:teacher_guide] = {
            text: "Teacher Guide",
            target: "_blank",
            url: material.teacher_guide_url
          }
        end
      end

      if material.respond_to?(:rubric_doc_url) && !material.rubric_doc_url.blank?
        if current_visitor.portal_teacher || current_visitor.has_role?('admin','manager')
          links[:rubric_doc] = {
            text: "Rubric",
            target: "_blank",
            url: material.rubric_doc_url
          }
        end
      end

      if policy(material).edit?
        links[:edit] = {
          text: "(portal settings)",
          url: edit_polymorphic_url(material),
          className: ''
        }
      end

      assignPopupConfig = { :material_id => material.id, :material_type => material.class.to_s, :lightbox_material_text => t('material').pluralize.capitalize, :skip_reload => true }
      if !current_visitor.portal_student
        if current_visitor.portal_teacher && material.respond_to?(:offerings)
          assignPopupConfig[:anonymous] = false
        else
          assignPopupConfig[:anonymous] = true
        end

        links[:assign_material] = {
            text: "Assign or Share",
            url: "javascript:void(0)",
            onclick: "PortalComponents.renderAssignToClassModal(" + assignPopupConfig.to_json + ")"
        }
      end

      if (current_visitor.has_role?('admin') || current_visitor.is_project_admin?()) && material.respond_to?(:materials_collections)
        links[:assign_collection] = {
          text: "Add to Collection",
          target: "_blank",
          url: collections_external_activity_path(material)
        }
      end

      links
    end

    def assigned_clazz_names(material)
      return [] unless current_visitor.portal_teacher
      offerings = current_visitor.portal_teacher.offerings.includes(:runnable, :clazz).select { |o| o.runnable == material }
      offerings.map { |o| o.clazz.name }
    end
  end
end