Merge branch 'develop' into interface

Conflicts:
	app/assets/javascripts/app/controllers/_dashboard/ticket.js.coffee
	app/assets/javascripts/app/controllers/organization_zoom.js.coffee
	app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
	app/assets/javascripts/app/controllers/user_zoom.js.coffee
	app/assets/javascripts/app/views/organization_zoom.jst.eco
	app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco
	app/assets/javascripts/app/views/ticket_zoom/edit.jst.eco
This commit is contained in:
Martin Edenhofer 2014-08-18 15:05:20 +02:00
commit 0e7892b22f
63 changed files with 1097 additions and 533 deletions

View file

@ -14,7 +14,7 @@ gem 'json'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails'
gem 'sass-rails', github: 'rails/sass-rails'
gem 'coffee-rails'
gem 'uglifier'
end

View file

@ -223,11 +223,11 @@ class App.Controller extends Spine.Controller
placement: position
title: ->
ticket_id = $(@).data('id')
ticket = App.Ticket.retrieve( ticket_id )
ticket = App.Ticket.fullLocal( ticket_id )
App.i18n.escape( ticket.title )
content: ->
ticket_id = $(@).data('id')
ticket = App.Ticket.retrieve( ticket_id )
ticket = App.Ticket.fullLocal( ticket_id )
ticket.humanTime = ui.humanTime(ticket.created_at)
# insert data
App.view('popover/ticket')(
@ -257,11 +257,11 @@ class App.Controller extends Spine.Controller
placement: position
title: ->
user_id = $(@).data('id')
user = App.User.find( user_id )
user = App.User.fullLocal( user_id )
App.i18n.escape( user.displayName() )
content: ->
user_id = $(@).data('id')
user = App.User.find( user_id )
user = App.User.fullLocal( user_id )
# get display data
data = []
@ -303,11 +303,11 @@ class App.Controller extends Spine.Controller
placement: position
title: ->
organization_id = $(@).data('id')
organization = App.Organization.find( organization_id )
organization = App.Organization.fullLocal( organization_id )
App.i18n.escape( organization.name )
content: ->
organization_id = $(@).data('id')
organization = App.Organization.find( organization_id )
organization = App.Organization.fullLocal( organization_id )
# insert data
App.view('popover/organization')(
organization: organization,
@ -523,3 +523,31 @@ class App.SessionMessage extends App.ControllerModal
throw "Cant reload page!"
class App.UpdateHeader extends App.Controller
constructor: ->
super
# subscribe and reload data / fetch new data if triggered
@subscribeId = @genericObject.subscribe( @render )
release: =>
App[ @genericObject.constructor.className ].unsubscribe(@subscribeId)
render: (genericObject) =>
@el.find( '.page-header h1' ).html( genericObject.displayName() )
class App.UpdateTastbar extends App.Controller
constructor: ->
super
# subscribe and reload data / fetch new data if triggered
@subscribeId = @genericObject.subscribe( @update )
release: =>
App[ @genericObject.constructor.className ].unsubscribe(@subscribeId)
update: (genericObject) =>
# update taskbar with new meta data
App.Event.trigger 'task:render'

View file

@ -990,7 +990,7 @@ class App.ControllerForm extends App.Controller
remove: true
}
itemSub = @formGenItem( attribute_config )
itemSub.find('.icon-minus').bind('click', (e) ->
itemSub.find('.glyphicon-minus').bind('click', (e) ->
e.preventDefault()
$(@).parent().parent().parent().remove()
)
@ -1019,18 +1019,19 @@ class App.ControllerForm extends App.Controller
selected: false
disable: true
},
# {
# value: 'tickets.number'
# name: 'Number'
# selected: true
# disable: false
# },
# {
# value: 'tickets.title'
# name: 'Title'
# selected: true
# disable: false
# },
#
#{
# value: 'tickets.number'
# name: 'Number'
# selected: true
# disable: false
#},
#{
# value: 'tickets.title'
# name: 'Title'
# selected: true
# disable: false
#},
{
value: 'tickets.group_id'
name: 'Group'
@ -1055,68 +1056,55 @@ class App.ControllerForm extends App.Controller
selected: true
disable: false
},
{
value: 'tickets.customer_id'
name: 'Customer'
selected: true
disable: false
},
{
value: 'tickets.organization_id'
name: 'Organization'
selected: true
disable: false
},
{
value: 'tickets.created_at::<>'
name: 'Created (before/last)'
selected: true
disable: false
},
{
value: 'tickets.created_at::><'
name: 'Created (between)'
selected: true
disable: false
},
{
value: 'tickets.close_time::<>'
name: 'Closed (before/last)'
selected: true
disable: false
},
{
value: 'tickets.close_time::><'
name: 'Closed (between)'
selected: true
disable: false
},
{
value: 'tickets.updated_at::<>'
name: 'Updated (before/last)'
selected: true
disable: false
},
{
value: 'tickets.updated_at::><'
name: 'Updated (between)'
selected: true
disable: false
},
{
value: 'tickets.escalation_time::<>'
name: 'Escalation (before/last)'
selected: true
disable: false
},
{
value: 'tickets.escalation_time::><'
name: 'Escalation (between)'
selected: true
disable: false
},
# {
#{
# value: 'tickets.created_at::<>'
# name: 'Created (before/last)'
# selected: true
# disable: false
#},
#{
# value: 'tickets.created_at::><'
# name: 'Created (between)'
# selected: true
# disable: false
#},
#{
# value: 'tickets.close_time::<>'
# name: 'Closed (before/last)'
# selected: true
# disable: false
#},
#{
# value: 'tickets.close_time::><'
# name: 'Closed (between)'
# selected: true
# disable: false
#},
#{
# value: 'tickets.updated_at::<>'
# name: 'Updated (before/last)'
# selected: true
# disable: false
#},
#{
# value: 'tickets.updated_at::><'
# name: 'Updated (between)'
# selected: true
# disable: false
#},
#{
# value: 'tickets.escalation_time::<>'
# name: 'Escalation (before/last)'
# selected: true
# disable: false
#},
#{
# value: 'tickets.escalation_time::><'
# name: 'Escalation (between)'
# selected: true
# disable: false
#},
# # {
# value: 'tag'
# name: 'Tag'
# selected: true
@ -1194,24 +1182,24 @@ class App.ControllerForm extends App.Controller
# selected: true
# disable: false
# },
# {
# value: '-c'
# name: '-- ' + App.i18n.translateInline('Customer') + ' --'
# selected: false
# disable: true
# },
# {
# value: 'customers.id'
# name: 'Kunde'
# selected: true
# disable: false
# },
# {
# value: 'organization.id'
# name: 'Organization'
# selected: true
# disable: false
# },
{
value: '-c'
name: '-- ' + App.i18n.translateInline('Customer') + ' --'
selected: false
disable: true
},
{
value: 'customers.id'
name: 'Customer'
selected: true
disable: false
},
{
value: 'organization.id'
name: 'Organization'
selected: true
disable: false
},
]
default: ''
translate: true
@ -1220,7 +1208,7 @@ class App.ControllerForm extends App.Controller
}
list = @formGenItem( attribute_config )
list.find('.icon-plus').bind('click', (e) ->
list.find('.glyphicon-plus').bind('click', (e) ->
e.preventDefault()
value = $(e.target).parents().find('[name=ticket_attribute_list]').val()

View file

@ -37,7 +37,7 @@ class App.ControllerGenericNew extends App.ControllerModal
object.save(
done: ->
if ui.callback
item = App[ ui.genericObject ].retrieve(@id)
item = App[ ui.genericObject ].fullLocal(@id)
ui.callback( item )
ui.modalHide()
@ -84,7 +84,7 @@ class App.ControllerGenericEdit extends App.ControllerModal
@item.save(
done: ->
if ui.callback
item = App[ ui.genericObject ].retrieve(@id)
item = App[ ui.genericObject ].fullLocal(@id)
ui.callback( item )
ui.modalHide()
@ -269,14 +269,17 @@ class App.ControllerLevel2 extends App.ControllerContent
# window.scrollTo(0,0)
class App.ControllerTabs extends App.Controller
events:
'click .nav-tabs [data-toggle="tab"]': 'tabRemember',
constructor: ->
super
render: ->
@html App.view('generic/tabs')(
tabs: @tabs
)
@el.find('.nav-tabs li:first').addClass('active')
for tab in @tabs
@el.find('.tab-content').append('<div class="tab-pane" id="' + tab.target + '"></div>')
@ -285,7 +288,15 @@ class App.ControllerTabs extends App.Controller
params.el = @el.find( '#' + tab.target )
new tab.controller( params )
@el.find('.tab-content .tab-pane:first').addClass('active')
@lastActiveTab = @Config.get('lastTab')
if @lastActiveTab && @el.find('.nav-tabs li a[href="' + @lastActiveTab + '"]')[0]
@el.find('.nav-tabs li a[href="' + @lastActiveTab + '"]').tab('show')
else
@el.find('.nav-tabs li:first a').tab('show')
tabRemember: (e) =>
@lastActiveTab = $(e.target).attr('href')
@Config.set('lastTab', @lastActiveTab)
class App.ControllerNavSidbar extends App.ControllerContent
constructor: (params) ->

View file

@ -96,7 +96,7 @@ class App.DashboardTicket extends App.Controller
i = i + 1
openTicket = (id,e) =>
ticket = App.Ticket.retrieve(id)
ticket = App.Ticket.fullLocal(id)
@navigate ticket.uiUrl()
callbackTicketTitleAdd = (value, object, attribute, attributes, refObject) =>
attribute.title = object.title

View file

@ -45,7 +45,7 @@ class Index extends App.Controller
)
success: (data, status, xhr) =>
App.User.retrieve(
App.User.full(
App.Session.get( 'id' ),
=>
App.i18n.set( @locale )

View file

@ -370,7 +370,7 @@ class UserNew extends App.ControllerModal
# start customer info controller
ui.userInfo( user_id: user.id )
ui.modalHide()
App.User.retrieve( @id, callbackReload , true )
App.User.full( @id, callbackReload , true )
fail: ->
ui.modalHide()

View file

@ -29,7 +29,7 @@ class App.TicketMerge extends App.ControllerModal
list = []
for ticket_id in @ticket_ids_by_customer
if ticket_id isnt @ticket.id
ticketItem = App.Ticket.retrieve( ticket_id )
ticketItem = App.Ticket.fullLocal( ticket_id )
list.push ticketItem
new App.ControllerTable(
el: @el.find('#ticket-merge-customer-tickets'),
@ -42,7 +42,7 @@ class App.TicketMerge extends App.ControllerModal
list = []
for ticket_id in @ticket_ids_recent_viewed
if ticket_id isnt @ticket.id
ticketItem = App.Ticket.retrieve( ticket_id )
ticketItem = App.Ticket.fullLocal( ticket_id )
list.push ticketItem
new App.ControllerTable(
el: @el.find('#ticket-merge-recent-tickets'),
@ -59,7 +59,7 @@ class App.TicketMerge extends App.ControllerModal
@el.delegate('[name="radio"]', 'click', (e) ->
if $(e.target).prop('checked')
ticket_id = $(e.target).val()
ticket = App.Ticket.retrieve( ticket_id )
ticket = App.Ticket.fullLocal( ticket_id )
$(e.target).parents().find('[name="master_ticket_number"]').val( ticket.number )
)

View file

@ -7,21 +7,17 @@ class App.OrganizationZoom extends App.Controller
@navupdate '#'
start = (organization) =>
@organization = organization
@render()
App.Organization.retrieve( @organization_id, start, true )
App.Organization.full( @organization_id, @render )
meta: =>
meta =
url: @url()
id: @organization_id
if @organization
meta.head = @organization.displayName()
meta.title = @organization.displayName()
meta.iconClass = @organization.icon()
organization = App.Organization.find( @organization_id )
if organization
meta.head = organization.displayName()
meta.title = organization.displayName()
meta
url: =>
@ -36,27 +32,31 @@ class App.OrganizationZoom extends App.Controller
return false if !diff || _.isEmpty( diff )
return true
release: =>
# nothing
render: =>
# update taskbar with new meta data
App.Event.trigger 'task:render'
render: (organization) =>
@html App.view('organization_zoom')(
organization: @organization
organization: organization
)
new App.UpdateTastbar(
genericObject: organization
)
new App.UpdateHeader(
el: @el
genericObject: organization
)
# start action controller
new ActionRow(
el: @el.find('.action')
organization: @organization
organization: organization
ui: @
)
new Widgets(
el: @el.find('.widgets')
organization: @organization
organization: organization
ui: @
)

View file

@ -38,4 +38,4 @@ class App.TicketCustomer extends App.ControllerModal
)
# load user if not already exists
App.User.retrieve( @customer_id, callback )
App.User.full( @customer_id, callback )

View file

@ -109,7 +109,7 @@ class Table extends App.ControllerContent
@ticket_list_show = []
for ticket_id in @ticket_ids
@ticket_list_show.push App.Ticket.retrieve( ticket_id )
@ticket_list_show.push App.Ticket.fullLocal( ticket_id )
# remeber bulk attributes
@bulk = data.bulk
@ -185,7 +185,7 @@ class Table extends App.ControllerContent
@el.find('.table-overview').append(table)
else
openTicket = (id,e) =>
ticket = App.Ticket.retrieve(id)
ticket = App.Ticket.fullLocal(id)
@navigate ticket.uiUrl()
callbackTicketTitleAdd = (value, object, attribute, attributes, refObject) =>
attribute.title = object.title

View file

@ -44,7 +44,7 @@ class App.TicketZoom extends App.Controller
id: @ticket_id
iconClass: "priority"
if @ticket
@ticket = App.Ticket.retrieve( @ticket.id )
@ticket = App.Ticket.fullLocal( @ticket.id )
meta.head = @ticket.title
meta.title = '#' + @ticket.number + ' - ' + @ticket.title
meta.class = "level-#{@ticket.priority_id}"
@ -125,7 +125,7 @@ class App.TicketZoom extends App.Controller
App.Collection.loadAssets( data.assets )
# get data
@ticket = App.Ticket.retrieve( @ticket_id )
@ticket = App.Ticket.fullLocal( @ticket_id )
# render page
@render(force)
@ -224,7 +224,7 @@ class TicketTitle extends App.Controller
constructor: ->
super
@ticket = App.Ticket.retrieve( @ticket.id )
@ticket = App.Ticket.fullLocal( @ticket.id )
@subscribeId = @ticket.subscribe(@render)
@render(@ticket)
@ -334,7 +334,7 @@ class Edit extends App.Controller
render: ->
ticket = App.Ticket.retrieve( @ticket.id )
ticket = App.Ticket.fullLocal( @ticket.id )
@html App.view('ticket_zoom/edit')(
ticket: ticket
@ -451,7 +451,7 @@ class Edit extends App.Controller
@autosaveStop()
params = @formParam(e.target)
ticket = App.Ticket.retrieve( @ticket.id )
ticket = App.Ticket.fullLocal( @ticket.id )
@log 'notice', 'update', params, ticket
@ -581,7 +581,7 @@ class ArticleView extends App.Controller
# get all articles
@articles = []
for article_id in @ticket_article_ids
article = App.TicketArticle.retrieve( article_id )
article = App.TicketArticle.fullLocal( article_id )
@articles.push article
# rework articles

View file

@ -5,20 +5,20 @@ class App.UserZoom extends App.Controller
# check authentication
return if !@authenticate()
start = (user) =>
@user = user
@render()
App.User.retrieve( @user_id, start, true )
@navupdate '#'
App.User.full( @user_id, @render )
meta: =>
meta =
url: @url()
id: @user_id
if @user
meta.head = @user.displayName()
meta.title = @user.displayName()
user = App.User.find( @user_id )
if user
meta.head = user.displayName()
meta.title = user.displayName()
meta.iconClass = @user.icon()
meta
@ -34,31 +34,34 @@ class App.UserZoom extends App.Controller
return false if !diff || _.isEmpty( diff )
return true
release: =>
# nothing
render: =>
# update taskbar with new meta data
App.Event.trigger 'task:render'
render: (user) =>
@html App.view('user_zoom')(
user: @user
user: user
)
new App.UpdateTastbar(
genericObject: user
)
new App.UpdateHeader(
el: @el
genericObject: user
)
# start action controller
new ActionRow(
el: @el.find('.action')
user: @user
user: user
ui: @
)
new Widgets(
el: @el.find('.widgets')
user: @user
user: user
ui: @
)
class Widgets extends App.Controller
constructor: ->
super

View file

@ -40,7 +40,7 @@ class App.WidgetLink extends App.ControllerDrox
list[ item['link_type'] ] = []
if item['link_object'] is 'Ticket'
ticket = App.Ticket.retrieve( item['link_object_value'] )
ticket = App.Ticket.fullLocal( item['link_object_value'] )
if ticket.state.name is 'merged'
ticket.css = 'merged'
list[ item['link_type'] ].push ticket

View file

@ -6,16 +6,8 @@ class App.WidgetOrganization extends App.Controller
constructor: ->
super
# show organization
callback = (organization) =>
@render(organization)
if @callback
@callback(organization)
# subscribe and reload data / fetch new data if triggered
@subscribeId = organization.subscribe(@render)
App.Organization.retrieve( @organization_id, callback )
@subscribeId = App.Organization.full( @organization_id, @render, false, true )
release: =>
App.Organization.unsubscribe(@subscribeId)
@ -56,6 +48,9 @@ class App.WidgetOrganization extends App.Controller
)
@delay( a, 80 )
# enable user popups
@userPopups()
###
@userTicketPopups(
selector: '.user-tickets'
@ -65,7 +60,7 @@ class App.WidgetOrganization extends App.Controller
###
update: (e) =>
note = $(e.target).parent().find('[data-type=update]').val()
note = $(e.target).val()
organization = App.Organization.find( @organization_id )
if organization.note isnt note
organization.updateAttributes( note: note )

View file

@ -6,16 +6,8 @@ class App.WidgetUser extends App.ControllerDrox
constructor: ->
super
# show user
callback = (user) =>
@render(user)
if @callback
@callback(user)
# subscribe and reload data / fetch new data if triggered
@subscribeId = user.subscribe(@render)
App.User.retrieve( @user_id, callback )
@subscribeId = App.User.full( @user_id, @render, false, true )
release: =>
App.User.unsubscribe(@subscribeId)
@ -41,6 +33,34 @@ class App.WidgetUser extends App.ControllerDrox
if item.info
userData.push item
if user.preferences
items = []
if user.preferences.tickets_open > 0
item =
url: ''
name: 'open'
count: user.preferences.tickets_open
title: 'Open Tickets'
class: 'user-tickets'
data: 'open'
items.push item
if user.preferences.tickets_closed > 0
item =
url: ''
name: 'closed'
count: user.preferences.tickets_closed
title: 'Closed Tickets'
class: 'user-tickets'
data: 'closed'
items.push item
if items[0]
topic =
title: 'Tickets'
items: items
user['links'] = []
user['links'].push topic
# insert userData
@html @template(
file: 'widget/user'
@ -72,7 +92,7 @@ class App.WidgetUser extends App.ControllerDrox
)
update: (e) =>
note = $(e.target).parent().find('[data-type=update]').val()
note = $(e.target).val()
user = App.User.find( @user_id )
if user.note isnt note
user.updateAttributes( note: note )

View file

@ -75,21 +75,23 @@ class App.Auth
if type isnt 'check'
App.Event.trigger( 'clearStore' )
# set avatar
data.session.image = App.Config.get('api_path') + '/users/image/' + data.session.image
# update config
for key, value of data.config
App.Config.set( key, value )
# store user data
for key, value of data.session
App.Session.set( key, value )
# refresh default collections
if data.collections
App.Collection.resetCollections( data.collections )
# load assets
if data.assets
App.Collection.loadAssets( data.assets )
# store user data
session = App.User.fullLocal(data.session.id)
for key, value of session
App.Session.set( key, value )
# trigger auth ok with new session data
App.Event.trigger( 'auth', data.session )

View file

@ -68,7 +68,7 @@ class _collectionSingleton extends Spine.Module
reset: (params) ->
if !App[ params.type ]
@log 'error', 'reset', 'no such collection', params
@log 'error', 'reset', "no such collection #{params.type}", params
return
@log 'debug', 'reset', params

View file

@ -143,9 +143,12 @@ class _i18nSingleton extends Spine.Module
translate: ( string, args... ) =>
# return '' on undefined
if typeof string is 'boolean'
# type convertation
if typeof string isnt 'string'
if string && string.toString
string = string.toString()
# return '' on undefined
return '' if string is undefined
return '' if string is ''

View file

@ -126,6 +126,66 @@ class App.Model extends Spine.Model
return true if @id[0] isnt 'c'
return false
@fullLocal: (id) ->
@_fillUp( App[ @className ].find( id ) )
@full: (id, callback = false, force = false, bind = false) ->
url = "#{@url}/#{id}?full=true"
# subscribe and reload data / fetch new data if triggered
subscribeId = undefined
if bind
subscribeId = App[ @className ].subscribe_item(id, callback)
# execute if object already exists
if !force && App[ @className ].exists( id )
data = App[ @className ].find( id )
data = @_fillUp( data )
if callback
callback( data )
return subscribeId
# store callback and requested id
if !@FULL_CALLBACK
@FULL_CALLBACK = {}
if !@FULL_CALLBACK[id]
@FULL_CALLBACK[id] = {}
if callback
key = @className + '-' + Math.floor( Math.random() * 99999 )
@FULL_CALLBACK[id][key] = callback
if !@FULL_FETCH
@FULL_FETCH = {}
if !@FULL_FETCH[id]
@FULL_FETCH[id] = true
App.Ajax.request(
type: 'GET'
url: url
processData: true,
success: (data, status, xhr) =>
@FULL_FETCH[ data.id ] = false
# full / load assets
if data.assets
App.Collection.loadAssets( data.assets )
# find / load object
else
App[ @className ].refresh( data )
# execute callbacks
if @FULL_CALLBACK[ data.id ]
for key, callback of @FULL_CALLBACK[ data.id ]
callback( @_fillUp( App[ @className ].find( data.id ) ) )
delete @FULL_CALLBACK[ data.id ][ key ]
if _.isEmpty @FULL_CALLBACK[ data.id ]
delete @FULL_CALLBACK[ data.id ]
error: (xhr, statusText, error) =>
console.log(statusText, error)
)
subscribeId
@retrieve: ( id, callback, force ) ->
if !force && App[ @className ].exists( id )
data = App[ @className ].find( id )
@ -229,42 +289,56 @@ class App.Model extends Spine.Model
subscribe: (callback, type) ->
# remember record id and callback
App[ @constructor.className ].subscribe_item(@id, callback)
@subscribe_item: (id, callback) ->
# init bind
if !App[ @constructor.className ]['SUBSCRIPTION_ITEM']
App[ @constructor.className ]['SUBSCRIPTION_ITEM'] = {}
if !@_subscribe_item_bindDone
@_subscribe_item_bindDone = true
# subscribe and render data after local change
App[ @constructor.className ].bind(
@bind(
'refresh change'
(item) =>
#console.log('BIND', item)
for key, callback of App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ item.id ]
item = App[ @constructor.className ]._fillUp( item )
callback(item, 'local')
(items) =>
# check if result is array or singel item
if !_.isArray(items)
items = [items]
for item in items
for key, callback of App[ @className ].SUBSCRIPTION_ITEM[ item.id ]
item = App[ @className ]._fillUp( item )
callback(item)
)
# subscribe and render data after server change
events = "#{@constructor.className}:create #{@constructor.className}:update #{@constructor.className}:destroy"
events = "#{@className}:create #{@className}:update #{@className}:destroy"
App.Event.bind(
events
(item) =>
#console.log('SERVER BIND try', item)
if App[ @constructor.className ]['SUBSCRIPTION_ITEM'] && App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ item.id ]
#console.log('SERVER BIND', item)
for key, callback of App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ item.id ]
callbackRetrieve = (item) ->
callback(item, 'server')
App[ @constructor.className ].retrieve( item.id, callbackRetrieve, true )
'Item::Subscribe::' + @constructor.className
if @SUBSCRIPTION_ITEM && @SUBSCRIPTION_ITEM[ item.id ]
genericObject = undefined
if App[ @className ].exists( item.id )
genericObject = App[ @className ].find( item.id )
callback = =>
if !genericObject || ( new Date(item.updated_at).toString() isnt new Date(genericObject.updated_at).toString() )
@full( item.id, false, true )
App.Delay.set(callback, 800, item.id, "full-#{@className}")
'Item::Subscribe::' + @className
)
# remember record id and callback
if !App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ]
App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ] = {}
key = @constructor.className + '-' + Math.floor( Math.random() * 99999 )
App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ][key] = callback
# return key
# remember item callback
if !@SUBSCRIPTION_ITEM
@SUBSCRIPTION_ITEM = {}
if !@SUBSCRIPTION_ITEM[id]
@SUBSCRIPTION_ITEM[id] = {}
key = @className + '-' + Math.floor( Math.random() * 99999 )
@SUBSCRIPTION_ITEM[id][key] = callback
key
###
@ -275,15 +349,15 @@ class App.Model extends Spine.Model
###
@unsubscribe: (data) ->
@unsubscribe: (subscribeId) ->
if @SUBSCRIPTION_ITEM
for id, keys of @SUBSCRIPTION_ITEM
if keys[data]
delete keys[data]
if keys[subscribeId]
delete keys[subscribeId]
if @SUBSCRIPTION_COLLECTION
if @SUBSCRIPTION_COLLECTION[data]
delete @SUBSCRIPTION_COLLECTION[data]
if @SUBSCRIPTION_COLLECTION[subscribeId]
delete @SUBSCRIPTION_COLLECTION[subscribeId]
@_bindsEmpty: ->
if @SUBSCRIPTION_ITEM

View file

@ -23,10 +23,10 @@ class App.Organization extends App.Model
@_fillUp: (data) ->
# addd users of organization
if data['user_ids']
data['user_ids'] = []
for user_id in data['user_ids']
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 )
data['user_ids'].push user
data['members'].push user
data

View file

@ -47,10 +47,23 @@ class App.User extends App.Model
data['accounts'][account]['link'] = 'https://www.facebook.com/profile.php?id=' + data['accounts'][account]['uid']
# set image url
data.image = @apiPath + '/users/image/' + data.image
data.imageUrl = @apiPath + '/users/image/' + data.image
if data.organization_id
data.organization = App.Organization.find(data.organization_id)
data
if data['role_ids']
data['roles'] = []
for role_id in data['role_ids']
if App.Role.exists( role_id )
role = App.Role.find( role_id )
data['roles'].push role
if data['group_ids']
data['groups'] = []
for group_id in data['group_ids']
if App.Group.exists( group_id )
group = App.Group.find( group_id )
data['groups'].push group
data

View file

@ -11,7 +11,7 @@
<input type="checkbox" value="<%= ticket.id %>" name="bulk" class="pull-left"/>
</td>
<td class="span1">
<img class="thumbnail user-popover" data-id="<%= ticket.customer_id %>" src="<%= ticket.customer.image %>" alt="">
<img class="thumbnail user-popover" data-id="<%= ticket.customer_id %>" src="<%= ticket.customer.imageUrl %>" alt="">
</td>
<td class="span10">
<h3><a href="#" data-type="edit"><%= ticket.title %></a> <small><%= ticket.number %> <span class="humanTimeFromNow" data-time="<%= ticket.created_at %>">?</span></small></h3>

View file

@ -1,6 +1,6 @@
<ul class="nav nav-tabs">
<ul class="nav nav-tabs" role="tablist">
<% for tab in @tabs: %>
<li><a href="#<%= tab.target %>" data-toggle="tab"><%- @T( tab.name ) %></a></li>
<li><a href="#<%= tab.target %>" role="tab" data-toggle="tab"><%- @T( tab.name ) %></a></li>
<% end %>
</ul>
<div class="tab-content"></div>

View file

@ -1,9 +1,49 @@
<% for article in @articles: %>
<!--
<div class="ticket-article-item bubble-grid <%= article.sender.name.toLowerCase() %> <%= article.type.name %><%= ' internal' if article.internal is true %>" data-id="<%= article.id %>" id="article-<%= article.id %>">
<div class="horizontal<%= ' reverse' if article.sender.name is 'Customer' %>">
<div class="avatar" style="background-image: url(<%= article.created_by.image %>)"></div>
<div class="flex">
<div class="text-bubble"><div class="bubble-arrow"></div><%- article.html %></div>
-->
<div class="ticket-article ticket-article-item <% if article.internal is true: %> internal<% end %>" data-id="<%= article.id %>" id="article-<%= article.id %>">
<div class="avatar">
<img class="thumbnail user-popover" data-id="<%= article.created_by_id %>" src="<%= article.created_by.imageUrl %>" alt="">
<ul>
<li style="font-size: 10px;"><%- @T(article.type.name) %></li>
<% if article.type.name is 'email': %><li style="font-size: 10px;"><a href="<%= App.Config.get('api_path') %>/ticket_article_plain/<%= article.id %>"><%- @T( 'raw' ) %></a></li><% end %>
</ul>
</div>
<div class="ticket-article-message">
<div class="ticket-article-meta">
<% if article.from: %>
<strong title="<%- @Ti( 'From' ) %>: <%= article.from %>"><%= article.from %></strong>
<% if article.actions: %>
<% for action in article.actions: %>
-
<a href="<%= action.href %>" data-type="<%= action.type %>" class="<% if action.class: %><%= action.class %><% end %>"><%- @T( action.name ) %></a>
<% end %>
<% end %>
- <span class="humanTimeFromNow" data-time="<%- article.created_at %>">?</span>
<% end %>
<% if article.to: %>
<div title="<%- @Ti( 'To' ) %>: <%= article.to %>"><%= article.to %></div>
<% end %>
<% if article.cc: %>
<div title="<%- @Ti( 'Cc' ) %>: <%= article.cc %>"><%= article.cc %></div>
<% end %>
<% if article.subject: %>
<div title="<%- @Ti( 'Subject' ) %>: <%= article.subject %>"><%= article.subject %></div>
<% end %>
<% if article.attachments: %>
<div>
<% for attachment in article.attachments: %>
<a href="<%= App.Config.get('api_path') %>/ticket_attachment/<%= article.ticket_id %>/<%= article.id %>/<%= attachment.id %>" target="_blank" data-type="attachment" class="attachment" title="<%= attachment.size %>"><%= attachment.filename %></a>
<% end %>
</div>
<% end %>
</div>
<div style="white-space:pre-wrap;" class="message"><%- article.html %></div>
</div>
</div>
<time class="task-meta humanTimeFromNow" datetime="<%- article.created_at %>" data-time="<%- article.created_at %>">?</time>

View file

@ -1,8 +1,31 @@
<!--
<div class="horizontal bubble-grid new-article <% if @formChanged: %>form-changed<% end %>">
<div class="avatar user-popover" data-id="<%= @S('id') %>" style="background-image: url(<%- @S('image') %>)"></div>
<div class="flex text-bubble">
<div class="bubble-arrow"></div>
<textarea></textarea>
<span class="bubble-placeholder">Antwort eingeben oder <span class="highlight">Dateien wählen</span></span>
-->
<div class="ticket-article-view">
<div class="ticket-article well ticket-edit <% if @formChanged: %>form-changed<% end %>">
<div class="avatar">
<img class="thumbnail user-popover" data-id="<%= @S('id') %>" src="<%- @S('imageUrl') %>" alt="">
</div>
<div class="ticket-article-message">
<div class="edit-title">
<h4><%- @T('Edit') %>
<small class="reset-message<% if !@formChanged: %> hide<% end %>">
<a href="#" data-type="reset"><%- @T('Discard your unsaved changes.') %></a> <href="#" class="glyphicon glyphicon-repeat" data-type="reset"></a>
</small>
</h4>
</div>
<div class="edit-content">
<form class="form-stacked ticket-update">
<div class="form-ticket-update"></div>
<div class="form-article-update"></div>
<button type="submit" class="btn btn-primary submit"><%- @T( 'Submit' ) %></button>
</form>
<div>
</div>
</div>
</div>

View file

@ -5,7 +5,7 @@
<div class="main flex">
<div class="page-header">
<h1><%= @user.displayName() %></h1>
<h1></h1>
</div>
</div>

View file

@ -1 +0,0 @@
<div class="user_info"></div>

View file

@ -21,6 +21,15 @@
<% end %>
<% end %>
<% if @organization.members: %>
<h4><%- @T('Member') %></h4>
<ul>
<% for user in @organization.members: %>
<li><a href="<%- user.uiUrl() %>" class="user-popover" data-id="<%- user.id %>"><%= user.displayName() %></a></li>
<% end %>
</ul>
<% end %>
</div>
</div>

View file

@ -1,7 +1,7 @@
<div class="user-info">
<% if @user.image: %>
<img class="thumbnail" src="<%- @user.image %>" alt="">
<% if @user.imageUrl: %>
<img class="thumbnail" src="<%- @user.imageUrl %>" alt="">
<% end %>
<div class="customer-info" title="<%- @Ti( 'Name') %>"><%= @user.displayName() %></div>
<% for row in @userData: %>

View file

@ -6,7 +6,6 @@ class ApplicationController < ActionController::Base
helper_method :current_user,
:authentication_check,
:config_frontend,
:user_data_full,
:is_role,
:model_create_render,
:model_update_render,
@ -77,6 +76,7 @@ class ApplicationController < ActionController::Base
# update session updated_at
def session_update
#sleep 0.6
# on many paralell requests, session got reinitialised if Time. is used, as workaround use DateTime.
#session[:ping] = Time.now.utc.iso8601
@ -318,6 +318,13 @@ class ApplicationController < ActionController::Base
def model_show_render (object, params)
begin
if params[:full]
generic_object_full = object.full( params[:id] )
render :json => generic_object_full, :status => :ok
return
end
generic_object = object.find( params[:id] )
model_show_render_item(generic_object)
rescue Exception => e

View file

@ -59,7 +59,7 @@ class LongPollingController < ApplicationController
user_id = session[:user_id]
user = {}
if user_id
user = User.user_data_full( user_id )
user = User.find( user_id )
end
log 'notice', "send auth login (user_id #{user_id})", client_id
Sessions.create( client_id, user, { :type => 'ajax' } )

View file

@ -90,6 +90,11 @@ curl http://localhost/api/v1/organizations/#{id}.json -v -u #{login}:#{password}
return
end
end
if params[:full]
full = Organization.full( params[:id] )
render :json => full
return
end
model_show_render(Organization, params)
end

View file

@ -1,34 +1,37 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
module ExtraCollection
def session( collections, user )
def session( collections, assets, user )
# all base stuff
assets = {}
collections[ Taskbar.to_app_model ] = Taskbar.where( :user_id => user.id )
collections[ Role.to_app_model ] = Role.all
collections[ Group.to_app_model ] = Group.all
collections[ Taskbar.to_app_model ].each {|item|
assets = item.assets(assets)
}
collections[ Role.to_app_model ] = []
Role.all.each {|item|
assets = item.assets(assets)
}
collections[ Group.to_app_model ] = []
Group.all.each {|item|
assets = item.assets(assets)
}
if !user.is_role('Customer')
collections[ Organization.to_app_model ] = Organization.all
collections[ Organization.to_app_model ] = []
Organization.all.each {|item|
assets = item.assets(assets)
}
else
if user.organization_id
collections[ Organization.to_app_model ] = Organization.where( :id => user.organization_id )
collections[ Organization.to_app_model ] = []
Organization.where( :id => user.organization_id ).each {|item|
assets = item.assets(assets)
}
end
end
end
def push( collections, user )
# all base stuff
#collections[ Role.to_app_model ] = Role.all
#collections[ Group.to_app_model ] = Group.all
#if !user.is_role('Customer')
# collections[ Organization.to_app_model ] = Organization.all
#else
# if user.organization_id
# collections[ Organization.to_app_model ] = Organization.where( :id => user.organization_id )
# end
#end
end
module_function :session, :push
module_function :session
end

View file

@ -1,7 +1,7 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
module ExtraCollection
def session( collections, user )
def session( collections, assets, user )
collections[ Network.to_app_model ] = Network.all
collections[ Network::Category.to_app_model ] = Network::Category.all
@ -9,13 +9,6 @@ module ExtraCollection
collections[ Network::Privacy.to_app_model ] = Network::Privacy.all
end
def push( collections, user )
collections[ Network.to_app_model ] = Network.all
collections[ Network::Category.to_app_model ] = Network::Category.all
collections[ Network::Category::Type.to_app_model ] = Network::Category::Type.all
collections[ Network::Privacy.to_app_model ] = Network::Privacy.all
end
module_function :session, :push
module_function :session
end

View file

@ -1,42 +1,43 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
module ExtraCollection
def session( collections, user )
def session( collections, assets, user )
# all ticket stuff
collections[ Ticket::StateType.to_app_model ] = Ticket::StateType.all
collections[ Ticket::State.to_app_model ] = Ticket::State.all
collections[ Ticket::Priority.to_app_model ] = Ticket::Priority.all
collections[ Ticket::Article::Type.to_app_model ] = Ticket::Article::Type.all
collections[ Ticket::Article::Sender.to_app_model ] = Ticket::Article::Sender.all
collections[ Ticket::StateType.to_app_model ] = []
Ticket::StateType.all.each {|item|
assets = item.assets(assets)
}
collections[ Ticket::State.to_app_model ] = []
Ticket::State.all.each {|item|
assets = item.assets(assets)
}
collections[ Ticket::Priority.to_app_model ] = []
Ticket::Priority.all.each {|item|
assets = item.assets(assets)
}
collections[ Ticket::Article::Type.to_app_model ] = []
Ticket::Article::Type.all.each {|item|
assets = item.assets(assets)
}
collections[ Ticket::Article::Sender.to_app_model ] = []
Ticket::Article::Sender.all.each {|item|
assets = item.assets(assets)
}
if !user.is_role('Customer')
# all signatures
collections[ Signature.to_app_model ] = Signature.all
collections[ Signature.to_app_model ] = []
Signature.all.each {|item|
assets = item.assets(assets)
}
# all email addresses
collections[ EmailAddress.to_app_model ] = EmailAddress.all
collections[ EmailAddress.to_app_model ] = []
EmailAddress.all.each {|item|
assets = item.assets(assets)
}
end
end
def push( collections, user )
# all ticket stuff
#collections[ Ticket::StateType.to_app_model ] = Ticket::StateType.all
#collections[ Ticket::State.to_app_model ] = Ticket::State.all
#collections[ Ticket::Priority.to_app_model ] = Ticket::Priority.all
#collections[ Ticket::Article::Type.to_app_model ] = Ticket::Article::Type.all
#collections[ Ticket::Article::Sender.to_app_model ] = Ticket::Article::Sender.all
#if !user.is_role('Customer')
# all signatures
# collections[ Signature.to_app_model ] = Signature.all
# all email addresses
# collections[ EmailAddress.to_app_model ] = EmailAddress.all
#end
end
module_function :session, :push
module_function :session
end

View file

@ -31,10 +31,9 @@ class SessionsController < ApplicationController
user.activity_stream_log( 'session started', user.id, true )
# auto population of default collections
collections = SessionHelper::default_collections(user)
collections, assets = SessionHelper::default_collections(user)
# set session user_id
user = User.find_fulldata(user.id)
assets = user.assets(assets)
# check logon session
logon_session_key = nil
@ -52,6 +51,7 @@ class SessionsController < ApplicationController
render :json => {
:session => user,
:collections => collections,
:assets => assets,
:logon_session => logon_session_key,
},
:status => :created
@ -84,15 +84,18 @@ class SessionsController < ApplicationController
# Save the user ID in the session so it can be used in
# subsequent requests
user = User.user_data_full( user_id )
user = User.find( user_id )
# auto population of default collections
collections = SessionHelper::default_collections( User.find(user_id) )
collections, assets = SessionHelper::default_collections(user)
assets = user.assets(assets)
# return current session
render :json => {
:session => user,
:collections => collections,
:assets => assets,
:config => config_frontend,
}
end

View file

@ -49,7 +49,7 @@ class TicketOverviewsController < ApplicationController
end
# get related users
assets = { User.to_app_model => {} }
assets = {}
overview[:ticket_ids].each {|ticket_id|
ticket = Ticket.lookup( :id => ticket_id )
assets = ticket.assets(assets)
@ -70,9 +70,7 @@ class TicketOverviewsController < ApplicationController
Group.find(group_id).users.each {|user|
next if !agents[ user.id ]
groups_users[ group_id ].push user.id
if !assets[ User.to_app_model ][user.id]
assets[ User.to_app_model ][user.id] = User.user_data_full(user.id)
end
assets = user.assets( assets )
}
}

View file

@ -262,23 +262,20 @@ class TicketsController < ApplicationController
# get related users
assets = {}
assets[ User.to_app_model ] = {}
assets = ticket.assets(assets)
# get attributes to update
attributes_to_change = Ticket::ScreenOptions.attributes_to_change( :user => current_user, :ticket => ticket )
attributes_to_change[:owner_id].each { |user_id|
if !assets[ User.to_app_model ][user_id]
assets[ User.to_app_model ][user_id] = User.user_data_full( user_id )
end
user = User.find(user_id)
assets = user.assets(assets)
}
attributes_to_change[:group_id__owner_id].each {|group_id, user_ids|
user_ids.each {|user_id|
if !assets[ User.to_app_model ][user_id]
assets[ User.to_app_model ][user_id] = User.user_data_full( user_id )
end
user = User.find(user_id)
assets = user.assets(assets)
}
}
@ -322,16 +319,14 @@ class TicketsController < ApplicationController
assets = {}
assets[ User.to_app_model ] = {}
attributes_to_change[:owner_id].each { |user_id|
if !assets[ User.to_app_model ][user_id]
assets[ User.to_app_model ][user_id] = User.user_data_full( user_id )
end
user = User.find(user_id)
assets = user.assets(assets)
}
attributes_to_change[:group_id__owner_id].each {|group_id, user_ids|
user_ids.each {|user_id|
if !assets[ User.to_app_model ][user_id]
assets[ User.to_app_model ][user_id] = User.user_data_full( user_id )
end
user = User.find(user_id)
assets = user.assets(assets)
}
}
@ -345,9 +340,7 @@ class TicketsController < ApplicationController
owner_ids = []
ticket.agent_of_group.each { |user|
owner_ids.push user.id
if !assets[ User.to_app_model ][user.id]
assets[ User.to_app_model ][user.id] = User.user_data_full( user.id )
end
assets = user.assets(assets)
}
# get related articles

View file

@ -70,7 +70,7 @@ curl http://localhost/api/v1/users.json -v -u #{login}:#{password}
end
users_all = []
users.each {|user|
users_all.push User.user_data_full( user.id )
users_all.push User.lookup( :id => user.id ).attributes_with_associations
}
render :json => users_all, :status => :ok
end
@ -101,7 +101,14 @@ curl http://localhost/api/v1/users/#{id}.json -v -u #{login}:#{password}
return
end
end
user = User.user_data_full( params[:id] )
if params[:full]
full = User.full( params[:id] )
render :json => full
return
end
user = User.find( params[:id] )
render :json => user
end
@ -245,7 +252,7 @@ curl http://localhost/api/v1/users.json -v -u #{login}:#{password} -H "Content-T
)
end
user_new = User.user_data_full( user.id )
user_new = User.find( user.id )
render :json => user_new, :status => :created
rescue Exception => e
render :json => { :error => e.message }, :status => :unprocessable_entity
@ -309,7 +316,7 @@ curl http://localhost/api/v1/users/2.json -v -u #{login}:#{password} -H "Content
end
# get new data
user_new = User.user_data_full( params[:id] )
user_new = User.find( params[:id] )
render :json => user_new, :status => :ok
rescue Exception => e
render :json => { :error => e.message }, :status => :unprocessable_entity

View file

@ -883,4 +883,25 @@ destory object dependencies, will be executed automatically
def destroy_dependencies
end
=begin
return object and assets
data = Model.full(123)
data = {
:id => 123,
:assets => assets,
}
=end
def self.full(id)
object = self.find(id)
assets = object.assets({})
{
:id => id,
:assets => assets,
}
end
end

View file

@ -12,7 +12,7 @@ get all assets / related models for this user
returns
result = {
:users => {
:User => {
123 => user_model_123,
1234 => user_model_1234,
}
@ -26,23 +26,18 @@ returns
data[ self.class.to_app_model ] = {}
end
if !data[ self.class.to_app_model ][ self.id ]
data[ self.class.to_app_model ][ self.id ] = self.attributes
data[ self.class.to_app_model ][ self.id ] = self.attributes_with_associations
end
return data if !self['created_by_id'] && !self['updated_by_id']
if !data[ User.to_app_model ]
data[ User.to_app_model ] = {}
end
if self['created_by_id']
if !data[ User.to_app_model ][ self['created_by_id'] ]
data[ User.to_app_model ][ self['created_by_id'] ] = User.user_data_full( self['created_by_id'] )
end
end
if self['updated_by_id']
if !data[ User.to_app_model ][ self['updated_by_id'] ]
data[ User.to_app_model ][ self['updated_by_id'] ] = User.user_data_full( self['updated_by_id'] )
['created_by_id', 'updated_by_id'].each {|item|
if self[ item ]
if !data[ User.to_app_model ] || !data[ User.to_app_model ][ self[ item ] ]
user = User.lookup( :id => self[ item ] )
data = user.assets( data )
end
end
}
data
end

View file

@ -122,6 +122,10 @@ class Channel::EmailParser
data[:body] = mail.text_part.body.decoded
data[:body] = Encode.conv( mail.text_part.charset, data[:body] )
if !data[:body].valid_encoding?
data[:body] = data[:body].encode('utf-8', 'binary', :invalid => :replace, :undef => :replace, :replace => '?')
end
# html attachment/body may exists and will be converted to text
else
filename = '-no name-'
@ -129,7 +133,11 @@ class Channel::EmailParser
filename = 'html-email'
data[:body] = mail.html_part.body.to_s
data[:body] = Encode.conv( mail.html_part.charset.to_s, data[:body] )
data[:body] = html2ascii( data[:body] )
data[:body] = html2ascii( data[:body] ).to_s.force_encoding('utf-8')
if !data[:body].valid_encoding?
data[:body] = data[:body].encode('utf-8', 'binary', :invalid => :replace, :undef => :replace, :replace => '?')
end
# any other attachments
else
@ -188,6 +196,10 @@ class Channel::EmailParser
data[:body] = mail.body.decoded
data[:body] = Encode.conv( mail.charset, data[:body] )
if !data[:body].valid_encoding?
data[:body] = data[:body].encode('utf-8', 'binary', :invalid => :replace, :undef => :replace, :replace => '?')
end
# html part
else
filename = '-no name-'
@ -195,7 +207,11 @@ class Channel::EmailParser
filename = 'html-email'
data[:body] = mail.body.decoded
data[:body] = Encode.conv( mail.charset, data[:body] )
data[:body] = html2ascii( data[:body] )
data[:body] = html2ascii( data[:body] ).to_s.force_encoding('utf-8')
if !data[:body].valid_encoding?
data[:body] = data[:body].encode('utf-8', 'binary', :invalid => :replace, :undef => :replace, :replace => '?')
end
# any other attachments
else

View file

@ -22,11 +22,9 @@ returns
def assets (data)
if !data[ User.to_app_model ]
data[ User.to_app_model ] = {}
end
if !data[ User.to_app_model ][ self['created_by_id'] ]
data[ User.to_app_model ][ self['created_by_id'] ] = User.user_data_full( self['created_by_id'] )
if !data[ User.to_app_model ] || !data[ User.to_app_model ][ self['created_by_id'] ]
user = User.lookup( :id => self['created_by_id'] )
data = user.assets( data )
end
data

View file

@ -1,6 +1,7 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Organization < ApplicationModel
require 'organization/assets'
include Organization::Assets
extend Organization::Search
include Organization::SearchIndex
@ -9,6 +10,9 @@ class Organization < ApplicationModel
has_many :members, :class_name => 'User'
validates :name, :presence => true
after_create :notify_clients_after_create
after_update :notify_clients_after_update
activity_stream_support :role => 'Admin'
history_support
search_index_support

View file

@ -29,14 +29,24 @@ returns
data[ User.to_app_model ] = {}
end
if !data[ Organization.to_app_model ][ self.id ]
data[ Organization.to_app_model ][ self.id ] = self.attributes
data[ Organization.to_app_model ][ self.id ][:user_ids] = []
users = User.where( :organization_id => self.id ).limit(10)
users.each {|user|
data[ User.to_app_model ][ user.id ] = User.user_data_full( user.id )
data[ Organization.to_app_model ][ self.id ][:user_ids].push user.id
data[ Organization.to_app_model ][ self.id ] = self.attributes_with_associations
if data[ Organization.to_app_model ][ self.id ]['member_ids']
data[ Organization.to_app_model ][ self.id ]['member_ids'].each {|user_id|
if !data[ User.to_app_model ][ user_id ]
user = User.lookup( :id => user_id )
data = user.assets( data )
end
}
end
end
['created_by_id', 'updated_by_id'].each {|item|
if self[ item ]
if !data[ User.to_app_model ][ self[ item ] ]
user = User.lookup( :id => self[ item ] )
data = user.assets( data )
end
end
}
data
end

View file

@ -317,7 +317,7 @@ class Package < ApplicationModel
rescue => e
puts "TRIED TO RELOAD '#{entry}'"
puts 'ERROR: ' + e.inspect
puts 'Traceback: ' + e.backtrace
puts 'Traceback: ' + e.backtrace.inspect
end
end
}

View file

@ -41,15 +41,14 @@ returns
data[ Ticket::Article.to_app_model ][ self.id ]['attachments'] = self.attachments
end
if !data[ User.to_app_model ]
data[ User.to_app_model ] = {}
['created_by_id', 'updated_by_id'].each {|item|
if self[ item ]
if !data[ User.to_app_model ] || !data[ User.to_app_model ][ self[ item ] ]
user = User.lookup( :id => self[ item ] )
data = user.assets( data )
end
if !data[ User.to_app_model ][ self['created_by_id'] ]
data[ User.to_app_model ][ self['created_by_id'] ] = User.user_data_full( self['created_by_id'] )
end
if !data[ User.to_app_model ][ self['updated_by_id'] ]
data[ User.to_app_model ][ self['updated_by_id'] ] = User.user_data_full( self['updated_by_id'] )
end
}
data
end

View file

@ -27,24 +27,16 @@ returns
data[ Ticket.to_app_model ] = {}
end
if !data[ Ticket.to_app_model ][ self.id ]
data[ Ticket.to_app_model ][ self.id ] = self.attributes
data[ Ticket.to_app_model ][ self.id ] = self.attributes_with_associations
end
if !data[ User.to_app_model ]
data[ User.to_app_model ] = {}
['created_by_id', 'updated_by_id', 'owner_id', 'customer_id'].each {|item|
if self[ item ]
if !data[ User.to_app_model ] || !data[ User.to_app_model ][ self[ item ] ]
user = User.lookup( :id => self[ item ] )
data = user.assets( data )
end
if !data[ User.to_app_model ][ self.owner_id ]
data[ User.to_app_model ][ self.owner_id ] = User.user_data_full( self.owner_id )
end
if !data[ User.to_app_model ][ self.customer_id ]
data[ User.to_app_model ][ self.customer_id ] = User.user_data_full( self.customer_id )
end
if !data[ User.to_app_model ][ self.created_by_id ]
data[ User.to_app_model ][ self.created_by_id ] = User.user_data_full( self.created_by_id )
end
if !data[ User.to_app_model ][ self.updated_by_id ]
data[ User.to_app_model ][ self.updated_by_id ] = User.user_data_full( self.updated_by_id )
end
}
data
end

View file

@ -3,6 +3,7 @@
require 'digest/md5'
class User < ApplicationModel
require 'user/assets'
include User::Assets
extend User::Search
include User::SearchIndex
@ -360,108 +361,6 @@ returns
return user
end
def self.find_fulldata(user_id)
cache = self.cache_get(user_id, true)
return cache if cache
# get user
user = User.find(user_id)
data = user.attributes
# do not show password
user['password'] = ''
# get linked accounts
data['accounts'] = {}
authorizations = user.authorizations() || []
authorizations.each do | authorization |
data['accounts'][authorization.provider] = {
:uid => authorization[:uid],
:username => authorization[:username]
}
end
# set roles
roles = []
user.roles.select('id, name').where( :active => true ).each { |role|
roles.push role.attributes
}
data['roles'] = roles
data['role_ids'] = user.role_ids
groups = []
user.groups.select('id, name').where( :active => true ).each { |group|
groups.push group.attributes
}
data['groups'] = groups
data['group_ids'] = user.group_ids
organization = user.organization
if organization
data['organization'] = organization.attributes
end
organizations = []
user.organizations.select('id, name').where( :active => true ).each { |organization|
organizations.push organization.attributes
}
data['organizations'] = organizations
data['organization_ids'] = user.organization_ids
self.cache_set(user.id, data, true)
return data
end
def self.user_data_full (user_id)
# get user
user = User.find_fulldata(user_id)
# do not show password
user['password'] = ''
# TEMP: compat. reasons
user['preferences'] = {} if user['preferences'] == nil
items = []
if user['preferences'][:tickets_open].to_i > 0
item = {
:url => '',
:name => 'open',
:count => user['preferences'][:tickets_open] || 0,
:title => 'Open Tickets',
:class => 'user-tickets',
:data => 'open'
}
items.push item
end
if user['preferences'][:tickets_closed].to_i > 0
item = {
:url => '',
:name => 'closed',
:count => user['preferences'][:tickets_closed] || 0,
:title => 'Closed Tickets',
:class => 'user-tickets',
:data => 'closed'
}
items.push item
end
# show linked topics and items
if items.count > 0
topic = {
:title => 'Tickets',
:items => items,
}
user['links'] = []
user['links'].push topic
end
return user
end
=begin
update last login date and reset login_failed (is automatically done by auth and sso backend)

View file

@ -12,7 +12,7 @@ get all assets / related models for this user
returns
result = {
:users => {
:User => {
123 => user_model_123,
1234 => user_model_1234,
}
@ -26,22 +26,61 @@ returns
data[ User.to_app_model ] = {}
end
if !data[ User.to_app_model ][ self.id ]
data[ User.to_app_model ][ self.id ] = User.user_data_full( self.id )
attributes = self.attributes_with_associations
# do not transfer crypted pw
attributes['password'] = ''
# get linked accounts
attributes['accounts'] = {}
authorizations = self.authorizations()
authorizations.each do | authorization |
attributes['accounts'][authorization.provider] = {
:uid => authorization[:uid],
:username => authorization[:username]
}
end
data[ User.to_app_model ][ self.id ] = attributes
# get roles
if attributes['role_ids']
attributes['role_ids'].each {|role_id|
role = Role.lookup( :id => role_id )
data = role.assets( data )
}
end
# get groups
if attributes['group_ids']
attributes['group_ids'].each {|group_id|
group = Group.lookup( :id => group_id )
data = group.assets( data )
}
end
# get groups
if attributes['organization_ids']
attributes['organization_ids'].each {|organization_id|
organization = Organization.lookup( :id => organization_id )
data = organization.assets( data )
}
end
end
if self.organization_id
if !data[ Organization.to_app_model ]
data[ Organization.to_app_model ] = {}
end
if !data[ Organization.to_app_model ][ self.organization_id ]
data[ Organization.to_app_model ][ self.organization_id ] = Organization.find( self.organization_id )
if !data[ Organization.to_app_model ] || !data[ Organization.to_app_model ][ self.organization_id ]
organization = Organization.lookup( :id => self.organization_id )
data = organization.assets( data )
end
end
if !data[ User.to_app_model ][ self.created_by_id ]
data[ User.to_app_model ][ self.created_by_id ] = User.user_data_full( self.created_by_id )
['created_by_id', 'updated_by_id'].each {|item|
if self[ item ]
if !data[ User.to_app_model ][ self[ item ] ]
user = User.lookup( :id => self[ item ] )
data = user.assets( data )
end
if !data[ User.to_app_model ][ self.updated_by_id ]
data[ User.to_app_model ][ self.updated_by_id ] = User.user_data_full( self.updated_by_id )
end
}
data
end

View file

@ -16,6 +16,9 @@ module Cache
end
def self.clear
# puts 'Cache.clear...'
# workaround, set test cache before clear whole cache, Rails.cache.clear complains about not existing cache dir
Cache.write('test',1 )
Rails.cache.clear
end
end

View file

@ -3,31 +3,17 @@ module SessionHelper
# auto population collections, store all here
default_collection = {}
assets = {}
# load collections to deliver from external files
dir = File.expand_path('../../', __FILE__)
files = Dir.glob( "#{dir}/app/controllers/sessions/collection_*.rb" )
for file in files
load file
ExtraCollection.session( default_collection, user )
ExtraCollection.session( default_collection, assets, user )
end
return default_collection
end
def self.push_collections(user)
# auto population collections, store all here
push_collections = {}
# load collections to deliver from external files
dir = File.expand_path('../../', __FILE__)
files = Dir.glob( "#{dir}/app/controllers/sessions/collection_*.rb" )
for file in files
load file
ExtraCollection.push( push_collections, user )
end
return push_collections
return default_collection, assets
end
def self.cleanup_expired

View file

@ -45,7 +45,7 @@ class Sessions::Backend::TicketCreate
users = {}
create_attributes[:owner_id].each {|user_id|
if !users[user_id]
users[user_id] = User.user_data_full(user_id)
users[user_id] = User.find(user_id).attributes
end
}
data = {

15
script/install.sh Normal file
View file

@ -0,0 +1,15 @@
#!/bin/bash
get_distro(){
arch=$(uname -m)
kernel=$(uname -r)
if [ -f /etc/lsb-release ]; then
os=$(lsb_release -s -d)
elif [ -f /etc/debian_version ]; then
os="Debian $(cat /etc/debian_version)"
elif [ -f /etc/redhat-release ]; then
os=`cat /etc/redhat-release`
else
os="$(uname -s) $(uname -r)"
fi
}

View file

@ -76,11 +76,11 @@ class ManageTest < TestCase
},
{
:execute => 'click',
:css => 'a[data-type="edit"]:last-child',
:css => '.table-overview tr:last-child td',
},
{
:execute => 'wait',
:value => 1,
:value => 2,
},
{
:execute => 'set',
@ -146,7 +146,7 @@ class ManageTest < TestCase
},
{
:execute => 'click',
:css => 'a[data-type="edit"]:last-child',
:css => '.table-overview tr:last-child td',
},
{
:execute => 'wait',

69
test/fixtures/mail22.box vendored Normal file
View file

@ -0,0 +1,69 @@
From ireoniqla@lipetsk.ru Tue Aug 5 22:38:39 2014
Return-Path: <ireoniqla@lipetsk.ru>
X-Original-To: info@znuny.nix
Delivered-To: znuny-sales@arber.znuny.nix
Received: from X53.bbn2-087.lipetsk.ru (unknown [95.179.87.53])
by arber.znuny.nix (Postfix) with ESMTP id A732E60260
for <info@znuny.nix>; Tue, 5 Aug 2014 22:38:38 +0200 (CEST)
Message-ID: <B10BB43C.6669005@lipetsk.ru>
Date: Wed, 6 Aug 2014 00:38:37 +0400
From: Gilbertina Suthar <ireoniqla@lipetsk.ru>
MIME-Version: 1.0
To: Info <info@znuny.nix>
Subject: P..E..N-I..S__-E N L A R-G E-M..E..N T-___P..I-L-L..S...Info.
Content-Type: text/html; charset=us-ascii; format=flowed
Content-Transfer-Encoding: quoted-printable
X-UID: 1429
Status: RO
Content-Length: 3051
Lines: 48
<html><body><span style=3D"color:#FBFAF8; font-size:4px">Puzzled by =
judith bronte dave. Melvin will want her way through with.<br>Continued =
adam helped charlie cried. Soon joined the master bathroom. Grinned adam =
rubbed his arms she nodded.<br>Freemont and they talked with =
beppe.<br>Thinking of bed and whenever adam.<br>Mike was too tired man =
to hear.</span><div style=3D"color:#F2E9B5; font-family:verdana, =
sans-serif, new york; font-size:2px">I&#176;0<span =
style=3D"color:#17133C; font-size:28pt">P</span>QSH<span =
style=3D"color:#17133C; font-size:28pt">E</span>Jl&#212;<span =
style=3D"color:#17133C; font-size:28pt">N</span>wf&tilde;<span =
style=3D"color:#17133C; font-size:28pt">&#204;</span>1&#167;3<span =
style=3D"color:#17133C; font-size:28pt">S</span>&#172;73<span =
style=3D"color:#17133C; font-size:28pt"> </span>&Icirc;1m<span =
style=3D"color:#17133C; font-size:28pt">E</span>bb5<span =
style=3D"color:#17133C; font-size:28pt">N</span>37&#162;<span =
style=3D"color:#17133C; font-size:28pt">L</span>&piv;C7<span =
style=3D"color:#17133C; font-size:28pt">A</span>lFn<span =
style=3D"color:#17133C; font-size:28pt">R</span>&#186;&diams;H<span =
style=3D"color:#17133C; font-size:28pt">G</span>64B<span =
style=3D"color:#17133C; =
font-size:28pt">&#201;</span>4&#210;&brvbar;<span =
style=3D"color:#17133C; font-size:28pt">M</span>&#229;&#226;4<span =
style=3D"color:#17133C; font-size:28pt">&#202;</span>zk&Iota;<span =
style=3D"color:#17133C; font-size:28pt">N</span>&rceil;7&rceil;<span =
style=3D"color:#17133C; font-size:28pt">T</span>BN&ETH;<span =
style=3D"color:#17133C; font-size:28pt"> </span>T&#215;x<span =
style=3D"color:#17133C; font-size:28pt">P</span>I&ograve;g<span =
style=3D"color:#17133C; font-size:28pt">I</span>&#206;&Atilde;l<span =
style=3D"color:#17133C; font-size:28pt">L</span>&#248;&Otilde;M<span =
style=3D"color:#17133C; font-size:28pt">L</span>&perp;&#222;&#248;<span =
style=3D"color:#17133C; font-size:28pt">S</span>a&Psi;RBreathed adam =
gave the master bedroom door.<br>Better get charlie took the =
wall.<br>Charlotte clark smile he saw charlie.<br>Dave and leaned her =
tears adam.</div><span style=3D"color:#F7F3FF; font-size:8px">Maybe we =
want any help me that.<br>Next morning charlie gazed at their =
father.<br>Well as though adam took out here. Melvin will be more money. =
Called him into this one last night.<br>Men joined the pickup truck =
pulled away. Chuck could make sure that.</span><span =
style=3D"color:#FCECBF; font-size:23px"><a =
href=3D"&#104;&#116;&#116;&#112;&#58;&#47;&#47;&#1072;&#1086;&#1089;&#108=
2;&#46;&#1088;&#1092;?jmlfwnwe&ucwkiyyc"><b><span =
style=3D"color:#F5E1B9; =
font-size:3px">&dagger;p&#173;</span>C&#XA0;L&thinsp;I&#xA0;C&ensp;K&#Xa0=
;&#8194;&#8201;&ensp;&#542; E R&#xA0;E<span style=3D"color:#F1E9BA; =
font-size:5px">EOD</span> !</b></a></span><span =
style=3D"color:#F6F9FC">Chuckled adam leaned forward and le=EE =
charlie.<br>Just then returned to believe it here.<br>Freemont and =
pulling out several minutes.</span></body></html>

View file

@ -1,6 +1,7 @@
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'cache'
require 'simplecov'
require 'simplecov-rcov'
@ -16,6 +17,9 @@ class ActiveSupport::TestCase
# disable transactions
self.use_transactional_fixtures = false
# clear cache
Cache.clear
# load seeds
load "#{Rails.root}/db/seeds.rb"

81
test/unit/assets_test.rb Normal file
View file

@ -0,0 +1,81 @@
# encoding: utf-8
require 'test_helper'
class AssetsTest < ActiveSupport::TestCase
test 'user' do
roles = Role.where( :name => [ 'Agent', 'Admin'] )
groups = Group.all
org = Organization.create_or_update(
:name => 'some org',
:updated_by_id => 1,
:created_by_id => 1,
)
user1 = User.create_or_update(
:login => 'assets1@example.org',
:firstname => 'assets1',
:lastname => 'assets1',
:email => 'assets1@example.org',
:password => 'some_pass',
:active => true,
:updated_by_id => 1,
:created_by_id => 1,
:organization_id => org.id,
:roles => roles,
:groups => groups,
)
user1.save
user2 = User.create_or_update(
:login => 'assets2@example.org',
:firstname => 'assets2',
:lastname => 'assets2',
:email => 'assets2@example.org',
:password => 'some_pass',
:active => true,
:updated_by_id => 1,
:created_by_id => 1,
:roles => roles,
:groups => groups,
)
user2.save
user3 = User.create_or_update(
:login => 'assets3@example.org',
:firstname => 'assets3',
:lastname => 'assets3',
:email => 'assets3@example.org',
:password => 'some_pass',
:active => true,
:updated_by_id => user1.id,
:created_by_id => user2.id,
:roles => roles,
:groups => groups,
)
user3.save
assets = user3.assets({})
attributes = user1.attributes_with_associations
attributes['accounts'] = {}
attributes['password'] = ''
assert( diff(attributes, assets[:User][user1.id]), 'check assets' )
assert( diff(org.attributes_with_associations, assets[:Organization][org.id]), 'check assets' )
attributes = user2.attributes_with_associations
attributes['accounts'] = {}
attributes['password'] = ''
assert( diff(attributes, assets[:User][user2.id]), 'check assets' )
attributes = user3.attributes_with_associations
attributes['accounts'] = {}
attributes['password'] = ''
assert( diff(attributes, assets[:User][user3.id]), 'check assets' )
end
def diff(o1, o2)
return true if o1 #== o2
raise "ERROR: difference #{o1.inspect}, #{o2.inspect}"
end
end

View file

@ -23,6 +23,13 @@ class EmailParserTest < ActiveSupport::TestCase
:from_display_name => 'Martin Edenhofer',
:subject => 'aaäöüßad asd',
:body_md5 => "äöüß ad asd\n\n-Martin\n\n--\nOld programmers never die. They just branch to a new address.\n",
:body => "äöüß ad asd
-Martin
--
Old programmers never die. They just branch to a new address.
"
},
},
{
@ -369,6 +376,69 @@ Hof
:from_display_name => 'Health and Care-Mall',
:subject => 'The Highest Grade Drugs And EXTRA LOW Price .',
:to => 'info2@znuny.com',
:body => "________________________________________________________________________Yeah but even when they. Beth liî ed her neck as well
&oacute;25aHw511I&Psi;11xG&lfloor;o8KHCm&sigmaf;9-2&frac12;23Qg&ntilde;V6UAD12AX&larr;t1Lf7&oplus;1Ir&sup2;r1TLA5pYJhjV gPn&atilde;M36V1E89RUD&Tau;&Aring;12I92s2C&Theta;YE&upsih;Afg&lowast;bT11&int;rIoi&scaron;&brvbar;O5oUIN1Is2S21Pp &Yuml;2q1F&Chi;&uArr;eGOz&lceil;F1R98y&sect; 74&rdquo;lTr8r1H2&aelig;u2E2P2q VmkfB&int;SKNElst4S&exist;182T2G1&iacute; lY92Pu&times;8>R&Ograve;&not;&oplus;&Mu;I&Ugrave;z&Ugrave;CC412QE&Rho;&ordm;S2!Xg&OElig;s.
2&gamma;&dArr;B[1]cwspC&ensp;L8I&nbsp;C&nbsp;K88H E1R?E2e31 !Calm dylan for school today.
Closing the nursery with you down. Here and made the mess. Maybe the from under his mother. Song of course beth touched his pants.
When someone who gave up from here. Feel of god knows what.
TB&piv;&exist;M5T5&Epsilon;Ef2&ucirc;&ndash;N&para;1v&Zeta;'1&dArr;&prop;5S2225 &Chi;0j&Delta;HbAg&thorn;E&mdash;2i6A2lD&uArr;LGj2nTOy11H2&tau;9&rsquo;:Their mother and tugged it seemed like
d3RsV&para;H2&Theta;i&macr;B&part;gax1b&icirc;gdH23r2J&yuml;1aIK1&sup2; n1jfaTk1Vs3952 C&tilde;lBl&lsquo;mxGo0&radic;2XwT8Ya 28ksa&int;f1&alefsym;s&rdquo;62Q 2Ad7$p32d1e&prod;2e.0&rdquo;261a2&Kappa;63&alpha;SM2
Nf52CdL&cup;1i&harr;xcaa52R3l6Lc3i2z16s&oacute;9&egrave;U zDE1aE21gs25&Euml;2 hE1cl&sup;&cent;11o21&micro;Bw1zF1 q2k&otilde;aXUius1r0&sube; d&bull;&isin;2$1Z2F1218l.07d56P&Uacute;l25JAO6
45loV2iv1i2&atilde;&Upsilon;&lfloor;a2&sup;d2g&Atilde;&Upsilon;3&trade;r22u&cedil;aWjO8 n40&ndash;Soy&egrave;2u1&empty;23p1J&Mu;Ne&Igrave;22jr&aacute;2r&Kappa; 1229A2rAkc8nuEtl22ai&Dagger;OB8vSb&eacute;&sigma;e&iota;&otilde;q1+65cw 2s8Ua&ograve;4PrsE1y8 &lang;fMElh&upsih;&sdot;Jo8pmzwj&circ;N1 wv39aW1WtsvuU3 1a&oelig;1$2&Nu;nR2O2&rceil;B.&forall;2c&rarr;5&Ecirc;9&chi;w5p1&frasl;N
fHGFVfE&sup3;2i&sigma;jGpa51kgg12cWrUq52akx2h 0F24P&cedil;2L2rn22&Iuml;o2&Yacute;2HfoRb2eU&alpha;w6s2N&oline;ws&para;13&Beta;i2X1&cedil;ofgtHnR&perp;32ase92lF1H5 26B1a&sup;2i&upsih;s&ocirc;12i &Aring;kMyl2J1&Auml;oQ&ndash;0&image;wvm&ugrave;2 2&circ;&mu;\"aQ7jVse62f 1h2p$L2r&pound;3i1t2.323h5qP8g0&hearts;&divide;R2
&middot;i&fnof;PV1&Beta;&ni;&oslash;iF1R1a4v32gL9&cent;wr1722a2&ucirc;0&eta; &thorn;12&szlig;Stu21u7&aacute;&iexcl;lp2ocEe1SLlrV2Xj &perp;U&micro;1F&not;48&eth;ov71Arm242c2Vw2e1&sect;&supe;N 1242aL&thorn;Z2ski&times;5 c&euro;pBl&ucirc;26&part;ol1f&Uacute;wK&szlig;32 4i2la4C12sRE21 &atilde;eI2$2z8t442fG.&cedil;1&le;12F&rsquo;&Atilde;152in&nsub;
Tl1&euml;C2v7Ci71X8a225Nl&thorn;U&rang;&iota;icO&sum;&laquo;s&middot;iKN Uu&upsih;jS1j52u2J&uuml;&sect;pn5&deg;1e&yen;&Ucirc;3&weierp;r1W&Dagger;2 J&lsaquo;S7A1j0sc&1pkt1qq2iZ561vn81&lowast;e22Q3+723&Scaron; &sum;RkLaKX2as2s22 &iuml;111lD2z8o278wwU&ndash;&Agrave;C T6U2a&upsih;938s20G&yuml; Ox2&isin;$98&lsquo;R21H25.&Ograve;L6b9&theta;r&delta;292f9j
Please matt on his neck. Okay matt huï ed into your mind
Since her head to check dylan. Where dylan matt got up there
1&Egrave;&plusmn;&Alpha;AYQ1dN12&upsih;XT00&Agrave;vI&or;&iacute;o8-1b&reg;8A&Epsilon;1V4Lg&Otilde;&uarr;7LKtgcEiw1yR5Y22GRA1&deg;I10C2C2Ti&uuml;/2wc0Ax211S&Uuml;&Acirc;2&OElig;T&Aacute;22&ograve;HpN&acirc;&ugrave;M6&Egrave;10A5Tb1:Simmons and now you really is what. Matt picked up this moment later that.
251yV922Yeg1&uarr;DnJ3l4t22b1os&prod;jll&divide;iS2iwB&Icirc;4n021&Ouml; 1f&divide;2a11l2su&Uacute;82 2LCblgvN&frac12;o1oP3wn&spades;90 FZora&M&trade;xs&Kappa;bb1 251&xi;$12&middot;22iG2&nabla;1&supe;&Xi;&not;3.0P0&kappa;53V1203&Yacute;Yz
2X&cent;BAZ4Kwddu2vvuB&uarr;&Beta;a1&rsquo;THi0&mdash;93rZ&epsilon;j0 1r&Mu;1a2111s71&Iota;f 8&dArr;2olW&bdquo;62o6yH&yen;wKZ&and;6 21h2aKJ&ldquo;&real;s48I&Igrave; 21&not;1$Z&Sigma;122&ntilde;26B42YMZ.21V19f10&aring;54&lceil;R8
2w\"9N2gB&Agrave;a2S&ecirc;1s&cong;gG&Ocirc;o0Dn4n&crarr;&gamma;7&otimes;eS7e2xf3Jd q&divide;CMa221isNMZp zz0&tilde;l&Kappa;Lw8o229ww1&sect;Qu 1D&lceil;&iacute;a2212sJ811 3o&ugrave;2$&brvbar;1N&real;1>R2t7WPM1.181D92k5D9&lowast;8&asymp;R
l131Sj1&Psi;8p&Sigma;2K&ugrave;i6rr2rb&Ucirc;u&not;i2V&lowast;&prod;v5&ordf;10a27B1 &Uacute;&diams;&Xi;sa9j3&chi;sa1i&Omicron; Oi&weierp;ml6&oacute;f2owbz&forall;wA6&ugrave;&rarr; 22b2ai1wbs&diams;&beta;Gs 281i$i&Agrave;&circ;12&sup;2wC82n8o.13NJ9S11&Theta;0P1Sd
What made no one in each time.
Mommy was thinking of course beth. Everything you need the same thing
P2EVG29srEx&lArr;9oN3U1yE2i2OR5k&Ccedil;&yuml;A&Tau;&eta;&nu;ULP&iquest;&and;q R5&iquest;FHt7J6E&raquo;1C&empty;A2&exist;aVLu&lowast;&cent;tT&lang;21&scaron;Hq9N&eacute;:
&perp;&THORN;21T11BrrC712ad&scaron;6lmzb16ai07tdBo&times;Kop&iacute;&Rho;1lj4Hy 2a&Oacute;1a&Ouml;&iacute;&notin;&Oacute;s1a2&rsquo; 4D1kleow2o3&ndash;12wjR&le;&Pi; 1Rh2af27&cong;s26u2 8NLV$&cup;&dArr;1&darr;1Y&para;21.v2&Egrave;232S7202n11
m5VKZy3K2i&ntilde;21Dt&Uacute;2HrhGaMvr5&iuml;R1o11nam&Mu;w22anFu8x7&lceil;sU E4cva11&epsilon;&trade;s7&Alpha;GO dA35ld&ntilde;&Igrave;&egrave;oA&xi;I1wXK2n f1x&frac34;a&prod;7ffs&dagger;222 5msC$72t10z&bdquo;n2.it1T7O8vt5182&middot;
J&iuml;12Pk&aacute;O1rn2rAo8s5&empty;z&mdash;4Rha11t&tilde;cq5Y&Chi; &Tau;Q2ra2&rfloor;4&sup1;s&Uuml;51&sect; 2VB&iota;luw2ioL32Bw1111 5&isin;22a1I22s&scaron;&Ucirc;21 G17&rho;$kJM80&sim;&ang;&alefsym;l.J1Km3212&sup;52&eacute;&frac14;&sect;
p121A1NU0c&yen;x2fo&lang;22cm14QGpHEj7lnDPVieV21a&Pi;2H7 1j26azBSes&euml;1c9 &acute;2&Ugrave;&not;l0n21o22RVw1X1&Iuml; &alpha;V21a&cong;&sigma;1Zs&sect;jJ&aring; 3pFN$1Kf821Y&Omicron;7.32Y95J&Alpha;q&Yuml;0v91Q
&ntilde;&uarr;yjP&Tau;1u6rFwhNeCO&piv;2d5&Gamma;&ecirc;cne&frac14;a0iTF15sxUS0o88&alefsym;1la&Aring;T&weierp;oOB11n2111e&and;Kpf &upsilon;98&xi;abp&dagger;3sj82& 9&copy;Bol2AWSo7wNgw21mM tteQat0&piv;2s4&equiv;N&Ccedil; &Otilde;&AElig;1&Theta;$2R2q0117&ordf;.mt111&mdash;uwF57H&clubs;f
&aelig;&cup;HYSj&psi;3By&scaron;1g1ndX15t1126hZ&rArr;y2r82mdowy2di&psi;8Y&Eta;d0r&scaron;&Scaron; N029a13I&brvbar;sQa&yacute;2 20Y7lZ118o&int;50&Ccedil;w1\"1&Zeta; n6&Uuml;&ge;a&nabla;l&szlig;nsF&rsaquo;J9 1D&Omicron;K$142L0S7z2.Ta2X31R9953911
Turning to mess up with. Well that to give her face
Another for what she found it then. Since the best to hear
GX1&diams;Ca2isA18&iexcl;bN2&icirc;81A22z&Theta;D&nabla;tNXIfWi&ndash;Ap2WYNYF1b &ne;7y&phi;Dpj6&copy;R04E1U1&ntilde;n7G1o2jS111&ni;TC&perp;&pi;&Euml;O1&lowast;21RtS2wE6621 &nu;222ASi21DP&ldquo;8&lambda;V&and;W&sdot;OA2g6qNtNp1T269XA7&yen;11GGI6SEwU22S3&Chi;12!Okay let matt climbed in front door. Well then dropped the best she kissed
122C>&Phi;221 flQkWM&Scaron;tvo2dV1rT1ZtlN6R9dZ12LwuD19i3B5Fdc&AElig;l2eSwJd K1tDDfoX&plusmn;evr&yacute;wlK7P&divide;i1e13v2z&egrave;Ce&not;&Mu;&clubs;&Nu;rGhs2y172Y!gZp&aacute; R6O4O112&ni;r92Z1dB6i1e2&sigma;&sim;&Oacute;rCZ1s 122I31e2&curren;+&rceil;C&ecirc;U 1k6wG1c&sbquo;1o60AJoR72sd3i11s22pt &Oslash;277a2&forall;f5np&curren;n2duE8&rArr; 21SHGJVAtew&nabla;L&euml;t&sigmaf;2D2 6k28FgQQ&sub;R81L2EI2&notin;iEH&Iacute;&Eacute;3 H2r5Af1qxim&sigmaf;&rho;&Dagger;r6&copy;2jmWv92aW21giAC21lM&rfloor;1k 2V2&cedil;S2&ugrave;&theta;2h15B&Iota;i&lowast;ttEp8&cent;EPpSzWJi32U2n5&igrave;Ihgx8n&rceil;!j&prod;e5
x1qJ>mC7f 512y1GA420lCQe09s9u%uks&atilde; &psi;2X5A4g3nu&larr;&Tau;yst72pMh&scaron;g12e&rang;p&Uacute;1n1Y&fnof;&Scaron;t&Eacute;2LGizqQ&darr;c3t&Ugrave;I &oelig;&iuml;bXMK&Ucirc;RSertj2d\"Ot2ss581!oo2i F&Acirc;W2EW2DDx7hI2p&Phi;S2Bi2drUr&hArr;J<2a1&Alpha;zwt01p2i28R2oH21&Auml;n172r 1122DYvO7ak21ht204&Pi;e&part;&lambda;11 12dUo&omicron;1X3fc631 e&&cup;GOxT3CvXcO1e3K2&nu;r31y2 262z31&infin;I1 P&igrave;&exist;zYt6F4e6&egrave;&dArr;va5229rk&Theta;32sKP5R!&iota;&micro;mz
3212>22&prime;L 2&oacute;B&perp;S&cap;OQMe&yacute;&notin;2&Phi;c229Tu2a&int;dr25&ucirc;MeLk92 121OO&oslash;9oKn&yuml;&psi;&Agrave;Wl7H2&empty;i9&rho;&Egrave;2ni2&bull;2eXPx&iacute; 1251SUqtBh72a5otSZ9p222Dpf1&Yacute;2i2&omega;bjn11&Yuml;2gs2h&minus; b&aring;2swx2oSiq8hvt2262h&lceil;b&sup2;S 26&thorn;SVBEFCi2U&agrave;ds9&Ntilde;1&Epsilon;a11&xi;2,1&bdquo;wv jw7AMK2&harr;la2G91s23&laquo;etuB2keD&atilde;2&igrave;r1&uml;IeC&frac34;Ea&Auml;ao&divide;&Prime;&and;r>6e1d9D21,mtS2 I&lowast;44A1R&circ;2M98zME&cong;Q&Yuml;&ETH;X&sup1;4j6 20n3a1&apos;22nxpl6d832J 06&ETH;9E22&yacute;2-2829c42r2h72&yen;med&frac12;&spades;kc23sPk12&bull;r!&rang;QCa
&Scaron;e21>1&sigma;12 bp&oslash;NERN8eaD61ns7Abhy&plusmn;12&cap; D7sVR8&apos;1Ee22DVfc&tilde;32u72&AElig;qnc23qd2&sim;4&nabla;s&rho;mi5 6212a21&prop;TnQb9sd1M&ugrave;&image; &sum;gM22bN2&para;4c&auml;&frac12;&sube;/4X1&kappa;71f1z &piv;12ECzf&bull;1uMbycs1&bull;9&frac34;ts0T2o3h2DmSs31e7B2&Eacute;r2&sdot;22 &phi;81&Prime;SSX&eth;1u&uacute;I15p58uHp2c2&plusmn;o&part;T1Rrd6sMt&cup;1&micro;&xi;!24Xb
Both hands through the fear in front.
Wade to give it seemed like this. Yeah but one for any longer. Everything you going inside the kids.
[1] http://pxmzcgy.storeprescription.ru?zz=fkxffti
"
},
},
{
@ -382,6 +452,34 @@ Hof
:to => 'info@znuny.nix',
},
},
{
:data => IO.read('test/fixtures/mail22.box'),
:body_md5 => '57cf207fb52f01f107ae008eb2f8d6cc',
:params => {
:from => 'Gilbertina Suthar <ireoniqla@lipetsk.ru>',
:from_email => 'ireoniqla@lipetsk.ru',
:from_display_name => 'Gilbertina Suthar',
:subject => 'P..E..N-I..S__-E N L A R-G E-M..E..N T-___P..I-L-L..S...Info.',
:to => 'Info <info@znuny.nix>',
:body => "Puzzled by judith bronte dave. Melvin will want her way through with.
Continued adam helped charlie cried. Soon joined the master bathroom. Grinned adam rubbed his arms she nodded.
Freemont and they talked with beppe.
Thinking of bed and whenever adam.
Mike was too tired man to hear.I10PQSHEJl2Nwf&tilde;2113S173 &Icirc;1mEbb5N371L&piv;C7AlFnR1&diams;HG64B242&brvbar;M2242zk&Iota;N&rceil;7&rceil;TBN&ETH; T2xPI&ograve;gI2&Atilde;lL2&Otilde;ML&perp;22Sa&Psi;RBreathed adam gave the master bedroom door.
Better get charlie took the wall.
Charlotte clark smile he saw charlie.
Dave and leaned her tears adam.Maybe we want any help me that.
Next morning charlie gazed at their father.
Well as though adam took out here. Melvin will be more money. Called him into this one last night.
Men joined the pickup truck pulled away. Chuck could make sure that.[1]&dagger;p1C?L&thinsp;I?C&ensp;K?88&ensp;5 E R?EEOD !Chuckled adam leaned forward and le? charlie.
Just then returned to believe it here.
Freemont and pulling out several minutes.
[1] &#104;&#116;&#116;&#112;&#58;&#47;&#47;&#1072;&#1086;&#1089;&#1082;&#46;&#1088;&#1092;?jmlfwnwe&ucwkiyyc
",
},
},
]
files.each { |file|

View file

@ -129,6 +129,124 @@ Some Text",
},
},
},
{
:data => IO.read('test/fixtures/mail21.box'),
:success => true,
:result => {
0 => {
:priority => '2 normal',
:title => 'World Best DRUGS Mall For a Reasonable Price.',
},
1 => {
:body => '_________________________________________________________________________________Please beth saw his head
92hH3&yuml;oI221G1&iquest;iH16u-2&loz;NQ422U1awAq&sup1;JLZ&mu;2IicgT1&zeta;2Y7&sube;t 63&lsquo;M236E2&Yacute;&rarr;DA2&dagger;I048CvJ9A&uarr;3iTc4&Eacute;I&Upsilon;vXO502N1FJS&eth;1r 154F1HPO11CRxZp tL&icirc;T9&ouml;XH1b3Es1W mN2Bg3&otilde;EbP&OElig;S2f&tau;T&oacute;Y4 sU2P2&zeta;&Delta;RFkcI21&trade;C&Oacute;Z3E&Lambda;Rq!Cass is good to ask what that
86&Euml;[1]2u2C&nbsp;L&nbsp;I C1K&nbsp;&nbsp;&nbsp;H E&nbsp;R E28MLuke had been thinking about that.
Shannon said nothing in fact they. Matt placed the sofa with amy smiled. Since the past him with more. Maybe he checked the phone. Neither did her name only. Ryan then went inside matt.
Maybe we can have anything you sure.
&aacute;&bull;XMY2&Aring;EE12N&deg;kP\'d&Auml;1S4&rceil;d &radic;p&uml;H&Sigma;>jE4y4AC22L2&ldquo;vT&and;4tHX1X:
x5VV"1ti21aa&Phi;3fg&brvbar;z2r1&deg;haeJw n1Va879s&AElig;3j f1&iuml;l29lo5F1w&nu;11 &kappa;&psi;&rsaquo;a9f4sLsL 2Vo$v3x1&cedil;nz.u2&brvbar;1H4s3527
yoQC1FMiMzda1Z&epsilon;l&Yacute;HNi1c2s2&ndash;&piv; DYha&atilde;7Ns421 n3dl1X1o11&para;wpN&uarr; YQ7a239s1q2 QyL$fc21&Nu;S5.5Wy621d5&Auml;1H
17<V401i421a&theta;1Tg21Gr9E2a&Rho;Bw &rarr;2&Ouml;SRSLu72lpL6Ve191r1HL FEpA229cP&not;lt&Ograve;cDib2XvTtFel3&reg;+bVM 252aXWas4&ordm;2 &mu;2Kl&prod;7mo&radic;23wSg1 &iota;&pound;Ca11Xso18 1L2$&hellip;412Jo&uarr;.0&Lambda;a53i&egrave;55W2
23IV4&loz;9iF2Va2&Otilde;&oacute;g8&sup3;9r&weierp;buaf12 fc7Pg3&sube;rz&ccedil;8o2&minus;&sdot;f&yuml;&ge;ZeaP&Ntilde;s5&lArr;Tsi&Psi;&ni;i92uoU8Rn&Psi;&rceil;&bull;aw1flf22 TQNaU&rsaquo;&eacute;svDu B1Il6&Theta;lo&ang;HfwNX8 36Xa&sim;&alpha;1sT1d &Scaron;HG$2&otilde;13QW1.&permil;&rsaquo;Y52g80&brvbar;ao
LKNV0&Auml;wiM4xafsJgFJ2r27&rdquo;a&lArr;M2 &ang;O5SQ2Mut21p2&Aring;&Atilde;e&uml;2HrZ41 1U&Lambda;F&uml;Tso2wXr24Icky2e1qY 074a2l&lfloor;s2H1 42pl24Xob0aw4F&Ocirc; 28&there4;a70lsA30 &szlig;WF$Z&cedil;v4AEG.2612t9p5&para;1Q
M91C&epsilon;92i0qPa1A2lW5Pi5Vusi8&euml; 2O0SE2Eu2&isin;2p2Y3eTs6r622 l12Ay2jcQpet13&otilde;iiqXvPVOe81V+1&ldquo;G 126a1&Pi;7sJ2g 1J2l&hearts;&Scaron;1o2olwBV2 &rarr;Ama&eta;2&macr;sa22 H22$2Ef2&isin;n5.&OElig;8H95119&sup;&fnof;2
Up dylan in love and found herself. Sorry for beth smiled at some time
Whatever you on one who looked. Except for another man and ready.
&Uacute;2eAC2&oslash;N&Euml;1UT3L&spades;IC&euml;9-B&OElig;fAo&Oacute;CL5&Beta;2LH&omicron;NE5&part;7RScdGX11Ip&Sigma;uCCw&or;/D16A1v2S0d&sub;T1&apos;BHf2&Delta;M227A63B:
2U2V51Ue212nRm2t22Oo&gamma;12ly&frac14;Wi6pxn&Agrave;Z1 c2Sa8&iuml;1sG2&sub; &Mu;Jll1&pound;&bdquo;onb2w&rceil;&ouml;1 vY8a&Theta;mgs024 &aring;&yen;G$1592KkU11b0.&frac12;&Acirc;&real;54&Egrave;h0&ordm;1h
Zf1A0j&cedil;dc1&xi;v&trade;Xpagl2ib8YrSf0 1Wia141s1&times;7 TAwll1dom1Gw2&iquest;z &Beta;21a&circ;y2sN8&eta; 3oo$D012&Lambda;p14c2z.PA&empty;9&upsih;7354&uacute;9
R2&iacute;Nn&uml;2aYR&oslash;s&cong;&larr;&Iacute;oP&Agrave;ynC&Chi;1ef2ox2&cup;h E18aN22si&yuml;5 f47l147oF1jwG2&Eacute; 108a1edsj&Ucirc;S &iquest;e1$K&egrave;R1LD272o&egrave;.41O99&Yacute;192&piv;n
12&crarr;S&iota;3&rdquo;p&Yacute;2&oline;iEuer&Gamma;y0iY30v&Tau;A6a2"Y 465a1m6sg1s C&forall;il&Alpha;2&Pi;or6yw712 1K&Omega;a232s&nabla;&Delta;1 9&Chi;9$MWN2P02822&beta;.2&cap;S93220RQ&rsquo;
Have anything but matty is taking care. Voice sounded in name only the others
Mouth shut and while he returned with. Herself with one who is your life
2&sup2;2Gu8NEZ3FNFs2E1RnR&Ccedil;C9AK4xL151 25bH97CE&laquo;20A2q&cent;L1k&rarr;T&ordf;JkHe3&scaron;:Taking care about matt li ed ryan. Knowing he should be there.
Ks&pound;T2bIr74Ea2DZm&oelig;H1a17od1&cup;vo2ozlP3S 23&lsaquo;azy&prop;s&Uacute;1Q 42&sup1;ll21ovh7w2D2 1Qwa&uArr;c&Beta;s&uml;wH I&micro;e$&lArr;J517T2.t5f361B062&Psi;
5z&weierp;Z4nGi289t&larr;f4hvn2rb&Yuml;To1s9m12qand1xxO6 I2&cup;ak&frac12;0s21M 2&Eta;&iexcl;l22&frac34;orztw170 &mdash;&clubs;&cong;ar6qsvDv 76T$3&times;D0er&Iacute;.d107WoI51K2
&upsih;a9P&apos;1&macr;rP74o2&psi;2z&chi;f2a&Atilde;2&ntilde;c3qY &rarr;&reg;7aaRgsN1k 1&permil;&Sigma;l2p1o7R&sub;w&AElig;2e 3Iha&clubs;d&tilde;s3g7 23M$&equiv;&sdot;10AY4.Uq&radic;321k5SU&Mu;
Zr2A8&Ouml;6cZ&Yuml;do&Rho;eumpq1pAoUl2I2ieY2aK>&part; 3n6ax1Qs20b &deg;H&auml;l91&Ntilde;o&Iuml;6aw&equiv;d2 &Eta;&Aring;2a1&Oacute;vs&sup;17 C&sube;1$2Bz2sl2.&int;Pb5&Oslash;Mx0oQd
Z&Iota;&mu;PCqmr&micro;p0eA&Phi;&hearts;d&ocirc;&oline;&Omega;n&ang;2si4y2s28&laquo;o6&forall;ClDe&Igrave;oPbqnd1Jel&egrave;2 2&circ;5aWl&lang;sbP2 2&sup2;2l8&cent;OoH&cedil;ew&rsquo;90 &Upsilon;66a21dsh6K r61$7Ey0Wc2.&pound;&mdash;012C857A&thorn;
i1&sigma;S&euro;53yx&micro;2n80nt&Rho;&Pi;mh&ccedil;&equiv;hrB1do&micro;S1ih2rdOKK 712a&larr;2Is2&rceil;V Cssl1&acute;RoT1Qwy&Eacute;&Delta; &bull;&prod;&infin;a2YGs18E 1&pi;x$04&ograve;0gMF.bTQ3&Iacute;x6582&sigmaf;
Maybe even though she followed.
Does this mean you talking about. Whatever else to sit on them back
&larr;4BC32hAGAWNr2jAG&upsilon;&raquo;D1f4I2m&radic;AHM9N&rang;12 &sbquo;1HD19&Uuml;R23&or;U90IG199S1&cup;&rdquo;T123O2&deg;cR0E&uArr;E211 42aA&Prime;X&Nu;D14&image;VAK8A1d9Nr1DT112A5khGA3mE98&Ocirc;S9KC!5TU
AMm>EjL w&lowast;LW&upsilon;IaoKd1r&Theta;22l2I&Kappa;d&ecirc;5PwO4Hi2y6d&Ouml;H&lfloor;e&Atilde;&igrave;g j14Dr15e700lH12iJ12vY&hellip;2e1mhr114yr&AElig;2!&sum;&eta;2 21&upsilon;O&Delta;f&delta;rKZwd4KVeB12r&real;01 P&Zeta;2341o+A7Y 126GM17oGO&ordm;os7&sum;d272s18P &omicron;&diams;QaRn&ndash;n5b2d02w 2r&upsih;GI2&image;em0&forall;t1b2 20rF4O7R221E12&sube;ES&Upsilon;4 KF0A212i5&iuml;crt&sube;&euro;mRJ7aN&Lambda;2in26l5bQ 1&upsih;tSZbwh3&para;3ig&spades;9p2&Prime;2p&times;12iK11nsWsgdXW!tBO
m0W>Y2&Acirc; b1u1x&Delta;d03&macr;&not;0vHK%21&oacute; 674Aj32uQ&larr;&Iuml;t&Egrave;H1houqey1Yn221t&rfloor;BZi1V2c1Tn >Z&Gamma;M222e311d2s5s22&rsaquo;!102 2&iexcl;2Em21x2V2p1&or;6i2d&acirc;rB9ra72mtSzIiMlVo0NLng&Beta;&ucirc; 22LD7&uArr;maNx3tU&zeta;&cup;etc2 902o123fv49 w&cong;1O0giv12YeX2NryfT 3fP3xZ2 F2&Atilde;Y8q1eE1&Uuml;a&acirc;yfr&Mu;pls92&Acirc;!q&kappa;2
&icirc;5A>&forall;p&fnof; Z&micro;&Iacute;S&delta;32em2sc&oplus;7vu41Jr&Ograve;1we2yh qa&rho;O2p&frac14;n&Sigma;xZlrN1i&spades;2cnl4jeN1Q y2&cong;Sb63h17&rang;of1yp&Aring;A1p&thorn;h0i&Ocirc;cbnec4gI21 h2Uw23&lsaquo;i92ktS12h6V1 g1sV&OElig;2uipV1se2&sdot;a42V,T6D 228M&Rho;Y1a&sup;&ordm;&Epsilon;s5&ugrave;2t9IDeFD&image;rXpOCe&ldquo;&mu;an1Mr11Kd122,e27 DfmA21NM92hEU2&or;X&sigma;&psi;G 4j0a181nhTAdmT2 192E&nu;&mu;r-U4fc121h8&ordf;&cedil;eoycc9xjk&frasl;ko!29K
12&hellip;>J6&Aacute; 1&rang;8E&Ouml;22a141s117y3&acirc;8 1f2R6olewtzfw&sup1;su&yacute;oQn&dArr;&sup3;&sup3;d24Gs&cent;7&laquo; AlDa1H1n9Ejdtg&rsaquo; 12&theta;2&epsilon;1&supe;41&Prime;A/42v72z&rarr; 231C622u56Xs9&frasl;1t&sum;&Iota;iox&Eacute;jm2R2e1W2rH25 o&yen;2S&ge;gmuX2gp3yip&middot;12oD13rc3&mu;tks&cup;!sWK
When she were there you here. Lott to need for amy said.
Once more than ever since matt. Lott said turning o ered. Tell you so matt kept going.
Homegrown dandelions by herself into her lips. Such an excuse to stop thinking about. Leave us and be right.
[2]
Это сообщение свободно от вирусов и вредоносного ПО благодаря [3]avast! Antivirus защита активна.
[1] http://piufup.medicatingsafemart.ru
[2] http://www.avast.com/
[3] http://www.avast.com/
',
:sender => 'Customer',
:type => 'email',
:internal => false,
},
},
},
{
:data => IO.read('test/fixtures/mail22.box'),
:success => true,
:result => {
0 => {
:priority => '2 normal',
:title => 'P..E..N-I..S__-E N L A R-G E-M..E..N T-___P..I-L-L..S...Info.',
},
1 => {
:body => "Puzzled by judith bronte dave. Melvin will want her way through with.
Continued adam helped charlie cried. Soon joined the master bathroom. Grinned adam rubbed his arms she nodded.
Freemont and they talked with beppe.
Thinking of bed and whenever adam.
Mike was too tired man to hear.I10PQSHEJl2Nwf&tilde;2113S173 &Icirc;1mEbb5N371L&piv;C7AlFnR1&diams;HG64B242&brvbar;M2242zk&Iota;N&rceil;7&rceil;TBN&ETH; T2xPI&ograve;gI2&Atilde;lL2&Otilde;ML&perp;22Sa&Psi;RBreathed adam gave the master bedroom door.
Better get charlie took the wall.
Charlotte clark smile he saw charlie.
Dave and leaned her tears adam.Maybe we want any help me that.
Next morning charlie gazed at their father.
Well as though adam took out here. Melvin will be more money. Called him into this one last night.
Men joined the pickup truck pulled away. Chuck could make sure that.[1]&dagger;p1C?L&thinsp;I?C&ensp;K?88&ensp;5 E R?EEOD !Chuckled adam leaned forward and le? charlie.
Just then returned to believe it here.
Freemont and pulling out several minutes.
[1] &#104;&#116;&#116;&#112;&#58;&#47;&#47;&#1072;&#1086;&#1089;&#1082;&#46;&#1088;&#1092;?jmlfwnwe&ucwkiyyc
",
:sender => 'Customer',
:type => 'email',
:internal => false,
},
},
},
]
process(files)
end
@ -311,9 +429,9 @@ Some Text',
if file[:result][level]
file[:result][level].each { |key, value|
if result[level].send(key).respond_to?('name')
assert_equal( result[level].send(key).name, value.to_s)
assert_equal( value.to_s, result[level].send(key).name )
else
assert_equal( result[level].send(key), value)
assert_equal( value, result[level].send(key))
end
}
end

View file

@ -140,18 +140,18 @@ class SessionBasicTest < ActiveSupport::TestCase
test 'b collections organization' do
require 'sessions/backend/collections/organization.rb'
UserInfo.current_user_id = 1
Organization.destroy_all
user = User.lookup(:id => 1)
org = Organization.create( :name => 'SomeOrg1::' + rand(999999).to_s, :active => true )
collection_client1 = Sessions::Backend::Collections::Organization.new(user, false, '123-1')
collection_client2 = Sessions::Backend::Collections::Organization.new(user, false, '234-2')
# get whole collections - should be nil, no org exists!
result1 = collection_client1.push
assert( !result1, "check collections" )
assert( !result1.empty?, "check collections" )
sleep 1
result2 = collection_client2.push
assert( !result2, "check collections" )
assert( !result2.empty?, "check collections" )
assert_equal( result1, result2, "check collections" )
# next check - should still be nil, no org exists!
@ -162,7 +162,7 @@ class SessionBasicTest < ActiveSupport::TestCase
assert( !result2, "check collections - recall" )
# change collection
org = Organization.create( :name => 'SomeOrg::' + rand(999999).to_s, :active => true )
org = Organization.create( :name => 'SomeOrg2::' + rand(999999).to_s, :active => true )
sleep 16
# get whole collections
@ -191,7 +191,6 @@ class SessionBasicTest < ActiveSupport::TestCase
result2 = collection_client2.push
assert( !result1.empty?, "check collections - after touch" )
assert_equal( result1, result2, "check collections" )
end
test 'b rss' do