Reduced rerendering of object (introduction of App.ObserverController).

This commit is contained in:
Martin Edenhofer 2016-05-04 18:12:53 +02:00
parent dfc5900234
commit 08c5ca3e62
23 changed files with 292 additions and 225 deletions

View file

@ -260,7 +260,7 @@ class App.Controller extends Spine.Controller
# open ticket in new task if curent user agent # open ticket in new task if curent user agent
if @isRole('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') id = $(e.target).data('id')
if id if id
ticket = App.Ticket.find(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 # open user in new task if current user is agent
return if !@isRole('Agent') return if !@isRole('Agent')
@$('div.user-popover, span.user-popover').bind('click', (e) =>
@el.find('div.user-popover, span.user-popover').bind('click', (e) =>
id = $(e.target).data('id') id = $(e.target).data('id')
if id if id
user = App.User.find(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 # open org in new task if current user agent
return if !@isRole('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') id = $(e.target).data('id')
if id if id
organization = App.Organization.find(id) organization = App.Organization.find(id)

View file

@ -1068,3 +1068,83 @@ class App.CollectionController extends App.Controller
onRemoved: (id) -> onRemoved: (id) ->
# nothing # 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)

View file

@ -59,8 +59,8 @@ class App.DashboardActivityStream extends App.Controller
item: item item: item
)) ))
new App.WidgetAvatar( new App.WidgetAvatar(
el: html.find('.js-avatar') el: html.find('.js-avatar')
user_id: item.created_by_id object_id: item.created_by_id
size: 40 size: 40
) )
html html

View file

@ -125,9 +125,9 @@ class App.Navigation extends App.ControllerWidgetPermanent
# only start avatar widget on existing session # only start avatar widget on existing session
if App.Session.get('id') if App.Session.get('id')
new App.WidgetAvatar( new App.WidgetAvatar(
el: @$('.js-avatar') el: @$('.js-avatar')
user_id: App.Session.get('id') object_id: App.Session.get('id')
type: 'personal' type: 'personal'
) )
renderResult: (result = []) => renderResult: (result = []) =>

View file

@ -49,9 +49,9 @@ class App.OrganizationProfile extends App.Controller
)) ))
new Object( new Object(
el: elLocal.find('.js-object-container') el: elLocal.find('.js-object-container')
organization: organization object_id: organization.id
task_key: @task_key task_key: @task_key
) )
new App.TicketStats( new App.TicketStats(
@ -65,19 +65,20 @@ class App.OrganizationProfile extends App.Controller
genericObject: organization 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: events:
'focusout [contenteditable]': 'update' '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) => render: (organization) =>
# update taskbar with new meta data # update taskbar with new meta data
@ -111,6 +112,17 @@ class Object extends App.Controller
maxlength: 250 maxlength: 250
}) })
# show members
members = []
for userId in organization.member_ids
el = $('<div></div>')
new Member(
object_id: userId
el: el
)
members.push el
@$('.js-userList').html(members)
# start action controller # start action controller
showHistory = -> showHistory = ->
new App.OrganizationHistory( new App.OrganizationHistory(
@ -151,13 +163,26 @@ class Object extends App.Controller
update: (e) => update: (e) =>
name = $(e.target).attr('data-name') name = $(e.target).attr('data-name')
value = $(e.target).html() value = $(e.target).html()
org = App.Organization.find(@organization.id) org = App.Organization.find(@object_id)
if org[name] isnt value if org[name] isnt value
@lastAttributres[name] = value
data = {} data = {}
data[name] = value data[name] = value
org.updateAttributes(data) org.updateAttributes(data)
@log 'notice', 'update', name, value, org @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 class Router extends App.ControllerPermanent
constructor: (params) -> constructor: (params) ->

View file

@ -300,15 +300,15 @@ class App.TicketZoom extends App.Controller
) )
new App.TicketZoomTitle( new App.TicketZoomTitle(
ticket: @ticket 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(
ticket: @ticket object_id: @ticket.id
el: elLocal.find('.ticket-meta') el: elLocal.find('.ticket-meta')
) )
new App.TicketZoomAttributeBar( new App.TicketZoomAttributeBar(
@ -352,9 +352,9 @@ class App.TicketZoom extends App.Controller
else else
el = @el el = @el
new App.WidgetAvatar( new App.WidgetAvatar(
el: el.find('.ticketZoom-header .js-avatar') el: el.find('.ticketZoom-header .js-avatar')
user_id: @ticket.customer_id object_id: @ticket.customer_id
size: 50 size: 50
) )
@sidebar = new App.TicketZoomSidebar( @sidebar = new App.TicketZoomSidebar(
el: el.find('.tabsSidebar') el: el.find('.tabsSidebar')

View file

@ -23,7 +23,7 @@ class App.TicketZoomArticleActions extends App.Controller
else else
@html '' @html ''
publicInternal: (e) -> publicInternal: (e) =>
e.preventDefault() e.preventDefault()
articleContainer = $(e.target).closest('.ticket-article-item') articleContainer = $(e.target).closest('.ticket-article-item')
article_id = $(e.target).parents('[data-id]').data('id') article_id = $(e.target).parents('[data-id]').data('id')
@ -33,9 +33,8 @@ class App.TicketZoomArticleActions extends App.Controller
internal = true internal = true
if article.internal == true if article.internal == true
internal = false internal = false
article.updateAttributes( @lastAttributres.internal = internal
internal: internal article.updateAttributes(internal: internal)
)
# runntime update # runntime update
if internal if internal

View file

@ -175,11 +175,10 @@ class App.TicketZoomArticleNew extends App.Controller
@setArticleType(@type) @setArticleType(@type)
new App.WidgetAvatar( new App.WidgetAvatar(
el: @$('.js-avatar') el: @$('.js-avatar')
user_id: App.Session.get('id') object_id: App.Session.get('id')
size: 40 size: 40
position: 'right' position: 'right'
class: 'zIndex-5'
) )
configure_attributes = [ configure_attributes = [

View file

@ -1,7 +1,7 @@
class App.TicketZoomArticleView extends App.Controller class App.TicketZoomArticleView extends App.Controller
constructor: -> constructor: ->
super super
@article_controller = {} @articleController = {}
@run() @run()
execute: (params) => execute: (params) =>
@ -11,20 +11,39 @@ class App.TicketZoomArticleView extends App.Controller
run: => run: =>
all = [] all = []
for ticket_article_id in @ticket_article_ids for ticket_article_id in @ticket_article_ids
if !@article_controller[ticket_article_id] controllerKey = ticket_article_id.toString()
if !@articleController[controllerKey]
el = $('<div></div>') el = $('<div></div>')
@article_controller[ticket_article_id] = new ArticleViewItem( @articleController[controllerKey] = new ArticleViewItem(
ticket: @ticket ticket: @ticket
ticket_article_id: ticket_article_id object_id: ticket_article_id
el: el el: el
ui: @ui ui: @ui
highligher: @highligher highligher: @highligher
) )
all.push el all.push el
@el.append(all) @el.append(all)
class ArticleViewItem extends App.Controller # check elements to remove
hasChangedAttributes: ['from', 'to', 'cc', 'subject', 'body', 'preferences'] 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: elements:
'.textBubble-content': 'textBubbleContent' '.textBubble-content': 'textBubbleContent'
@ -36,12 +55,7 @@ class ArticleViewItem extends App.Controller
'click .js-unfold': 'unfold' 'click .js-unfold': 'unfold'
constructor: -> constructor: ->
super
@seeMore = false @seeMore = false
@force = true
@render()
# set expand of text area only once # set expand of text area only once
@bind('ui::ticket::shown', (data) => @bind('ui::ticket::shown', (data) =>
@ -54,112 +68,75 @@ class ArticleViewItem extends App.Controller
@setSeeMore() @setSeeMore()
) )
# rerender, e. g. on language change super
@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)
setHighlighter: => setHighlighter: =>
return if @el.is(':hidden') return if @el.is(':hidden')
# use delay do no ui blocking # use delay do no ui blocking
#@highligher.loadHighlights(@ticket_article_id) #@highligher.loadHighlights(@object_id)
d = => d = =>
@highligher.loadHighlights(@ticket_article_id) @highligher.loadHighlights(@object_id)
@delay(d, 200) @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) => render: (article) =>
# get articles
@article = App.TicketArticle.fullLocal(@ticket_article_id)
# set @el attributes # set @el attributes
if !article @el.addClass("ticket-article-item #{article.sender.name.toLowerCase()}")
@el.addClass("ticket-article-item #{@article.sender.name.toLowerCase()}") @el.attr('data-id', article.id)
@el.attr('data-id', @article.id) @el.attr('id', "article-#{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
# prepare html body # prepare html body
if @article.content_type is 'text/html' if article.content_type is 'text/html'
if @article.sender.name is 'Agent' if article.sender.name is 'Agent'
@article['html'] = App.Utils.signatureIdentify(@article.body, false, true) article['html'] = App.Utils.signatureIdentify(article.body, false, true)
else else
@article['html'] = App.Utils.signatureIdentify(@article.body) article['html'] = App.Utils.signatureIdentify(article.body)
else else
# client signature detection # client signature detection
bodyHtml = App.Utils.text2html(@article.body) bodyHtml = App.Utils.text2html(article.body)
@article['html'] = App.Utils.signatureIdentify(bodyHtml) article['html'] = App.Utils.signatureIdentify(bodyHtml)
# if no signature detected or within frist 25 lines, check if signature got detected in backend # 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 signatureDetected = false
body = @article.body body = article.body
if @article.preferences && @article.preferences.signature_detection if article.preferences && article.preferences.signature_detection
signatureDetected = '########SIGNATURE########' signatureDetected = '########SIGNATURE########'
# coffeelint: disable=no_unnecessary_double_quotes # coffeelint: disable=no_unnecessary_double_quotes
body = body.split("\n") body = body.split("\n")
body.splice(@article.preferences.signature_detection, 0, signatureDetected) body.splice(article.preferences.signature_detection, 0, signatureDetected)
body = body.join("\n") body = body.join("\n")
# coffeelint: enable=no_unnecessary_double_quotes # coffeelint: enable=no_unnecessary_double_quotes
if signatureDetected if signatureDetected
body = App.Utils.textCleanup(body) body = App.Utils.textCleanup(body)
@article['html'] = App.Utils.text2html(body) article['html'] = App.Utils.text2html(body)
@article['html'] = @article['html'].replace(signatureDetected, '<span class="js-signatureMarker"></span>') article['html'] = article['html'].replace(signatureDetected, '<span class="js-signatureMarker"></span>')
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')( @html App.view('ticket_zoom/article_view')(
ticket: @ticket ticket: @ticket
article: @article article: article
isCustomer: @isRole('Customer') isCustomer: @isRole('Customer')
) )
new App.WidgetAvatar( new App.WidgetAvatar(
el: @$('.js-avatar') el: @$('.js-avatar')
user_id: @article.created_by_id object_id: article.created_by_id
size: 40 size: 40
) )
new App.TicketZoomArticleActions( new App.TicketZoomArticleActions(
el: @$('.js-article-actions') el: @$('.js-article-actions')
ticket: @ticket ticket: @ticket
article: @article article: article
lastAttributres: @lastAttributres
) )
# set see more # set see more
@ -343,3 +320,6 @@ class ArticleViewItem extends App.Controller
return false return false
return true return true
false false
remove: =>
@el.remove()

View file

@ -1,24 +1,12 @@
class App.TicketZoomMeta extends App.Controller class App.TicketZoomMeta extends App.ObserverController
constructor: -> model: 'Ticket'
super observe:
@render() number: true
created_at: true
# rerender, e. g. on language change escalation_time: true
@bind('ui:rerender', =>
@render()
)
render: (ticket) => render: (ticket) =>
if !ticket
ticket = App.Ticket.fullLocal(@ticket.id)
if !@subscribeId
@subscribeId = @ticket.subscribe(@render)
@html App.view('ticket_zoom/meta')( @html App.view('ticket_zoom/meta')(
ticket: ticket ticket: ticket
isCustomer: @isRole('Customer') isCustomer: @isRole('Customer')
) )
release: =>
App.Ticket.unsubscribe(@subscribeId)

View file

@ -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: events:
'blur .ticket-title-update': 'update' 'blur .ticket-title-update': 'update'
constructor: -> renderPost: (object) =>
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
)
@$('.ticket-title-update').ce({ @$('.ticket-title-update').ce({
mode: 'textonly' mode: 'textonly'
multiline: false multiline: false
@ -39,21 +18,18 @@ class App.TicketZoomTitle extends App.Controller
title = $(e.target).ceg() || '' title = $(e.target).ceg() || ''
# update title # update title
if title isnt @title return if title is @lastAttributres.title
ticket = App.Ticket.find(@ticket.id) ticket = App.Ticket.find(@object_id)
ticket.title = title ticket.title = title
# 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
ticket.save() ticket.save()
App.TaskManager.mute(@task_key) App.TaskManager.mute(@task_key)
# update taskbar with new meta data # update taskbar with new meta data
App.TaskManager.touch(@task_key) App.TaskManager.touch(@task_key)
App.Event.trigger('overview:fetch') App.Event.trigger('overview:fetch')
release: =>
App.Ticket.unsubscribe(@subscribeId)

View file

@ -49,9 +49,9 @@ class App.UserProfile extends App.Controller
)) ))
new Object( new Object(
el: elLocal.find('.js-object-container') el: elLocal.find('.js-object-container')
user: user object_id: user.id
task_key: @task_key task_key: @task_key
) )
new App.TicketStats( new App.TicketStats(
@ -65,19 +65,23 @@ class App.UserProfile extends App.Controller
genericObject: user 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: events:
'focusout [contenteditable]': 'update' '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) => render: (user) =>
# update taskbar with new meta data # update taskbar with new meta data
@ -111,6 +115,12 @@ class Object extends App.Controller
maxlength: 250 maxlength: 250
}) })
if user.organization_id
new Organization(
object_id: user.organization_id
el: @$('.js-organization')
)
# start action controller # start action controller
showHistory = => showHistory = =>
new App.UserHistory( new App.UserHistory(
@ -151,13 +161,24 @@ class Object extends App.Controller
update: (e) => update: (e) =>
name = $(e.target).attr('data-name') name = $(e.target).attr('data-name')
value = $(e.target).html() value = $(e.target).html()
user = App.User.find(@user.id) user = App.User.find(@object_id)
if user[name] isnt value if user[name] isnt value
@lastAttributres[name] = value
data = {} data = {}
data[name] = value data[name] = value
user.updateAttributes(data) user.updateAttributes(data)
@log 'notice', 'update', name, value, user @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 class Router extends App.ControllerPermanent
constructor: (params) -> constructor: (params) ->
super super

View file

@ -1,15 +1,11 @@
class App.WidgetAvatar extends App.Controller class App.WidgetAvatar extends App.ObserverController
constructor: -> model: 'User'
super observe:
login: true
# subscribe and reload data / fetch new data if triggered firstname: true
@subscribeId = App.User.full(@user_id, @render, false, true) lastname: true
email: true
release: =>
App.User.unsubscribe(@subscribeId)
render: (user) => render: (user) =>
@html user.avatar @size, @position, undefined, false, false, @type @html(user.avatar @size, @position, undefined, false, false, @type)
# start user popups
@userPopups(@position) @userPopups(@position)

View file

@ -1,5 +1,5 @@
class App.Organization extends App.Model 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 @extend Spine.Model.Ajax
@url: @apiPath + '/organizations' @url: @apiPath + '/organizations'
@configure_attributes = [ @configure_attributes = [
@ -36,8 +36,8 @@ Mit **Organisationen** können Sie Kunden **gruppieren**. Dies hat u. a. zwei be
if data['member_ids'] if data['member_ids']
data['members'] = [] data['members'] = []
for user_id in data['member_ids'] for user_id in data['member_ids']
if App.User.exists( user_id ) if App.User.exists(user_id)
user = App.User.find( user_id ) user = App.User.find(user_id)
data['members'].push user data['members'].push user
data data

View file

@ -0,0 +1,4 @@
<div class="userList-entry">
<%- @user.avatar("40") %>
<a href="<%- @user.uiUrl() %>" class="userList-name user-popover" data-id="<%- @user.id %>"><%= @user.displayName() %></a>
</div>

View file

@ -27,13 +27,6 @@
<% if @organization.members: %> <% if @organization.members: %>
<div class="profile-section profile-memberSection"> <div class="profile-section profile-memberSection">
<label><%- @T('Members') %></label> <label><%- @T('Members') %></label>
<div class="userList"> <div class="userList js-userList"></div>
<% for user in @organization.members: %>
<div class="userList-entry">
<%- user.avatar("40") %>
<a href="<%- user.uiUrl() %>" class="userList-name user-popover" data-id="<%- user.id %>"><%= user.displayName() %></a>
</div>
<% end %>
</div>
</div> </div>
<% end %> <% end %>

View file

@ -32,7 +32,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="article-content zIndex-5 bubble-gap"> <div class="article-content bubble-gap">
<div class="internal-border"> <div class="internal-border">
<div class="input form-group"> <div class="input form-group">

View file

@ -29,7 +29,7 @@
</div> </div>
</div> </div>
<div class="article-content zIndex-1"> <div class="article-content">
<% if @article.sender.name isnt 'Agent': %> <% if @article.sender.name isnt 'Agent': %>
<% position = 'left' %> <% position = 'left' %>
<% else: %> <% else: %>

View file

@ -0,0 +1,3 @@
<div class="small task-subline">
"<%= @article.subject %>" -&gt; "<%= @article.to %>"
</div>

View file

@ -1 +1 @@
<div contenteditable="true" class="ticket-title-update" data-placeholder="<%- @Ti('Enter Title...') %>"><%= @ticket.title %></div> <div contenteditable="true" class="ticket-title-update" data-placeholder="<%- @Ti('Enter Title...') %>"><%= @object.title %></div>

View file

@ -3,7 +3,7 @@
<%- @user.avatar("80") %> <%- @user.avatar("80") %>
<h1><%= @user.displayName() %></h1> <h1><%= @user.displayName() %></h1>
<% if @user.organization: %> <% if @user.organization: %>
<div class="profile-organization"><a href="<%- @user.organization.uiUrl() %>"><%= @user.organization.displayName() %></a></div> <div class="profile-organization js-organization"></div>
<% end %> <% end %>
</div> </div>
<div class="profile-section"> <div class="profile-section">

View file

@ -0,0 +1 @@
<a href="<%- @organization.uiUrl() %>"><%= @organization.displayName() %></a>

View file

@ -23,7 +23,10 @@ class Observer::Ticket::ArticleChanges < ActiveRecord::Observer
end end
# save ticket # save ticket
return if !changed if !changed
record.ticket.touch
return
end
record.ticket.save record.ticket.save
end end