diff --git a/app/assets/javascripts/app/controllers/_application_controller.coffee b/app/assets/javascripts/app/controllers/_application_controller.coffee index 7e99ab3b9..26baa655c 100644 --- a/app/assets/javascripts/app/controllers/_application_controller.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller.coffee @@ -260,7 +260,7 @@ class App.Controller extends Spine.Controller # open ticket in new task if curent user agent if @isRole('Agent') - @el.find('div.ticket-popover, span.ticket-popover').bind('click', (e) => + @$('div.ticket-popover, span.ticket-popover').bind('click', (e) => id = $(e.target).data('id') if id ticket = App.Ticket.find(id) @@ -304,8 +304,7 @@ class App.Controller extends Spine.Controller # open user in new task if current user is agent return if !@isRole('Agent') - - @el.find('div.user-popover, span.user-popover').bind('click', (e) => + @$('div.user-popover, span.user-popover').bind('click', (e) => id = $(e.target).data('id') if id user = App.User.find(id) @@ -363,7 +362,7 @@ class App.Controller extends Spine.Controller # open org in new task if current user agent return if !@isRole('Agent') - @el.find('div.organization-popover, span.organization-popover').bind('click', (e) => + @$('div.organization-popover, span.organization-popover').bind('click', (e) => id = $(e.target).data('id') if id organization = App.Organization.find(id) diff --git a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee index 5b7127bb1..d95cd0cdf 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee @@ -1068,3 +1068,83 @@ class App.CollectionController extends App.Controller onRemoved: (id) -> # nothing + +class App.ObserverController extends App.Controller + model: 'Ticket' + template: 'ticket_zoom/title' + + ### + observe: + title: true + + observeNot: + title: true + + ### + + constructor: -> + super + #console.trace() + @log 'debug', 'new', @object_id, @model + + object = App[@model].fullLocal(@object_id) + if !object + App[@model].full(@object_id, @maybeRender) + else + @maybeRender(object) + + # rerender, e. g. on language change + @bind('ui:rerender', => + @lastAttributres = undefined + object = App[@model].fullLocal(@object_id) + @maybeRender(object) + ) + + subscribe: (object) => + console.log('subscribe', object) + @maybeRender(object) + + maybeRender: (object) => + @log 'debug', 'maybeRender', @object_id, object, @model + + if !@subscribeId + @subscribeId = object.subscribe(@subscribe) + + # remember current attributes + currentAttributes = {} + if @observe + for key, active of @observe + if active + currentAttributes[key] = object[key] + if @observeNot + attributes = object.attributes() + for key, value of attributes + if !@observeNot[key] + currentAttributes[key] = value + + if !@lastAttributres + @lastAttributres = {} + else + diff = difference(currentAttributes, @lastAttributres) + if _.isEmpty(diff) + @log 'debug', 'maybeRender no diff, no rerender' + return + + @log 'debug', 'maybeRender.diff', diff + @lastAttributres = currentAttributes + + @render(object) + + render: (object) => + @log 'debug', 'render', @template, object + @html App.view(@template)( + object: object + ) + + if @renderPost + @renderPost(object) + + release: => + #console.trace() + @log 'debug', 'release', @object_id, @model + App[@model].unsubscribe(@subscribeId) diff --git a/app/assets/javascripts/app/controllers/_dashboard/activity_stream.coffee b/app/assets/javascripts/app/controllers/_dashboard/activity_stream.coffee index b39cff8a6..e805f6d46 100644 --- a/app/assets/javascripts/app/controllers/_dashboard/activity_stream.coffee +++ b/app/assets/javascripts/app/controllers/_dashboard/activity_stream.coffee @@ -59,8 +59,8 @@ class App.DashboardActivityStream extends App.Controller item: item )) new App.WidgetAvatar( - el: html.find('.js-avatar') - user_id: item.created_by_id - size: 40 + el: html.find('.js-avatar') + object_id: item.created_by_id + size: 40 ) html \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/navigation.coffee b/app/assets/javascripts/app/controllers/navigation.coffee index d57086efb..0ba838543 100644 --- a/app/assets/javascripts/app/controllers/navigation.coffee +++ b/app/assets/javascripts/app/controllers/navigation.coffee @@ -125,9 +125,9 @@ class App.Navigation extends App.ControllerWidgetPermanent # only start avatar widget on existing session if App.Session.get('id') new App.WidgetAvatar( - el: @$('.js-avatar') - user_id: App.Session.get('id') - type: 'personal' + el: @$('.js-avatar') + object_id: App.Session.get('id') + type: 'personal' ) renderResult: (result = []) => diff --git a/app/assets/javascripts/app/controllers/organization_profile.coffee b/app/assets/javascripts/app/controllers/organization_profile.coffee index 764833e2f..b038e93de 100644 --- a/app/assets/javascripts/app/controllers/organization_profile.coffee +++ b/app/assets/javascripts/app/controllers/organization_profile.coffee @@ -49,9 +49,9 @@ class App.OrganizationProfile extends App.Controller )) new Object( - el: elLocal.find('.js-object-container') - organization: organization - task_key: @task_key + el: elLocal.find('.js-object-container') + object_id: organization.id + task_key: @task_key ) new App.TicketStats( @@ -65,19 +65,20 @@ class App.OrganizationProfile extends App.Controller genericObject: organization ) -class Object extends App.Controller +class Object extends App.ObserverController + model: 'Organization' + observeNot: + created_at: true + created_by_id: true + updated_at: true + updated_by_id: true + preferences: true + source: true + image_source: true + events: 'focusout [contenteditable]': 'update' - constructor: (params) -> - super - - # subscribe and reload data / fetch new data if triggered - @subscribeId = App.Organization.full(@organization.id, @render, false, true) - - release: => - App.Organization.unsubscribe(@subscribeId) - render: (organization) => # update taskbar with new meta data @@ -111,6 +112,17 @@ class Object extends App.Controller maxlength: 250 }) + # show members + members = [] + for userId in organization.member_ids + el = $('
') + new Member( + object_id: userId + el: el + ) + members.push el + @$('.js-userList').html(members) + # start action controller showHistory = -> new App.OrganizationHistory( @@ -151,13 +163,26 @@ class Object extends App.Controller update: (e) => name = $(e.target).attr('data-name') value = $(e.target).html() - org = App.Organization.find(@organization.id) + org = App.Organization.find(@object_id) if org[name] isnt value + @lastAttributres[name] = value data = {} data[name] = value org.updateAttributes(data) @log 'notice', 'update', name, value, org +class Member extends App.ObserverController + model: 'User' + observe: + firstname: true + lastname: true + login: true + email: true + + render: (user) => + @html App.view('organization_profile/member')( + user: user + ) class Router extends App.ControllerPermanent constructor: (params) -> diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.coffee index 19ea0a99d..4a791b9c8 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom.coffee @@ -300,15 +300,15 @@ class App.TicketZoom extends App.Controller ) new App.TicketZoomTitle( - ticket: @ticket + object_id: @ticket.id overview_id: @overview_id el: elLocal.find('.ticket-title') task_key: @task_key ) new App.TicketZoomMeta( - ticket: @ticket - el: elLocal.find('.ticket-meta') + object_id: @ticket.id + el: elLocal.find('.ticket-meta') ) new App.TicketZoomAttributeBar( @@ -352,9 +352,9 @@ class App.TicketZoom extends App.Controller else el = @el new App.WidgetAvatar( - el: el.find('.ticketZoom-header .js-avatar') - user_id: @ticket.customer_id - size: 50 + el: el.find('.ticketZoom-header .js-avatar') + object_id: @ticket.customer_id + size: 50 ) @sidebar = new App.TicketZoomSidebar( el: el.find('.tabsSidebar') diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee index 8b3c12112..735c87509 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee @@ -23,7 +23,7 @@ class App.TicketZoomArticleActions extends App.Controller else @html '' - publicInternal: (e) -> + publicInternal: (e) => e.preventDefault() articleContainer = $(e.target).closest('.ticket-article-item') article_id = $(e.target).parents('[data-id]').data('id') @@ -33,9 +33,8 @@ class App.TicketZoomArticleActions extends App.Controller internal = true if article.internal == true internal = false - article.updateAttributes( - internal: internal - ) + @lastAttributres.internal = internal + article.updateAttributes(internal: internal) # runntime update if internal 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 f557cd23f..26d8d153e 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee @@ -175,11 +175,10 @@ class App.TicketZoomArticleNew extends App.Controller @setArticleType(@type) new App.WidgetAvatar( - el: @$('.js-avatar') - user_id: App.Session.get('id') - size: 40 - position: 'right' - class: 'zIndex-5' + el: @$('.js-avatar') + object_id: App.Session.get('id') + size: 40 + position: 'right' ) configure_attributes = [ 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 6dda62756..6ba141f9c 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee @@ -1,7 +1,7 @@ class App.TicketZoomArticleView extends App.Controller constructor: -> super - @article_controller = {} + @articleController = {} @run() execute: (params) => @@ -11,20 +11,39 @@ class App.TicketZoomArticleView extends App.Controller run: => all = [] for ticket_article_id in @ticket_article_ids - if !@article_controller[ticket_article_id] + controllerKey = ticket_article_id.toString() + if !@articleController[controllerKey] el = $('') - @article_controller[ticket_article_id] = new ArticleViewItem( - ticket: @ticket - ticket_article_id: ticket_article_id - el: el - ui: @ui - highligher: @highligher + @articleController[controllerKey] = new ArticleViewItem( + ticket: @ticket + object_id: ticket_article_id + el: el + ui: @ui + highligher: @highligher ) all.push el @el.append(all) -class ArticleViewItem extends App.Controller - hasChangedAttributes: ['from', 'to', 'cc', 'subject', 'body', 'preferences'] + # check elements to remove + for article_id, controller of @articleController + exists = false + for localArticleId in @ticket_article_ids + if localArticleId.toString() is article_id.toString() + exists = true + if !exists + controller.remove() + delete @articleController[article_id.toString()] + +class ArticleViewItem extends App.ObserverController + model: 'TicketArticle' + observe: + from: true + to: true + cc: true + subject: true + body: true + internal: true + preferences: true elements: '.textBubble-content': 'textBubbleContent' @@ -36,12 +55,7 @@ class ArticleViewItem extends App.Controller 'click .js-unfold': 'unfold' constructor: -> - super - @seeMore = false - @force = true - - @render() # set expand of text area only once @bind('ui::ticket::shown', (data) => @@ -54,112 +68,75 @@ class ArticleViewItem extends App.Controller @setSeeMore() ) - # rerender, e. g. on language change - @bind('ui:rerender', => - @force = true - @render(undefined) - ) - - # subscribe to changes - @subscribeId = App.TicketArticle.full(@ticket_article_id, @render, false, true) - - release: => - App.TicketArticle.unsubscribe(@subscribeId) + super setHighlighter: => return if @el.is(':hidden') # use delay do no ui blocking - #@highligher.loadHighlights(@ticket_article_id) + #@highligher.loadHighlights(@object_id) d = => - @highligher.loadHighlights(@ticket_article_id) + @highligher.loadHighlights(@object_id) @delay(d, 200) - hasChanged: (article) => - - # if no last article exists, remember it and return true - if !@articleAttributesLastUpdate - @articleAttributesLastUpdate = {} - for item in @hasChangedAttributes - @articleAttributesLastUpdate[item] = article[item] - return true - - # compare last and current article attributes - articleAttributesLastUpdateCheck = {} - for item in @hasChangedAttributes - articleAttributesLastUpdateCheck[item] = article[item] - diff = difference(@articleAttributesLastUpdate, articleAttributesLastUpdateCheck) - return false if !diff || _.isEmpty( diff ) - @articleAttributesLastUpdate = articleAttributesLastUpdateCheck - true - render: (article) => - # get articles - @article = App.TicketArticle.fullLocal(@ticket_article_id) - # set @el attributes - if !article - @el.addClass("ticket-article-item #{@article.sender.name.toLowerCase()}") - @el.attr('data-id', @article.id) - @el.attr('id', "article-#{@article.id}") - - # set internal change directly in dom, without rerender while article - if !article || ( @lastArticle && @lastArticle.internal isnt @article.internal ) - if @article.internal is true - @el.addClass('is-internal') - else - @el.removeClass('is-internal') - - # check if rerender is needed - if !@force && !@hasChanged(@article) - @lastArticle = @article.attributes() - return - @force = false + @el.addClass("ticket-article-item #{article.sender.name.toLowerCase()}") + @el.attr('data-id', article.id) + @el.attr('id', "article-#{article.id}") # prepare html body - if @article.content_type is 'text/html' - if @article.sender.name is 'Agent' - @article['html'] = App.Utils.signatureIdentify(@article.body, false, true) + if article.content_type is 'text/html' + if article.sender.name is 'Agent' + article['html'] = App.Utils.signatureIdentify(article.body, false, true) else - @article['html'] = App.Utils.signatureIdentify(@article.body) + article['html'] = App.Utils.signatureIdentify(article.body) else # client signature detection - bodyHtml = App.Utils.text2html(@article.body) - @article['html'] = App.Utils.signatureIdentify(bodyHtml) + bodyHtml = App.Utils.text2html(article.body) + article['html'] = App.Utils.signatureIdentify(bodyHtml) # if no signature detected or within frist 25 lines, check if signature got detected in backend - if @article['html'] is bodyHtml || (@article.preferences && @article.preferences.signature_detection < 25) + if article['html'] is bodyHtml || (article.preferences && article.preferences.signature_detection < 25) signatureDetected = false - body = @article.body - if @article.preferences && @article.preferences.signature_detection + body = article.body + if article.preferences && article.preferences.signature_detection signatureDetected = '########SIGNATURE########' # coffeelint: disable=no_unnecessary_double_quotes body = body.split("\n") - body.splice(@article.preferences.signature_detection, 0, signatureDetected) + body.splice(article.preferences.signature_detection, 0, signatureDetected) body = body.join("\n") # coffeelint: enable=no_unnecessary_double_quotes if signatureDetected body = App.Utils.textCleanup(body) - @article['html'] = App.Utils.text2html(body) - @article['html'] = @article['html'].replace(signatureDetected, '') + article['html'] = App.Utils.text2html(body) + article['html'] = article['html'].replace(signatureDetected, '') + if article.sender.name is 'System' + @html App.view('ticket_zoom/article_view_system')( + ticket: @ticket + article: article + isCustomer: @isRole('Customer') + ) + return @html App.view('ticket_zoom/article_view')( ticket: @ticket - article: @article + article: article isCustomer: @isRole('Customer') ) new App.WidgetAvatar( - el: @$('.js-avatar') - user_id: @article.created_by_id - size: 40 + el: @$('.js-avatar') + object_id: article.created_by_id + size: 40 ) new App.TicketZoomArticleActions( - el: @$('.js-article-actions') - ticket: @ticket - article: @article + el: @$('.js-article-actions') + ticket: @ticket + article: article + lastAttributres: @lastAttributres ) # set see more @@ -343,3 +320,6 @@ class ArticleViewItem extends App.Controller return false return true false + + remove: => + @el.remove() diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/meta.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/meta.coffee index 715147886..b707a1861 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/meta.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/meta.coffee @@ -1,24 +1,12 @@ -class App.TicketZoomMeta extends App.Controller - constructor: -> - super - @render() - - # rerender, e. g. on language change - @bind('ui:rerender', => - @render() - ) +class App.TicketZoomMeta extends App.ObserverController + model: 'Ticket' + observe: + number: true + created_at: true + escalation_time: true render: (ticket) => - if !ticket - ticket = App.Ticket.fullLocal(@ticket.id) - - if !@subscribeId - @subscribeId = @ticket.subscribe(@render) - @html App.view('ticket_zoom/meta')( ticket: ticket isCustomer: @isRole('Customer') ) - - release: => - App.Ticket.unsubscribe(@subscribeId) diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/title.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/title.coffee index 466e9700e..159362533 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/title.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/title.coffee @@ -1,34 +1,13 @@ -class App.TicketZoomTitle extends App.Controller +class App.TicketZoomTitle extends App.ObserverController + model: 'Ticket' + template: 'ticket_zoom/title' + observe: + title: true + events: 'blur .ticket-title-update': 'update' - constructor: -> - super - @render() - - # rerender, e. g. on language change - @bind('ui:rerender', => - @render() - ) - - render: (ticket) => - if !ticket - ticket = App.Ticket.fullLocal(@ticket.id) - - if !@subscribeId - @subscribeId = @ticket.subscribe(@render) - - @title = ticket.title - - # check if render is needed - if @lastTitle && @lastTitle is ticket.title - return - @lastTitle = ticket.title - - @html App.view('ticket_zoom/title')( - ticket: ticket - ) - + renderPost: (object) => @$('.ticket-title-update').ce({ mode: 'textonly' multiline: false @@ -39,21 +18,18 @@ class App.TicketZoomTitle extends App.Controller title = $(e.target).ceg() || '' # update title - if title isnt @title - ticket = App.Ticket.find(@ticket.id) - ticket.title = title + return if title is @lastAttributres.title + ticket = App.Ticket.find(@object_id) + ticket.title = title - # reset article - should not be resubmited on next ticket update - ticket.article = undefined + # reset article - should not be resubmited on next ticket update + ticket.article = undefined - ticket.save() + ticket.save() - App.TaskManager.mute(@task_key) + App.TaskManager.mute(@task_key) - # update taskbar with new meta data - App.TaskManager.touch(@task_key) + # update taskbar with new meta data + App.TaskManager.touch(@task_key) - App.Event.trigger('overview:fetch') - - release: => - App.Ticket.unsubscribe(@subscribeId) + App.Event.trigger('overview:fetch') diff --git a/app/assets/javascripts/app/controllers/user_profile.coffee b/app/assets/javascripts/app/controllers/user_profile.coffee index 5e448a589..34613d103 100644 --- a/app/assets/javascripts/app/controllers/user_profile.coffee +++ b/app/assets/javascripts/app/controllers/user_profile.coffee @@ -49,9 +49,9 @@ class App.UserProfile extends App.Controller )) new Object( - el: elLocal.find('.js-object-container') - user: user - task_key: @task_key + el: elLocal.find('.js-object-container') + object_id: user.id + task_key: @task_key ) new App.TicketStats( @@ -65,19 +65,23 @@ class App.UserProfile extends App.Controller genericObject: user ) -class Object extends App.Controller +class Object extends App.ObserverController + model: 'User' + observeNot: + created_at: true + created_by_id: true + updated_at: true + updated_by_id: true + preferences: true + password: true + last_login: true + login_failed: true + source: true + image_source: true + events: 'focusout [contenteditable]': 'update' - constructor: (params) -> - super - - # subscribe and reload data / fetch new data if triggered - @subscribeId = App.User.full(@user.id, @render, false, true) - - release: => - App.User.unsubscribe(@subscribeId) - render: (user) => # update taskbar with new meta data @@ -111,6 +115,12 @@ class Object extends App.Controller maxlength: 250 }) + if user.organization_id + new Organization( + object_id: user.organization_id + el: @$('.js-organization') + ) + # start action controller showHistory = => new App.UserHistory( @@ -151,13 +161,24 @@ class Object extends App.Controller update: (e) => name = $(e.target).attr('data-name') value = $(e.target).html() - user = App.User.find(@user.id) + user = App.User.find(@object_id) if user[name] isnt value + @lastAttributres[name] = value data = {} data[name] = value user.updateAttributes(data) @log 'notice', 'update', name, value, user +class Organization extends App.ObserverController + model: 'Organization' + observe: + name: true + + render: (organization) => + @html App.view('user_profile/organization')( + organization: organization + ) + class Router extends App.ControllerPermanent constructor: (params) -> super diff --git a/app/assets/javascripts/app/controllers/widget/avatar.coffee b/app/assets/javascripts/app/controllers/widget/avatar.coffee index 73991604f..8ef120488 100644 --- a/app/assets/javascripts/app/controllers/widget/avatar.coffee +++ b/app/assets/javascripts/app/controllers/widget/avatar.coffee @@ -1,15 +1,11 @@ -class App.WidgetAvatar extends App.Controller - constructor: -> - super - - # subscribe and reload data / fetch new data if triggered - @subscribeId = App.User.full(@user_id, @render, false, true) - - release: => - App.User.unsubscribe(@subscribeId) +class App.WidgetAvatar extends App.ObserverController + model: 'User' + observe: + login: true + firstname: true + lastname: true + email: true render: (user) => - @html user.avatar @size, @position, undefined, false, false, @type - - # start user popups + @html(user.avatar @size, @position, undefined, false, false, @type) @userPopups(@position) diff --git a/app/assets/javascripts/app/models/organization.coffee b/app/assets/javascripts/app/models/organization.coffee index f8fd9dc38..3bead9c5a 100644 --- a/app/assets/javascripts/app/models/organization.coffee +++ b/app/assets/javascripts/app/models/organization.coffee @@ -1,5 +1,5 @@ class App.Organization extends App.Model - @configure 'Organization', 'name', 'shared', 'note', 'active', 'updated_at' + @configure 'Organization', 'name', 'shared', 'note', 'member_ids', 'active', 'updated_at' @extend Spine.Model.Ajax @url: @apiPath + '/organizations' @configure_attributes = [ @@ -36,8 +36,8 @@ Mit **Organisationen** können Sie Kunden **gruppieren**. Dies hat u. a. zwei be if data['member_ids'] data['members'] = [] for user_id in data['member_ids'] - if App.User.exists( user_id ) - user = App.User.find( user_id ) + if App.User.exists(user_id) + user = App.User.find(user_id) data['members'].push user data diff --git a/app/assets/javascripts/app/views/organization_profile/member.jst.eco b/app/assets/javascripts/app/views/organization_profile/member.jst.eco new file mode 100644 index 000000000..43b116ac4 --- /dev/null +++ b/app/assets/javascripts/app/views/organization_profile/member.jst.eco @@ -0,0 +1,4 @@ +