diff --git a/app/controllers/repository_rows_controller.rb b/app/controllers/repository_rows_controller.rb
index cb34496f3..ddb8dbd37 100644
--- a/app/controllers/repository_rows_controller.rb
+++ b/app/controllers/repository_rows_controller.rb
@@ -5,11 +5,23 @@ class RepositoryRowsController < ApplicationController
before_action :load_info_modal_vars, only: :show
before_action :load_vars, only: %i(edit update)
- before_action :load_repository, only: %i(create delete_records)
+ before_action :load_repository, only: %i(create delete_records index)
+ before_action :load_columns_mappings, only: :index
before_action :check_create_permissions, only: :create
before_action :check_edit_permissions, only: %i(edit update)
before_action :check_destroy_permissions, only: :delete_records
+ def index
+ @draw = params[:draw].to_i
+ per_page = params[:length] == '-1' ? 100 : params[:length].to_i
+ page = (params[:start].to_i / per_page) + 1
+ records = RepositoryDatatableService.new(@repository,
+ params,
+ @columns_mappings)
+ @repository_row_count = records.repository_rows.count
+ @repository_rows = records.repository_rows.page(page).per(per_page)
+ end
+
def create
record = RepositoryRow.new(repository: @repository,
created_by: current_user,
@@ -238,6 +250,17 @@ class RepositoryRowsController < ApplicationController
def load_repository
@repository = Repository.find_by_id(params[:repository_id])
render_404 unless @repository
+ render_403 unless can_read_team?(@repository.team)
+ end
+
+ def load_columns_mappings
+ # Make mappings of custom columns, so we have same id for every column
+ i = 5
+ @columns_mappings = {}
+ @repository.repository_columns.order(:id).each do |column|
+ @columns_mappings[column.id] = i.to_s
+ i += 1
+ end
end
def check_create_permissions
diff --git a/app/helpers/repository_datatable_helper.rb b/app/helpers/repository_datatable_helper.rb
new file mode 100644
index 000000000..c93bdf09d
--- /dev/null
+++ b/app/helpers/repository_datatable_helper.rb
@@ -0,0 +1,46 @@
+module RepositoryDatatableHelper
+ include InputSanitizeHelper
+ def prepare_row_columns(repository_rows, repository, columns_mappings, team)
+ parsed_records = []
+ repository_rows.each do |record|
+ row = {
+ 'DT_RowId': record.id,
+ '1': assigned_row(record),
+ '2': escape_input(record.name),
+ '3': I18n.l(record.created_at, format: :full),
+ '4': escape_input(record.created_by.full_name),
+ 'recordEditUrl':
+ Rails.application.routes.url_helpers
+ .edit_repository_repository_row_path(repository,
+ record.id),
+ 'recordUpdateUrl':
+ Rails.application.routes.url_helpers
+ .repository_repository_row_path(repository, record.id),
+ 'recordInfoUrl':
+ Rails.application.routes.url_helpers.repository_row_path(record.id)
+ }
+
+ # Add custom columns
+ # byebug
+ record.repository_cells.each do |cell|
+ row[columns_mappings[cell.repository_column.id]] =
+ custom_auto_link(
+ display_tooltip(cell.value.data,
+ Constants::NAME_MAX_LENGTH),
+ simple_format: true,
+ team: team
+ )
+ end
+ parsed_records << row
+ end
+ parsed_records
+ end
+
+ def assigned_row(record)
+ # if @assigned_rows && @assigned_rows.include?(record)
+ # " "
+ # else
+ " "
+ # end
+ end
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index fce0a148d..ed5775492 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -12,6 +12,8 @@ class Repository < ApplicationRecord
inverse_of: :repository, dependent: :destroy
has_many :report_elements, inverse_of: :repository, dependent: :destroy
has_many :repository_list_items, inverse_of: :repository, dependent: :destroy
+ has_many :repository_searchable_rows,
+ class_name: '::Views::Datatables::SearchRepository'
auto_strip_attributes :name, nullify: false
validates :name,
diff --git a/app/models/repository_list_value.rb b/app/models/repository_list_value.rb
index ae92b068b..c53856809 100644
--- a/app/models/repository_list_value.rb
+++ b/app/models/repository_list_value.rb
@@ -13,7 +13,11 @@ class RepositoryListValue < ApplicationRecord
validates :repository_cell, presence: true
def formatted
- return '' unless repository_list_item
+ data.to_s
+ end
+
+ def data
+ return nil unless repository_list_item
repository_list_item.data
end
end
diff --git a/app/models/views/datatables/search_repository.rb b/app/models/views/datatables/search_repository.rb
new file mode 100644
index 000000000..2ff6a6f84
--- /dev/null
+++ b/app/models/views/datatables/search_repository.rb
@@ -0,0 +1,21 @@
+module Views
+ module Datatables
+ class SearchRepository < ApplicationRecord
+ belongs_to :repository
+ # def self.records(repository, search_value)
+ # # binding.pry
+ # # # where('repository_rows.repository_id', repository.id).to_a
+ # # where(repository_id: repository.id)
+ # # .where()
+ # end
+
+ private
+
+ # this isn't strictly necessary, but it will prevent
+ # rails from calling save, which would fail anyway.
+ def readonly?
+ true
+ end
+ end
+ end
+end
diff --git a/app/services/repository_datatable_service.rb b/app/services/repository_datatable_service.rb
new file mode 100644
index 000000000..61fe08d12
--- /dev/null
+++ b/app/services/repository_datatable_service.rb
@@ -0,0 +1,73 @@
+class RepositoryDatatableService
+
+ attr_reader :repository_rows
+
+ def initialize(repository, params, mappings)
+ @mappings = mappings
+ @repository = repository
+ process_query(params)
+ end
+
+ private
+
+ def process_query(params)
+ contitions = build_conditions(params)
+ if contitions[:search_value].present?
+ @repository_rows = search(contitions[:search_value])
+ else
+ @repository_rows = fetch_records
+ end
+ # byebug
+ end
+
+ def fetch_records
+ RepositoryRow.preload(:repository_columns,
+ :created_by,
+ repository_cells: :value)
+ .joins(:created_by)
+ .where(repository: @repository)
+ end
+
+ def search(value)
+ # binding.pry
+ filtered_rows = @repository.repository_searchable_rows.where(
+ 'name ILIKE :value
+ OR to_char(created_at, :time) ILIKE :value
+ OR user_full_name ILIKE :value
+ OR text_value ILIKE :value
+ OR date_value ILIKE :value
+ OR list_value ILIKE :value',
+ value: "%#{value}%",
+ time: "DD.MM.YYYY HH24:MI"
+ ).pluck(:id)
+ fetch_records.where(id: filtered_rows)
+ end
+
+ def build_conditions(params)
+ search_value = params[:search][:value]
+ order_by_column = { column: params[:order][:column].to_i,
+ dir: params[:order][:dir] }
+ { search_value: search_value, order_by_column: order_by_column }
+ end
+
+ def sortable_columns
+ sort_array = [
+ 'assigned',
+ 'RepositoryRow.name',
+ 'RepositoryRow.created_at',
+ 'User.full_name'
+ ]
+
+ sort_array.push(*repository_columns_sort_by)
+ @sortable_columns = sort_array
+ end
+
+ def repository_columns_sort_by
+ array = []
+ @repository.repository_columns.count.times do
+ array << 'RepositoryCell.value'
+ end
+ array
+ end
+
+end
diff --git a/app/views/repository_rows/index.json.jbuilder b/app/views/repository_rows/index.json.jbuilder
new file mode 100644
index 000000000..23af2e9f3
--- /dev/null
+++ b/app/views/repository_rows/index.json.jbuilder
@@ -0,0 +1,6 @@
+json.draw @draw
+json.recordsTotal @repository_rows.total_count
+json.recordsFiltered @repository_row_count
+json.data do
+ json.array! prepare_row_columns(@repository_rows, @repository, @columns_mappings, @repository.team)
+end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 5f6bbe557..580ca9c48 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -75,7 +75,7 @@ Rails.application.configure do
config.assets.raise_runtime_errors = true
# Only log info and higher on development
- config.log_level = :info
+ config.log_level = :debug
# Only allow Better Errors to work on trusted ip, use ifconfig to see which
# one you use and put it into application.yml!
diff --git a/config/routes.rb b/config/routes.rb
index e28efbd58..e550e303d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -449,7 +449,7 @@ Rails.application.routes.draw do
resources :repositories do
post 'repository_index',
- to: 'repositories#repository_table_index',
+ to: 'repository_rows#index', # repository_rows#index repositories#repository_table_index
as: 'table_index',
defaults: { format: 'json' }
# Save repository table state
diff --git a/db/migrate/20180306074931_create_search_repositories.rb b/db/migrate/20180306074931_create_search_repositories.rb
new file mode 100644
index 000000000..9311752f9
--- /dev/null
+++ b/db/migrate/20180306074931_create_search_repositories.rb
@@ -0,0 +1,5 @@
+class CreateSearchRepositories < ActiveRecord::Migration[5.1]
+ def change
+ create_view :search_repositories
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4fac94123..a3a437072 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180207095200) do
+ActiveRecord::Schema.define(version: 20180306074931) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -964,4 +964,65 @@ ActiveRecord::Schema.define(version: 20180207095200) do
JOIN user_teams ON ((teams.id = user_teams.team_id)));
SQL
+ create_view "search_repositories", sql_definition: <<-SQL
+ SELECT DISTINCT repository_rows.id,
+ repository_rows.repository_id,
+ repository_rows.created_by_id,
+ repository_rows.last_modified_by_id,
+ repository_rows.name,
+ repository_rows.created_at,
+ repository_rows.updated_at,
+ users.full_name AS user_full_name,
+ "values".text_value,
+ "values".date_value,
+ "values".list_value
+ FROM ((repository_rows
+ JOIN ( SELECT users_1.id,
+ users_1.full_name,
+ users_1.initials,
+ users_1.email,
+ users_1.encrypted_password,
+ users_1.reset_password_token,
+ users_1.reset_password_sent_at,
+ users_1.remember_created_at,
+ users_1.sign_in_count,
+ users_1.current_sign_in_at,
+ users_1.last_sign_in_at,
+ users_1.current_sign_in_ip,
+ users_1.last_sign_in_ip,
+ users_1.created_at,
+ users_1.updated_at,
+ users_1.avatar_file_name,
+ users_1.avatar_content_type,
+ users_1.avatar_file_size,
+ users_1.avatar_updated_at,
+ users_1.confirmation_token,
+ users_1.confirmed_at,
+ users_1.confirmation_sent_at,
+ users_1.unconfirmed_email,
+ users_1.invitation_token,
+ users_1.invitation_created_at,
+ users_1.invitation_sent_at,
+ users_1.invitation_accepted_at,
+ users_1.invitation_limit,
+ users_1.invited_by_type,
+ users_1.invited_by_id,
+ users_1.invitations_count,
+ users_1.tutorial_status,
+ users_1.current_team_id,
+ users_1.authentication_token,
+ users_1.settings
+ FROM users users_1) users ON ((users.id = repository_rows.created_by_id)))
+ LEFT JOIN ( SELECT repository_cells.repository_row_id,
+ repository_text_values.data AS text_value,
+ to_char(repository_date_values.data, 'DD.MM.YYYY HH24:MI'::text) AS date_value,
+ ( SELECT repository_list_items.data
+ FROM repository_list_items
+ WHERE (repository_list_items.id = repository_list_values.repository_list_item_id)) AS list_value
+ FROM (((repository_cells
+ JOIN repository_text_values ON ((repository_text_values.id = repository_cells.value_id)))
+ FULL JOIN repository_date_values ON ((repository_date_values.id = repository_cells.value_id)))
+ FULL JOIN repository_list_values ON ((repository_list_values.id = repository_cells.value_id)))) "values" ON (("values".repository_row_id = repository_rows.id)));
+ SQL
+
end
diff --git a/db/views/search_repositories_v01.sql b/db/views/search_repositories_v01.sql
new file mode 100644
index 000000000..e9112c506
--- /dev/null
+++ b/db/views/search_repositories_v01.sql
@@ -0,0 +1,28 @@
+SELECT DISTINCT
+ repository_rows.*,
+ users.full_name AS user_full_name,
+ values.text_value AS text_value,
+ values.date_value AS date_value,
+ values.list_value AS list_value
+ FROM repository_rows
+ INNER JOIN (
+ SELECT users.*
+ FROM users
+ ) AS users
+ ON users.id = repository_rows.created_by_id
+ LEFT OUTER JOIN (
+ SELECT repository_cells.repository_row_id,
+ repository_text_values.data AS text_value,
+ to_char(repository_date_values.data, 'DD.MM.YYYY HH24:MI') AS date_value,
+ ( SELECT repository_list_items.data
+ FROM repository_list_items
+ WHERE repository_list_items.id = repository_list_values.repository_list_item_id ) AS list_value
+ FROM repository_cells
+ INNER JOIN repository_text_values
+ ON repository_text_values.id = repository_cells.value_id
+ FULL OUTER JOIN repository_date_values
+ ON repository_date_values.id = repository_cells.value_id
+ FUll OUTER JOIN repository_list_values
+ ON repository_list_values.id = repository_cells.value_id
+ ) AS values
+ ON values.repository_row_id = repository_rows.id
diff --git a/spec/models/repository_list_value_spec.rb b/spec/models/repository_list_value_spec.rb
index dcae9396c..4c9905bab 100644
--- a/spec/models/repository_list_value_spec.rb
+++ b/spec/models/repository_list_value_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe RepositoryListValue, type: :model do
it { should accept_nested_attributes_for(:repository_cell) }
end
- describe '#data' do
+ describe '#formatted' do
let!(:repository) { create :repository }
let!(:repository_column) { create :repository_column, name: 'My column' }
let!(:repository_column) do
@@ -32,7 +32,7 @@ RSpec.describe RepositoryListValue, type: :model do
}
end
- it 'returns the data of a selected item' do
+ it 'returns the formatted data of a selected item' do
list_item = create :repository_list_item,
data: 'my item',
repository: repository,
@@ -71,4 +71,58 @@ RSpec.describe RepositoryListValue, type: :model do
expect(repository_list_value.reload.formatted).to eq ''
end
end
+
+ describe '#data' do
+ let!(:repository) { create :repository }
+ let!(:repository_column) { create :repository_column, name: 'My column' }
+ let!(:repository_column) do
+ create :repository_column, data_type: :RepositoryListValue
+ end
+ let!(:repository_row) { create :repository_row, name: 'My row' }
+ let!(:repository_list_value) do
+ create :repository_list_value, repository_cell_attributes: {
+ repository_column: repository_column,
+ repository_row: repository_row
+ }
+ end
+
+ it 'returns the data of a selected item' do
+ list_item = create :repository_list_item,
+ data: 'my item',
+ repository: repository,
+ repository_column: repository_column
+ repository_list_value.repository_list_item = list_item
+ repository_list_value.save
+ expect(repository_list_value.reload.data).to eq 'my item'
+ end
+
+ it 'retuns only the the item related to the list' do
+ repository_row_two = create :repository_row, name: 'New row'
+ repository_list_value_two =
+ create :repository_list_value, repository_cell_attributes: {
+ repository_column: repository_column,
+ repository_row: repository_row_two
+ }
+ list_item = create :repository_list_item,
+ data: 'new item',
+ repository: repository,
+ repository_column: repository_column
+ repository_list_value.repository_list_item = list_item
+ expect(repository_list_value.reload.data).to_not eq 'my item'
+ expect(repository_list_value.data).to be_nil
+ end
+
+ it 'returns an empty string if no item selected' do
+ list_item = create :repository_list_item,
+ data: 'my item',
+ repository: repository,
+ repository_column: repository_column
+ expect(repository_list_value.reload.data).to be_nil
+ end
+
+ it 'returns an empty string if item does not exists' do
+ repository_list_value.repository_list_item = nil
+ expect(repository_list_value.reload.data).to be_nil
+ end
+ end
end
diff --git a/spec/models/search_repository_spec.rb b/spec/models/search_repository_spec.rb
new file mode 100644
index 000000000..052920181
--- /dev/null
+++ b/spec/models/search_repository_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe SearchRepository, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end