Merge remote-tracking branch 'origin/stable-5.0' into antifascista-stable
This commit is contained in:
commit
089e4c36d3
17 changed files with 453 additions and 151 deletions
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
[Full Changelog](https://github.com/zammad/zammad/compare/5.0.2...5.0.3)
|
[Full Changelog](https://github.com/zammad/zammad/compare/5.0.2...5.0.3)
|
||||||
|
|
||||||
|
|
||||||
**Implemented enhancements:**
|
**Implemented enhancements:**
|
||||||
|
|
||||||
- Possibility to specify the order of objects [294](https://github.com/zammad/zammad/issues/294) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
- Possibility to specify the order of objects [294](https://github.com/zammad/zammad/issues/294) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||||
|
|
126
Gemfile.lock
126
Gemfile.lock
|
@ -21,50 +21,50 @@ GEM
|
||||||
specs:
|
specs:
|
||||||
aasm (5.2.0)
|
aasm (5.2.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
actioncable (6.0.4.4)
|
actioncable (6.0.4.7)
|
||||||
actionpack (= 6.0.4.4)
|
actionpack (= 6.0.4.7)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (6.0.4.4)
|
actionmailbox (6.0.4.7)
|
||||||
actionpack (= 6.0.4.4)
|
actionpack (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.4)
|
activejob (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.4)
|
activerecord (= 6.0.4.7)
|
||||||
activestorage (= 6.0.4.4)
|
activestorage (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
actionmailer (6.0.4.4)
|
actionmailer (6.0.4.7)
|
||||||
actionpack (= 6.0.4.4)
|
actionpack (= 6.0.4.7)
|
||||||
actionview (= 6.0.4.4)
|
actionview (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.4)
|
activejob (= 6.0.4.7)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (6.0.4.4)
|
actionpack (6.0.4.7)
|
||||||
actionview (= 6.0.4.4)
|
actionview (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
rack (~> 2.0, >= 2.0.8)
|
rack (~> 2.0, >= 2.0.8)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (6.0.4.4)
|
actiontext (6.0.4.7)
|
||||||
actionpack (= 6.0.4.4)
|
actionpack (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.4)
|
activerecord (= 6.0.4.7)
|
||||||
activestorage (= 6.0.4.4)
|
activestorage (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (6.0.4.4)
|
actionview (6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||||
activejob (6.0.4.4)
|
activejob (6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (6.0.4.4)
|
activemodel (6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
activerecord (6.0.4.4)
|
activerecord (6.0.4.7)
|
||||||
activemodel (= 6.0.4.4)
|
activemodel (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
activerecord-import (1.2.0)
|
activerecord-import (1.2.0)
|
||||||
activerecord (>= 3.2)
|
activerecord (>= 3.2)
|
||||||
activerecord-nulldb-adapter (0.7.0)
|
activerecord-nulldb-adapter (0.7.0)
|
||||||
|
@ -75,12 +75,12 @@ GEM
|
||||||
multi_json (~> 1.11, >= 1.11.2)
|
multi_json (~> 1.11, >= 1.11.2)
|
||||||
rack (>= 2.0.8, < 3)
|
rack (>= 2.0.8, < 3)
|
||||||
railties (>= 5.2.4.1)
|
railties (>= 5.2.4.1)
|
||||||
activestorage (6.0.4.4)
|
activestorage (6.0.4.7)
|
||||||
actionpack (= 6.0.4.4)
|
actionpack (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.4)
|
activejob (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.4)
|
activerecord (= 6.0.4.7)
|
||||||
marcel (~> 1.0.0)
|
marcel (~> 1.0.0)
|
||||||
activesupport (6.0.4.4)
|
activesupport (6.0.4.7)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
|
@ -291,7 +291,7 @@ GEM
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
http_parser.rb (0.6.0-x86_64-linux-musl)
|
http_parser.rb (0.6.0-x86_64-linux-musl)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.8.10)
|
i18n (1.10.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
icalendar (2.7.1)
|
icalendar (2.7.1)
|
||||||
ice_cube (~> 0.16)
|
ice_cube (~> 0.16)
|
||||||
|
@ -319,11 +319,11 @@ GEM
|
||||||
logging (2.3.0)
|
logging (2.3.0)
|
||||||
little-plugger (~> 1.1)
|
little-plugger (~> 1.1)
|
||||||
multi_json (~> 1.14)
|
multi_json (~> 1.14)
|
||||||
loofah (2.13.0)
|
loofah (2.14.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
lumberjack (1.2.8)
|
lumberjack (1.2.8)
|
||||||
marcel (1.0.1)
|
marcel (1.0.2)
|
||||||
memoizable (0.4.2)
|
memoizable (0.4.2)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
messagebird-rest (3.0.0)
|
messagebird-rest (3.0.0)
|
||||||
|
@ -331,8 +331,8 @@ GEM
|
||||||
mime-types (3.3.1)
|
mime-types (3.3.1)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2021.0225)
|
mime-types-data (3.2021.0225)
|
||||||
mini_mime (1.1.1)
|
mini_mime (1.1.2)
|
||||||
mini_portile2 (2.6.1)
|
mini_portile2 (2.8.0)
|
||||||
mini_racer (0.2.9)
|
mini_racer (0.2.9)
|
||||||
libv8 (>= 6.9.411)
|
libv8 (>= 6.9.411)
|
||||||
minitest (5.15.0)
|
minitest (5.15.0)
|
||||||
|
@ -349,9 +349,8 @@ GEM
|
||||||
net-ldap (0.17.0)
|
net-ldap (0.17.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nio4r (2.5.8-x86_64-linux-musl)
|
nokogiri (1.13.3)
|
||||||
nokogiri (1.12.5)
|
mini_portile2 (~> 2.8.0)
|
||||||
mini_portile2 (~> 2.6.1)
|
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.12.5-x86_64-linux-musl)
|
nokogiri (1.12.5-x86_64-linux-musl)
|
||||||
mini_portile2 (~> 2.6.1)
|
mini_portile2 (~> 2.6.1)
|
||||||
|
@ -443,7 +442,7 @@ GEM
|
||||||
binding_of_caller (~> 1.0)
|
binding_of_caller (~> 1.0)
|
||||||
pry (~> 0.13)
|
pry (~> 0.13)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.6)
|
||||||
puma (4.3.10)
|
puma (4.3.11)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
puma (4.3.8-x86_64-linux-musl)
|
puma (4.3.8-x86_64-linux-musl)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
|
@ -451,27 +450,26 @@ GEM
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
pundit-matchers (1.7.0)
|
pundit-matchers (1.7.0)
|
||||||
rspec-rails (>= 3.0.0)
|
rspec-rails (>= 3.0.0)
|
||||||
racc (1.5.2)
|
racc (1.6.0)
|
||||||
racc (1.5.2-x86_64-linux-musl)
|
|
||||||
rack (2.2.3)
|
rack (2.2.3)
|
||||||
rack-livereload (0.3.17)
|
rack-livereload (0.3.17)
|
||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rails (6.0.4.4)
|
rails (6.0.4.7)
|
||||||
actioncable (= 6.0.4.4)
|
actioncable (= 6.0.4.7)
|
||||||
actionmailbox (= 6.0.4.4)
|
actionmailbox (= 6.0.4.7)
|
||||||
actionmailer (= 6.0.4.4)
|
actionmailer (= 6.0.4.7)
|
||||||
actionpack (= 6.0.4.4)
|
actionpack (= 6.0.4.7)
|
||||||
actiontext (= 6.0.4.4)
|
actiontext (= 6.0.4.7)
|
||||||
actionview (= 6.0.4.4)
|
actionview (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.4)
|
activejob (= 6.0.4.7)
|
||||||
activemodel (= 6.0.4.4)
|
activemodel (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.4)
|
activerecord (= 6.0.4.7)
|
||||||
activestorage (= 6.0.4.4)
|
activestorage (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 6.0.4.4)
|
railties (= 6.0.4.7)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.5)
|
rails-controller-testing (1.0.5)
|
||||||
actionpack (>= 5.0.1.rc1)
|
actionpack (>= 5.0.1.rc1)
|
||||||
|
@ -480,11 +478,11 @@ GEM
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.4.1)
|
rails-html-sanitizer (1.4.2)
|
||||||
loofah (~> 2.3)
|
loofah (~> 2.3)
|
||||||
railties (6.0.4.4)
|
railties (6.0.4.7)
|
||||||
actionpack (= 6.0.4.4)
|
actionpack (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.4)
|
activesupport (= 6.0.4.7)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.20.3, < 2.0)
|
thor (>= 0.20.3, < 2.0)
|
||||||
|
@ -610,7 +608,7 @@ GEM
|
||||||
tins (~> 1.0)
|
tins (~> 1.0)
|
||||||
test-unit (3.4.7)
|
test-unit (3.4.7)
|
||||||
power_assert
|
power_assert
|
||||||
thor (1.1.0)
|
thor (1.2.1)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tilt (2.0.10)
|
tilt (2.0.10)
|
||||||
timers (4.3.3)
|
timers (4.3.3)
|
||||||
|
@ -666,7 +664,7 @@ GEM
|
||||||
writeexcel (1.0.5)
|
writeexcel (1.0.5)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.4.2)
|
zeitwerk (2.5.4)
|
||||||
zendesk_api (1.33.0)
|
zendesk_api (1.33.0)
|
||||||
faraday (>= 0.9.0, < 2.0.0)
|
faraday (>= 0.9.0, < 2.0.0)
|
||||||
hashie (>= 3.5.2, < 5.0.0)
|
hashie (>= 3.5.2, < 5.0.0)
|
||||||
|
|
|
@ -2,6 +2,7 @@ class App.ControllerObserver extends App.Controller
|
||||||
model: 'Ticket'
|
model: 'Ticket'
|
||||||
template: 'tba'
|
template: 'tba'
|
||||||
globalRerender: true
|
globalRerender: true
|
||||||
|
lastAttributes: undefined
|
||||||
|
|
||||||
###
|
###
|
||||||
observe:
|
observe:
|
||||||
|
@ -25,7 +26,7 @@ class App.ControllerObserver extends App.Controller
|
||||||
# rerender, e. g. on language change
|
# rerender, e. g. on language change
|
||||||
if @globalRerender
|
if @globalRerender
|
||||||
@controllerBind('ui:rerender', =>
|
@controllerBind('ui:rerender', =>
|
||||||
@lastAttributres = undefined
|
@lastAttributes = undefined
|
||||||
@maybeRender(App[@model].fullLocal(@object_id))
|
@maybeRender(App[@model].fullLocal(@object_id))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,32 +44,40 @@ class App.ControllerObserver extends App.Controller
|
||||||
if !@subscribeId
|
if !@subscribeId
|
||||||
@subscribeId = object.subscribe(@subscribe)
|
@subscribeId = object.subscribe(@subscribe)
|
||||||
|
|
||||||
# remember current attributes
|
return if !@hasChanged(object)
|
||||||
|
|
||||||
|
@render(object)
|
||||||
|
|
||||||
|
hasChanged: (object) =>
|
||||||
currentAttributes = {}
|
currentAttributes = {}
|
||||||
|
|
||||||
|
objectCloned = $.extend(true, {}, object)
|
||||||
if @observe
|
if @observe
|
||||||
for key, active of @observe
|
for key, active of @observe
|
||||||
if active
|
if active && !_.isFunction(value)
|
||||||
currentAttributes[key] = object[key]
|
currentAttributes[key] = objectCloned[key]
|
||||||
|
|
||||||
if @observeNot
|
if @observeNot
|
||||||
for key, value of object
|
for key, value of objectCloned
|
||||||
if key isnt 'cid' && !@observeNot[key] && !_.isFunction(value) && !_.isObject(value)
|
if key isnt 'cid' && !@observeNot[key] && !_.isFunction(value)
|
||||||
currentAttributes[key] = value
|
currentAttributes[key] = value
|
||||||
|
|
||||||
if !@lastAttributres
|
if !@lastAttributes
|
||||||
@lastAttributres = {}
|
@lastAttributes = currentAttributes
|
||||||
else
|
return true
|
||||||
diff = difference(currentAttributes, @lastAttributres)
|
|
||||||
|
diff = difference(currentAttributes, @lastAttributes)
|
||||||
if _.isEmpty(diff)
|
if _.isEmpty(diff)
|
||||||
@log 'debug', 'maybeRender no diff, no rerender'
|
@log 'debug', 'maybeRender no diff, no rerender'
|
||||||
return
|
return false
|
||||||
|
|
||||||
@log 'debug', 'maybeRender.diff', diff, @observe, @model
|
@log 'debug', 'maybeRender.diff', diff, @observe, @model
|
||||||
@lastAttributres = currentAttributes
|
@lastAttributes = currentAttributes
|
||||||
|
|
||||||
@render(object, diff)
|
true
|
||||||
|
|
||||||
render: (object, diff) =>
|
render: (object) =>
|
||||||
@log 'debug', 'render', @template, object, diff
|
@log 'debug', 'render', @template, object
|
||||||
@html App.view(@template)(
|
@html App.view(@template)(
|
||||||
object: object
|
object: object
|
||||||
)
|
)
|
||||||
|
|
|
@ -202,7 +202,7 @@ class Object extends App.ControllerObserver
|
||||||
value = $(e.target).html()
|
value = $(e.target).html()
|
||||||
org = App.Organization.find(@object_id)
|
org = App.Organization.find(@object_id)
|
||||||
if org[name] isnt value
|
if org[name] isnt value
|
||||||
@lastAttributres[name] = value
|
@lastAttributes[name] = value
|
||||||
data = {}
|
data = {}
|
||||||
data[name] = value
|
data[name] = value
|
||||||
org.updateAttributes(data)
|
org.updateAttributes(data)
|
||||||
|
|
|
@ -257,7 +257,7 @@ class EmailReply extends App.Controller
|
||||||
|
|
||||||
quote_header = App.FullQuoteHeader.fullQuoteHeaderForward(article)
|
quote_header = App.FullQuoteHeader.fullQuoteHeaderForward(article)
|
||||||
|
|
||||||
body = "<br/><div>---Begin forwarded message:---<br/><br/></div><div><blockquote type=\"cite\">#{quote_header}#{body}</blockquote></div><div><br></div>"
|
body = "<br/><div>---#{App.i18n.translateInline('Begin forwarded message')}:---<br/><br/></div><div><blockquote type=\"cite\">#{quote_header}#{body}</blockquote></div><div><br></div>"
|
||||||
|
|
||||||
articleNew = {}
|
articleNew = {}
|
||||||
articleNew.body = body
|
articleNew.body = body
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Internal
|
||||||
internal = true
|
internal = true
|
||||||
if article.internal == true
|
if article.internal == true
|
||||||
internal = false
|
internal = false
|
||||||
ui.lastAttributres.internal = internal
|
ui.lastAttributes.internal = internal
|
||||||
article.updateAttributes(internal: internal)
|
article.updateAttributes(internal: internal)
|
||||||
|
|
||||||
# runtime update
|
# runtime update
|
||||||
|
|
|
@ -221,7 +221,7 @@ class ArticleViewItem extends App.ControllerObserver
|
||||||
el: @$('.js-article-actions')
|
el: @$('.js-article-actions')
|
||||||
ticket: @ticket
|
ticket: @ticket
|
||||||
article: article
|
article: article
|
||||||
lastAttributres: @lastAttributres
|
lastAttributes: @lastAttributes
|
||||||
form_id: @form_id
|
form_id: @form_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -305,13 +305,17 @@ class ArticleViewItem extends App.ControllerObserver
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
article_id = $(e.target).closest('.ticket-article-item').data('id')
|
article_id = $(e.target).closest('.ticket-article-item').data('id')
|
||||||
|
article = App.TicketArticle.find(article_id)
|
||||||
|
|
||||||
@ajax(
|
@ajax(
|
||||||
id: 'retrySecurityProcess'
|
id: 'retrySecurityProcess'
|
||||||
type: 'POST'
|
type: 'POST'
|
||||||
url: "#{@apiPath}/ticket_articles/#{article_id}/retry_security_process"
|
url: "#{@apiPath}/ticket_articles/#{article_id}/retry_security_process"
|
||||||
processData: true
|
processData: true
|
||||||
success: (data, status, xhr) =>
|
success: (encryption_data, status, xhr) =>
|
||||||
|
for data in encryption_data
|
||||||
|
continue if article.preferences.security.type isnt data.type
|
||||||
|
|
||||||
if data.sign.success
|
if data.sign.success
|
||||||
@notify
|
@notify
|
||||||
type: 'success'
|
type: 'success'
|
||||||
|
|
|
@ -19,7 +19,7 @@ class App.TicketZoomTitle extends App.ControllerObserver
|
||||||
title = $(e.target).ceg() || ''
|
title = $(e.target).ceg() || ''
|
||||||
|
|
||||||
# update title
|
# update title
|
||||||
return if title is @lastAttributres.title
|
return if title is @lastAttributes.title
|
||||||
ticket = App.Ticket.find(@object_id)
|
ticket = App.Ticket.find(@object_id)
|
||||||
ticket.title = title
|
ticket.title = title
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,7 @@ class Object extends App.ControllerObserver
|
||||||
value = $(e.target).html()
|
value = $(e.target).html()
|
||||||
user = App.User.find(@object_id)
|
user = App.User.find(@object_id)
|
||||||
if user[name] isnt value
|
if user[name] isnt value
|
||||||
@lastAttributres[name] = value
|
@lastAttributes[name] = value
|
||||||
data = {}
|
data = {}
|
||||||
data[name] = value
|
data[name] = value
|
||||||
user.updateAttributes(data)
|
user.updateAttributes(data)
|
||||||
|
|
|
@ -65,9 +65,9 @@ Date.prototype.getWeek = function() {
|
||||||
|
|
||||||
function difference(object1, object2) {
|
function difference(object1, object2) {
|
||||||
var changes = {};
|
var changes = {};
|
||||||
for (var name in object1) {
|
_.uniq(Object.keys(object1).concat(Object.keys(object2))).forEach(function(name) {
|
||||||
if (name in object2) {
|
if (name in object1 && name in object2) {
|
||||||
if (_.isObject(object2[name]) && !_.isArray(object2[name])) {
|
if (_.isObject(object1[name]) && !_.isArray(object1[name]) && _.isObject(object2[name]) && !_.isArray(object2[name])) {
|
||||||
var diff = difference(object1[name], object2[name]);
|
var diff = difference(object1[name], object2[name]);
|
||||||
if (!_.isEmpty(diff)) {
|
if (!_.isEmpty(diff)) {
|
||||||
changes[name] = diff;
|
changes[name] = diff;
|
||||||
|
@ -75,8 +75,10 @@ function difference(object1, object2) {
|
||||||
} else if (!_.isEqual(object1[name], object2[name])) {
|
} else if (!_.isEqual(object1[name], object2[name])) {
|
||||||
changes[name] = object2[name];
|
changes[name] = object2[name];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
changes[name] = object2[name]
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ when 'mysql2'
|
||||||
# Because of missing ticket updates in high load environments
|
# Because of missing ticket updates in high load environments
|
||||||
# we changed the transaction isolation level equally to postgres
|
# we changed the transaction isolation level equally to postgres
|
||||||
# to READ COMMITTED which fixed the problem entirely #3877
|
# to READ COMMITTED which fixed the problem entirely #3877
|
||||||
ActiveRecord::ConnectionAdapters::Mysql2Adapter.set_callback :checkout, :before do |conn|
|
ActiveRecord::ConnectionAdapters::Mysql2Adapter.set_callback :checkout, :after do |conn|
|
||||||
conn.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
|
conn.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
|
||||||
end
|
end
|
||||||
when 'postgresql'
|
when 'postgresql'
|
||||||
|
|
0
contrib/backup/zammad_db_user_helper.sh
Normal file → Executable file
0
contrib/backup/zammad_db_user_helper.sh
Normal file → Executable file
|
@ -10,9 +10,11 @@ class SecureMailing
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.retry(article)
|
def self.retry(article)
|
||||||
|
result = []
|
||||||
active_backends.each do |backend|
|
active_backends.each do |backend|
|
||||||
"#{backend}::Retry".constantize.process(article)
|
result << "#{backend}::Retry".constantize.process(article)
|
||||||
end
|
end
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.outgoing(mail, security)
|
def self.outgoing(mail, security)
|
||||||
|
|
|
@ -536,7 +536,9 @@ test('difference', function() {
|
||||||
object2 = {
|
object2 = {
|
||||||
key1: 123,
|
key1: 123,
|
||||||
}
|
}
|
||||||
result = {}
|
result = {
|
||||||
|
key2: undefined
|
||||||
|
}
|
||||||
item = difference(object1, object2)
|
item = difference(object1, object2)
|
||||||
deepEqual(item, result)
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
@ -547,38 +549,106 @@ test('difference', function() {
|
||||||
key1: 123,
|
key1: 123,
|
||||||
key2: 124
|
key2: 124
|
||||||
}
|
}
|
||||||
|
result = {
|
||||||
|
key2: 124
|
||||||
|
}
|
||||||
|
item = difference(object1, object2)
|
||||||
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
object1 = {
|
||||||
|
customer_id: 1,
|
||||||
|
organization_id: 2,
|
||||||
|
}
|
||||||
|
object2 = {
|
||||||
|
customer_id: 1,
|
||||||
|
organization_id: null,
|
||||||
|
}
|
||||||
|
result = {
|
||||||
|
organization_id: null,
|
||||||
|
}
|
||||||
|
item = difference(object1, object2)
|
||||||
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
object1 = {
|
||||||
|
customer_id: 1,
|
||||||
|
organization_id: null,
|
||||||
|
}
|
||||||
|
object2 = {
|
||||||
|
customer_id: 1,
|
||||||
|
organization_id: 2,
|
||||||
|
}
|
||||||
|
result = {
|
||||||
|
organization_id: 2,
|
||||||
|
}
|
||||||
|
item = difference(object1, object2)
|
||||||
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
object1 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: { resolved: true },
|
||||||
|
}
|
||||||
|
object2 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: {},
|
||||||
|
}
|
||||||
|
result = {
|
||||||
|
preferences: { resolved: undefined }
|
||||||
|
}
|
||||||
|
item = difference(object1, object2)
|
||||||
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
object1 = {
|
||||||
|
customer_id: 1,
|
||||||
|
}
|
||||||
|
object2 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: { resolved: true },
|
||||||
|
}
|
||||||
|
result = {
|
||||||
|
preferences: { resolved: true }
|
||||||
|
}
|
||||||
|
item = difference(object1, object2)
|
||||||
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
object1 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: {},
|
||||||
|
}
|
||||||
|
object2 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: { resolved: true },
|
||||||
|
}
|
||||||
|
result = {
|
||||||
|
preferences: { resolved: true }
|
||||||
|
}
|
||||||
|
item = difference(object1, object2)
|
||||||
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
object1 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: { resolved: false },
|
||||||
|
}
|
||||||
|
object2 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: { resolved: true },
|
||||||
|
}
|
||||||
|
result = {
|
||||||
|
preferences: { resolved: true }
|
||||||
|
}
|
||||||
|
item = difference(object1, object2)
|
||||||
|
deepEqual(item, result)
|
||||||
|
|
||||||
|
object1 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: { resolved: true },
|
||||||
|
}
|
||||||
|
object2 = {
|
||||||
|
customer_id: 1,
|
||||||
|
preferences: { resolved: true },
|
||||||
|
}
|
||||||
result = {}
|
result = {}
|
||||||
item = difference(object1, object2)
|
item = difference(object1, object2)
|
||||||
deepEqual(item, result)
|
deepEqual(item, result)
|
||||||
|
|
||||||
object1 = {
|
|
||||||
customer_id: 1,
|
|
||||||
organization_id: 2,
|
|
||||||
}
|
|
||||||
object2 = {
|
|
||||||
customer_id: 1,
|
|
||||||
organization_id: null,
|
|
||||||
}
|
|
||||||
result = {
|
|
||||||
organization_id: null,
|
|
||||||
}
|
|
||||||
item = difference(object1, object2)
|
|
||||||
deepEqual(item, result)
|
|
||||||
|
|
||||||
object1 = {
|
|
||||||
customer_id: 1,
|
|
||||||
organization_id: null,
|
|
||||||
}
|
|
||||||
object2 = {
|
|
||||||
customer_id: 1,
|
|
||||||
organization_id: 2,
|
|
||||||
}
|
|
||||||
result = {
|
|
||||||
organization_id: 2,
|
|
||||||
}
|
|
||||||
item = difference(object1, object2)
|
|
||||||
deepEqual(item, result)
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('auth - not existing user', function(assert) {
|
test('auth - not existing user', function(assert) {
|
||||||
|
|
202
public/assets/tests/qunit/controller_observer.js
Normal file
202
public/assets/tests/qunit/controller_observer.js
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
QUnit.test( "controller observer tests - observe", assert => {
|
||||||
|
|
||||||
|
App.Ticket.refresh([{
|
||||||
|
id: 1,
|
||||||
|
title: 'ticket',
|
||||||
|
state_id: 1,
|
||||||
|
customer_id: 33,
|
||||||
|
organization_id: 1,
|
||||||
|
owner_id: 1,
|
||||||
|
preferences: { a: 1, b: 2 },
|
||||||
|
}])
|
||||||
|
|
||||||
|
var observer1 = new App.ControllerObserver({
|
||||||
|
object_id: 1,
|
||||||
|
template: 'version',
|
||||||
|
observe: {
|
||||||
|
title: true,
|
||||||
|
preferences: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
var ticket = App.Ticket.find(1)
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track title changes
|
||||||
|
ticket.title = 'title 2'
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.title = undefined
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.title = 'title 3'
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track no owner_id changes
|
||||||
|
ticket.owner_id = 2
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track preferences changes
|
||||||
|
ticket.preferences['a'] = 3
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.preferences['c'] = 3
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track no new_attribute1 changes
|
||||||
|
ticket.new_attribute1 = 'na 3'
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.new_attribute2 = function() { console.log(1) }
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.new_attribute2 = function() { console.log(2) }
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track title changes
|
||||||
|
ticket.title = function() { console.log(1) }
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.title = function() { console.log(2) }
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.title = 1
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test( "controller observer tests - observeNot", assert => {
|
||||||
|
|
||||||
|
App.Ticket.refresh([{
|
||||||
|
id: 2,
|
||||||
|
title: 'ticket',
|
||||||
|
state_id: 1,
|
||||||
|
customer_id: 33,
|
||||||
|
organization_id: 1,
|
||||||
|
owner_id: 1,
|
||||||
|
preferences: { a: 1, b: 2 },
|
||||||
|
}])
|
||||||
|
|
||||||
|
var observer1 = new App.ControllerObserver({
|
||||||
|
object_id: 2,
|
||||||
|
template: 'version',
|
||||||
|
observeNot: {
|
||||||
|
title: true,
|
||||||
|
preferences: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
var ticket = App.Ticket.find(2)
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track no title changes
|
||||||
|
ticket.title = 'title 2'
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track owner_id changes
|
||||||
|
ticket.owner_id = 2
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.owner_id = undefined
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.owner_id = 3
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track no preferences changes
|
||||||
|
ticket.preferences['a'] = 3
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.preferences['c'] = 3
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track preferences2 changes
|
||||||
|
ticket.preferences2 = {}
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.preferences2['a'] = 3
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.preferences2['a'] = 2
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.preferences2['c'] = 3
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track new_attribute1 changes
|
||||||
|
ticket.new_attribute1 = 'na 3'
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track no new_attribute2 changes (because of function content)
|
||||||
|
ticket.new_attribute2 = function() { console.log(1) }
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.new_attribute2 = function() { console.log(2) }
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
// track owner_id changes (pnly if content has no function content)
|
||||||
|
ticket.owner_id = function() { console.log(1) }
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.owner_id = function() { console.log(2) }
|
||||||
|
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
ticket.owner_id = 1
|
||||||
|
|
||||||
|
assert.equal(true, observer1.hasChanged(ticket))
|
||||||
|
assert.equal(false, observer1.hasChanged(ticket))
|
||||||
|
|
||||||
|
});
|
|
@ -588,8 +588,7 @@ RSpec.describe 'Ticket zoom', type: :system do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'certificate not present at time of arrival' do
|
context 'certificate not present at time of arrival' do
|
||||||
|
let(:mail) do
|
||||||
it 'retry' do
|
|
||||||
smime1 = create(:smime_certificate, :with_private, fixture: system_email_address)
|
smime1 = create(:smime_certificate, :with_private, fixture: system_email_address)
|
||||||
smime2 = create(:smime_certificate, :with_private, fixture: sender_email_address)
|
smime2 = create(:smime_certificate, :with_private, fixture: sender_email_address)
|
||||||
|
|
||||||
|
@ -612,6 +611,10 @@ RSpec.describe 'Ticket zoom', type: :system do
|
||||||
smime1.destroy
|
smime1.destroy
|
||||||
smime2.destroy
|
smime2.destroy
|
||||||
|
|
||||||
|
mail
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does retry successfully' do
|
||||||
parsed_mail = Channel::EmailParser.new.parse(mail.to_s)
|
parsed_mail = Channel::EmailParser.new.parse(mail.to_s)
|
||||||
ticket, article, _user, _mail = Channel::EmailParser.new.process({ group_id: group.id }, parsed_mail['raw'])
|
ticket, article, _user, _mail = Channel::EmailParser.new.process({ group_id: group.id }, parsed_mail['raw'])
|
||||||
expect(Ticket::Article.find(article.id).body).to eq('no visible content')
|
expect(Ticket::Article.find(article.id).body).to eq('no visible content')
|
||||||
|
@ -624,6 +627,17 @@ RSpec.describe 'Ticket zoom', type: :system do
|
||||||
click '.js-securityRetryProcess'
|
click '.js-securityRetryProcess'
|
||||||
expect(page).to have_css('.article-content', text: 'somebody with some text')
|
expect(page).to have_css('.article-content', text: 'somebody with some text')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does fail on retry (S/MIME function buttons no longer working in tickets #3957)' do
|
||||||
|
parsed_mail = Channel::EmailParser.new.parse(mail.to_s)
|
||||||
|
ticket, article, _user, _mail = Channel::EmailParser.new.process({ group_id: group.id }, parsed_mail['raw'])
|
||||||
|
expect(Ticket::Article.find(article.id).body).to eq('no visible content')
|
||||||
|
|
||||||
|
visit "#ticket/zoom/#{ticket.id}"
|
||||||
|
expect(page).to have_no_css('.article-content', text: 'somebody with some text')
|
||||||
|
click '.js-securityRetryProcess'
|
||||||
|
expect(page).to have_css('#notify', text: 'Decryption failed! Unable to find private key to decrypt')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,7 @@ class ClearbitTest < ActiveSupport::TestCase
|
||||||
organization6 = Organization.find_by('name LIKE ?', 'APIHub Inc%')
|
organization6 = Organization.find_by('name LIKE ?', 'APIHub Inc%')
|
||||||
assert(ExternalSync.find_by(source: 'clearbit', object: 'Organization', o_id: organization6.id))
|
assert(ExternalSync.find_by(source: 'clearbit', object: 'Organization', o_id: organization6.id))
|
||||||
assert_equal(false, organization6.shared)
|
assert_equal(false, organization6.shared)
|
||||||
assert_equal('The marketing data engine to deeply understand your customers, identify future prospects, & personalize every single marketing & sales interaction.', organization6.note)
|
assert_equal('The Clearbit Data Activation Platform helps B2B teams understand customers, identify prospects, & personalize interactions with real-time intelligence.', organization6.note)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue