Refactoring of ticket zoom (less rerendering, just rerender particular area's instant of whole view). Added live broadcasting of tag and links.

This commit is contained in:
Martin Edenhofer 2016-06-07 21:22:08 +02:00
parent 65e06eb614
commit 3d878a5547
19 changed files with 449 additions and 276 deletions

View file

@ -1117,7 +1117,7 @@ class App.ObserverController extends App.Controller
currentAttributes[key] = object[key] currentAttributes[key] = object[key]
if @observeNot if @observeNot
for key, value of object for key, value of object
if !@observeNot[key] && !_.isFunction(value) && !_.isObject(value) if key isnt 'cid' && !@observeNot[key] && !_.isFunction(value) && !_.isObject(value)
currentAttributes[key] = value currentAttributes[key] = value
if !@lastAttributres if !@lastAttributres
@ -1128,13 +1128,13 @@ class App.ObserverController extends App.Controller
@log 'debug', 'maybeRender no diff, no rerender' @log 'debug', 'maybeRender no diff, no rerender'
return return
@log 'debug', 'maybeRender.diff', diff @log 'debug', 'maybeRender.diff', diff, @observe, @model
@lastAttributres = currentAttributes @lastAttributres = currentAttributes
@render(object) @render(object, diff)
render: (object) => render: (object, diff) =>
@log 'debug', 'render', @template, object @log 'debug', 'render', @template, object, diff
@html App.view(@template)( @html App.view(@template)(
object: object object: object
) )
@ -1144,5 +1144,5 @@ class App.ObserverController extends App.Controller
release: => release: =>
#console.trace() #console.trace()
@log 'debug', 'release', @object_id, @model @log 'debug', 'release', @object_id, @model, @subscribeId
App[@model].unsubscribe(@subscribeId) App[@model].unsubscribe(@subscribeId)

View file

@ -22,7 +22,6 @@ class App.TicketZoom extends App.Controller
@ticket_id = params.ticket_id @ticket_id = params.ticket_id
@article_id = params.article_id @article_id = params.article_id
@sidebarState = {} @sidebarState = {}
@ticketLastAttributes = {}
# if we are in init task startup, ignore overview_id # if we are in init task startup, ignore overview_id
if !params.init if !params.init
@ -51,7 +50,7 @@ class App.TicketZoom extends App.Controller
update = => update = =>
@fetch(@ticket_id, false) @fetch(@ticket_id, false)
if !@ticketUpdatedAtLastCall || ( new Date(data.updated_at).toString() isnt new Date(@ticketUpdatedAtLastCall).toString() ) if !@ticketUpdatedAtLastCall || ( new Date(data.updated_at).toString() isnt new Date(@ticketUpdatedAtLastCall).toString() )
@delay(update, 1200, "ticket-zoom-#{@ticket_id}") @delay(update, 1100, "ticket-zoom-#{@ticket_id}")
) )
# rerender view, e. g. on langauge change # rerender view, e. g. on langauge change
@ -59,6 +58,98 @@ class App.TicketZoom extends App.Controller
@fetch(@ticket_id, true) @fetch(@ticket_id, true)
) )
fetch: (ticket_id, force) ->
return if !@Session.get()
# get data
@ajax(
id: "ticket_zoom_#{ticket_id}"
type: 'GET'
url: "#{@apiPath}/tickets/#{ticket_id}?all=true"
processData: true
success: (data, status, xhr) =>
# check if ticket has changed
newTicketRaw = data.assets.Ticket[ticket_id]
if @ticketUpdatedAtLastCall && !force
# return if ticket hasnt changed
return if @ticketUpdatedAtLastCall is newTicketRaw.updated_at
# notify if ticket changed not by my self
if newTicketRaw.updated_by_id isnt @Session.get('id')
App.TaskManager.notify(@task_key)
# remember current data
@ticketUpdatedAtLastCall = newTicketRaw.updated_at
@load(data)
App.SessionStorage(@key, data)
if !@doNotLog
@doNotLog = 1
@recentView('Ticket', ticket_id)
error: (xhr) =>
@renderDone = false
statusText = xhr.statusText
status = xhr.status
detail = xhr.responseText
# ignore if request is aborted
return if statusText is 'abort'
# if ticket is already loaded, ignore status "0" - network issues e. g. temp. not connection
if @ticketUpdatedAtLastCall && status is 0
console.log('network issues e. g. temp. not connection', status, statusText, detail)
return
# show error message
if status is 401 || statusText is 'Unauthorized'
@taskHead = '» ' + App.i18n.translateInline('Unauthorized') + ' «'
@taskIconClass = 'diagonal-cross'
@renderScreenUnauthorized(objectName: 'Ticket')
else if status is 404 || statusText is 'Not Found'
@taskHead = '» ' + App.i18n.translateInline('Not Found') + ' «'
@taskIconClass = 'diagonal-cross'
@renderScreenNotFound(objectName: 'Ticket')
else
@taskHead = '» ' + App.i18n.translateInline('Error') + ' «'
@taskIconClass = 'diagonal-cross'
if !detail
detail = 'General communication error, maybe internet is not available!'
@renderScreenError(
status: status
detail: detail
objectName: 'Ticket'
)
)
load: (data) =>
# remember article ids
@ticket_article_ids = data.ticket_article_ids
# remember link
@links = data.links
# remember tags
@tags = data.tags
# get edit form attributes
@formMeta = data.form_meta
# load assets
App.Collection.loadAssets(data.assets)
# get data
@ticket = App.Ticket.fullLocal(@ticket_id)
@ticket.article = undefined
# render page
@render()
meta: => meta: =>
# default attributes # default attributes
@ -73,14 +164,14 @@ class App.TicketZoom extends App.Controller
meta.head = @taskHead meta.head = @taskHead
# set icon and title based on ticket # set icon and title based on ticket
if @ticket if @ticket_id && App.Ticket.exists(@ticket_id)
@ticket = App.Ticket.fullLocal(@ticket.id) ticket = App.Ticket.find(@ticket_id)
meta.head = @ticket.title meta.head = ticket.title
meta.title = '#' + @ticket.number + ' - ' + @ticket.title meta.title = "##{ticket.number} - #{ticket.title}"
meta.class = "task-state-#{ @ticket.getState() }" meta.class = "task-state-#{ ticket.getState() }"
meta.type = 'task' meta.type = 'task'
meta.iconTitle = @ticket.iconTitle() meta.iconTitle = ticket.iconTitle()
meta.iconClass = @ticket.iconClass() meta.iconClass = ticket.iconClass()
meta meta
url: => url: =>
@ -137,107 +228,9 @@ class App.TicketZoom extends App.Controller
@autosaveStop() @autosaveStop()
@positionPageHeaderStop() @positionPageHeaderStop()
fetch: (ticket_id, force) ->
return if !@Session.get()
# get data
@ajax(
id: "ticket_zoom_#{ticket_id}"
type: 'GET'
url: "#{@apiPath}/tickets/#{ticket_id}?full=true"
processData: true
success: (data, status, xhr) =>
# check if ticket has changed
newTicketRaw = data.assets.Ticket[ticket_id]
if @ticketUpdatedAtLastCall && !force
# return if ticket hasnt changed
return if @ticketUpdatedAtLastCall is newTicketRaw.updated_at
# notify if ticket changed not by my self
if newTicketRaw.updated_by_id isnt @Session.get('id')
App.TaskManager.notify(@task_key)
# remember current data
@ticketUpdatedAtLastCall = newTicketRaw.updated_at
@load(data, force)
App.SessionStorage(@key, data)
if !@doNotLog
@doNotLog = 1
@recentView('Ticket', ticket_id)
# scroll to end of page
if force
@scrollToBottom()
@positionPageHeaderUpdate()
error: (xhr) =>
statusText = xhr.statusText
status = xhr.status
detail = xhr.responseText
# ignore if request is aborted
if statusText is 'abort'
return
# if ticket is already loaded, ignore status "0" - network issues e. g. temp. not connection
if @ticketUpdatedAtLastCall && status is 0
console.log('network issues e. g. temp. not connection', status, statusText, detail)
return
# show error message
if status is 401 || statusText is 'Unauthorized'
@taskHead = '» ' + App.i18n.translateInline('Unauthorized') + ' «'
@taskIconClass = 'diagonal-cross'
@renderScreenUnauthorized(objectName: 'Ticket')
else if status is 404 || statusText is 'Not Found'
@taskHead = '» ' + App.i18n.translateInline('Not Found') + ' «'
@taskIconClass = 'diagonal-cross'
@renderScreenNotFound(objectName: 'Ticket')
else
@taskHead = '» ' + App.i18n.translateInline('Error') + ' «'
@taskIconClass = 'diagonal-cross'
if !detail
detail = 'General communication error, maybe internet is not available!'
@renderScreenError(
status: status
detail: detail
objectName: 'Ticket'
)
)
muteTask: => muteTask: =>
App.TaskManager.mute(@task_key) App.TaskManager.mute(@task_key)
load: (data, force) =>
# remember article ids
@ticket_article_ids = data.ticket_article_ids
# remember link
@links = data.links
# remember tags
@tags = data.tags
# get edit form attributes
@formMeta = data.form_meta
# load assets
App.Collection.loadAssets(data.assets)
# get data
@ticket = App.Ticket.fullLocal(@ticket_id)
@ticket.article = undefined
# render page
@render()
positionPageHeaderStart: => positionPageHeaderStart: =>
# init header update needed for safari, scroll event is fired # init header update needed for safari, scroll event is fired
@ -281,10 +274,9 @@ class App.TicketZoom extends App.Controller
# update taskbar with new meta data # update taskbar with new meta data
App.TaskManager.touch(@task_key) App.TaskManager.touch(@task_key)
@formEnable( @$('.submit') )
if !@renderDone if !@renderDone
@renderDone = true @renderDone = true
@autosaveLast = {}
elLocal = $(App.view('ticket_zoom') elLocal = $(App.view('ticket_zoom')
ticket: @ticket ticket: @ticket
nav: @nav nav: @nav
@ -294,24 +286,23 @@ class App.TicketZoom extends App.Controller
new App.TicketZoomOverviewNavigator( new App.TicketZoomOverviewNavigator(
el: elLocal.find('.overview-navigator') el: elLocal.find('.overview-navigator')
ticket_id: @ticket.id ticket_id: @ticket_id
overview_id: @overview_id overview_id: @overview_id
) )
new App.TicketZoomTitle( new App.TicketZoomTitle(
object_id: @ticket.id object_id: @ticket_id
overview_id: @overview_id overview_id: @overview_id
el: elLocal.find('.ticket-title') el: elLocal.find('.ticket-title')
task_key: @task_key task_key: @task_key
) )
new App.TicketZoomMeta( new App.TicketZoomMeta(
object_id: @ticket.id object_id: @ticket_id
el: elLocal.find('.ticket-meta') el: elLocal.find('.ticket-meta')
) )
new App.TicketZoomAttributeBar( new App.TicketZoomAttributeBar(
ticket: @ticket
el: elLocal.find('.js-attributeBar') el: elLocal.find('.js-attributeBar')
overview_id: @overview_id overview_id: @overview_id
callback: @submit callback: @submit
@ -322,7 +313,7 @@ class App.TicketZoom extends App.Controller
@articleNew = new App.TicketZoomArticleNew( @articleNew = new App.TicketZoomArticleNew(
ticket: @ticket ticket: @ticket
ticket_id: @ticket.id ticket_id: @ticket_id
el: elLocal.find('.article-new') el: elLocal.find('.article-new')
formMeta: @formMeta formMeta: @formMeta
form_id: @form_id form_id: @form_id
@ -333,7 +324,7 @@ class App.TicketZoom extends App.Controller
@highligher = new App.TicketZoomHighlighter( @highligher = new App.TicketZoomHighlighter(
el: elLocal.find('.highlighter') el: elLocal.find('.highlighter')
ticket_id: @ticket.id ticket_id: @ticket_id
) )
@articleView = new App.TicketZoomArticleView( @articleView = new App.TicketZoomArticleView(
@ -344,27 +335,20 @@ class App.TicketZoom extends App.Controller
ticket_article_ids: @ticket_article_ids ticket_article_ids: @ticket_article_ids
) )
# rerender whole sidebar if customer or organization has changed new App.TicketCustomerAvatar(
if @ticketLastAttributes.customer_id isnt @ticket.customer_id || @ticketLastAttributes.organization_id isnt @ticket.organization_id object_id: @ticket_id
if elLocal el: elLocal.find('.ticketZoom-header')
el = elLocal
else
el = @el
new App.WidgetAvatar(
el: el.find('.ticketZoom-header .js-avatar')
object_id: @ticket.customer_id
size: 50
) )
@sidebar = new App.TicketZoomSidebar( @sidebar = new App.TicketZoomSidebar(
el: el.find('.tabsSidebar') el: elLocal
sidebarState: @sidebarState sidebarState: @sidebarState
object_id: @ticket.id object_id: @ticket_id
model: 'Ticket' model: 'Ticket'
taskGet: @taskGet taskGet: @taskGet
task_key: @task_key task_key: @task_key
tags: @tags
links: @links
formMeta: @formMeta formMeta: @formMeta
markForm: @markForm
) )
# render init content # render init content
@ -377,24 +361,34 @@ class App.TicketZoom extends App.Controller
ticket_article_ids: @ticket_article_ids ticket_article_ids: @ticket_article_ids
) )
if @sidebar
# update tags
if @sidebar.tagWidget
@sidebar.tagWidget.reload(@tags)
# update links
if @sidebar.linkWidget
@sidebar.linkWidget.reload(@links)
# scroll to article if given # scroll to article if given
if @article_id && document.getElementById('article-' + @article_id) if @article_id && document.getElementById("article-#{@article_id}")
offset = document.getElementById('article-' + @article_id).offsetTop offset = document.getElementById("article-#{@article_id}").offsetTop
offset = offset - 45 offset = offset - 45
scrollTo = -> scrollTo = ->
@scrollTo(0, offset) @scrollTo(0, offset)
@delay(scrollTo, 100, false) @delay(scrollTo, 100, false)
@ticketLastAttributes = @ticket.attributes() if @shown
if @shown && !@initDone # scroll to end if new article has been added
@initDone = true if !@last_ticket_article_ids || !_.isEqual(_.sortBy(@last_ticket_article_ids), _.sortBy(@ticket_article_ids))
@last_ticket_article_ids = @ticket_article_ids
# scroll to end of page
@scrollToBottom() @scrollToBottom()
@positionPageHeaderUpdate()
# observe content header position return if @initDone
@positionPageHeaderStart() @initDone = true
# trigger shown if init shown render # trigger shown if init shown render
App.Event.trigger('ui::ticket::shown', { ticket_id: @ticket_id }) App.Event.trigger('ui::ticket::shown', { ticket_id: @ticket_id })
@ -403,20 +397,27 @@ class App.TicketZoom extends App.Controller
@main.scrollTop( @main.prop('scrollHeight') ) @main.scrollTop( @main.prop('scrollHeight') )
autosaveStop: => autosaveStop: =>
console.log('autosaveStop')
@clearDelay('ticket-zoom-form-update') @clearDelay('ticket-zoom-form-update')
@autosaveLast = {} @autosaveLast = {}
@el.off('change.local blur.local keyup.local paste.local input.local') @el.off('change.local blur.local keyup.local paste.local input.local')
autosaveStart: => autosaveStart: =>
console.log('autosaveStart')
@el.on('change.local blur.local keyup.local paste.local input.local', 'form, .js-textarea', (e) =>
@delay(@markForm, 250, 'ticket-zoom-form-update')
)
@delay(@markForm, 800, 'ticket-zoom-form-update')
markForm: (force) =>
if !@autosaveLast if !@autosaveLast
@autosaveLast = @taskGet() @autosaveLast = @taskGet()
update = =>
return if !@ticket return if !@ticket
currentParams = @formCurrent() currentParams = @formCurrent()
# check changed between last autosave # check changed between last autosave
sameAsLastSave = _.isEqual(currentParams, @autosaveLast) sameAsLastSave = _.isEqual(currentParams, @autosaveLast)
return if sameAsLastSave return if !force && sameAsLastSave
@autosaveLast = clone(currentParams) @autosaveLast = clone(currentParams)
# update changes in ui # update changes in ui
@ -425,11 +426,6 @@ class App.TicketZoom extends App.Controller
@markFormDiff(modelDiff) @markFormDiff(modelDiff)
@taskUpdateAll(modelDiff) @taskUpdateAll(modelDiff)
@el.on('change.local blur.local keyup.local paste.local input.local', 'form, .js-textarea', (e) =>
@delay(update, 250, 'ticket-zoom-form-update')
)
@delay(update, 800, 'ticket-zoom-form-update')
currentStore: => currentStore: =>
return if !@ticket return if !@ticket
currentStoreTicket = @ticket.attributes() currentStoreTicket = @ticket.attributes()
@ -534,7 +530,7 @@ class App.TicketZoom extends App.Controller
ticketParams = @formParam( @$('.edit') ) ticketParams = @formParam( @$('.edit') )
# validate ticket # validate ticket
ticket = App.Ticket.fullLocal(@ticket.id) ticket = App.Ticket.find(@ticket_id)
# reset article - should not be resubmited on next ticket update # reset article - should not be resubmited on next ticket update
ticket.article = undefined ticket.article = undefined
@ -550,13 +546,13 @@ class App.TicketZoom extends App.Controller
# apply tag changes # apply tag changes
if attributes[1] is 'tags' if attributes[1] is 'tags'
if @sidebar && @sidebar.tagWidget if @sidebar && @sidebar.edit && @sidebar.edit.tagWidget
tags = content.value.split(',') tags = content.value.split(',')
for tag in tags for tag in tags
if content.operator is 'remove' if content.operator is 'remove'
@sidebar.tagWidget.remove(tag) @sidebar.edit.tagWidget.remove(tag)
else else
@sidebar.tagWidget.add(tag) @sidebar.edit.tagWidget.add(tag)
# apply user changes # apply user changes
else if attributes[1] is 'owner_id' else if attributes[1] is 'owner_id'
@ -671,7 +667,7 @@ class App.TicketZoom extends App.Controller
@autosaveStart() @autosaveStart()
@muteTask() @muteTask()
@fetch(ticket.id, true) @fetch(ticket.id, false)
# enable form # enable form
@formEnable(e) @formEnable(e)
@ -701,7 +697,7 @@ class App.TicketZoom extends App.Controller
@$('.js-reset').addClass('hide') @$('.js-reset').addClass('hide')
# reset edit ticket / reset new article # reset edit ticket / reset new article
App.Event.trigger('ui::ticket::taskReset', { ticket_id: @ticket.id }) App.Event.trigger('ui::ticket::taskReset', { ticket_id: @ticket_id })
# remove change flag on tab # remove change flag on tab
@$('.tabsSidebar-tab[data-tab="ticket"]').removeClass('is-changed') @$('.tabsSidebar-tab[data-tab="ticket"]').removeClass('is-changed')
@ -744,7 +740,7 @@ class TicketZoomRouter extends App.ControllerPermanent
shown: true shown: true
App.TaskManager.execute( App.TaskManager.execute(
key: 'Ticket-' + @ticket_id key: "Ticket-#{@ticket_id}"
controller: 'TicketZoom' controller: 'TicketZoom'
params: clean_params params: clean_params
show: true show: true

View file

@ -257,6 +257,7 @@ class App.TicketZoomArticleNew extends App.Controller
ticket: ticket ticket: ticket
user: App.Session.get() user: App.Session.get()
) )
if !@subscribeIdTextModule
@subscribeIdTextModule = ticket.subscribe(callback) @subscribeIdTextModule = ticket.subscribe(callback)
params: => params: =>

View file

@ -86,6 +86,8 @@ class ArticleViewItem extends App.ObserverController
@el.attr('id', "article-#{article.id}") @el.attr('id', "article-#{article.id}")
if article.internal if article.internal
@el.addClass('is-internal') @el.addClass('is-internal')
else
@el.removeClass('is-internal')
# prepare html body # prepare html body
if article.content_type is 'text/html' if article.content_type is 'text/html'

View file

@ -0,0 +1,11 @@
class App.TicketCustomerAvatar extends App.ObserverController
model: 'Ticket'
observe:
customer_id: true
render: (ticket) =>
new App.WidgetAvatar(
el: @el.find('.js-avatar')
object_id: ticket.customer_id
size: 50
)

View file

@ -1,28 +1,18 @@
class App.TicketZoomSidebar extends App.ObserverController class Edit extends App.ObserverController
model: 'Ticket' model: 'Ticket'
observeNot: observeNot:
created_at: true created_at: true
updated_at: true updated_at: true
render: (ticket) => render: (ticket, diff) =>
editTicket = (el) =>
el.append('<form class="edit"></form>')
@editEl = el
show = (ticket) =>
el.find('.edit').html('')
defaults = ticket.attributes() defaults = ticket.attributes()
delete defaults.article # ignore article infos delete defaults.article # ignore article infos
taskState = @taskGet('ticket') taskState = @taskGet('ticket')
modelDiff = App.Utils.formDiff(taskState, defaults)
if !_.isEmpty(taskState) if !_.isEmpty(taskState)
defaults = _.extend(defaults, taskState) defaults = _.extend(defaults, taskState)
new App.ControllerForm( form = new App.ControllerForm(
el: el.find('.edit')
model: App.Ticket model: App.Ticket
screen: 'edit' screen: 'edit'
handlers: [ handlers: [
@ -32,28 +22,45 @@ class App.TicketZoomSidebar extends App.ObserverController
params: defaults params: defaults
#bookmarkable: true #bookmarkable: true
) )
#console.log('Ichanges', modelDiff, taskState, ticket.attributes(), defaults) @html form.html()
#@markFormDiff( modelDiff )
show(ticket) @markForm(true)
@bind(
'ui::ticket::taskReset' return if @resetBind
(data) -> @resetBind = true
if data.ticket_id is ticket.id @bind('ui::ticket::taskReset', (data) =>
show(ticket) return if data.ticket_id isnt ticket.id
@render(ticket)
)
class App.TicketZoomSidebar extends App.ObserverController
model: 'Ticket'
observe:
customer_id: true
organization_id: true
render: (ticket) =>
editTicket = (el) =>
el.append('<form><fieldset class="edit"></fieldset></form><div class="tags"></div><div class="links"></div>')
@edit = new Edit(
object_id: ticket.id
el: el.find('.edit')
taskGet: @taskGet
formMeta: @formMeta
markForm: @markForm
) )
if !@isRole('Customer') if !@isRole('Customer')
el.append('<div class="tags"></div>')
@tagWidget = new App.WidgetTag( @tagWidget = new App.WidgetTag(
el: el.find('.tags') el: @el.find('.tags')
object_type: 'Ticket' object_type: 'Ticket'
object: ticket object: ticket
tags: @tags tags: @tags
) )
el.append('<div class="links"></div>')
@linkWidget = new App.WidgetLink( @linkWidget = new App.WidgetLink(
el: el.find('.links') el: @el.find('.links')
object_type: 'Ticket' object_type: 'Ticket'
object: ticket object: ticket
links: @links links: @links
@ -166,7 +173,7 @@ class App.TicketZoomSidebar extends App.ObserverController
callback: showOrganization callback: showOrganization
} }
new App.Sidebar( new App.Sidebar(
el: @el el: @el.find('.tabsSidebar')
sidebarState: @sidebarState sidebarState: @sidebarState
items: @sidebarItems items: @sidebarItems
) )

View file

@ -30,7 +30,13 @@ class App.WidgetLink extends App.Controller
@render() @render()
) )
reload: (links) ->
@links = links
@render()
render: => render: =>
return if @lastLinks && _.isEqual(@lastLinks, @links)
lastLinks = @links
list = {} list = {}
for item in @links for item in @links
if !list[ item['link_type'] ] if !list[ item['link_type'] ]

View file

@ -29,7 +29,7 @@ class App.WidgetTag extends App.Controller
@ajax( @ajax(
id: @key id: @key
type: 'GET' type: 'GET'
url: @apiPath + '/tags' url: "#{@apiPath}/tags"
data: data:
object: @object_type object: @object_type
o_id: @object.id o_id: @object.id
@ -39,7 +39,13 @@ class App.WidgetTag extends App.Controller
@render() @render()
) )
reload: (tags) ->
@tags = tags
@render()
render: -> render: ->
return if @lastTags && _.isEqual(@lastTags, @tags)
lastTags = @tags
@html App.view('widget/tag')( @html App.view('widget/tag')(
tags: @tags || [], tags: @tags || [],
) )
@ -87,7 +93,7 @@ class App.WidgetTag extends App.Controller
@ajax( @ajax(
type: 'GET' type: 'GET'
url: @apiPath + '/tags/add' url: "#{@apiPath}/tags/add"
data: data:
object: @object_type object: @object_type
o_id: @object.id o_id: @object.id
@ -110,7 +116,7 @@ class App.WidgetTag extends App.Controller
@ajax( @ajax(
type: 'GET' type: 'GET'
url: @apiPath + '/tags/remove' url: "#{@apiPath}/tags/remove"
data: data:
object: @object_type object: @object_type
o_id: @object.id o_id: @object.id

View file

@ -65,8 +65,17 @@ class _collectionSingleton extends Spine.Module
loadAssets: (assets) -> loadAssets: (assets) ->
@log 'debug', 'loadAssets', assets @log 'debug', 'loadAssets', assets
# proess not existing assets / to avoid not exising ref errors
loadAssetsLater = []
for type, collections of assets for type, collections of assets
@load(type: type, data: collections) later = @load(type: type, data: collections, later: true)
if later
loadAssetsLater[type] = later
# proess existing assets
for type, collections of loadAssetsLater
App[type].refresh(collections)
load: (params) -> load: (params) ->
@ -81,11 +90,13 @@ class _collectionSingleton extends Spine.Module
# load full array once # load full array once
if _.isArray(params.data) if _.isArray(params.data)
@log 'debug', 'refresh', params.data
appObject.refresh(params.data) appObject.refresh(params.data)
return return
# load data from object # load data from object
listToRefresh = [] listToRefresh = []
listToRefreshLater = []
for key, object of params.data for key, object of params.data
if !params.refresh && appObject if !params.refresh && appObject
@log 'debug', 'refrest try', params.type, key @log 'debug', 'refrest try', params.type, key
@ -93,15 +104,22 @@ class _collectionSingleton extends Spine.Module
# check if new object is newer, just load newer objects # check if new object is newer, just load newer objects
if object.updated_at && appObject.exists(key) if object.updated_at && appObject.exists(key)
exists = appObject.find(key) exists = appObject.find(key)
objectToLoad = undefined
if exists.updated_at if exists.updated_at
if exists.updated_at < object.updated_at if exists.updated_at < object.updated_at
listToRefresh.push object objectToLoad = object
@log 'debug', 'refrest newser', params.type, key @log 'debug', 'refrest newser', params.type, key
else else
listToRefresh.push object objectToLoad = object
@log 'debug', 'refrest try no updated_at', params.type, key @log 'debug', 'refrest try no updated_at', params.type, key
if objectToLoad
if params.later
listToRefreshLater.push objectToLoad
else
listToRefresh.push object
else else
listToRefresh.push object listToRefresh.push object
@log 'debug', 'refrest new', params.type, key @log 'debug', 'refrest new', params.type, key
return if _.isEmpty(listToRefresh) return listToRefreshLater if _.isEmpty(listToRefresh)
appObject.refresh(listToRefresh) appObject.refresh(listToRefresh)
listToRefreshLater

View file

@ -347,7 +347,7 @@ class App.Model extends Spine.Model
clear: true clear: true
) )
'Collection::Subscribe::' + @className "Collection::Subscribe::#{@className}"
) )
key = @className + '-' + Math.floor( Math.random() * 99999 ) key = @className + '-' + Math.floor( Math.random() * 99999 )
@ -454,9 +454,9 @@ class App.Model extends Spine.Model
if !genericObject || new Date(item.updated_at) >= new Date(genericObject.updated_at) if !genericObject || new Date(item.updated_at) >= new Date(genericObject.updated_at)
App.Log.debug('Model', "request #{@className}.find(#{item.id}) from server") App.Log.debug('Model', "request #{@className}.find(#{item.id}) from server")
@full(item.id, false, true) @full(item.id, false, true)
App.Delay.set(callback, 500, item.id, "full-#{@className}") App.Delay.set(callback, 500, item.id, "full-#{@className}-#{item.id}")
'Item::Subscribe::' + @className "Item::Subscribe::#{@className}"
) )
# remember item callback # remember item callback

View file

@ -339,6 +339,13 @@ class ApplicationController < ActionController::Base
false false
end end
def article_permission(article)
ticket = Ticket.lookup(id: article.ticket_id)
return true if ticket.permission(current_user: current_user)
response_access_deny
false
end
def deny_if_not_role(role_name) def deny_if_not_role(role_name)
return false if role?(role_name) return false if role?(role_name)
response_access_deny response_access_deny
@ -445,6 +452,12 @@ class ApplicationController < ActionController::Base
def model_show_render (object, params) def model_show_render (object, params)
if params[:expand]
generic_object = object.find(params[:id])
model_show_render_item(generic_object)
return
end
if params[:full] if params[:full]
generic_object_full = object.full(params[:id]) generic_object_full = object.full(params[:id])
render json: generic_object_full, status: :ok render json: generic_object_full, status: :ok

View file

@ -19,19 +19,24 @@ class TicketArticlesController < ApplicationController
# POST /articles # POST /articles
def create def create
form_id = params[:ticket_article][:form_id] form_id = params[:form_id]
params[:ticket_article].delete(:form_id)
@article = Ticket::Article.new(Ticket::Article.param_validation( params[:ticket_article])) clean_params = Ticket::Article.param_association_lookup(params)
clean_params = Ticket::Article.param_cleanup(clean_params, true)
article = Ticket::Article.new(clean_params)
# permission check
return if !article_permission(article)
# find attachments in upload cache # find attachments in upload cache
if form_id if form_id
@article.attachments = Store.list( article.attachments = Store.list(
object: 'UploadCache', object: 'UploadCache',
o_id: form_id, o_id: form_id,
) )
end end
if @article.save if article.save
# remove attachments from upload cache # remove attachments from upload cache
Store.remove( Store.remove(
@ -39,27 +44,34 @@ class TicketArticlesController < ApplicationController
o_id: form_id, o_id: form_id,
) )
render json: @article, status: :created render json: article, status: :created
else else
render json: @article.errors, status: :unprocessable_entity render json: article.errors, status: :unprocessable_entity
end end
end end
# PUT /articles/1 # PUT /articles/1
def update def update
@article = Ticket::Article.find(params[:id])
if @article.update_attributes(Ticket::Article.param_validation(params[:ticket_article])) # permission check
render json: @article, status: :ok article = Ticket::Article.find(params[:id])
return if !article_permission(article)
clean_params = Ticket::Article.param_association_lookup(params)
clean_params = Ticket::Article.param_cleanup(clean_params, true)
if article.update_attributes(clean_params)
render json: article, status: :ok
else else
render json: @article.errors, status: :unprocessable_entity render json: article.errors, status: :unprocessable_entity
end end
end end
# DELETE /articles/1 # DELETE /articles/1
def destroy def destroy
@article = Ticket::Article.find(params[:id]) article = Ticket::Article.find(params[:id])
@article.destroy return if !article_permission(article)
article.destroy
head :ok head :ok
end end
@ -121,18 +133,18 @@ class TicketArticlesController < ApplicationController
} }
end end
# GET /ticket_attachment/1 # GET /ticket_attachment/:ticket_id/:article_id/:id
def attachment def attachment
# permission check # permission check
ticket = Ticket.lookup(id: params[:ticket_id]) ticket = Ticket.lookup(id: params[:ticket_id])
if !ticket_permission(ticket) if !ticket_permission(ticket)
render(json: 'No such ticket.', status: :unauthorized) render json: 'No such ticket.', status: :unauthorized
return return
end end
article = Ticket::Article.find(params[:article_id]) article = Ticket::Article.find(params[:article_id])
if ticket.id != article.ticket_id if ticket.id != article.ticket_id
render(json: 'No access, article_id/ticket_id is not matching.', status: :unauthorized) render json: 'No access, article_id/ticket_id is not matching.', status: :unauthorized
return return
end end
@ -144,7 +156,7 @@ class TicketArticlesController < ApplicationController
end end
} }
if !access if !access
render(json: 'Requested file id is not linked with article_id.', status: :unauthorized) render json: 'Requested file id is not linked with article_id.', status: :unauthorized
return return
end end
@ -163,7 +175,7 @@ class TicketArticlesController < ApplicationController
# permission check # permission check
article = Ticket::Article.find(params[:id]) article = Ticket::Article.find(params[:id])
return if !ticket_permission(article.ticket) return if !article_permission(article)
list = Store.list( list = Store.list(
object: 'Ticket::Article::Mail', object: 'Ticket::Article::Mail',

View file

@ -41,7 +41,13 @@ class TicketsController < ApplicationController
return if !ticket_permission(ticket) return if !ticket_permission(ticket)
if params[:full] if params[:full]
render json: ticket_full(ticket) full = Ticket.full(params[:id])
render json: full
return
end
if params[:all]
render json: ticket_all(ticket)
return return
end end
@ -555,7 +561,7 @@ class TicketsController < ApplicationController
) )
end end
def ticket_full(ticket) def ticket_all(ticket)
# get attributes to update # get attributes to update
attributes_to_change = Ticket::ScreenOptions.attributes_to_change(user: current_user, ticket: ticket) attributes_to_change = Ticket::ScreenOptions.attributes_to_change(user: current_user, ticket: ticket)

View file

@ -1280,6 +1280,24 @@ get assets and record_ids of selector
assets assets
end end
=begin
touch references by params
Model.touch_reference_by_params(
object: 'Ticket',
o_id: 123,
)
=end
def self.touch_reference_by_params(data)
object_class = Kernel.const_get(data[:object])
object = object_class.lookup(id: data[:o_id])
return if !object
object.touch
end
private private
def attachments_buffer def attachments_buffer

View file

@ -83,12 +83,20 @@ class Link < ApplicationModel
if data.key?(:link_object_source) if data.key?(:link_object_source)
linkobject = link_object_get(name: data[:link_object_source]) linkobject = link_object_get(name: data[:link_object_source])
data[:link_object_source_id] = linkobject.id data[:link_object_source_id] = linkobject.id
touch_reference_by_params(
object: data[:link_object_source],
o_id: data[:link_object_source_value],
)
data.delete(:link_object_source) data.delete(:link_object_source)
end end
if data.key?(:link_object_target) if data.key?(:link_object_target)
linkobject = link_object_get(name: data[:link_object_target]) linkobject = link_object_get(name: data[:link_object_target])
data[:link_object_target_id] = linkobject.id data[:link_object_target_id] = linkobject.id
touch_reference_by_params(
object: data[:link_object_target],
o_id: data[:link_object_target_value],
)
data.delete(:link_object_target) data.delete(:link_object_target)
end end
@ -130,7 +138,19 @@ class Link < ApplicationModel
link_object_target_id: data[:link_object_target_id], link_object_target_id: data[:link_object_target_id],
link_object_target_value: data[:link_object_target_value] link_object_target_value: data[:link_object_target_value]
) )
links.each(&:destroy)
# touch references
links.each {|link|
link.destroy
touch_reference_by_params(
object: Link::Object.lookup(id: link.link_object_source_id).name,
o_id: link.link_object_source_value,
)
touch_reference_by_params(
object: Link::Object.lookup(id: link.link_object_target_id).name,
o_id: link.link_object_target_value,
)
}
# from the other site # from the other site
if data.key?(:link_type) if data.key?(:link_type)
@ -144,15 +164,25 @@ class Link < ApplicationModel
link_object_source_id: data[:link_object_target_id], link_object_source_id: data[:link_object_target_id],
link_object_source_value: data[:link_object_target_value] link_object_source_value: data[:link_object_target_value]
) )
links.each(&:destroy)
# touch references
links.each {|link|
link.destroy
touch_reference_by_params(
object: Link::Object.lookup(id: link.link_object_source_id).name,
o_id: link.link_object_source_value,
)
touch_reference_by_params(
object: Link::Object.lookup(id: link.link_object_target_id).name,
o_id: link.link_object_target_value,
)
}
end end
def self.link_type_get(data) def self.link_type_get(data)
linktype = Link::Type.find_by(name: data[:name]) linktype = Link::Type.find_by(name: data[:name])
if !linktype if !linktype
linktype = Link::Type.create( linktype = Link::Type.create(name: data[:name])
name: data[:name]
)
end end
linktype linktype
end end
@ -160,9 +190,7 @@ class Link < ApplicationModel
def self.link_object_get(data) def self.link_object_get(data)
linkobject = Link::Object.find_by(name: data[:name]) linkobject = Link::Object.find_by(name: data[:name])
if !linkobject if !linkobject
linkobject = Link::Object.create( linkobject = Link::Object.create(name: data[:name])
name: data[:name]
)
end end
linkobject linkobject
end end

View file

@ -9,6 +9,19 @@ class Tag < ApplicationModel
@@cache_object = {} @@cache_object = {}
# rubocop:enable Style/ClassVars # rubocop:enable Style/ClassVars
=begin
add tags for certain object
Tag.tag_add(
object: 'Ticket',
o_id: ticket.id,
item: 'some tag',
created_by_id: current_user.id,
)
=end
def self.tag_add(data) def self.tag_add(data)
# lookups # lookups
@ -30,9 +43,25 @@ class Tag < ApplicationModel
o_id: data[:o_id], o_id: data[:o_id],
created_by_id: data[:created_by_id], created_by_id: data[:created_by_id],
) )
# touch reference
touch_reference_by_params(data)
true true
end end
=begin
remove tags of certain object
Tag.tag_add(
object: 'Ticket',
o_id: ticket.id,
item: 'some tag',
created_by_id: current_user.id,
)
=end
def self.tag_remove(data) def self.tag_remove(data)
# lookups # lookups
@ -50,9 +79,29 @@ class Tag < ApplicationModel
o_id: data[:o_id], o_id: data[:o_id],
) )
result.each(&:destroy) result.each(&:destroy)
# touch reference
touch_reference_by_params(data)
true true
end end
=begin
tag list for certain object
tags = Tag.tag_list(
object: 'Ticket',
o_id: ticket.id,
item: 'some tag',
created_by_id: current_user.id,
)
returns
['tag 1', 'tag2', ...]
=end
def self.tag_list(data) def self.tag_list(data)
tag_object_id_requested = tag_object_lookup(data[:object]) tag_object_id_requested = tag_object_lookup(data[:object])
tag_search = Tag.where( tag_search = Tag.where(

View file

@ -11,11 +11,11 @@ get all assets / related models for this ticket
returns returns
result = { result = {
:users => { users: {
123 => user_model_123, 123: user_model_123,
1234 => user_model_1234, 1234: user_model_1234,
} },
:tickets => [ ticket_model1 ] tickets: [ ticket_model1 ]
} }
=end =end