class LoadFromRepositoryProtocolsDatatable < CustomDatatable # Needed for sanitize_sql_like method include ActiveRecord::Sanitization::ClassMethods include InputSanitizeHelper PREFIXED_ID_SQL = "('#{Protocol::ID_PREFIX}' || COALESCE(\"protocols\".\"parent_id\", \"protocols\".\"id\"))".freeze def initialize(view, team, user) super(view) @team = team @user = user end def sortable_columns @sortable_columns ||= [ 'Protocol.name', 'Protocol.version_number', 'adjusted_parent_id', 'protocol_keywords_str', 'full_username_str', 'Protocol.published_on' ] end def searchable_columns @searchable_columns ||= [ 'Protocol.name', "Protocol.#{PREFIXED_ID_SQL}", 'Protocol.published_on', 'Protocol.version_number', 'ProtocolKeyword.name' ] end def as_json(_options = {}) { draw: dt_params[:draw].to_i, recordsTotal: get_raw_records_base.distinct.count, recordsFiltered: records.present? ? records.first.filtered_count : 0, data: data } end private # Returns json of current protocols (already paginated) def data records.map do |record| parent = record.parent || record { DT_RowId: parent.id, '0': escape_input(record.name), '1': record.version_number, '2': parent.code, '3': keywords_html(record), '4': escape_input(record.published_by&.full_name), '5': I18n.l(record.published_on, format: :full) } end end def new_search_condition(column, value) model, column = column.split('.', 2) model = model.constantize casted_column = case column when PREFIXED_ID_SQL ::Arel::Nodes::SqlLiteral.new(PREFIXED_ID_SQL) when 'published_on' ::Arel::Nodes::NamedFunction.new( 'CAST', [Arel.sql("to_char( protocols.published_on, '#{formated_date}' ) AS VARCHAR")] ) else ::Arel::Nodes::NamedFunction.new('CAST', [model.arel_table[column.to_sym].as(typecast)]) end casted_column.matches("%#{ActiveRecord::Base.sanitize_sql_like(value)}%") end def fetch_records super.select('COUNT("protocols"."id") OVER() AS filtered_count') end def get_raw_records_base original_without_versions = @team.protocols .left_outer_joins(:published_versions) .where(protocol_type: Protocol.protocol_types[:in_repository_published_original]) .where(published_versions: { id: nil }) .select(:id) published_versions = @team.protocols .where(protocol_type: Protocol.protocol_types[:in_repository_published_version]) .order('parent_id, version_number DESC') .select('DISTINCT ON (parent_id) id') Protocol.where("protocols.id IN ((#{original_without_versions.to_sql}) UNION (#{published_versions.to_sql}))") .active .with_granted_permissions(@user, ProtocolPermissions::READ) end # OVERRIDE - query database for records (this will be # later paginated and filtered) after that "data" function # will return json def get_raw_records get_raw_records_base .preload(:parent, :protocol_keywords, user_assignments: %i(user user_role)) .joins('LEFT OUTER JOIN "protocol_protocol_keywords" ' \ 'ON "protocol_protocol_keywords"."protocol_id" = "protocols"."id"') .joins('LEFT OUTER JOIN "protocol_keywords" ' \ 'ON "protocol_protocol_keywords"."protocol_keyword_id" = "protocol_keywords"."id"') .joins('LEFT OUTER JOIN "users" ON "users"."id" = "protocols"."published_by_id"') .group('"protocols"."id"') .select( '"protocols".*', 'COALESCE("protocols"."parent_id", "protocols"."id") AS adjusted_parent_id', 'STRING_AGG(DISTINCT("protocol_keywords"."name"), \', \') AS "protocol_keywords_str"', 'MAX("users"."full_name") AS "full_username_str"' ) end # Various helper methods def keywords_html(record) if record.protocol_keywords.blank? "#{I18n.t('protocols.no_keywords')}" else res = [] record.protocol_keywords.sort_by { |kw| kw.name.downcase }.each do |kw| sanitized_kw = sanitize_input(kw.name) res << "#{sanitized_kw}" end res.join(', ') end end end