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 # Gems used only for assets and not required
# in production environments by default. # in production environments by default.
group :assets do group :assets do
gem 'sass-rails' gem 'sass-rails', github: 'rails/sass-rails'
gem 'coffee-rails' gem 'coffee-rails'
gem 'uglifier' gem 'uglifier'
end end

View file

@ -25,7 +25,7 @@ Getting Started
``` ```
zammad@shell> cd zammad zammad@shell> cd zammad
zammad@shell> gem install rails zammad@shell> gem install rails
zammad@shell> vi Gemfile # enable libv8, execjs and therubyracer if needed! zammad@shell> vi Gemfile # enable libv8, execjs and therubyracer if needed!
zammad@shell> sudo bundle install zammad@shell> sudo bundle install
``` ```

View file

@ -223,11 +223,11 @@ class App.Controller extends Spine.Controller
placement: position placement: position
title: -> title: ->
ticket_id = $(@).data('id') ticket_id = $(@).data('id')
ticket = App.Ticket.retrieve( ticket_id ) ticket = App.Ticket.fullLocal( ticket_id )
App.i18n.escape( ticket.title ) App.i18n.escape( ticket.title )
content: -> content: ->
ticket_id = $(@).data('id') ticket_id = $(@).data('id')
ticket = App.Ticket.retrieve( ticket_id ) ticket = App.Ticket.fullLocal( ticket_id )
ticket.humanTime = ui.humanTime(ticket.created_at) ticket.humanTime = ui.humanTime(ticket.created_at)
# insert data # insert data
App.view('popover/ticket')( App.view('popover/ticket')(
@ -257,11 +257,11 @@ class App.Controller extends Spine.Controller
placement: position placement: position
title: -> title: ->
user_id = $(@).data('id') user_id = $(@).data('id')
user = App.User.find( user_id ) user = App.User.fullLocal( user_id )
App.i18n.escape( user.displayName() ) App.i18n.escape( user.displayName() )
content: -> content: ->
user_id = $(@).data('id') user_id = $(@).data('id')
user = App.User.find( user_id ) user = App.User.fullLocal( user_id )
# get display data # get display data
data = [] data = []
@ -303,11 +303,11 @@ class App.Controller extends Spine.Controller
placement: position placement: position
title: -> title: ->
organization_id = $(@).data('id') organization_id = $(@).data('id')
organization = App.Organization.find( organization_id ) organization = App.Organization.fullLocal( organization_id )
App.i18n.escape( organization.name ) App.i18n.escape( organization.name )
content: -> content: ->
organization_id = $(@).data('id') organization_id = $(@).data('id')
organization = App.Organization.find( organization_id ) organization = App.Organization.fullLocal( organization_id )
# insert data # insert data
App.view('popover/organization')( App.view('popover/organization')(
organization: organization, organization: organization,
@ -523,3 +523,31 @@ class App.SessionMessage extends App.ControllerModal
throw "Cant reload page!" 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 remove: true
} }
itemSub = @formGenItem( attribute_config ) itemSub = @formGenItem( attribute_config )
itemSub.find('.icon-minus').bind('click', (e) -> itemSub.find('.glyphicon-minus').bind('click', (e) ->
e.preventDefault() e.preventDefault()
$(@).parent().parent().parent().remove() $(@).parent().parent().parent().remove()
) )
@ -1019,18 +1019,19 @@ class App.ControllerForm extends App.Controller
selected: false selected: false
disable: true disable: true
}, },
# { #
# value: 'tickets.number' #{
# name: 'Number' # value: 'tickets.number'
# selected: true # name: 'Number'
# disable: false # selected: true
# }, # disable: false
# { #},
# value: 'tickets.title' #{
# name: 'Title' # value: 'tickets.title'
# selected: true # name: 'Title'
# disable: false # selected: true
# }, # disable: false
#},
{ {
value: 'tickets.group_id' value: 'tickets.group_id'
name: 'Group' name: 'Group'
@ -1055,68 +1056,55 @@ class App.ControllerForm extends App.Controller
selected: true selected: true
disable: false disable: false
}, },
{ #{
value: 'tickets.customer_id' # value: 'tickets.created_at::<>'
name: 'Customer' # name: 'Created (before/last)'
selected: true # selected: true
disable: false # disable: false
}, #},
{ #{
value: 'tickets.organization_id' # value: 'tickets.created_at::><'
name: 'Organization' # name: 'Created (between)'
selected: true # selected: true
disable: false # disable: false
}, #},
{ #{
value: 'tickets.created_at::<>' # value: 'tickets.close_time::<>'
name: 'Created (before/last)' # name: 'Closed (before/last)'
selected: true # selected: true
disable: false # disable: false
}, #},
{ #{
value: 'tickets.created_at::><' # value: 'tickets.close_time::><'
name: 'Created (between)' # name: 'Closed (between)'
selected: true # selected: true
disable: false # disable: false
}, #},
{ #{
value: 'tickets.close_time::<>' # value: 'tickets.updated_at::<>'
name: 'Closed (before/last)' # name: 'Updated (before/last)'
selected: true # selected: true
disable: false # disable: false
}, #},
{ #{
value: 'tickets.close_time::><' # value: 'tickets.updated_at::><'
name: 'Closed (between)' # name: 'Updated (between)'
selected: true # selected: true
disable: false # disable: false
}, #},
{ #{
value: 'tickets.updated_at::<>' # value: 'tickets.escalation_time::<>'
name: 'Updated (before/last)' # name: 'Escalation (before/last)'
selected: true # selected: true
disable: false # disable: false
}, #},
{ #{
value: 'tickets.updated_at::><' # value: 'tickets.escalation_time::><'
name: 'Updated (between)' # name: 'Escalation (between)'
selected: true # selected: true
disable: false # 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' # value: 'tag'
# name: 'Tag' # name: 'Tag'
# selected: true # selected: true
@ -1194,24 +1182,24 @@ class App.ControllerForm extends App.Controller
# selected: true # selected: true
# disable: false # disable: false
# }, # },
# { {
# value: '-c' value: '-c'
# name: '-- ' + App.i18n.translateInline('Customer') + ' --' name: '-- ' + App.i18n.translateInline('Customer') + ' --'
# selected: false selected: false
# disable: true disable: true
# }, },
# { {
# value: 'customers.id' value: 'customers.id'
# name: 'Kunde' name: 'Customer'
# selected: true selected: true
# disable: false disable: false
# }, },
# { {
# value: 'organization.id' value: 'organization.id'
# name: 'Organization' name: 'Organization'
# selected: true selected: true
# disable: false disable: false
# }, },
] ]
default: '' default: ''
translate: true translate: true
@ -1220,7 +1208,7 @@ class App.ControllerForm extends App.Controller
} }
list = @formGenItem( attribute_config ) list = @formGenItem( attribute_config )
list.find('.icon-plus').bind('click', (e) -> list.find('.glyphicon-plus').bind('click', (e) ->
e.preventDefault() e.preventDefault()
value = $(e.target).parents().find('[name=ticket_attribute_list]').val() value = $(e.target).parents().find('[name=ticket_attribute_list]').val()

View file

@ -37,7 +37,7 @@ class App.ControllerGenericNew extends App.ControllerModal
object.save( object.save(
done: -> done: ->
if ui.callback if ui.callback
item = App[ ui.genericObject ].retrieve(@id) item = App[ ui.genericObject ].fullLocal(@id)
ui.callback( item ) ui.callback( item )
ui.modalHide() ui.modalHide()
@ -84,7 +84,7 @@ class App.ControllerGenericEdit extends App.ControllerModal
@item.save( @item.save(
done: -> done: ->
if ui.callback if ui.callback
item = App[ ui.genericObject ].retrieve(@id) item = App[ ui.genericObject ].fullLocal(@id)
ui.callback( item ) ui.callback( item )
ui.modalHide() ui.modalHide()
@ -269,14 +269,17 @@ class App.ControllerLevel2 extends App.ControllerContent
# window.scrollTo(0,0) # window.scrollTo(0,0)
class App.ControllerTabs extends App.Controller class App.ControllerTabs extends App.Controller
events:
'click .nav-tabs [data-toggle="tab"]': 'tabRemember',
constructor: -> constructor: ->
super super
render: -> render: ->
@html App.view('generic/tabs')( @html App.view('generic/tabs')(
tabs: @tabs tabs: @tabs
) )
@el.find('.nav-tabs li:first').addClass('active')
for tab in @tabs for tab in @tabs
@el.find('.tab-content').append('<div class="tab-pane" id="' + tab.target + '"></div>') @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 ) params.el = @el.find( '#' + tab.target )
new tab.controller( params ) 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 class App.ControllerNavSidbar extends App.ControllerContent
constructor: (params) -> constructor: (params) ->

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -38,4 +38,4 @@ class App.TicketCustomer extends App.ControllerModal
) )
# load user if not already exists # 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 = [] @ticket_list_show = []
for ticket_id in @ticket_ids 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 # remeber bulk attributes
@bulk = data.bulk @bulk = data.bulk
@ -185,7 +185,7 @@ class Table extends App.ControllerContent
@el.find('.table-overview').append(table) @el.find('.table-overview').append(table)
else else
openTicket = (id,e) => openTicket = (id,e) =>
ticket = App.Ticket.retrieve(id) ticket = App.Ticket.fullLocal(id)
@navigate ticket.uiUrl() @navigate ticket.uiUrl()
callbackTicketTitleAdd = (value, object, attribute, attributes, refObject) => callbackTicketTitleAdd = (value, object, attribute, attributes, refObject) =>
attribute.title = object.title attribute.title = object.title

View file

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

View file

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

View file

@ -40,7 +40,7 @@ class App.WidgetLink extends App.ControllerDrox
list[ item['link_type'] ] = [] list[ item['link_type'] ] = []
if item['link_object'] is 'Ticket' 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' if ticket.state.name is 'merged'
ticket.css = 'merged' ticket.css = 'merged'
list[ item['link_type'] ].push ticket list[ item['link_type'] ].push ticket

View file

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

View file

@ -6,16 +6,8 @@ class App.WidgetUser extends App.ControllerDrox
constructor: -> constructor: ->
super super
# show user # subscribe and reload data / fetch new data if triggered
callback = (user) => @subscribeId = App.User.full( @user_id, @render, false, true )
@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 )
release: => release: =>
App.User.unsubscribe(@subscribeId) App.User.unsubscribe(@subscribeId)
@ -41,6 +33,34 @@ class App.WidgetUser extends App.ControllerDrox
if item.info if item.info
userData.push item 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 # insert userData
@html @template( @html @template(
file: 'widget/user' file: 'widget/user'
@ -72,7 +92,7 @@ class App.WidgetUser extends App.ControllerDrox
) )
update: (e) => update: (e) =>
note = $(e.target).parent().find('[data-type=update]').val() note = $(e.target).val()
user = App.User.find( @user_id ) user = App.User.find( @user_id )
if user.note isnt note if user.note isnt note
user.updateAttributes( note: note ) user.updateAttributes( note: note )

View file

@ -75,21 +75,23 @@ class App.Auth
if type isnt 'check' if type isnt 'check'
App.Event.trigger( 'clearStore' ) App.Event.trigger( 'clearStore' )
# set avatar
data.session.image = App.Config.get('api_path') + '/users/image/' + data.session.image
# update config # update config
for key, value of data.config for key, value of data.config
App.Config.set( key, value ) App.Config.set( key, value )
# store user data
for key, value of data.session
App.Session.set( key, value )
# refresh default collections # refresh default collections
if data.collections if data.collections
App.Collection.resetCollections( 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 # trigger auth ok with new session data
App.Event.trigger( 'auth', data.session ) App.Event.trigger( 'auth', data.session )

View file

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

View file

@ -143,19 +143,22 @@ class _i18nSingleton extends Spine.Module
translate: ( string, args... ) => translate: ( string, args... ) =>
# type convertation
if typeof string isnt 'string'
if string && string.toString
string = string.toString()
# return '' on undefined # return '' on undefined
if typeof string is 'boolean'
string = string.toString()
return '' if string is undefined return '' if string is undefined
return '' if string is '' return '' if string is ''
# return translation # return translation
if @map[string] isnt undefined if @map[string] isnt undefined
@_translated = true @_translated = true
translated = @map[string] translated = @map[string]
else else
@_translated = false @_translated = false
translated = string translated = string
# search %s # search %s
for arg in args for arg in args

View file

@ -126,6 +126,66 @@ class App.Model extends Spine.Model
return true if @id[0] isnt 'c' return true if @id[0] isnt 'c'
return false 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 ) -> @retrieve: ( id, callback, force ) ->
if !force && App[ @className ].exists( id ) if !force && App[ @className ].exists( id )
data = App[ @className ].find( id ) data = App[ @className ].find( id )
@ -229,42 +289,56 @@ class App.Model extends Spine.Model
subscribe: (callback, type) -> subscribe: (callback, type) ->
# remember record id and callback
App[ @constructor.className ].subscribe_item(@id, callback)
@subscribe_item: (id, callback) ->
# init bind # init bind
if !App[ @constructor.className ]['SUBSCRIPTION_ITEM'] if !@_subscribe_item_bindDone
App[ @constructor.className ]['SUBSCRIPTION_ITEM'] = {} @_subscribe_item_bindDone = true
# subscribe and render data after local change # subscribe and render data after local change
App[ @constructor.className ].bind( @bind(
'refresh change' 'refresh change'
(item) => (items) =>
#console.log('BIND', item)
for key, callback of App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ item.id ] # check if result is array or singel item
item = App[ @constructor.className ]._fillUp( item ) if !_.isArray(items)
callback(item, 'local') 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 # 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( App.Event.bind(
events events
(item) => (item) =>
#console.log('SERVER BIND try', item) if @SUBSCRIPTION_ITEM && @SUBSCRIPTION_ITEM[ item.id ]
if App[ @constructor.className ]['SUBSCRIPTION_ITEM'] && App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ item.id ] genericObject = undefined
#console.log('SERVER BIND', item) if App[ @className ].exists( item.id )
for key, callback of App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ item.id ] genericObject = App[ @className ].find( item.id )
callbackRetrieve = (item) ->
callback(item, 'server') callback = =>
App[ @constructor.className ].retrieve( item.id, callbackRetrieve, true ) if !genericObject || ( new Date(item.updated_at).toString() isnt new Date(genericObject.updated_at).toString() )
'Item::Subscribe::' + @constructor.className @full( item.id, false, true )
App.Delay.set(callback, 800, item.id, "full-#{@className}")
'Item::Subscribe::' + @className
) )
# remember record id and callback # remember item callback
if !App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ] if !@SUBSCRIPTION_ITEM
App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ] = {} @SUBSCRIPTION_ITEM = {}
key = @constructor.className + '-' + Math.floor( Math.random() * 99999 ) if !@SUBSCRIPTION_ITEM[id]
App[ @constructor.className ]['SUBSCRIPTION_ITEM'][ @id ][key] = callback @SUBSCRIPTION_ITEM[id] = {}
key = @className + '-' + Math.floor( Math.random() * 99999 )
# return key @SUBSCRIPTION_ITEM[id][key] = callback
key key
### ###
@ -275,15 +349,15 @@ class App.Model extends Spine.Model
### ###
@unsubscribe: (data) -> @unsubscribe: (subscribeId) ->
if @SUBSCRIPTION_ITEM if @SUBSCRIPTION_ITEM
for id, keys of @SUBSCRIPTION_ITEM for id, keys of @SUBSCRIPTION_ITEM
if keys[data] if keys[subscribeId]
delete keys[data] delete keys[subscribeId]
if @SUBSCRIPTION_COLLECTION if @SUBSCRIPTION_COLLECTION
if @SUBSCRIPTION_COLLECTION[data] if @SUBSCRIPTION_COLLECTION[subscribeId]
delete @SUBSCRIPTION_COLLECTION[data] delete @SUBSCRIPTION_COLLECTION[subscribeId]
@_bindsEmpty: -> @_bindsEmpty: ->
if @SUBSCRIPTION_ITEM if @SUBSCRIPTION_ITEM

View file

@ -23,10 +23,10 @@ class App.Organization extends App.Model
@_fillUp: (data) -> @_fillUp: (data) ->
# addd users of organization # addd users of organization
if data['user_ids'] if data['member_ids']
data['user_ids'] = [] data['members'] = []
for user_id in data['user_ids'] for user_id in data['member_ids']
if App.User.exists( user_id ) if App.User.exists( user_id )
user = App.User.find( user_id ) user = App.User.find( user_id )
data['user_ids'].push user data['members'].push user
data 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'] data['accounts'][account]['link'] = 'https://www.facebook.com/profile.php?id=' + data['accounts'][account]['uid']
# set image url # set image url
data.image = @apiPath + '/users/image/' + data.image data.imageUrl = @apiPath + '/users/image/' + data.image
if data.organization_id if data.organization_id
data.organization = App.Organization.find(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"/> <input type="checkbox" value="<%= ticket.id %>" name="bulk" class="pull-left"/>
</td> </td>
<td class="span1"> <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>
<td class="span10"> <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> <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: %> <% 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 %> <% end %>
</ul> </ul>
<div class="tab-content"></div> <div class="tab-content"></div>

View file

@ -1,11 +1,51 @@
<% for article in @articles: %> <% 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="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="horizontal<%= ' reverse' if article.sender.name is 'Customer' %>">
<div class="avatar" style="background-image: url(<%= article.created_by.image %>)"></div> <div class="avatar" style="background-image: url(<%= article.created_by.image %>)"></div>
<div class="flex"> <div class="flex">
<div class="text-bubble"><div class="bubble-arrow"></div><%- article.html %></div> <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>
</div> </div>
<time class="task-meta humanTimeFromNow" datetime="<%- article.created_at %>" data-time="<%- article.created_at %>">?</time> <time class="task-meta humanTimeFromNow" datetime="<%- article.created_at %>" data-time="<%- article.created_at %>">?</time>
</div> </div>
<% end %> <% end %>

View file

@ -1,8 +1,31 @@
<!--
<div class="horizontal bubble-grid new-article <% if @formChanged: %>form-changed<% end %>"> <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="avatar user-popover" data-id="<%= @S('id') %>" style="background-image: url(<%- @S('image') %>)"></div>
<div class="flex text-bubble"> <div class="flex text-bubble">
<div class="bubble-arrow"></div> <div class="bubble-arrow"></div>
<textarea></textarea> <textarea></textarea>
<span class="bubble-placeholder">Antwort eingeben oder <span class="highlight">Dateien wählen</span></span> <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>
</div> </div>

View file

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

View file

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

View file

@ -21,6 +21,15 @@
<% end %> <% end %>
<% 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>
</div> </div>

View file

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

View file

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

View file

@ -59,7 +59,7 @@ class LongPollingController < ApplicationController
user_id = session[:user_id] user_id = session[:user_id]
user = {} user = {}
if user_id if user_id
user = User.user_data_full( user_id ) user = User.find( user_id )
end end
log 'notice', "send auth login (user_id #{user_id})", client_id log 'notice', "send auth login (user_id #{user_id})", client_id
Sessions.create( client_id, user, { :type => 'ajax' } ) 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 return
end end
end end
if params[:full]
full = Organization.full( params[:id] )
render :json => full
return
end
model_show_render(Organization, params) model_show_render(Organization, params)
end end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -883,4 +883,25 @@ destory object dependencies, will be executed automatically
def destroy_dependencies def destroy_dependencies
end 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 end

View file

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

View file

@ -122,6 +122,10 @@ class Channel::EmailParser
data[:body] = mail.text_part.body.decoded data[:body] = mail.text_part.body.decoded
data[:body] = Encode.conv( mail.text_part.charset, data[:body] ) 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 # html attachment/body may exists and will be converted to text
else else
filename = '-no name-' filename = '-no name-'
@ -129,7 +133,11 @@ class Channel::EmailParser
filename = 'html-email' filename = 'html-email'
data[:body] = mail.html_part.body.to_s data[:body] = mail.html_part.body.to_s
data[:body] = Encode.conv( mail.html_part.charset.to_s, data[:body] ) 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 # any other attachments
else else
@ -188,6 +196,10 @@ class Channel::EmailParser
data[:body] = mail.body.decoded data[:body] = mail.body.decoded
data[:body] = Encode.conv( mail.charset, data[:body] ) 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 # html part
else else
filename = '-no name-' filename = '-no name-'
@ -195,7 +207,11 @@ class Channel::EmailParser
filename = 'html-email' filename = 'html-email'
data[:body] = mail.body.decoded data[:body] = mail.body.decoded
data[:body] = Encode.conv( mail.charset, data[:body] ) 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 # any other attachments
else else

View file

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

View file

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

View file

@ -29,15 +29,25 @@ returns
data[ User.to_app_model ] = {} data[ User.to_app_model ] = {}
end end
if !data[ Organization.to_app_model ][ self.id ] if !data[ Organization.to_app_model ][ self.id ]
data[ Organization.to_app_model ][ self.id ] = self.attributes data[ Organization.to_app_model ][ self.id ] = self.attributes_with_associations
data[ Organization.to_app_model ][ self.id ][:user_ids] = [] if data[ Organization.to_app_model ][ self.id ]['member_ids']
users = User.where( :organization_id => self.id ).limit(10) data[ Organization.to_app_model ][ self.id ]['member_ids'].each {|user_id|
users.each {|user| if !data[ User.to_app_model ][ user_id ]
data[ User.to_app_model ][ user.id ] = User.user_data_full( user.id ) user = User.lookup( :id => user_id )
data[ Organization.to_app_model ][ self.id ][:user_ids].push user.id data = user.assets( data )
} end
}
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 data
end end
end end

View file

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

View file

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

View file

@ -27,24 +27,16 @@ returns
data[ Ticket.to_app_model ] = {} data[ Ticket.to_app_model ] = {}
end end
if !data[ Ticket.to_app_model ][ self.id ] 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 ] = {}
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 end
['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
end
}
data data
end end

View file

@ -3,6 +3,7 @@
require 'digest/md5' require 'digest/md5'
class User < ApplicationModel class User < ApplicationModel
require 'user/assets'
include User::Assets include User::Assets
extend User::Search extend User::Search
include User::SearchIndex include User::SearchIndex
@ -360,108 +361,6 @@ returns
return user return user
end 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 =begin
update last login date and reset login_failed (is automatically done by auth and sso backend) 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 returns
result = { result = {
:users => { :User => {
123 => user_model_123, 123 => user_model_123,
1234 => user_model_1234, 1234 => user_model_1234,
} }
@ -26,22 +26,61 @@ returns
data[ User.to_app_model ] = {} data[ User.to_app_model ] = {}
end end
if !data[ User.to_app_model ][ self.id ] 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 end
if self.organization_id if self.organization_id
if !data[ Organization.to_app_model ] if !data[ Organization.to_app_model ] || !data[ Organization.to_app_model ][ self.organization_id ]
data[ Organization.to_app_model ] = {} organization = Organization.lookup( :id => self.organization_id )
end data = organization.assets( data )
if !data[ Organization.to_app_model ][ self.organization_id ]
data[ Organization.to_app_model ][ self.organization_id ] = Organization.find( self.organization_id )
end end
end end
if !data[ User.to_app_model ][ self.created_by_id ] ['created_by_id', 'updated_by_id'].each {|item|
data[ User.to_app_model ][ self.created_by_id ] = User.user_data_full( self.created_by_id ) if self[ item ]
end if !data[ User.to_app_model ][ self[ item ] ]
if !data[ User.to_app_model ][ self.updated_by_id ] user = User.lookup( :id => self[ item ] )
data[ User.to_app_model ][ self.updated_by_id ] = User.user_data_full( self.updated_by_id ) data = user.assets( data )
end end
end
}
data data
end end

View file

@ -16,6 +16,9 @@ module Cache
end end
def self.clear def self.clear
# puts 'Cache.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 Rails.cache.clear
end end
end end

View file

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

View file

@ -45,7 +45,7 @@ class Sessions::Backend::TicketCreate
users = {} users = {}
create_attributes[:owner_id].each {|user_id| create_attributes[:owner_id].each {|user_id|
if !users[user_id] if !users[user_id]
users[user_id] = User.user_data_full(user_id) users[user_id] = User.find(user_id).attributes
end end
} }
data = { 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', :execute => 'click',
:css => 'a[data-type="edit"]:last-child', :css => '.table-overview tr:last-child td',
}, },
{ {
:execute => 'wait', :execute => 'wait',
:value => 1, :value => 2,
}, },
{ {
:execute => 'set', :execute => 'set',
@ -146,7 +146,7 @@ class ManageTest < TestCase
}, },
{ {
:execute => 'click', :execute => 'click',
:css => 'a[data-type="edit"]:last-child', :css => '.table-overview tr:last-child td',
}, },
{ {
:execute => 'wait', :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" ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__) require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help' require 'rails/test_help'
require 'cache'
require 'simplecov' require 'simplecov'
require 'simplecov-rcov' require 'simplecov-rcov'
@ -16,8 +17,11 @@ class ActiveSupport::TestCase
# disable transactions # disable transactions
self.use_transactional_fixtures = false self.use_transactional_fixtures = false
# clear cache
Cache.clear
# load seeds # load seeds
load "#{Rails.root}/db/seeds.rb" load "#{Rails.root}/db/seeds.rb"
setup do setup do

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', :from_display_name => 'Martin Edenhofer',
:subject => 'aaäöüßad asd', :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_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', :from_display_name => 'Health and Care-Mall',
:subject => 'The Highest Grade Drugs And EXTRA LOW Price .', :subject => 'The Highest Grade Drugs And EXTRA LOW Price .',
:to => 'info2@znuny.com', :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', :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| 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) process(files)
end end
@ -311,9 +429,9 @@ Some Text',
if file[:result][level] if file[:result][level]
file[:result][level].each { |key, value| file[:result][level].each { |key, value|
if result[level].send(key).respond_to?('name') 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 else
assert_equal( result[level].send(key), value) assert_equal( value, result[level].send(key))
end end
} }
end end

View file

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