From 3d878a554763c2122e076cd2d9c0030c7b08659e Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Tue, 7 Jun 2016 21:22:08 +0200 Subject: [PATCH] Refactoring of ticket zoom (less rerendering, just rerender particular area's instant of whole view). Added live broadcasting of tag and links. --- .../_application_controller_generic.coffee | 12 +- .../app/controllers/ticket_zoom.coffee | 330 +++++++++--------- .../ticket_zoom/article_new.coffee | 3 +- .../ticket_zoom/article_view.coffee | 2 + .../ticket_zoom/customer_avatar.coffee | 11 + .../controllers/ticket_zoom/sidebar.coffee | 85 ++--- .../app/controllers/widget/link.coffee | 6 + .../app/controllers/widget/tag.coffee | 12 +- .../app/lib/app_post/collection.coffee | 26 +- .../app/models/_application_model.coffee | 6 +- app/controllers/application_controller.rb | 13 + app/controllers/organizations_controller.rb | 2 +- app/controllers/taskbar_controller.rb | 10 +- app/controllers/ticket_articles_controller.rb | 48 ++- app/controllers/tickets_controller.rb | 10 +- app/models/application_model.rb | 18 + app/models/link.rb | 68 ++-- app/models/tag.rb | 51 ++- app/models/ticket/assets.rb | 12 +- 19 files changed, 449 insertions(+), 276 deletions(-) create mode 100644 app/assets/javascripts/app/controllers/ticket_zoom/customer_avatar.coffee diff --git a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee index 763826791..e6c2c963c 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee @@ -1117,7 +1117,7 @@ class App.ObserverController extends App.Controller currentAttributes[key] = object[key] if @observeNot 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 if !@lastAttributres @@ -1128,13 +1128,13 @@ class App.ObserverController extends App.Controller @log 'debug', 'maybeRender no diff, no rerender' return - @log 'debug', 'maybeRender.diff', diff + @log 'debug', 'maybeRender.diff', diff, @observe, @model @lastAttributres = currentAttributes - @render(object) + @render(object, diff) - render: (object) => - @log 'debug', 'render', @template, object + render: (object, diff) => + @log 'debug', 'render', @template, object, diff @html App.view(@template)( object: object ) @@ -1144,5 +1144,5 @@ class App.ObserverController extends App.Controller release: => #console.trace() - @log 'debug', 'release', @object_id, @model + @log 'debug', 'release', @object_id, @model, @subscribeId App[@model].unsubscribe(@subscribeId) diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.coffee index ce4d921ed..e658c033c 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom.coffee @@ -18,11 +18,10 @@ class App.TicketZoom extends App.Controller App.TaskManager.remove(@task_key) return - @formMeta = undefined - @ticket_id = params.ticket_id - @article_id = params.article_id - @sidebarState = {} - @ticketLastAttributes = {} + @formMeta = undefined + @ticket_id = params.ticket_id + @article_id = params.article_id + @sidebarState = {} # if we are in init task startup, ignore overview_id if !params.init @@ -51,7 +50,7 @@ class App.TicketZoom extends App.Controller update = => @fetch(@ticket_id, false) 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 @@ -59,6 +58,98 @@ class App.TicketZoom extends App.Controller @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: => # default attributes @@ -73,14 +164,14 @@ class App.TicketZoom extends App.Controller meta.head = @taskHead # set icon and title based on ticket - if @ticket - @ticket = App.Ticket.fullLocal(@ticket.id) - meta.head = @ticket.title - meta.title = '#' + @ticket.number + ' - ' + @ticket.title - meta.class = "task-state-#{ @ticket.getState() }" + if @ticket_id && App.Ticket.exists(@ticket_id) + ticket = App.Ticket.find(@ticket_id) + meta.head = ticket.title + meta.title = "##{ticket.number} - #{ticket.title}" + meta.class = "task-state-#{ ticket.getState() }" meta.type = 'task' - meta.iconTitle = @ticket.iconTitle() - meta.iconClass = @ticket.iconClass() + meta.iconTitle = ticket.iconTitle() + meta.iconClass = ticket.iconClass() meta url: => @@ -137,107 +228,9 @@ class App.TicketZoom extends App.Controller @autosaveStop() @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: => 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: => # 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 App.TaskManager.touch(@task_key) - @formEnable( @$('.submit') ) - if !@renderDone @renderDone = true + @autosaveLast = {} elLocal = $(App.view('ticket_zoom') ticket: @ticket nav: @nav @@ -294,24 +286,23 @@ class App.TicketZoom extends App.Controller new App.TicketZoomOverviewNavigator( el: elLocal.find('.overview-navigator') - ticket_id: @ticket.id + ticket_id: @ticket_id overview_id: @overview_id ) new App.TicketZoomTitle( - object_id: @ticket.id + object_id: @ticket_id overview_id: @overview_id el: elLocal.find('.ticket-title') task_key: @task_key ) new App.TicketZoomMeta( - object_id: @ticket.id + object_id: @ticket_id el: elLocal.find('.ticket-meta') ) new App.TicketZoomAttributeBar( - ticket: @ticket el: elLocal.find('.js-attributeBar') overview_id: @overview_id callback: @submit @@ -322,7 +313,7 @@ class App.TicketZoom extends App.Controller @articleNew = new App.TicketZoomArticleNew( ticket: @ticket - ticket_id: @ticket.id + ticket_id: @ticket_id el: elLocal.find('.article-new') formMeta: @formMeta form_id: @form_id @@ -333,7 +324,7 @@ class App.TicketZoom extends App.Controller @highligher = new App.TicketZoomHighlighter( el: elLocal.find('.highlighter') - ticket_id: @ticket.id + ticket_id: @ticket_id ) @articleView = new App.TicketZoomArticleView( @@ -344,27 +335,20 @@ class App.TicketZoom extends App.Controller ticket_article_ids: @ticket_article_ids ) - # rerender whole sidebar if customer or organization has changed - if @ticketLastAttributes.customer_id isnt @ticket.customer_id || @ticketLastAttributes.organization_id isnt @ticket.organization_id - if elLocal - el = elLocal - else - el = @el - new App.WidgetAvatar( - el: el.find('.ticketZoom-header .js-avatar') - object_id: @ticket.customer_id - size: 50 + new App.TicketCustomerAvatar( + object_id: @ticket_id + el: elLocal.find('.ticketZoom-header') ) + @sidebar = new App.TicketZoomSidebar( - el: el.find('.tabsSidebar') + el: elLocal sidebarState: @sidebarState - object_id: @ticket.id + object_id: @ticket_id model: 'Ticket' taskGet: @taskGet task_key: @task_key - tags: @tags - links: @links formMeta: @formMeta + markForm: @markForm ) # render init content @@ -377,25 +361,35 @@ class App.TicketZoom extends App.Controller 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 - if @article_id && document.getElementById('article-' + @article_id) - offset = document.getElementById('article-' + @article_id).offsetTop + if @article_id && document.getElementById("article-#{@article_id}") + offset = document.getElementById("article-#{@article_id}").offsetTop offset = offset - 45 scrollTo = -> @scrollTo(0, offset) @delay(scrollTo, 100, false) - @ticketLastAttributes = @ticket.attributes() + if @shown - if @shown && !@initDone + # scroll to end if new article has been added + if !@last_ticket_article_ids || !_.isEqual(_.sortBy(@last_ticket_article_ids), _.sortBy(@ticket_article_ids)) + @last_ticket_article_ids = @ticket_article_ids + @scrollToBottom() + @positionPageHeaderUpdate() + + return if @initDone @initDone = true - # scroll to end of page - @scrollToBottom() - - # observe content header position - @positionPageHeaderStart() - # trigger shown if init shown render App.Event.trigger('ui::ticket::shown', { ticket_id: @ticket_id }) @@ -403,32 +397,34 @@ class App.TicketZoom extends App.Controller @main.scrollTop( @main.prop('scrollHeight') ) autosaveStop: => + console.log('autosaveStop') @clearDelay('ticket-zoom-form-update') @autosaveLast = {} @el.off('change.local blur.local keyup.local paste.local input.local') 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 @autosaveLast = @taskGet() - update = => - return if !@ticket - currentParams = @formCurrent() + return if !@ticket + currentParams = @formCurrent() - # check changed between last autosave - sameAsLastSave = _.isEqual(currentParams, @autosaveLast) - return if sameAsLastSave - @autosaveLast = clone(currentParams) + # check changed between last autosave + sameAsLastSave = _.isEqual(currentParams, @autosaveLast) + return if !force && sameAsLastSave + @autosaveLast = clone(currentParams) - # update changes in ui - currentStore = @currentStore() - modelDiff = @formDiff(currentParams, currentStore) - @markFormDiff(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') + # update changes in ui + currentStore = @currentStore() + modelDiff = @formDiff(currentParams, currentStore) + @markFormDiff(modelDiff) + @taskUpdateAll(modelDiff) currentStore: => return if !@ticket @@ -534,7 +530,7 @@ class App.TicketZoom extends App.Controller ticketParams = @formParam( @$('.edit') ) # validate ticket - ticket = App.Ticket.fullLocal(@ticket.id) + ticket = App.Ticket.find(@ticket_id) # reset article - should not be resubmited on next ticket update ticket.article = undefined @@ -550,13 +546,13 @@ class App.TicketZoom extends App.Controller # apply tag changes if attributes[1] is 'tags' - if @sidebar && @sidebar.tagWidget + if @sidebar && @sidebar.edit && @sidebar.edit.tagWidget tags = content.value.split(',') for tag in tags if content.operator is 'remove' - @sidebar.tagWidget.remove(tag) + @sidebar.edit.tagWidget.remove(tag) else - @sidebar.tagWidget.add(tag) + @sidebar.edit.tagWidget.add(tag) # apply user changes else if attributes[1] is 'owner_id' @@ -671,7 +667,7 @@ class App.TicketZoom extends App.Controller @autosaveStart() @muteTask() - @fetch(ticket.id, true) + @fetch(ticket.id, false) # enable form @formEnable(e) @@ -701,7 +697,7 @@ class App.TicketZoom extends App.Controller @$('.js-reset').addClass('hide') # 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 @$('.tabsSidebar-tab[data-tab="ticket"]').removeClass('is-changed') @@ -744,7 +740,7 @@ class TicketZoomRouter extends App.ControllerPermanent shown: true App.TaskManager.execute( - key: 'Ticket-' + @ticket_id + key: "Ticket-#{@ticket_id}" controller: 'TicketZoom' params: clean_params show: true diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee index 58d98d44e..343308813 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee @@ -257,7 +257,8 @@ class App.TicketZoomArticleNew extends App.Controller ticket: ticket user: App.Session.get() ) - @subscribeIdTextModule = ticket.subscribe(callback) + if !@subscribeIdTextModule + @subscribeIdTextModule = ticket.subscribe(callback) params: => params = @formParam( @$('.article-add') ) diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee index 116e30e27..92a71239a 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee @@ -86,6 +86,8 @@ class ArticleViewItem extends App.ObserverController @el.attr('id', "article-#{article.id}") if article.internal @el.addClass('is-internal') + else + @el.removeClass('is-internal') # prepare html body if article.content_type is 'text/html' diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/customer_avatar.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/customer_avatar.coffee new file mode 100644 index 000000000..f9a632078 --- /dev/null +++ b/app/assets/javascripts/app/controllers/ticket_zoom/customer_avatar.coffee @@ -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 + ) diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/sidebar.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar.coffee index 2a58e5af4..a07c37781 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/sidebar.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar.coffee @@ -1,59 +1,66 @@ -class App.TicketZoomSidebar extends App.ObserverController +class Edit extends App.ObserverController model: 'Ticket' observeNot: created_at: true updated_at: true + render: (ticket, diff) => + defaults = ticket.attributes() + delete defaults.article # ignore article infos + taskState = @taskGet('ticket') + + if !_.isEmpty(taskState) + defaults = _.extend(defaults, taskState) + + form = new App.ControllerForm( + model: App.Ticket + screen: 'edit' + handlers: [ + @ticketFormChanges + ] + filter: @formMeta.filter + params: defaults + #bookmarkable: true + ) + @html form.html() + + @markForm(true) + + return if @resetBind + @resetBind = true + @bind('ui::ticket::taskReset', (data) => + 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('
') - @editEl = el + el.append('
') - show = (ticket) => - el.find('.edit').html('') - - defaults = ticket.attributes() - delete defaults.article # ignore article infos - taskState = @taskGet('ticket') - modelDiff = App.Utils.formDiff(taskState, defaults) - - if !_.isEmpty(taskState) - defaults = _.extend(defaults, taskState) - - new App.ControllerForm( - el: el.find('.edit') - model: App.Ticket - screen: 'edit' - handlers: [ - @ticketFormChanges - ] - filter: @formMeta.filter - params: defaults - #bookmarkable: true - ) - #console.log('Ichanges', modelDiff, taskState, ticket.attributes(), defaults) - #@markFormDiff( modelDiff ) - - show(ticket) - @bind( - 'ui::ticket::taskReset' - (data) -> - if data.ticket_id is ticket.id - show(ticket) + @edit = new Edit( + object_id: ticket.id + el: el.find('.edit') + taskGet: @taskGet + formMeta: @formMeta + markForm: @markForm ) if !@isRole('Customer') - el.append('
') @tagWidget = new App.WidgetTag( - el: el.find('.tags') + el: @el.find('.tags') object_type: 'Ticket' object: ticket tags: @tags ) - el.append('') @linkWidget = new App.WidgetLink( - el: el.find('.links') + el: @el.find('.links') object_type: 'Ticket' object: ticket links: @links @@ -166,7 +173,7 @@ class App.TicketZoomSidebar extends App.ObserverController callback: showOrganization } new App.Sidebar( - el: @el + el: @el.find('.tabsSidebar') sidebarState: @sidebarState items: @sidebarItems ) diff --git a/app/assets/javascripts/app/controllers/widget/link.coffee b/app/assets/javascripts/app/controllers/widget/link.coffee index ba16440c4..595533aaf 100644 --- a/app/assets/javascripts/app/controllers/widget/link.coffee +++ b/app/assets/javascripts/app/controllers/widget/link.coffee @@ -30,7 +30,13 @@ class App.WidgetLink extends App.Controller @render() ) + reload: (links) -> + @links = links + @render() + render: => + return if @lastLinks && _.isEqual(@lastLinks, @links) + lastLinks = @links list = {} for item in @links if !list[ item['link_type'] ] diff --git a/app/assets/javascripts/app/controllers/widget/tag.coffee b/app/assets/javascripts/app/controllers/widget/tag.coffee index 53894a279..41f89fbb9 100644 --- a/app/assets/javascripts/app/controllers/widget/tag.coffee +++ b/app/assets/javascripts/app/controllers/widget/tag.coffee @@ -29,7 +29,7 @@ class App.WidgetTag extends App.Controller @ajax( id: @key type: 'GET' - url: @apiPath + '/tags' + url: "#{@apiPath}/tags" data: object: @object_type o_id: @object.id @@ -39,7 +39,13 @@ class App.WidgetTag extends App.Controller @render() ) + reload: (tags) -> + @tags = tags + @render() + render: -> + return if @lastTags && _.isEqual(@lastTags, @tags) + lastTags = @tags @html App.view('widget/tag')( tags: @tags || [], ) @@ -87,7 +93,7 @@ class App.WidgetTag extends App.Controller @ajax( type: 'GET' - url: @apiPath + '/tags/add' + url: "#{@apiPath}/tags/add" data: object: @object_type o_id: @object.id @@ -110,7 +116,7 @@ class App.WidgetTag extends App.Controller @ajax( type: 'GET' - url: @apiPath + '/tags/remove' + url: "#{@apiPath}/tags/remove" data: object: @object_type o_id: @object.id diff --git a/app/assets/javascripts/app/lib/app_post/collection.coffee b/app/assets/javascripts/app/lib/app_post/collection.coffee index cb99b3832..973d3c765 100644 --- a/app/assets/javascripts/app/lib/app_post/collection.coffee +++ b/app/assets/javascripts/app/lib/app_post/collection.coffee @@ -65,8 +65,17 @@ class _collectionSingleton extends Spine.Module loadAssets: (assets) -> @log 'debug', 'loadAssets', assets + + # proess not existing assets / to avoid not exising ref errors + loadAssetsLater = [] 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) -> @@ -81,11 +90,13 @@ class _collectionSingleton extends Spine.Module # load full array once if _.isArray(params.data) + @log 'debug', 'refresh', params.data appObject.refresh(params.data) return # load data from object listToRefresh = [] + listToRefreshLater = [] for key, object of params.data if !params.refresh && appObject @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 if object.updated_at && appObject.exists(key) exists = appObject.find(key) + objectToLoad = undefined if exists.updated_at if exists.updated_at < object.updated_at - listToRefresh.push object + objectToLoad = object @log 'debug', 'refrest newser', params.type, key else - listToRefresh.push object + objectToLoad = object @log 'debug', 'refrest try no updated_at', params.type, key + if objectToLoad + if params.later + listToRefreshLater.push objectToLoad + else + listToRefresh.push object else listToRefresh.push object @log 'debug', 'refrest new', params.type, key - return if _.isEmpty(listToRefresh) + return listToRefreshLater if _.isEmpty(listToRefresh) appObject.refresh(listToRefresh) + listToRefreshLater diff --git a/app/assets/javascripts/app/models/_application_model.coffee b/app/assets/javascripts/app/models/_application_model.coffee index 9c8cc2fc5..24901b29a 100644 --- a/app/assets/javascripts/app/models/_application_model.coffee +++ b/app/assets/javascripts/app/models/_application_model.coffee @@ -347,7 +347,7 @@ class App.Model extends Spine.Model clear: true ) - 'Collection::Subscribe::' + @className + "Collection::Subscribe::#{@className}" ) 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) App.Log.debug('Model', "request #{@className}.find(#{item.id}) from server") @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 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 455cf6f81..c7cce27e4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -339,6 +339,13 @@ class ApplicationController < ActionController::Base false 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) return false if role?(role_name) response_access_deny @@ -445,6 +452,12 @@ class ApplicationController < ActionController::Base 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] generic_object_full = object.full(params[:id]) render json: generic_object_full, status: :ok diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index 8abc20cdd..2fdc72f3c 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -113,7 +113,7 @@ curl http://localhost/api/v1/organizations/#{id} -v -u #{login}:#{password} end end if params[:full] - full = Organization.full( params[:id] ) + full = Organization.full(params[:id]) render json: full return end diff --git a/app/controllers/taskbar_controller.rb b/app/controllers/taskbar_controller.rb index e93cb3f10..dd643d5f2 100644 --- a/app/controllers/taskbar_controller.rb +++ b/app/controllers/taskbar_controller.rb @@ -5,13 +5,13 @@ class TaskbarController < ApplicationController def index - current_user_tasks = Taskbar.where( user_id: current_user.id ) + current_user_tasks = Taskbar.where(user_id: current_user.id) model_index_render_result(current_user_tasks) end def show - taskbar = Taskbar.find( params[:id] ) + taskbar = Taskbar.find(params[:id]) return if !access(taskbar) model_show_render_item(taskbar) @@ -22,15 +22,15 @@ class TaskbarController < ApplicationController end def update - taskbar = Taskbar.find( params[:id] ) + taskbar = Taskbar.find(params[:id]) return if !access(taskbar) - taskbar.update_attributes!( Taskbar.param_cleanup(params) ) + taskbar.update_attributes!(Taskbar.param_cleanup(params)) model_update_render_item(taskbar) end def destroy - taskbar = Taskbar.find( params[:id] ) + taskbar = Taskbar.find(params[:id]) return if !access(taskbar) taskbar.destroy diff --git a/app/controllers/ticket_articles_controller.rb b/app/controllers/ticket_articles_controller.rb index a80438273..e1b23a1ff 100644 --- a/app/controllers/ticket_articles_controller.rb +++ b/app/controllers/ticket_articles_controller.rb @@ -19,19 +19,24 @@ class TicketArticlesController < ApplicationController # POST /articles def create - form_id = params[:ticket_article][:form_id] - params[:ticket_article].delete(:form_id) - @article = Ticket::Article.new(Ticket::Article.param_validation( params[:ticket_article])) + form_id = params[:form_id] + + 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 if form_id - @article.attachments = Store.list( + article.attachments = Store.list( object: 'UploadCache', o_id: form_id, ) end - if @article.save + if article.save # remove attachments from upload cache Store.remove( @@ -39,27 +44,34 @@ class TicketArticlesController < ApplicationController o_id: form_id, ) - render json: @article, status: :created + render json: article, status: :created else - render json: @article.errors, status: :unprocessable_entity + render json: article.errors, status: :unprocessable_entity end end # PUT /articles/1 def update - @article = Ticket::Article.find(params[:id]) - if @article.update_attributes(Ticket::Article.param_validation(params[:ticket_article])) - render json: @article, status: :ok + # permission check + 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 - render json: @article.errors, status: :unprocessable_entity + render json: article.errors, status: :unprocessable_entity end end # DELETE /articles/1 def destroy - @article = Ticket::Article.find(params[:id]) - @article.destroy + article = Ticket::Article.find(params[:id]) + return if !article_permission(article) + article.destroy head :ok end @@ -121,18 +133,18 @@ class TicketArticlesController < ApplicationController } end - # GET /ticket_attachment/1 + # GET /ticket_attachment/:ticket_id/:article_id/:id def attachment # permission check ticket = Ticket.lookup(id: params[:ticket_id]) if !ticket_permission(ticket) - render(json: 'No such ticket.', status: :unauthorized) + render json: 'No such ticket.', status: :unauthorized return end article = Ticket::Article.find(params[:article_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 end @@ -144,7 +156,7 @@ class TicketArticlesController < ApplicationController end } 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 end @@ -163,7 +175,7 @@ class TicketArticlesController < ApplicationController # permission check article = Ticket::Article.find(params[:id]) - return if !ticket_permission(article.ticket) + return if !article_permission(article) list = Store.list( object: 'Ticket::Article::Mail', diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb index 17dc0da70..2c1c21fa8 100644 --- a/app/controllers/tickets_controller.rb +++ b/app/controllers/tickets_controller.rb @@ -41,7 +41,13 @@ class TicketsController < ApplicationController return if !ticket_permission(ticket) 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 end @@ -555,7 +561,7 @@ class TicketsController < ApplicationController ) end - def ticket_full(ticket) + def ticket_all(ticket) # get attributes to update attributes_to_change = Ticket::ScreenOptions.attributes_to_change(user: current_user, ticket: ticket) diff --git a/app/models/application_model.rb b/app/models/application_model.rb index 3ad603ddf..1d6620c1c 100644 --- a/app/models/application_model.rb +++ b/app/models/application_model.rb @@ -1280,6 +1280,24 @@ get assets and record_ids of selector assets 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 def attachments_buffer diff --git a/app/models/link.rb b/app/models/link.rb index 78beb34ba..76178eafa 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -75,21 +75,29 @@ class Link < ApplicationModel def self.add(data) if data.key?(:link_type) - linktype = link_type_get( name: data[:link_type] ) + linktype = link_type_get(name: data[:link_type]) data[:link_type_id] = linktype.id - data.delete( :link_type ) + data.delete(:link_type) end 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.delete( :link_object_source ) + touch_reference_by_params( + object: data[:link_object_source], + o_id: data[:link_object_source_value], + ) + data.delete(:link_object_source) end 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.delete( :link_object_target ) + touch_reference_by_params( + object: data[:link_object_target], + o_id: data[:link_object_target_value], + ) + data.delete(:link_object_target) end Link.create(data) @@ -109,18 +117,18 @@ class Link < ApplicationModel def self.remove(data) 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 end 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 end # from one site if data.key?(:link_type) - linktype = link_type_get( name: data[:link_type] ) + linktype = link_type_get(name: data[:link_type]) data[:link_type_id] = linktype.id end links = Link.where( @@ -130,11 +138,23 @@ class Link < ApplicationModel link_object_target_id: data[:link_object_target_id], 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 if data.key?(:link_type) - linktype = link_type_get( name: @map[ data[:link_type] ] ) + linktype = link_type_get(name: @map[ data[:link_type] ]) data[:link_type_id] = linktype.id end links = Link.where( @@ -144,25 +164,33 @@ class Link < ApplicationModel link_object_source_id: data[:link_object_target_id], 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 def self.link_type_get(data) - linktype = Link::Type.find_by( name: data[:name] ) + linktype = Link::Type.find_by(name: data[:name]) if !linktype - linktype = Link::Type.create( - name: data[:name] - ) + linktype = Link::Type.create(name: data[:name]) end linktype end def self.link_object_get(data) - linkobject = Link::Object.find_by( name: data[:name] ) + linkobject = Link::Object.find_by(name: data[:name]) if !linkobject - linkobject = Link::Object.create( - name: data[:name] - ) + linkobject = Link::Object.create(name: data[:name]) end linkobject end diff --git a/app/models/tag.rb b/app/models/tag.rb index 368dd732c..6f5d682ca 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -7,7 +7,20 @@ class Tag < ApplicationModel # rubocop:disable Style/ClassVars @@cache_item = {} @@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) @@ -30,9 +43,25 @@ class Tag < ApplicationModel o_id: data[:o_id], created_by_id: data[:created_by_id], ) + + # touch reference + touch_reference_by_params(data) true 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) # lookups @@ -50,9 +79,29 @@ class Tag < ApplicationModel o_id: data[:o_id], ) result.each(&:destroy) + + # touch reference + touch_reference_by_params(data) true 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) tag_object_id_requested = tag_object_lookup(data[:object]) tag_search = Tag.where( diff --git a/app/models/ticket/assets.rb b/app/models/ticket/assets.rb index 65b5c5f76..906650574 100644 --- a/app/models/ticket/assets.rb +++ b/app/models/ticket/assets.rb @@ -6,16 +6,16 @@ module Ticket::Assets get all assets / related models for this ticket ticket = Ticket.find(123) - result = ticket.assets( assets_if_exists ) + result = ticket.assets(assets_if_exists) returns result = { - :users => { - 123 => user_model_123, - 1234 => user_model_1234, - } - :tickets => [ ticket_model1 ] + users: { + 123: user_model_123, + 1234: user_model_1234, + }, + tickets: [ ticket_model1 ] } =end