Fixes #2337 - Zammad stops Organization pagination after 500 entries.
This commit is contained in:
parent
5b4c7ab886
commit
3fd0609f61
30 changed files with 590 additions and 251 deletions
|
@ -138,7 +138,7 @@ class App.ControllerGenericIndex extends App.Controller
|
|||
@render()
|
||||
|
||||
# fetch all
|
||||
if !@disableInitFetch
|
||||
if !@disableInitFetch && !@pageData.pagerAjax
|
||||
App[ @genericObject ].fetchFull(
|
||||
->
|
||||
clear: true
|
||||
|
@ -156,12 +156,45 @@ class App.ControllerGenericIndex extends App.Controller
|
|||
if @subscribeId
|
||||
App[ @genericObject ].unsubscribe(@subscribeId)
|
||||
|
||||
paginate: (page) =>
|
||||
return if page is @pageData.pagerSelected
|
||||
@pageData.pagerSelected = page
|
||||
@render()
|
||||
|
||||
render: =>
|
||||
if @pageData.pagerAjax
|
||||
sortBy = @table?.customOrderBy || @table?.orderBy || @defaultSortBy || 'id'
|
||||
orderBy = @table?.customOrderDirection || @table?.orderDirection || @defaultOrder || 'ASC'
|
||||
|
||||
fallbackSortBy = sortBy
|
||||
fallbackOrderBy = orderBy
|
||||
if sortBy isnt 'id'
|
||||
fallbackSortBy = "#{sortBy}, id"
|
||||
fallbackOrderBy = "#{orderBy}, ASC"
|
||||
|
||||
@startLoading()
|
||||
App[@genericObject].indexFull(
|
||||
(collection, data) =>
|
||||
@pageData.pagerTotalCount = data.total_count
|
||||
@stopLoading()
|
||||
@renderObjects(collection)
|
||||
{
|
||||
refresh: false
|
||||
sort_by: fallbackSortBy
|
||||
order_by: fallbackOrderBy
|
||||
page: @pageData.pagerSelected
|
||||
per_page: @pageData.pagerPerPage
|
||||
}
|
||||
)
|
||||
return
|
||||
|
||||
objects = App[@genericObject].search(
|
||||
sortBy: @defaultSortBy || 'name'
|
||||
order: @defaultOrder
|
||||
)
|
||||
@renderObjects(objects)
|
||||
|
||||
renderObjects: (objects) =>
|
||||
|
||||
# remove ignored items from collection
|
||||
if @ignoreObjectIDs
|
||||
|
@ -208,10 +241,24 @@ class App.ControllerGenericIndex extends App.Controller
|
|||
},
|
||||
@pageData.tableExtend
|
||||
)
|
||||
|
||||
if @pageData.pagerAjax
|
||||
params = _.extend(
|
||||
{
|
||||
pagerAjax: @pageData.pagerAjax
|
||||
pagerBaseUrl: @pageData.pagerBaseUrl
|
||||
pagerSelected: @pageData.pagerSelected
|
||||
pagerPerPage: @pageData.pagerPerPage
|
||||
pagerTotalCount: @pageData.pagerTotalCount
|
||||
sortRenderCallback: @render
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if !@table
|
||||
@table = new App.ControllerTable(params)
|
||||
else
|
||||
@table.update(objects: objects)
|
||||
@table.update(objects: objects, pagerSelected: @pageData.pagerSelected, pagerTotalCount: @pageData.pagerTotalCount)
|
||||
|
||||
edit: (id, e) =>
|
||||
e.preventDefault()
|
||||
|
|
|
@ -196,6 +196,29 @@ class App.ControllerTable extends App.Controller
|
|||
App.QueueManager.run('tableRender')
|
||||
|
||||
renderPager: (el, find = false) =>
|
||||
if @pagerAjax
|
||||
@renderPagerAjax(el, find)
|
||||
else
|
||||
@renderPagerStatic(el, find)
|
||||
|
||||
renderPagerAjax: (el, find = false) =>
|
||||
pages = parseInt((@pagerTotalCount - 1) / @pagerPerPage)
|
||||
if pages < 1
|
||||
if find
|
||||
el.find('.js-pager').html('')
|
||||
else
|
||||
el.filter('.js-pager').html('')
|
||||
return
|
||||
pager = App.view('generic/table_pager')(
|
||||
page: @pagerSelected - 1
|
||||
pages: pages
|
||||
)
|
||||
if find
|
||||
el.find('.js-pager').html(pager)
|
||||
else
|
||||
el.filter('.js-pager').html(pager)
|
||||
|
||||
renderPagerStatic: (el, find = false) =>
|
||||
pages = parseInt(((@objects.length - 1) / @shownPerPage))
|
||||
if pages < 1
|
||||
if find
|
||||
|
@ -214,6 +237,11 @@ class App.ControllerTable extends App.Controller
|
|||
|
||||
render: =>
|
||||
@setMaxPage()
|
||||
|
||||
# always render pager in case of ajax pagination
|
||||
if @pagerTotalCount
|
||||
@renderPager(@el, true)
|
||||
|
||||
if @renderState is undefined
|
||||
|
||||
# check if table is empty
|
||||
|
@ -451,11 +479,14 @@ class App.ControllerTable extends App.Controller
|
|||
container.delegate('.js-page', 'click', (e) =>
|
||||
e.stopPropagation()
|
||||
page = $(e.currentTarget).attr 'data-page'
|
||||
render = =>
|
||||
@shownPage = page
|
||||
@renderTableFull()
|
||||
App.QueueManager.add('tableRender', render)
|
||||
App.QueueManager.run('tableRender')
|
||||
if @pagerAjax
|
||||
@navigate "#{@pagerBaseUrl}#{(parseInt(page) + 1)}"
|
||||
else
|
||||
render = =>
|
||||
@shownPage = page
|
||||
@renderTableFull()
|
||||
App.QueueManager.add('tableRender', render)
|
||||
App.QueueManager.run('tableRender')
|
||||
)
|
||||
|
||||
@el.html(container)
|
||||
|
@ -660,6 +691,19 @@ class App.ControllerTable extends App.Controller
|
|||
@lastOrderDirection = orderDirection
|
||||
@lastOrderBy = orderBy
|
||||
|
||||
# sorting for ajax pagination will be made in the backend
|
||||
# so we only set the arrow for the sort direction
|
||||
if @pagerAjax
|
||||
for header in @headers
|
||||
if header.name is orderBy || "#{header.name}_id" is orderBy || header.name is "#{orderBy}_id"
|
||||
if orderDirection is 'DESC'
|
||||
header.sortOrderIcon = ['arrow-down', 'table-sort-arrow']
|
||||
else
|
||||
header.sortOrderIcon = ['arrow-up', 'table-sort-arrow']
|
||||
else
|
||||
header.sortOrderIcon = undefined
|
||||
return
|
||||
|
||||
# Underscore's sortBy cannot deal with null values, so we replace null values with a place holder string
|
||||
sortBy = (list, iteratee) ->
|
||||
_.sortBy(
|
||||
|
@ -964,6 +1008,10 @@ class App.ControllerTable extends App.Controller
|
|||
sortByColumn: (event) =>
|
||||
column = $(event.currentTarget).closest('[data-column-key]').attr('data-column-key')
|
||||
|
||||
# for ajax pagination we only accept valid attributes for sorting
|
||||
if @model && @pagerAjax
|
||||
return if !@attributesList[column]
|
||||
|
||||
orderBy = @customOrderBy || @orderBy
|
||||
orderDirection = @customOrderDirection || @orderDirection
|
||||
|
||||
|
@ -988,6 +1036,10 @@ class App.ControllerTable extends App.Controller
|
|||
render = =>
|
||||
@renderTableFull(false, skipHeadersResize: true)
|
||||
App.QueueManager.add('tableRender', render)
|
||||
|
||||
if @sortRenderCallback
|
||||
App.QueueManager.add('tableRender', @sortRenderCallback)
|
||||
|
||||
App.QueueManager.run('tableRender')
|
||||
|
||||
preferencesStore: (type, key, value) ->
|
||||
|
|
|
@ -8,10 +8,15 @@ class Index extends App.ControllerSubContent
|
|||
el: @el
|
||||
id: @id
|
||||
genericObject: 'Group'
|
||||
defaultSortBy: 'name'
|
||||
pageData:
|
||||
home: 'groups'
|
||||
object: 'Group'
|
||||
objects: 'Groups'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/groups/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#groups'
|
||||
notes: [
|
||||
'Groups are ...'
|
||||
|
@ -22,4 +27,11 @@ class Index extends App.ControllerSubContent
|
|||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
App.Config.set('Group', { prio: 1500, name: 'Groups', parent: '#manage', target: '#manage/groups', controller: Index, permission: ['admin.group'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -13,6 +13,10 @@ class Index extends App.ControllerSubContent
|
|||
home: 'Jobs'
|
||||
object: 'Scheduler'
|
||||
objects: 'Schedulers'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/job/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#Jobs'
|
||||
notes: [
|
||||
'Scheduler are ...'
|
||||
|
@ -24,4 +28,11 @@ class Index extends App.ControllerSubContent
|
|||
large: true
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
App.Config.set('Job', { prio: 3400, name: 'Scheduler', parent: '#manage', target: '#manage/job', controller: Index, permission: ['admin.scheduler'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -8,10 +8,15 @@ class Index extends App.ControllerSubContent
|
|||
el: @el
|
||||
id: @id
|
||||
genericObject: 'Macro'
|
||||
defaultSortBy: 'name'
|
||||
pageData:
|
||||
home: 'macros'
|
||||
object: 'Macro'
|
||||
objects: 'Macros'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/macros/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#macros'
|
||||
notes: [
|
||||
'Text modules are ...'
|
||||
|
@ -22,4 +27,11 @@ class Index extends App.ControllerSubContent
|
|||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
App.Config.set('Macros', { prio: 2310, name: 'Macros', parent: '#manage', target: '#manage/macros', controller: Index, permission: ['admin.macro'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -21,6 +21,7 @@ class ManageRouter extends App.ControllerPermanent
|
|||
|
||||
App.Config.set('manage', ManageRouter, 'Routes')
|
||||
App.Config.set('manage/:target', ManageRouter, 'Routes')
|
||||
App.Config.set('manage/:target/:page', ManageRouter, 'Routes')
|
||||
App.Config.set('settings/:target', ManageRouter, 'Routes')
|
||||
App.Config.set('channels/:target', ManageRouter, 'Routes')
|
||||
App.Config.set('channels/:target/:channel_id', ManageRouter, 'Routes')
|
||||
|
|
|
@ -13,10 +13,15 @@ class Index extends App.ControllerSubContent
|
|||
baseUrl: '/api/v1/organizations'
|
||||
container: @el.closest('.content')
|
||||
)
|
||||
defaultSortBy: 'name'
|
||||
pageData:
|
||||
home: 'organizations'
|
||||
object: 'Organization'
|
||||
objects: 'Organizations'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/organizations/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#organizations'
|
||||
notes: [
|
||||
'Organizations are for any person in the system. Agents (Owners, Resposbiles, ...) and Customers.'
|
||||
|
@ -28,4 +33,12 @@ class Index extends App.ControllerSubContent
|
|||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
|
||||
App.Config.set('Organization', { prio: 2000, name: 'Organizations', parent: '#manage', target: '#manage/organizations', controller: Index, permission: ['admin.organization'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -8,10 +8,15 @@ class Index extends App.ControllerSubContent
|
|||
el: @el
|
||||
id: @id
|
||||
genericObject: 'ReportProfile'
|
||||
defaultSortBy: 'name'
|
||||
pageData:
|
||||
home: 'report_profiles'
|
||||
object: 'Report Profile'
|
||||
objects: 'Report Profiles'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/report_profiles/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#report_profiles'
|
||||
notes: [
|
||||
# 'Report Profile are ...'
|
||||
|
@ -22,4 +27,11 @@ class Index extends App.ControllerSubContent
|
|||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
App.Config.set('ReportProfile', { prio: 8000, name: 'Report Profiles', parent: '#manage', target: '#manage/report_profiles', controller: Index, permission: ['admin.report_profile'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -8,10 +8,15 @@ class Index extends App.ControllerSubContent
|
|||
el: @el
|
||||
id: @id
|
||||
genericObject: 'Role'
|
||||
defaultSortBy: 'name'
|
||||
pageData:
|
||||
home: 'roles'
|
||||
object: 'Role'
|
||||
objects: 'Roles'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/roles/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#roles'
|
||||
notes: [
|
||||
'Roles are ...'
|
||||
|
@ -22,4 +27,11 @@ class Index extends App.ControllerSubContent
|
|||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
App.Config.set('Role', { prio: 1600, name: 'Roles', parent: '#manage', target: '#manage/roles', controller: Index, permission: ['admin.role'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -8,6 +8,7 @@ class Index extends App.ControllerSubContent
|
|||
el: @el
|
||||
id: @id
|
||||
genericObject: 'TextModule'
|
||||
defaultSortBy: 'name'
|
||||
importCallback: ->
|
||||
new App.Import(
|
||||
baseUrl: '/api/v1/text_modules'
|
||||
|
@ -18,6 +19,10 @@ class Index extends App.ControllerSubContent
|
|||
home: 'text_modules'
|
||||
object: 'TextModule'
|
||||
objects: 'Text modules'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/text_modules/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#text_modules'
|
||||
notes: [
|
||||
'Text modules are ...'
|
||||
|
@ -29,4 +34,11 @@ class Index extends App.ControllerSubContent
|
|||
container: @el.closest('.content')
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
App.Config.set('TextModule', { prio: 2300, name: 'Text modules', parent: '#manage', target: '#manage/text_modules', controller: Index, permission: ['admin.text_module'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -13,6 +13,10 @@ class Index extends App.ControllerSubContent
|
|||
home: 'triggers'
|
||||
object: 'Trigger'
|
||||
objects: 'Triggers'
|
||||
pagerAjax: true
|
||||
pagerBaseUrl: '#manage/trigger/'
|
||||
pagerSelected: ( @page || 1 )
|
||||
pagerPerPage: 150
|
||||
navupdate: '#triggers'
|
||||
notes: [
|
||||
'Triggers are ...'
|
||||
|
@ -24,4 +28,11 @@ class Index extends App.ControllerSubContent
|
|||
veryLarge: true
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
for key, value of params
|
||||
if key isnt 'el' && key isnt 'shown' && key isnt 'match'
|
||||
@[key] = value
|
||||
|
||||
@genericController.paginate( @page || 1 )
|
||||
|
||||
App.Config.set('Trigger', { prio: 3300, name: 'Trigger', parent: '#manage', target: '#manage/trigger', controller: Index, permission: ['admin.trigger'] }, 'NavBarAdmin')
|
||||
|
|
|
@ -621,6 +621,74 @@ set new attributes of model (remove already available attributes)
|
|||
App.Log.error('Model', statusText, error, url)
|
||||
)
|
||||
|
||||
###
|
||||
|
||||
index full collection (with assets)
|
||||
|
||||
App.Model.indexFull(@callback)
|
||||
|
||||
App.Model.indexFull(
|
||||
@callback
|
||||
page: 1
|
||||
per_page: 10
|
||||
sort_by: 'name'
|
||||
order_by: 'ASC'
|
||||
)
|
||||
|
||||
|
||||
###
|
||||
@indexFull: (callback, params = {}) ->
|
||||
url = "#{@url}?full=true"
|
||||
for key in ['page', 'per_page', 'sort_by', 'order_by']
|
||||
continue if !params[key]
|
||||
url += "&#{key}=#{params[key]}"
|
||||
|
||||
App.Log.debug('Model', "indexFull collection #{@className}", url)
|
||||
|
||||
# request already active, queue callback
|
||||
queueManagerName = "#{@className}::indexFull"
|
||||
|
||||
if params.refresh is undefined
|
||||
params.refresh = true
|
||||
|
||||
App.Ajax.request(
|
||||
type: 'GET'
|
||||
url: url
|
||||
processData: true,
|
||||
success: (data, status, xhr) =>
|
||||
App.Log.debug('Model', "got indexFull collection #{@className}", data)
|
||||
|
||||
recordIds = data.record_ids
|
||||
if data.record_ids is undefined
|
||||
recordIds = data[ @className.toLowerCase() + '_ids' ]
|
||||
|
||||
# full / load assets
|
||||
if data.assets
|
||||
App.Collection.loadAssets(data.assets, targetModel: @className)
|
||||
|
||||
# if no record_ids are found, no initial render is fired
|
||||
if data.record_ids && _.isEmpty(data.record_ids) && params.refresh
|
||||
App[@className].trigger('refresh', [])
|
||||
|
||||
# find / load object
|
||||
else if params.refresh
|
||||
App[@className].refresh(data)
|
||||
|
||||
if callback
|
||||
localCallback = =>
|
||||
collection = []
|
||||
for id in recordIds
|
||||
collection.push App[@className].find(id)
|
||||
callback(collection, data)
|
||||
App.QueueManager.add(queueManagerName, localCallback)
|
||||
|
||||
App.QueueManager.run(queueManagerName)
|
||||
|
||||
error: (xhr, statusText, error) =>
|
||||
@indexFullActive = false
|
||||
App.Log.error('Model', statusText, error, url)
|
||||
)
|
||||
|
||||
@_bindsEmpty: ->
|
||||
if @SUBSCRIPTION_ITEM
|
||||
for id, keys of @SUBSCRIPTION_ITEM
|
||||
|
@ -667,8 +735,9 @@ set new attributes of model (remove already available attributes)
|
|||
|
||||
# just show this values in result, all filters need to match to get shown
|
||||
filter:
|
||||
some_attribute1: ['only_this_value1', 'only_that_value1']
|
||||
some_attribute2: ['only_this_value2', 'only_that_value2']
|
||||
|
||||
# check single value
|
||||
some_attribute1: 'only_this_value1'
|
||||
|
||||
# just show this values in result, all filters need to match to get shown
|
||||
filterExtended:
|
||||
|
|
|
@ -105,7 +105,12 @@ module ApplicationController::RendersModels
|
|||
per_page = (params[:per_page] || 500).to_i
|
||||
offset = (page - 1) * per_page
|
||||
|
||||
generic_objects = object.order(id: :asc).offset(offset).limit(per_page)
|
||||
sql_helper = ::SqlHelper.new(object: object)
|
||||
sort_by = sql_helper.get_sort_by(params, 'id')
|
||||
order_by = sql_helper.get_order_by(params, 'ASC')
|
||||
order_sql = sql_helper.get_order(sort_by, order_by)
|
||||
|
||||
generic_objects = object.order(Arel.sql(order_sql)).offset(offset).limit(per_page)
|
||||
|
||||
if response_expand?
|
||||
list = []
|
||||
|
@ -124,8 +129,9 @@ module ApplicationController::RendersModels
|
|||
assets = item.assets(assets)
|
||||
end
|
||||
render json: {
|
||||
record_ids: item_ids,
|
||||
assets: assets,
|
||||
record_ids: item_ids,
|
||||
assets: assets,
|
||||
total_count: object.count
|
||||
}, status: :ok
|
||||
return
|
||||
end
|
||||
|
|
|
@ -48,48 +48,7 @@ curl http://localhost/api/v1/organizations -v -u #{login}:#{password}
|
|||
=end
|
||||
|
||||
def index
|
||||
offset = 0
|
||||
per_page = 500
|
||||
|
||||
if params[:page] && params[:per_page]
|
||||
offset = (params[:page].to_i - 1) * params[:per_page].to_i
|
||||
per_page = params[:per_page].to_i
|
||||
end
|
||||
|
||||
if per_page > 500
|
||||
per_page = 500
|
||||
end
|
||||
|
||||
organizations = policy_scope(Organization).order(id: :asc).offset(offset).limit(per_page)
|
||||
|
||||
if response_expand?
|
||||
list = []
|
||||
organizations.each do |organization|
|
||||
list.push organization.attributes_with_association_names
|
||||
end
|
||||
render json: list, status: :ok
|
||||
return
|
||||
end
|
||||
|
||||
if response_full?
|
||||
assets = {}
|
||||
item_ids = []
|
||||
organizations.each do |item|
|
||||
item_ids.push item.id
|
||||
assets = item.assets(assets)
|
||||
end
|
||||
render json: {
|
||||
record_ids: item_ids,
|
||||
assets: assets,
|
||||
}, status: :ok
|
||||
return
|
||||
end
|
||||
|
||||
list = []
|
||||
organizations.each do |organization|
|
||||
list.push organization.attributes_with_association_ids
|
||||
end
|
||||
render json: list
|
||||
model_index_render(policy_scope(Organization), params)
|
||||
end
|
||||
|
||||
=begin
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||
module HasSearchSortable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# methods defined here are going to extend the class, not the instance of it
|
||||
class_methods do
|
||||
|
||||
=begin
|
||||
|
||||
This function will check the params for the "sort_by" attribute
|
||||
and validate its values.
|
||||
|
||||
sort_by = search_get_sort_by(params, default)
|
||||
|
||||
returns
|
||||
|
||||
sort_by = [
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
|
||||
=end
|
||||
|
||||
def search_get_sort_by(params, default)
|
||||
sort_by = []
|
||||
if params[:sort_by].present? && params[:sort_by].is_a?(String)
|
||||
params[:sort_by] = [params[:sort_by]]
|
||||
elsif params[:sort_by].blank?
|
||||
params[:sort_by] = []
|
||||
end
|
||||
|
||||
# check order
|
||||
params[:sort_by].each do |value|
|
||||
|
||||
# only accept values which are set for the db schema
|
||||
raise "Found invalid column '#{value}' for sorting." if columns_hash[value].blank?
|
||||
|
||||
sort_by.push(value)
|
||||
end
|
||||
|
||||
if sort_by.blank?
|
||||
if default.is_a?(Array)
|
||||
sort_by = default
|
||||
else
|
||||
sort_by.push(default)
|
||||
end
|
||||
end
|
||||
|
||||
sort_by
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
This function will check the params for the "order_by" attribute
|
||||
and validate its values.
|
||||
|
||||
order_by = search_get_order_by(params, default)
|
||||
|
||||
returns
|
||||
|
||||
order_by = [
|
||||
'asc',
|
||||
'desc',
|
||||
]
|
||||
|
||||
=end
|
||||
|
||||
def search_get_order_by(params, default)
|
||||
order_by = []
|
||||
if params[:order_by].present? && params[:order_by].is_a?(String)
|
||||
params[:order_by] = [ params[:order_by] ]
|
||||
elsif params[:order_by].blank?
|
||||
params[:order_by] = []
|
||||
end
|
||||
|
||||
# check order
|
||||
params[:order_by].each do |value|
|
||||
raise "Found invalid order by value #{value}. Please use 'asc' or 'desc'." if !value.match?(/\A(asc|desc)\z/i)
|
||||
|
||||
order_by.push(value.downcase)
|
||||
end
|
||||
|
||||
if order_by.blank?
|
||||
if default.is_a?(Array)
|
||||
order_by = default
|
||||
else
|
||||
order_by.push(default)
|
||||
end
|
||||
end
|
||||
|
||||
order_by
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
This function will use the evaluated values for sort_by and
|
||||
order_by to generate the ORDER-SELECT sql statement for the sorting
|
||||
of the result.
|
||||
|
||||
sort_by = [ 'created_at', 'updated_at' ]
|
||||
order_by = [ 'asc', 'desc' ]
|
||||
default = 'tickets.created_at'
|
||||
|
||||
sql = search_get_order_select_sql(sort_by, order_by, default)
|
||||
|
||||
returns
|
||||
|
||||
sql = 'tickets.created_at, tickets.updated_at'
|
||||
|
||||
=end
|
||||
|
||||
def search_get_order_select_sql(sort_by, order_by, default)
|
||||
sql = []
|
||||
|
||||
sort_by.each_with_index do |value, index|
|
||||
next if value.blank?
|
||||
next if order_by[index].blank?
|
||||
|
||||
sql.push( "#{ActiveRecord::Base.connection.quote_table_name(table_name)}.#{ActiveRecord::Base.connection.quote_column_name(value)}" )
|
||||
end
|
||||
|
||||
if sql.blank?
|
||||
sql.push("#{ActiveRecord::Base.connection.quote_table_name(table_name)}.#{ActiveRecord::Base.connection.quote_column_name(default)}")
|
||||
end
|
||||
|
||||
sql.join(', ')
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
This function will use the evaluated values for sort_by and
|
||||
order_by to generate the ORDER- sql statement for the sorting
|
||||
of the result.
|
||||
|
||||
sort_by = [ 'created_at', 'updated_at' ]
|
||||
order_by = [ 'asc', 'desc' ]
|
||||
default = 'tickets.created_at DESC'
|
||||
|
||||
sql = search_get_order_sql(sort_by, order_by, default)
|
||||
|
||||
returns
|
||||
|
||||
sql = 'tickets.created_at ASC, tickets.updated_at DESC'
|
||||
|
||||
=end
|
||||
|
||||
def search_get_order_sql(sort_by, order_by, default)
|
||||
sql = []
|
||||
|
||||
sort_by.each_with_index do |value, index|
|
||||
next if value.blank?
|
||||
next if order_by[index].blank?
|
||||
|
||||
sql.push( "#{ActiveRecord::Base.connection.quote_table_name(table_name)}.#{ActiveRecord::Base.connection.quote_column_name(value)} #{order_by[index]}" )
|
||||
end
|
||||
|
||||
if sql.blank?
|
||||
sql.push("#{ActiveRecord::Base.connection.quote_table_name(table_name)}.#{ActiveRecord::Base.connection.quote_column_name(default)}")
|
||||
end
|
||||
|
||||
sql.join(', ')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -2,21 +2,19 @@ class KnowledgeBase
|
|||
module Search
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include HasSearchSortable
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def search(params)
|
||||
current_user = params[:current_user]
|
||||
# enable search only for agents and admins
|
||||
return [] if !search_preferences(current_user)
|
||||
|
||||
sql_helper = ::SqlHelper.new(object: self)
|
||||
|
||||
options = {
|
||||
limit: params[:limit] || 10,
|
||||
from: params[:offset] || 0,
|
||||
sort_by: search_get_sort_by(params, 'updated_at'),
|
||||
order_by: search_get_order_by(params, 'desc'),
|
||||
sort_by: sql_helper.get_sort_by(params, 'updated_at'),
|
||||
order_by: sql_helper.get_order_by(params, 'desc'),
|
||||
user: current_user
|
||||
}
|
||||
|
||||
|
@ -41,8 +39,9 @@ class KnowledgeBase
|
|||
end
|
||||
|
||||
def search_sql(query, kb_locales, options)
|
||||
table_name = arel_table.name
|
||||
order_sql = search_get_order_sql(options[:sort_by], options[:order_by], "#{table_name}.updated_at ASC")
|
||||
table_name = arel_table.name
|
||||
sql_helper = ::SqlHelper.new(object: self)
|
||||
order_sql = sql_helper.get_order(options[:sort_by], options[:order_by], "#{table_name}.updated_at ASC")
|
||||
|
||||
# - stip out * we already search for *query* -
|
||||
query.delete! '*'
|
||||
|
|
|
@ -4,10 +4,6 @@ class Organization
|
|||
module Search
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include HasSearchSortable
|
||||
end
|
||||
|
||||
# methods defined here are going to extend the class, not the instance of it
|
||||
class_methods do
|
||||
|
||||
|
@ -72,11 +68,13 @@ returns
|
|||
offset = params[:offset] || 0
|
||||
current_user = params[:current_user]
|
||||
|
||||
sql_helper = ::SqlHelper.new(object: self)
|
||||
|
||||
# check sort - positions related to order by
|
||||
sort_by = search_get_sort_by(params, %w[active updated_at])
|
||||
sort_by = sql_helper.get_sort_by(params, %w[active updated_at])
|
||||
|
||||
# check order - positions related to sort by
|
||||
order_by = search_get_order_by(params, %w[desc desc])
|
||||
order_by = sql_helper.get_order_by(params, %w[desc desc])
|
||||
|
||||
# enable search only for agents and admins
|
||||
return [] if !search_preferences(current_user)
|
||||
|
@ -97,8 +95,8 @@ returns
|
|||
return organizations
|
||||
end
|
||||
|
||||
order_select_sql = search_get_order_select_sql(sort_by, order_by, 'organizations.updated_at')
|
||||
order_sql = search_get_order_sql(sort_by, order_by, 'organizations.updated_at ASC')
|
||||
order_select_sql = sql_helper.get_order_select(sort_by, order_by, 'organizations.updated_at')
|
||||
order_sql = sql_helper.get_order(sort_by, order_by, 'organizations.updated_at ASC')
|
||||
|
||||
# fallback do sql query
|
||||
# - stip out * we already search for *query* -
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
module Ticket::Search
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include HasSearchSortable
|
||||
end
|
||||
|
||||
# methods defined here are going to extend the class, not the instance of it
|
||||
class_methods do
|
||||
|
||||
|
@ -119,11 +115,13 @@ returns
|
|||
full = true
|
||||
end
|
||||
|
||||
sql_helper = ::SqlHelper.new(object: self)
|
||||
|
||||
# check sort
|
||||
sort_by = search_get_sort_by(params, 'updated_at')
|
||||
sort_by = sql_helper.get_sort_by(params, 'updated_at')
|
||||
|
||||
# check order
|
||||
order_by = search_get_order_by(params, 'desc')
|
||||
order_by = sql_helper.get_order_by(params, 'desc')
|
||||
|
||||
# try search index backend
|
||||
if condition.blank? && SearchIndexBackend.enabled?
|
||||
|
@ -197,8 +195,8 @@ returns
|
|||
# do query
|
||||
# - stip out * we already search for *query* -
|
||||
|
||||
order_select_sql = search_get_order_select_sql(sort_by, order_by, 'tickets.updated_at')
|
||||
order_sql = search_get_order_sql(sort_by, order_by, 'tickets.updated_at DESC')
|
||||
order_select_sql = sql_helper.get_order_select(sort_by, order_by, 'tickets.updated_at')
|
||||
order_sql = sql_helper.get_order(sort_by, order_by, 'tickets.updated_at DESC')
|
||||
if query
|
||||
query.delete! '*'
|
||||
tickets_all = Ticket.select("DISTINCT(tickets.id), #{order_select_sql}")
|
||||
|
|
|
@ -4,10 +4,6 @@ class User
|
|||
module Search
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include HasSearchSortable
|
||||
end
|
||||
|
||||
# methods defined here are going to extend the class, not the instance of it
|
||||
class_methods do
|
||||
|
||||
|
@ -83,11 +79,13 @@ returns
|
|||
offset = params[:offset] || 0
|
||||
current_user = params[:current_user]
|
||||
|
||||
sql_helper = ::SqlHelper.new(object: self)
|
||||
|
||||
# check sort - positions related to order by
|
||||
sort_by = search_get_sort_by(params, %w[active updated_at])
|
||||
sort_by = sql_helper.get_sort_by(params, %w[active updated_at])
|
||||
|
||||
# check order - positions related to sort by
|
||||
order_by = search_get_order_by(params, %w[desc desc])
|
||||
order_by = sql_helper.get_order_by(params, %w[desc desc])
|
||||
|
||||
# enable search only for agents and admins
|
||||
return [] if !search_preferences(current_user)
|
||||
|
@ -129,7 +127,7 @@ returns
|
|||
return users
|
||||
end
|
||||
|
||||
order_sql = search_get_order_sql(sort_by, order_by, 'users.updated_at DESC')
|
||||
order_sql = sql_helper.get_order(sort_by, order_by, 'users.updated_at DESC')
|
||||
|
||||
# fallback do sql query
|
||||
# - stip out * we already search for *query* -
|
||||
|
|
172
lib/sql_helper.rb
Normal file
172
lib/sql_helper.rb
Normal file
|
@ -0,0 +1,172 @@
|
|||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
class SqlHelper
|
||||
|
||||
def initialize(object:)
|
||||
@object = object
|
||||
end
|
||||
|
||||
def get_param_key(key, params)
|
||||
sort_by = []
|
||||
if params[key].present? && params[key].is_a?(String)
|
||||
params[key] = params[key].split(/\s*,\s*/)
|
||||
elsif params[key].blank?
|
||||
params[key] = []
|
||||
end
|
||||
|
||||
sort_by
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
This function will check the params for the "sort_by" attribute
|
||||
and validate its values.
|
||||
|
||||
sql_helper = SqlHelper.new(object: Ticket)
|
||||
sort_by = sql_helper.get_sort_by(params, default)
|
||||
|
||||
returns
|
||||
|
||||
sort_by = [
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
|
||||
=end
|
||||
|
||||
def get_sort_by(params, default = nil)
|
||||
sort_by = get_param_key(:sort_by, params)
|
||||
|
||||
# check order
|
||||
params[:sort_by].each do |value|
|
||||
|
||||
# only accept values which are set for the db schema
|
||||
raise "Found invalid column '#{value}' for sorting." if @object.columns_hash[value].blank?
|
||||
|
||||
sort_by.push(value)
|
||||
end
|
||||
|
||||
if sort_by.blank? && default.present?
|
||||
if default.is_a?(Array)
|
||||
sort_by = default
|
||||
else
|
||||
sort_by.push(default)
|
||||
end
|
||||
end
|
||||
|
||||
sort_by
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
This function will check the params for the "order_by" attribute
|
||||
and validate its values.
|
||||
|
||||
sql_helper = SqlHelper.new(object: Ticket)
|
||||
order_by = sql_helper.get_order_by(params, default)
|
||||
|
||||
returns
|
||||
|
||||
order_by = [
|
||||
'asc',
|
||||
'desc',
|
||||
]
|
||||
|
||||
=end
|
||||
|
||||
def get_order_by(params, default = nil)
|
||||
order_by = get_param_key(:order_by, params)
|
||||
|
||||
# check order
|
||||
params[:order_by].each do |value|
|
||||
raise "Found invalid order by value #{value}. Please use 'asc' or 'desc'." if !value.match?(/\A(asc|desc)\z/i)
|
||||
|
||||
order_by.push(value.downcase)
|
||||
end
|
||||
|
||||
if order_by.blank? && default.present?
|
||||
if default.is_a?(Array)
|
||||
order_by = default
|
||||
else
|
||||
order_by.push(default)
|
||||
end
|
||||
end
|
||||
|
||||
order_by
|
||||
end
|
||||
|
||||
def set_sql_order_default(sql, default)
|
||||
if sql.blank? && default.present?
|
||||
sql.push("#{ActiveRecord::Base.connection.quote_table_name(@object.table_name)}.#{ActiveRecord::Base.connection.quote_column_name(default)}")
|
||||
end
|
||||
sql
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
This function will use the evaluated values for sort_by and
|
||||
order_by to generate the ORDER-SELECT sql statement for the sorting
|
||||
of the result.
|
||||
|
||||
sort_by = [ 'created_at', 'updated_at' ]
|
||||
order_by = [ 'asc', 'desc' ]
|
||||
default = 'tickets.created_at'
|
||||
|
||||
sql_helper = SqlHelper.new(object: Ticket)
|
||||
sql = sql_helper.get_order_select(sort_by, order_by, default)
|
||||
|
||||
returns
|
||||
|
||||
sql = 'tickets.created_at, tickets.updated_at'
|
||||
|
||||
=end
|
||||
|
||||
def get_order_select(sort_by, order_by, default = nil)
|
||||
sql = []
|
||||
|
||||
sort_by.each_with_index do |value, index|
|
||||
next if value.blank?
|
||||
next if order_by[index].blank?
|
||||
|
||||
sql.push( "#{ActiveRecord::Base.connection.quote_table_name(@object.table_name)}.#{ActiveRecord::Base.connection.quote_column_name(value)}" )
|
||||
end
|
||||
|
||||
sql = set_sql_order_default(sql, default)
|
||||
|
||||
sql.join(', ')
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
This function will use the evaluated values for sort_by and
|
||||
order_by to generate the ORDER- sql statement for the sorting
|
||||
of the result.
|
||||
|
||||
sort_by = [ 'created_at', 'updated_at' ]
|
||||
order_by = [ 'asc', 'desc' ]
|
||||
default = 'tickets.created_at DESC'
|
||||
|
||||
sql_helper = SqlHelper.new(object: Ticket)
|
||||
sql = sql_helper.get_order(sort_by, order_by, default)
|
||||
|
||||
returns
|
||||
|
||||
sql = 'tickets.created_at ASC, tickets.updated_at DESC'
|
||||
|
||||
=end
|
||||
|
||||
def get_order(sort_by, order_by, default = nil)
|
||||
sql = []
|
||||
|
||||
sort_by.each_with_index do |value, index|
|
||||
next if value.blank?
|
||||
next if order_by[index].blank?
|
||||
|
||||
sql.push( "#{ActiveRecord::Base.connection.quote_table_name(@object.table_name)}.#{ActiveRecord::Base.connection.quote_column_name(value)} #{order_by[index]}" )
|
||||
end
|
||||
|
||||
sql = set_sql_order_default(sql, default)
|
||||
|
||||
sql.join(', ')
|
||||
end
|
||||
end
|
8
spec/factories/report/profile.rb
Normal file
8
spec/factories/report/profile.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
FactoryBot.define do
|
||||
factory :'report/profile', aliases: %i[report_profile] do
|
||||
sequence(:name) { |n| "Report #{n}" }
|
||||
active { true }
|
||||
created_by_id { 1 }
|
||||
updated_by_id { 1 }
|
||||
end
|
||||
end
|
45
spec/system/examples/pagination_examples.rb
Normal file
45
spec/system/examples/pagination_examples.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
RSpec.shared_examples 'pagination' do |model:, klass:, path:, sort_by: :name|
|
||||
def prepare(model)
|
||||
create_list(model, 500)
|
||||
end
|
||||
|
||||
it 'does paginate' do
|
||||
prepare(model)
|
||||
visit path
|
||||
refresh # more stability
|
||||
expect(page).to have_css('.js-pager', wait: 10)
|
||||
|
||||
class_page1 = klass.order(sort_by => :asc, id: :asc).offset(50).first
|
||||
expect(page).to have_text(class_page1.name, wait: 10)
|
||||
|
||||
page.first('.js-page', text: '2').click
|
||||
await_empty_ajax_queue
|
||||
|
||||
class_page2 = klass.order(sort_by => :asc, id: :asc).offset(175).first
|
||||
expect(page).to have_text(class_page2.name, wait: 10)
|
||||
|
||||
page.first('.js-page', text: '3').click
|
||||
await_empty_ajax_queue
|
||||
|
||||
class_page3 = klass.order(sort_by => :asc, id: :asc).offset(325).first
|
||||
expect(page).to have_text(class_page3.name, wait: 10)
|
||||
|
||||
page.first('.js-page', text: '4').click
|
||||
await_empty_ajax_queue
|
||||
|
||||
class_page4 = klass.order(sort_by => :asc, id: :asc).offset(475).first
|
||||
expect(page).to have_text(class_page4.name, wait: 10)
|
||||
|
||||
page.first('.js-page', text: '1').click
|
||||
await_empty_ajax_queue
|
||||
|
||||
page.first('.js-tableHead[data-column-key=name]').click
|
||||
await_empty_ajax_queue
|
||||
expect(page).to have_text(class_page1.name, wait: 10)
|
||||
|
||||
page.first('.js-tableHead[data-column-key=name]').click
|
||||
await_empty_ajax_queue
|
||||
class_last = klass.order(sort_by => :desc).first
|
||||
expect(page).to have_text(class_last.name, wait: 10)
|
||||
end
|
||||
end
|
8
spec/system/manage/groups_spec.rb
Normal file
8
spec/system/manage/groups_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Groups', type: :system do
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :group, klass: Group, path: 'manage/groups'
|
||||
end
|
||||
end
|
8
spec/system/manage/jobs_spec.rb
Normal file
8
spec/system/manage/jobs_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Job', type: :system do
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :job, klass: Job, path: 'manage/job'
|
||||
end
|
||||
end
|
8
spec/system/manage/macros_spec.rb
Normal file
8
spec/system/manage/macros_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Macro', type: :system do
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :macro, klass: Macro, path: 'manage/macros'
|
||||
end
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Organizations', type: :system do
|
||||
|
||||
|
@ -48,4 +49,8 @@ RSpec.describe 'Manage > Organizations', type: :system do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :organization, klass: Organization, path: 'manage/organizations'
|
||||
end
|
||||
end
|
||||
|
|
8
spec/system/manage/report_profiles_spec.rb
Normal file
8
spec/system/manage/report_profiles_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Report Profiles', type: :system do
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :report_profile, klass: Report::Profile, path: 'manage/report_profiles'
|
||||
end
|
||||
end
|
8
spec/system/manage/roles_spec.rb
Normal file
8
spec/system/manage/roles_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Role', type: :system do
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :role, klass: Role, path: 'manage/roles'
|
||||
end
|
||||
end
|
8
spec/system/manage/text_modules_spec.rb
Normal file
8
spec/system/manage/text_modules_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Text Module', type: :system do
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :text_module, klass: TextModule, path: 'manage/text_modules'
|
||||
end
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
require 'system/examples/pagination_examples'
|
||||
|
||||
RSpec.describe 'Manage > Trigger', type: :system do
|
||||
|
||||
|
@ -57,4 +58,8 @@ RSpec.describe 'Manage > Trigger', type: :system do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ajax pagination' do
|
||||
include_examples 'pagination', model: :trigger, klass: Trigger, path: 'manage/trigger'
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue