Refactoring: Splitted ApplicationModel into multiple concerns. Notice: Includes object some method name changes.

This commit is contained in:
Thorsten Eckel 2017-01-31 18:13:45 +01:00
parent f7e9b570ae
commit ef6fbe8f2a
68 changed files with 2211 additions and 2277 deletions

View file

@ -394,7 +394,7 @@ class ApplicationController < ActionController::Base
# remember time accounting
time_unit = params[:time_unit]
clean_params = Ticket::Article.param_association_lookup(params)
clean_params = Ticket::Article.association_name_to_id_convert(params)
clean_params = Ticket::Article.param_cleanup(clean_params, true)
# overwrite params
@ -514,7 +514,7 @@ class ApplicationController < ActionController::Base
# model helper
def model_create_render(object, params)
clean_params = object.param_association_lookup(params)
clean_params = object.association_name_to_id_convert(params)
clean_params = object.param_cleanup(clean_params, true)
# create object
@ -524,10 +524,10 @@ class ApplicationController < ActionController::Base
generic_object.save!
# set relations
generic_object.param_set_associations(params)
generic_object.associations_from_param(params)
if params[:expand]
render json: generic_object.attributes_with_relation_names, status: :created
render json: generic_object.attributes_with_association_names, status: :created
return
end
@ -535,7 +535,7 @@ class ApplicationController < ActionController::Base
end
def model_create_render_item(generic_object)
render json: generic_object.attributes_with_associations, status: :created
render json: generic_object.attributes_with_association_ids, status: :created
end
def model_update_render(object, params)
@ -543,7 +543,7 @@ class ApplicationController < ActionController::Base
# find object
generic_object = object.find(params[:id])
clean_params = object.param_association_lookup(params)
clean_params = object.association_name_to_id_convert(params)
clean_params = object.param_cleanup(clean_params, true)
generic_object.with_lock do
@ -552,11 +552,11 @@ class ApplicationController < ActionController::Base
generic_object.update_attributes!(clean_params)
# set relations
generic_object.param_set_associations(params)
generic_object.associations_from_param(params)
end
if params[:expand]
render json: generic_object.attributes_with_relation_names, status: :ok
render json: generic_object.attributes_with_association_names, status: :ok
return
end
@ -564,7 +564,7 @@ class ApplicationController < ActionController::Base
end
def model_update_render_item(generic_object)
render json: generic_object.attributes_with_associations, status: :ok
render json: generic_object.attributes_with_association_ids, status: :ok
end
def model_destroy_render(object, params)
@ -581,7 +581,7 @@ class ApplicationController < ActionController::Base
if params[:expand]
generic_object = object.find(params[:id])
render json: generic_object.attributes_with_relation_names, status: :ok
render json: generic_object.attributes_with_association_names, status: :ok
return
end
@ -596,7 +596,7 @@ class ApplicationController < ActionController::Base
end
def model_show_render_item(generic_object)
render json: generic_object.attributes_with_associations, status: :ok
render json: generic_object.attributes_with_association_ids, status: :ok
end
def model_index_render(object, params)
@ -620,7 +620,7 @@ class ApplicationController < ActionController::Base
if params[:expand]
list = []
generic_objects.each { |generic_object|
list.push generic_object.attributes_with_relation_names
list.push generic_object.attributes_with_association_names
}
render json: list, status: :ok
return
@ -642,7 +642,7 @@ class ApplicationController < ActionController::Base
generic_objects_with_associations = []
generic_objects.each { |item|
generic_objects_with_associations.push item.attributes_with_associations
generic_objects_with_associations.push item.attributes_with_association_ids
}
model_index_render_result(generic_objects_with_associations)
end

View file

@ -43,7 +43,7 @@ class ObjectManagerAttributesController < ApplicationController
position: 1550,
editable: true,
)
render json: object_manager_attribute.attributes_with_associations, status: :created
render json: object_manager_attribute.attributes_with_association_ids, status: :created
rescue => e
raise Exceptions::UnprocessableEntity, e
end
@ -64,7 +64,7 @@ class ObjectManagerAttributesController < ApplicationController
position: 1550,
editable: true,
)
render json: object_manager_attribute.attributes_with_associations, status: :ok
render json: object_manager_attribute.attributes_with_association_ids, status: :ok
rescue => e
raise Exceptions::UnprocessableEntity, e
end

View file

@ -72,7 +72,7 @@ curl http://localhost/api/v1/organizations -v -u #{login}:#{password}
if params[:expand]
list = []
organizations.each { |organization|
list.push organization.attributes_with_relation_names
list.push organization.attributes_with_association_names
}
render json: list, status: :ok
return
@ -124,7 +124,7 @@ curl http://localhost/api/v1/organizations/#{id} -v -u #{login}:#{password}
end
if params[:expand]
organization = Organization.find(params[:id]).attributes_with_relation_names
organization = Organization.find(params[:id]).attributes_with_association_names
render json: organization, status: :ok
return
end
@ -256,7 +256,7 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co
if params[:expand]
list = []
organization_all.each { |organization|
list.push organization.attributes_with_relation_names
list.push organization.attributes_with_association_names
}
render json: list, status: :ok
return

View file

@ -17,7 +17,7 @@ class TicketArticlesController < ApplicationController
article_permission(article)
if params[:expand]
result = article.attributes_with_relation_names
result = article.attributes_with_association_names
result[:attachments] = article.attachments
render json: result, status: :ok
return
@ -29,7 +29,7 @@ class TicketArticlesController < ApplicationController
return
end
render json: article.attributes_with_relation_names
render json: article.attributes_with_association_names
end
# GET /ticket_articles/by_ticket/1
@ -46,7 +46,7 @@ class TicketArticlesController < ApplicationController
# ignore internal article if customer is requesting
next if article.internal == true && current_user.permissions?('ticket.customer')
result = article.attributes_with_relation_names
result = article.attributes_with_association_names
# add attachments
result[:attachments] = article.attachments
@ -79,7 +79,7 @@ class TicketArticlesController < ApplicationController
# ignore internal article if customer is requesting
next if article.internal == true && current_user.permissions?('ticket.customer')
articles.push article.attributes_with_relation_names
articles.push article.attributes_with_association_names
}
render json: articles
end
@ -91,7 +91,7 @@ class TicketArticlesController < ApplicationController
article = article_create(ticket, params)
if params[:expand]
result = article.attributes_with_relation_names
result = article.attributes_with_association_names
result[:attachments] = article.attachments
render json: result, status: :created
return
@ -103,7 +103,7 @@ class TicketArticlesController < ApplicationController
return
end
render json: article.attributes_with_relation_names, status: :created
render json: article.attributes_with_association_names, status: :created
end
# PUT /articles/1
@ -117,13 +117,13 @@ class TicketArticlesController < ApplicationController
raise Exceptions::NotAuthorized, 'Not authorized (ticket.agent or admin permission required)!'
end
clean_params = Ticket::Article.param_association_lookup(params)
clean_params = Ticket::Article.association_name_to_id_convert(params)
clean_params = Ticket::Article.param_cleanup(clean_params, true)
article.update_attributes!(clean_params)
if params[:expand]
result = article.attributes_with_relation_names
result = article.attributes_with_association_names
result[:attachments] = article.attachments
render json: result, status: :ok
return
@ -135,7 +135,7 @@ class TicketArticlesController < ApplicationController
return
end
render json: article.attributes_with_relation_names, status: :ok
render json: article.attributes_with_association_names, status: :ok
end
# DELETE /articles/1

View file

@ -23,7 +23,7 @@ class TicketsController < ApplicationController
if params[:expand]
list = []
tickets.each { |ticket|
list.push ticket.attributes_with_relation_names
list.push ticket.attributes_with_association_names
}
render json: list, status: :ok
return
@ -54,7 +54,7 @@ class TicketsController < ApplicationController
ticket_permission(ticket)
if params[:expand]
result = ticket.attributes_with_relation_names
result = ticket.attributes_with_association_names
render json: result, status: :ok
return
end
@ -75,7 +75,7 @@ class TicketsController < ApplicationController
# POST /api/v1/tickets
def create
clean_params = Ticket.param_association_lookup(params)
clean_params = Ticket.association_name_to_id_convert(params)
# overwrite params
if !current_user.permissions?('ticket.agent')
@ -166,7 +166,7 @@ class TicketsController < ApplicationController
end
if params[:expand]
result = ticket.reload.attributes_with_relation_names
result = ticket.reload.attributes_with_association_names
render json: result, status: :created
return
end
@ -186,7 +186,7 @@ class TicketsController < ApplicationController
ticket = Ticket.find(params[:id])
ticket_permission(ticket)
clean_params = Ticket.param_association_lookup(params)
clean_params = Ticket.association_name_to_id_convert(params)
clean_params = Ticket.param_cleanup(clean_params, true)
# overwrite params
@ -204,7 +204,7 @@ class TicketsController < ApplicationController
end
if params[:expand]
result = ticket.reload.attributes_with_relation_names
result = ticket.reload.attributes_with_association_names
render json: result, status: :ok
return
end
@ -430,7 +430,7 @@ class TicketsController < ApplicationController
if params[:expand]
list = []
tickets.each { |ticket|
list.push ticket.attributes_with_relation_names
list.push ticket.attributes_with_association_names
}
render json: list, status: :ok
return

View file

@ -34,7 +34,7 @@ class UsersController < ApplicationController
if params[:expand]
list = []
users.each { |user|
list.push user.attributes_with_relation_names
list.push user.attributes_with_association_names
}
render json: list, status: :ok
return
@ -56,7 +56,7 @@ class UsersController < ApplicationController
users_all = []
users.each { |user|
users_all.push User.lookup(id: user.id).attributes_with_associations
users_all.push User.lookup(id: user.id).attributes_with_association_ids
}
render json: users_all, status: :ok
end
@ -79,7 +79,7 @@ class UsersController < ApplicationController
permission_check_local
if params[:expand]
user = User.find(params[:id]).attributes_with_relation_names
user = User.find(params[:id]).attributes_with_association_names
render json: user, status: :ok
return
end
@ -90,7 +90,7 @@ class UsersController < ApplicationController
return
end
user = User.find(params[:id]).attributes_with_associations
user = User.find(params[:id]).attributes_with_association_ids
user.delete('password')
render json: user
end
@ -109,10 +109,10 @@ class UsersController < ApplicationController
# in case of authentication, set current_user to access later
authentication_check_only({})
clean_params = User.param_association_lookup(params)
clean_params = User.association_name_to_id_convert(params)
clean_params = User.param_cleanup(clean_params, true)
user = User.new(clean_params)
user.param_set_associations(params)
user.associations_from_param(params)
# check if it's first user, the admin user
# inital admin account
@ -227,12 +227,12 @@ class UsersController < ApplicationController
end
if params[:expand]
user = User.find(user.id).attributes_with_relation_names
user = User.find(user.id).attributes_with_association_names
render json: user, status: :created
return
end
user_new = User.find(user.id).attributes_with_associations
user_new = User.find(user.id).attributes_with_association_ids
user_new.delete('password')
render json: user_new, status: :created
end
@ -253,7 +253,7 @@ class UsersController < ApplicationController
permission_check_local
user = User.find(params[:id])
clean_params = User.param_association_lookup(params)
clean_params = User.association_name_to_id_convert(params)
clean_params = User.param_cleanup(clean_params, true)
# permission check
@ -264,29 +264,29 @@ class UsersController < ApplicationController
# only allow Admin's
if current_user.permissions?('admin.user') && (params[:role_ids] || params[:roles])
user.role_ids = params[:role_ids]
user.param_set_associations({ role_ids: params[:role_ids], roles: params[:roles] })
user.associations_from_param({ role_ids: params[:role_ids], roles: params[:roles] })
end
# only allow Admin's
if current_user.permissions?('admin.user') && (params[:group_ids] || params[:groups])
user.group_ids = params[:group_ids]
user.param_set_associations({ group_ids: params[:group_ids], groups: params[:groups] })
user.associations_from_param({ group_ids: params[:group_ids], groups: params[:groups] })
end
# only allow Admin's and Agent's
if current_user.permissions?(['admin.user', 'ticket.agent']) && (params[:organization_ids] || params[:organizations])
user.param_set_associations({ organization_ids: params[:organization_ids], organizations: params[:organizations] })
user.associations_from_param({ organization_ids: params[:organization_ids], organizations: params[:organizations] })
end
if params[:expand]
user = User.find(user.id).attributes_with_relation_names
user = User.find(user.id).attributes_with_association_names
render json: user, status: :ok
return
end
end
# get new data
user_new = User.find(user.id).attributes_with_associations
user_new = User.find(user.id).attributes_with_association_ids
user_new.delete('password')
render json: user_new, status: :ok
end
@ -318,7 +318,7 @@ class UsersController < ApplicationController
def me
if params[:expand]
user = current_user.attributes_with_relation_names
user = current_user.attributes_with_association_names
render json: user, status: :ok
return
end
@ -329,7 +329,7 @@ class UsersController < ApplicationController
return
end
user = current_user.attributes_with_associations
user = current_user.attributes_with_association_ids
user.delete('password')
render json: user
end
@ -390,7 +390,7 @@ class UsersController < ApplicationController
if params[:expand]
list = []
user_all.each { |user|
list.push user.attributes_with_relation_names
list.push user.attributes_with_association_names
}
render json: list, status: :ok
return

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::ActivityStreamBase
module ApplicationModel::ActivityStreamLoggable
extend ActiveSupport::Concern
=begin
@ -25,19 +26,31 @@ returns
# return if we run on init mode
return if !Setting.get('system_init_done')
permission = self.class.activity_stream_support_config[:permission]
permission = self.class.instance_variable_get(:@activity_stream_permission)
updated_at = self.updated_at
if force
updated_at = Time.zone.now
end
ActivityStream.add(
o_id: self['id'],
type: type,
object: self.class.name,
group_id: self['group_id'],
permission: permission,
created_at: updated_at,
attributes = {
o_id: self['id'],
type: type,
object: self.class.name,
group_id: self['group_id'],
permission: permission,
created_at: updated_at,
created_by_id: user_id,
)
}.merge(activity_stream_log_attributes)
ActivityStream.add(attributes)
end
private
# callback function to overwrite
# default history stream log attributes
# gets called from activity_stream_log
def activity_stream_log_attributes
{}
end
end

View file

@ -1,44 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::Assets
=begin
get all assets / related models for this user
user = User.find(123)
result = user.assets(assets_if_exists)
returns
result = {
:User => {
123 => user_model_123,
1234 => user_model_1234,
}
}
=end
def assets(data = {})
app_model = self.class.to_app_model
if !data[ app_model ]
data[ app_model ] = {}
end
if !data[ app_model ][ id ]
data[ app_model ][ id ] = attributes_with_associations
end
return data if !self['created_by_id'] && !self['updated_by_id']
app_model_user = User.to_app_model
%w(created_by_id updated_by_id).each { |local_user_id|
next if !self[ local_user_id ]
next if data[ app_model_user ] && data[ app_model_user ][ self[ local_user_id ] ]
user = User.lookup(id: self[ local_user_id ])
next if !user
data = user.assets(data)
}
data
end
end

View file

@ -1,16 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class ApplicationModel::BackgroundJobSearchIndex
def initialize(object, o_id)
@object = object
@o_id = o_id
end
def perform
record = Object.const_get(@object).lookup(id: @o_id)
if !record
Rails.logger.info "Can't index #{@object}.find(#{@o_id}), no such record found"
return
end
record.search_index_update_backend
end
end

View file

@ -0,0 +1,70 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::Cacheable
extend ActiveSupport::Concern
included do
before_create :cache_delete
after_create :cache_delete
after_update :cache_delete
after_touch :cache_delete
after_destroy :cache_delete
end
def cache_update(o)
cache_delete if respond_to?('cache_delete')
o.cache_delete if o.respond_to?('cache_delete')
end
def cache_delete
keys = []
# delete by id caches
keys.push "#{self.class}::#{id}"
# delete by id with attributes_with_association_ids caches
keys.push "#{self.class}::aws::#{id}"
# delete by name caches
if self[:name]
keys.push "#{self.class}::#{name}"
end
# delete by login caches
if self[:login]
keys.push "#{self.class}::#{login}"
end
keys.each { |key|
Cache.delete(key)
}
# delete old name / login caches
if changed?
if changes.key?('name')
name = changes['name'][0]
key = "#{self.class}::#{name}"
Cache.delete(key)
end
if changes.key?('login')
name = changes['login'][0]
key = "#{self.class}::#{name}"
Cache.delete(key)
end
end
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
def cache_set(data_id, data)
key = "#{self}::#{data_id}"
Cache.write(key, data)
end
def cache_get(data_id)
key = "#{self}::#{data_id}"
Cache.get(key)
end
end
end

View file

@ -0,0 +1,87 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::CanLookup
extend ActiveSupport::Concern
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
lookup model from cache (if exists) or retrieve it from db, id, name, login or email possible
result = Model.lookup(id: 123)
result = Model.lookup(name: 'some name')
result = Model.lookup(login: 'some login')
result = Model.lookup(email: 'some login')
returns
result = model # with all attributes
=end
def lookup(data)
if data[:id]
cache = cache_get(data[:id])
return cache if cache
record = find_by(id: data[:id])
cache_set(data[:id], record)
return record
elsif data[:name]
cache = cache_get(data[:name])
return cache if cache
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(name) = LOWER(?)', data[:name])
else
where(name: data[:name])
end
records.each { |loop_record|
if loop_record.name == data[:name]
cache_set(data[:name], loop_record)
return loop_record
end
}
return
elsif data[:login]
cache = cache_get(data[:login])
return cache if cache
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(login) = LOWER(?)', data[:login])
else
where(login: data[:login])
end
records.each { |loop_record|
if loop_record.login == data[:login]
cache_set(data[:login], loop_record)
return loop_record
end
}
return
elsif data[:email]
cache = cache_get(data[:email])
return cache if cache
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(email) = LOWER(?)', data[:email])
else
where(email: data[:email])
end
records.each { |loop_record|
if loop_record.email == data[:email]
cache_set(data[:email], loop_record)
return loop_record
end
}
return
end
raise ArgumentError, 'Need name, id, login or email for lookup()'
end
end
end

View file

@ -0,0 +1,66 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::CanLookupSearchIndexAttributes
extend ActiveSupport::Concern
=begin
lookup name of ref. objects
ticket = Ticket.find(3)
attributes = ticket.search_index_attribute_lookup
returns
attributes # object with lookup data
=end
def search_index_attribute_lookup
attributes = self.attributes
self.attributes.each { |key, value|
next if !value
# get attribute name
attribute_name_with_id = key.to_s
attribute_name = key.to_s
next if attribute_name[-3, 3] != '_id'
attribute_name = attribute_name[ 0, attribute_name.length - 3 ]
# check if attribute method exists
next if !respond_to?(attribute_name)
# check if method has own class
relation_class = send(attribute_name).class
next if !relation_class
# lookup ref object
relation_model = relation_class.lookup(id: value)
next if !relation_model
# get name of ref object
value = nil
if relation_model.respond_to?('search_index_data')
value = relation_model.send('search_index_data')
end
if relation_model.respond_to?('name')
value = relation_model.send('name')
end
next if !value
# save name of ref object
attributes[ attribute_name ] = value
}
ignored_attributes = self.class.instance_variable_get(:@search_index_attributes_ignored) || []
return attributes if ignored_attributes.empty?
ignored_attributes.each { |attribute|
attributes.delete(attribute.to_s)
}
attributes
end
end

View file

@ -0,0 +1,38 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::ChecksAttributeLength
extend ActiveSupport::Concern
included do
before_create :check_attribute_length
before_update :check_attribute_length
end
=begin
check string/varchar size and cut them if needed
=end
def check_attribute_length
attributes.each { |attribute|
next if !self[ attribute[0] ]
next if !self[ attribute[0] ].instance_of?(String)
next if self[ attribute[0] ].empty?
column = self.class.columns_hash[ attribute[0] ]
next if !column
limit = column.limit
if column && limit
current_length = attribute[1].to_s.length
if limit < current_length
logger.warn "WARNING: cut string because of database length #{self.class}.#{attribute[0]}(#{limit} but is #{current_length}:#{attribute[1]})"
self[ attribute[0] ] = attribute[1][ 0, limit ]
end
end
# strip 4 bytes utf8 chars if needed
if column && self[ attribute[0] ]
self[attribute[0]] = self[ attribute[0] ].utf8_to_3bytesutf8
end
}
end
end

View file

@ -0,0 +1,90 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::CleansParam
extend ActiveSupport::Concern
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
remove all not used model attributes of params
result = Model.param_cleanup(params)
for object creation, ignore id's
result = Model.param_cleanup(params, true)
returns
result = params # params with valid attributes of model
=end
def param_cleanup(params, new_object = false)
if params.respond_to?('permit!')
params.permit!
end
if params.nil?
raise ArgumentError, "No params for #{self}!"
end
data = {}
params.each { |key, value|
data[key.to_sym] = value
}
# ignore id for new objects
if new_object && params[:id]
data.delete(:id)
end
# only use object attributes
clean_params = {}
new.attributes.each { |attribute, _value|
next if !data.key?(attribute.to_sym)
# check reference records, referenced by _id attributes
reflect_on_all_associations.map { |assoc|
class_name = assoc.options[:class_name]
next if !class_name
name = "#{assoc.name}_id".to_sym
next if !data.key?(name)
next if data[name].blank?
next if assoc.klass.lookup(id: data[name])
raise ArgumentError, "Invalid value for param '#{name}': #{data[name].inspect}"
}
clean_params[attribute.to_sym] = data[attribute.to_sym]
}
# we do want to set this via database
filter_unused_params(clean_params)
end
private
=begin
remove all not used params of object (per default :updated_at, :created_at, :updated_by_id and :created_by_id)
result = Model.filter_unused_params(params)
returns
result = params # params without listed attributes
=end
def filter_unused_params(data)
# we do want to set this via database
[:action, :controller, :updated_at, :created_at, :updated_by_id, :created_by_id, :updated_by, :created_by].each { |key|
data.delete(key)
}
data
end
end
end

View file

@ -0,0 +1,21 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::CleansRecentView
extend ActiveSupport::Concern
included do
before_destroy :recent_view_destroy
end
=begin
delete object recent viewed list, will be executed automatically
model = Model.find(123)
model.recent_view_destroy
=end
def recent_view_destroy
RecentView.log_destroy(self.class.to_s, id)
end
end

View file

@ -0,0 +1,64 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::FillsByUserColumns
extend ActiveSupport::Concern
included do
before_create :fill_up_user_create
before_update :fill_up_user_update
end
=begin
set created_by_id & updated_by_id if not given based on UserInfo (current session)
Used as before_create callback, no own use needed
result = Model.fill_up_user_create(params)
returns
result = params # params with updated_by_id & created_by_id if not given based on UserInfo (current session)
=end
def fill_up_user_create
if self.class.column_names.include? 'updated_by_id'
if UserInfo.current_user_id
if updated_by_id && updated_by_id != UserInfo.current_user_id
logger.info "NOTICE create - self.updated_by_id is different: #{updated_by_id}/#{UserInfo.current_user_id}"
end
self.updated_by_id = UserInfo.current_user_id
end
end
return if !self.class.column_names.include? 'created_by_id'
return if !UserInfo.current_user_id
if created_by_id && created_by_id != UserInfo.current_user_id
logger.info "NOTICE create - self.created_by_id is different: #{created_by_id}/#{UserInfo.current_user_id}"
end
self.created_by_id = UserInfo.current_user_id
end
=begin
set updated_by_id if not given based on UserInfo (current session)
Used as before_update callback, no own use needed
result = Model.fill_up_user_update(params)
returns
result = params # params with updated_by_id & created_by_id if not given based on UserInfo (current session)
=end
def fill_up_user_update
return if !self.class.column_names.include? 'updated_by_id'
return if !UserInfo.current_user_id
self.updated_by_id = UserInfo.current_user_id
end
end

View file

@ -0,0 +1,201 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::HandlesCreatesAndUpdates
extend ActiveSupport::Concern
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
create model if not exists (check exists based on id, name, login, email or locale)
result = Model.create_if_not_exists(attributes)
returns
result = model # with all attributes
=end
def create_if_not_exists(data)
if data[:id]
record = find_by(id: data[:id])
return record if record
elsif data[:name]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(name) = LOWER(?)', data[:name])
else
where(name: data[:name])
end
records.each { |loop_record|
return loop_record if loop_record.name == data[:name]
}
elsif data[:login]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(login) = LOWER(?)', data[:login])
else
where(login: data[:login])
end
records.each { |loop_record|
return loop_record if loop_record.login == data[:login]
}
elsif data[:email]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(email) = LOWER(?)', data[:email])
else
where(email: data[:email])
end
records.each { |loop_record|
return loop_record if loop_record.email == data[:email]
}
elsif data[:locale] && data[:source]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(locale) = LOWER(?) AND LOWER(source) = LOWER(?)', data[:locale], data[:source])
else
where(locale: data[:locale], source: data[:source])
end
records.each { |loop_record|
return loop_record if loop_record.source == data[:source]
}
end
create(data)
end
=begin
Model.create_or_update with ref lookups
result = Model.create_or_update(attributes)
returns
result = model # with all attributes
=end
def create_or_update_with_ref(data)
data = association_name_to_id_convert(data)
create_or_update(data)
end
=begin
Model.create_if_not_exists with ref lookups
result = Model.create_if_not_exists_with_ref(attributes)
returns
result = model # with all attributes
=end
def create_if_not_exists_with_ref(data)
data = association_name_to_id_convert(data)
create_or_update(data)
end
=begin
create or update model (check exists based on id, name, login, email or locale)
result = Model.create_or_update(attributes)
returns
result = model # with all attributes
=end
def create_or_update(data)
if data[:id]
record = find_by(id: data[:id])
if record
record.update_attributes(data)
return record
end
record = new(data)
record.save
return record
elsif data[:name]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(name) = LOWER(?)', data[:name])
else
where(name: data[:name])
end
records.each { |loop_record|
if loop_record.name == data[:name]
loop_record.update_attributes(data)
return loop_record
end
}
record = new(data)
record.save
return record
elsif data[:login]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(login) = LOWER(?)', data[:login])
else
where(login: data[:login])
end
records.each { |loop_record|
if loop_record.login.casecmp(data[:login]).zero?
loop_record.update_attributes(data)
return loop_record
end
}
record = new(data)
record.save
return record
elsif data[:email]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(email) = LOWER(?)', data[:email])
else
where(email: data[:email])
end
records.each { |loop_record|
if loop_record.email.casecmp(data[:email]).zero?
loop_record.update_attributes(data)
return loop_record
end
}
record = new(data)
record.save
return record
elsif data[:locale]
# do lookup with == to handle case insensitive databases
records = if Rails.application.config.db_case_sensitive
where('LOWER(locale) = LOWER(?)', data[:locale])
else
where(locale: data[:locale])
end
records.each { |loop_record|
if loop_record.locale.casecmp(data[:locale]).zero?
loop_record.update_attributes(data)
return loop_record
end
}
record = new(data)
record.save
return record
else
raise ArgumentError, 'Need name, login, email or locale for create_or_update()'
end
end
end
end

View file

@ -0,0 +1,153 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::HasAssets
extend ActiveSupport::Concern
=begin
get all assets / related models for this user
user = User.find(123)
result = user.assets(assets_if_exists)
returns
result = {
:User => {
123 => user_model_123,
1234 => user_model_1234,
}
}
=end
def assets(data = {})
app_model = self.class.to_app_model
if !data[ app_model ]
data[ app_model ] = {}
end
if !data[ app_model ][ id ]
data[ app_model ][ id ] = attributes_with_association_ids
end
return data if !self['created_by_id'] && !self['updated_by_id']
app_model_user = User.to_app_model
%w(created_by_id updated_by_id).each { |local_user_id|
next if !self[ local_user_id ]
next if data[ app_model_user ] && data[ app_model_user ][ self[ local_user_id ] ]
user = User.lookup(id: self[ local_user_id ])
next if !user
data = user.assets(data)
}
data
end
=begin
get assets and record_ids of selector
model = Model.find(123)
assets = model.assets_of_selector('attribute_name_of_selector', assets)
=end
def assets_of_selector(selector, assets = {})
# get assets of condition
models = Models.all
send(selector).each { |item, content|
attribute = item.split(/\./)
next if !attribute[1]
begin
attribute_class = attribute[0].to_classname.constantize
rescue => e
logger.error "Unable to get asset for '#{attribute[0]}': #{e.inspect}"
next
end
reflection = attribute[1].sub(/_id$/, '')
#reflection = reflection.to_sym
next if !models[attribute_class]
next if !models[attribute_class][:reflections]
next if !models[attribute_class][:reflections][reflection]
next if !models[attribute_class][:reflections][reflection].klass
attribute_ref_class = models[attribute_class][:reflections][reflection].klass
if content['value'].instance_of?(Array)
content['value'].each { |item_id|
attribute_object = attribute_ref_class.find_by(id: item_id)
if attribute_object
assets = attribute_object.assets(assets)
end
}
else
attribute_object = attribute_ref_class.find_by(id: content['value'])
if attribute_object
assets = attribute_object.assets(assets)
end
end
}
assets
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
return object and assets
data = Model.full(123)
data = {
id: 123,
assets: assets,
}
=end
def full(id)
object = find(id)
assets = object.assets({})
{
id: id,
assets: assets,
}
end
=begin
get assets of object list
list = [
{
object => 'Ticket',
o_id => 1,
},
{
object => 'User',
o_id => 121,
},
]
assets = Model.assets_of_object_list(list, assets)
=end
def assets_of_object_list(list, assets = {})
list.each { |item|
require item['object'].to_filename
record = Kernel.const_get(item['object']).find(item['o_id'])
assets = record.assets(assets)
if item['created_by_id']
user = User.find(item['created_by_id'])
assets = user.assets(assets)
end
if item['updated_by_id']
user = User.find(item['updated_by_id'])
assets = user.assets(assets)
end
}
assets
end
end
end

View file

@ -0,0 +1,357 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::HasAssociations
extend ActiveSupport::Concern
=begin
set relations of model based on params
model = Model.find(1)
result = model.associations_from_param(params)
returns
result = true|false
=end
def associations_from_param(params)
# set relations by id/verify if ref exists
self.class.reflect_on_all_associations.map { |assoc|
assoc_name = assoc.name
real_ids = assoc_name[0, assoc_name.length - 1] + '_ids'
real_ids = real_ids.to_sym
next if !params.key?(real_ids)
list_of_items = params[real_ids]
if !params[real_ids].instance_of?(Array)
list_of_items = [ params[real_ids] ]
end
list = []
list_of_items.each { |item_id|
next if !item_id
lookup = assoc.klass.lookup(id: item_id)
# complain if we found no reference
if !lookup
raise ArgumentError, "No value found for '#{assoc_name}' with id #{item_id.inspect}"
end
list.push item_id
}
send("#{real_ids}=", list)
}
# set relations by name/lookup
self.class.reflect_on_all_associations.map { |assoc|
assoc_name = assoc.name
real_ids = assoc_name[0, assoc_name.length - 1] + '_ids'
next if !respond_to?(real_ids)
real_values = assoc_name[0, assoc_name.length - 1] + 's'
real_values = real_values.to_sym
next if !respond_to?(real_values)
next if !params[real_values]
next if !params[real_values].instance_of?(Array)
list = []
class_object = assoc.klass
params[real_values].each { |value|
lookup = nil
if class_object == User
if !lookup
lookup = class_object.lookup(login: value)
end
if !lookup
lookup = class_object.lookup(email: value)
end
else
lookup = class_object.lookup(name: value)
end
# complain if we found no reference
if !lookup
raise ArgumentError, "No lookup value found for '#{assoc_name}': #{value.inspect}"
end
list.push lookup.id
}
send("#{real_ids}=", list)
}
end
=begin
get relations of model based on params
model = Model.find(1)
attributes = model.attributes_with_association_ids
returns
hash with attributes and association ids
=end
def attributes_with_association_ids
key = "#{self.class}::aws::#{id}"
cache = Cache.get(key)
return cache if cache
ignored_attributes = self.class.instance_variable_get(:@association_attributes_ignored) || []
# get relations
attributes = self.attributes
self.class.reflect_on_all_associations.map { |assoc|
real_ids = assoc.name.to_s[0, assoc.name.to_s.length - 1] + '_ids'
next if ignored_attributes.include?(real_ids.to_sym)
next if !respond_to?(real_ids)
attributes[real_ids] = send(real_ids)
}
Cache.write(key, attributes)
attributes
end
=begin
get relation name of model based on params
model = Model.find(1)
attributes = model.attributes_with_association_names
returns
hash with attributes, association ids, association names and relation name
=end
def attributes_with_association_names
# get relations
attributes = attributes_with_association_ids
self.class.reflect_on_all_associations.map { |assoc|
next if !respond_to?(assoc.name)
ref = send(assoc.name)
next if !ref
if ref.respond_to?(:first)
attributes[assoc.name.to_s] = []
ref.each { |item|
if item[:login]
attributes[assoc.name.to_s].push item[:login]
next
end
next if !item[:name]
attributes[assoc.name.to_s].push item[:name]
}
if ref.count.positive? && attributes[assoc.name.to_s].empty?
attributes.delete(assoc.name.to_s)
end
next
end
if ref[:login]
attributes[assoc.name.to_s] = ref[:login]
next
end
next if !ref[:name]
attributes[assoc.name.to_s] = ref[:name]
}
# fill created_by/updated_by
{
'created_by_id' => 'created_by',
'updated_by_id' => 'updated_by',
}.each { |source, destination|
next if !attributes[source]
user = User.lookup(id: attributes[source])
next if !user
attributes[destination] = user.login
}
# remove forbitten attributes
%w(password token tokens token_ids).each { |item|
attributes.delete(item)
}
attributes
end
=begin
reference if association id check
model = Model.find(123)
attributes = model.association_id_validation('attribute_id', value)
returns
true | false
=end
def association_id_validation(attribute_id, value)
return true if value.nil?
attributes.each { |key, _value|
next if key != attribute_id
# check if id is assigned
next if !key.end_with?('_id')
key_short = key.chomp('_id')
self.class.reflect_on_all_associations.map { |assoc|
next if assoc.name.to_s != key_short
item = assoc.class_name.constantize
return false if !item.respond_to?(:find_by)
ref_object = item.find_by(id: value)
return false if !ref_object
return true
}
}
true
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
serve methode to ignore model attribute associations
class Model < ApplicationModel
include AssociationConcern
association_attributes_ignored :user_ids
end
=end
def association_attributes_ignored(*attributes)
@association_attributes_ignored = attributes
end
=begin
do name/login/email based lookup for associations
params = {
login: 'some login',
firstname: 'some firstname',
lastname: 'some lastname',
email: 'some email',
organization: 'some organization',
roles: ['Agent', 'Admin'],
}
attributes = Model.association_name_to_id_convert(params)
returns
attributes = params # params with possible lookups
attributes = {
login: 'some login',
firstname: 'some firstname',
lastname: 'some lastname',
email: 'some email',
organization_id: 123,
role_ids: [2,1],
}
=end
def association_name_to_id_convert(params)
data = {}
params.each { |key, value|
data[key.to_sym] = value
}
data.symbolize_keys!
available_attributes = attribute_names
reflect_on_all_associations.map { |assoc|
assoc_name = assoc.name
value = data[assoc_name]
next if !value # next if we do not have a value
ref_name = "#{assoc_name}_id"
# handle _id values
if available_attributes.include?(ref_name) # if we do have an _id attribute
next if data[ref_name.to_sym] # next if we have already the _id filled
# get association class and do lookup
class_object = assoc.klass
lookup = nil
if class_object == User
if value.instance_of?(String)
if !lookup
lookup = class_object.lookup(login: value)
end
if !lookup
lookup = class_object.lookup(email: value)
end
else
raise ArgumentError, "String is needed as ref value #{value.inspect} for '#{assoc_name}'"
end
else
lookup = class_object.lookup(name: value)
end
# complain if we found no reference
if !lookup
raise ArgumentError, "No lookup value found for '#{assoc_name}': #{value.inspect}"
end
# release data value
data.delete(assoc_name)
# remember id reference
data[ref_name.to_sym] = lookup.id
next
end
next if !value.instance_of?(Array)
next if value.empty?
next if !value[0].instance_of?(String)
# handle _ids values
next if !assoc_name.to_s.end_with?('s')
ref_names = "#{assoc_name.to_s.chomp('s')}_ids"
generic_object_tmp = new
next unless generic_object_tmp.respond_to?(ref_names) # if we do have an _ids attribute
next if data[ref_names.to_sym] # next if we have already the _ids filled
# get association class and do lookup
class_object = assoc.klass
lookup_ids = []
value.each { |item|
lookup = nil
if class_object == User
if item.instance_of?(String)
if !lookup
lookup = class_object.lookup(login: item)
end
if !lookup
lookup = class_object.lookup(email: item)
end
else
raise ArgumentError, "String is needed in array ref as ref value #{value.inspect} for '#{assoc_name}'"
end
else
lookup = class_object.lookup(name: item)
end
# complain if we found no reference
if !lookup
raise ArgumentError, "No lookup value found for '#{assoc_name}': #{item.inspect}"
end
lookup_ids.push lookup.id
}
# release data value
data.delete(assoc_name)
# remember id reference
data[ref_names.to_sym] = lookup_ids
}
data
end
end
end

View file

@ -0,0 +1,74 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::HasAttachments
extend ActiveSupport::Concern
included do
after_create :attachments_buffer_check
after_update :attachments_buffer_check
end
=begin
get list of attachments of this object
item = Model.find(123)
list = item.attachments
returns
# array with Store model objects
=end
def attachments
Store.list(object: self.class.to_s, o_id: id)
end
=begin
store attachments for this object
item = Model.find(123)
item.attachments = [ Store-Object1, Store-Object2 ]
=end
def attachments=(attachments)
self.attachments_buffer = attachments
# update if object already exists
return if !(id && id.nonzero?)
attachments_buffer_check
end
private
def attachments_buffer
@attachments_buffer_data
end
def attachments_buffer=(attachments)
@attachments_buffer_data = attachments
end
def attachments_buffer_check
# do nothing if no attachment exists
return 1 if attachments_buffer.nil?
# store attachments
article_store = []
attachments_buffer.each do |attachment|
article_store.push Store.add(
object: self.class.to_s,
o_id: id,
data: attachment.content,
filename: attachment.filename,
preferences: attachment.preferences,
created_by_id: created_by_id,
)
end
attachments_buffer = nil
end
end

View file

@ -0,0 +1,46 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::HasLatestChangeTimestamp
extend ActiveSupport::Concern
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
get latest updated_at object timestamp
latest_change = Ticket.latest_change
returns
result = timestamp
=end
def latest_change
key = "#{new.class.name}_latest_change"
updated_at = Cache.get(key)
# if we do not have it cached, do lookup
if !updated_at
o = select(:updated_at).order(updated_at: :desc).limit(1).first
if o
updated_at = o.updated_at
latest_change_set(updated_at)
end
end
updated_at
end
def latest_change_set(updated_at)
key = "#{new.class.name}_latest_change"
expires_in = 31_536_000 # 1 year
if updated_at.nil?
Cache.delete(key)
else
Cache.write(key, updated_at, { expires_in: expires_in })
end
end
end
end

View file

@ -1,5 +1,6 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::HistoryLogBase
module ApplicationModel::HistoryLoggable
extend ActiveSupport::Concern
=begin
@ -14,16 +15,27 @@ returns
=end
def history_log (type, user_id, data = {})
data[:o_id] = self['id']
data[:history_type] = type
data[:history_object] = self.class.name
data[:related_o_id] = nil
data[:related_history_object] = nil
data[:created_by_id] = user_id
data[:updated_at] = updated_at
data[:created_at] = updated_at
History.add(data)
def history_log(type, user_id, attributes = {})
attributes.merge!(
o_id: self['id'],
history_type: type,
history_object: self.class.name,
related_o_id: nil,
related_history_object: nil,
created_by_id: user_id,
updated_at: updated_at,
created_at: updated_at,
).merge!(history_log_attributes)
History.add(attributes)
end
# callback function to overwrite
# default history log attributes
# gets called from history_log
def history_log_attributes
{}
end
=begin

View file

@ -0,0 +1,19 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::Importable
extend ActiveSupport::Concern
included do
before_create :check_attributes_protected
end
def check_attributes_protected
import_class_list = ['Ticket', 'Ticket::Article', 'History', 'Ticket::State', 'Ticket::StateType', 'Ticket::Priority', 'Group', 'User', 'Role' ]
# do noting, use id as it is
return if !Setting.get('system_init_done')
return if Setting.get('import_mode') && import_class_list.include?(self.class.to_s)
self[:id] = nil
end
end

View file

@ -1,127 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::SearchIndexBase
=begin
collect data to index and send to backend
ticket = Ticket.find(123)
result = ticket.search_index_update_backend
returns
result = true # false
=end
def search_index_update_backend
return if !self.class.search_index_support_config
# fill up with search data
attributes = search_index_attribute_lookup
return if !attributes
# update backend
SearchIndexBackend.add(self.class.to_s, attributes)
end
=begin
get data to store in search index
ticket = Ticket.find(123)
result = ticket.search_index_data
returns
result = {
attribute1: 'some value',
attribute2: ['value 1', 'value 2'],
...
}
=end
def search_index_data
attributes = {}
%w(name note).each { |key|
next if !self[key]
next if self[key].respond_to?('empty?') && self[key].empty?
attributes[key] = self[key]
}
return if attributes.empty?
attributes
end
=begin
lookup name of ref. objects
ticket = Ticket.find(3)
attributes = ticket.search_index_attribute_lookup
returns
attributes # object with lookup data
=end
def search_index_attribute_lookup
attributes = self.attributes
self.attributes.each { |key, value|
next if !value
# get attribute name
attribute_name_with_id = key.to_s
attribute_name = key.to_s
next if attribute_name[-3, 3] != '_id'
attribute_name = attribute_name[ 0, attribute_name.length - 3 ]
# check if attribute method exists
next if !respond_to?(attribute_name)
# check if method has own class
relation_class = send(attribute_name).class
next if !relation_class
# lookup ref object
relation_model = relation_class.lookup(id: value)
next if !relation_model
# get name of ref object
value = nil
if relation_model.respond_to?('search_index_data')
value = relation_model.send('search_index_data')
end
if relation_model.respond_to?('name')
value = relation_model.send('name')
end
next if !value
# save name of ref object
attributes[ attribute_name ] = value
}
# default ignored attributes
config = self.class.search_index_support_config
if config
ignore_attributes = {}
if config[:ignore_attributes]
config[:ignore_attributes].each { |key, value|
ignore_attributes[key] = value
}
end
# remove ignored attributes
ignore_attributes.each { |key, value|
next if value != true
attributes.delete(key.to_s)
}
end
attributes
end
end

View file

@ -0,0 +1,30 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::TouchesReferences
extend ActiveSupport::Concern
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
touch references by params
Model.touch_reference_by_params(
object: 'Ticket',
o_id: 123,
)
=end
def touch_reference_by_params(data)
object_class = Kernel.const_get(data[:object])
object = object_class.lookup(id: data[:o_id])
return if !object
object.touch
rescue => e
logger.error e.message
logger.error e.backtrace.inspect
end
end
end

View file

@ -1,6 +1,9 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Calendar < ApplicationModel
include NotifiesClients
include UniqNamed
store :business_hours
store :public_holidays
@ -10,8 +13,6 @@ class Calendar < ApplicationModel
after_update :sync_default, :min_one_check
after_destroy :min_one_check
notify_clients_support
=begin
set inital default calendar
@ -38,7 +39,7 @@ returns calendar object
calendar_details = Service::GeoCalendar.location(ip)
return if !calendar_details
calendar_details['name'] = Calendar.genrate_uniq_name(calendar_details['name'])
calendar_details['name'] = Calendar.generate_uniq_name(calendar_details['name'])
calendar_details['default'] = true
calendar_details['created_by_id'] = 1
calendar_details['updated_by_id'] = 1

View file

@ -29,7 +29,7 @@ returns
data[ app_model ] = {}
end
if !data[ app_model ][ id ]
attributes = attributes_with_associations
attributes = attributes_with_association_ids
# remove passwords if use is no admin
access = false

View file

@ -602,7 +602,7 @@ returns
return if !class_object
class_instance = class_object.new
return false if !class_instance.association_id_check(attribute, value)
return false if !class_instance.association_id_validation(attribute, value)
true
end

View file

@ -0,0 +1,140 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Historisable
extend ActiveSupport::Concern
included do
attr_accessor :history_changes_last_done
after_create :history_create
after_update :history_update
after_destroy :history_destroy
end
=begin
log object create history, if configured - will be executed automatically
model = Model.find(123)
model.history_create
=end
def history_create
history_log('created', created_by_id)
end
=begin
log object update history with all updated attributes, if configured - will be executed automatically
model = Model.find(123)
model.history_update
=end
def history_update
return if !changed?
# return if it's no update
return if new_record?
# new record also triggers update, so ignore new records
changes = self.changes
if history_changes_last_done
history_changes_last_done.each { |key, value|
if changes.key?(key) && changes[key] == value
changes.delete(key)
end
}
end
self.history_changes_last_done = changes
#logger.info 'updated ' + self.changes.inspect
return if changes['id'] && !changes['id'][0]
ignored_attributes = self.class.instance_variable_get(:@history_attributes_ignored) || []
ignored_attributes += %i(created_at updated_at created_by_id updated_by_id)
changes.each { |key, value|
next if ignored_attributes.include?(key.to_sym)
# get attribute name
attribute_name = key.to_s
if attribute_name[-3, 3] == '_id'
attribute_name = attribute_name[ 0, attribute_name.length - 3 ]
end
value_id = []
value_str = [ value[0], value[1] ]
if key.to_s[-3, 3] == '_id'
value_id[0] = value[0]
value_id[1] = value[1]
if respond_to?(attribute_name) && send(attribute_name)
relation_class = send(attribute_name).class
if relation_class && value_id[0]
relation_model = relation_class.lookup(id: value_id[0])
if relation_model
if relation_model['name']
value_str[0] = relation_model['name']
elsif relation_model.respond_to?('fullname')
value_str[0] = relation_model.send('fullname')
end
end
end
if relation_class && value_id[1]
relation_model = relation_class.lookup(id: value_id[1])
if relation_model
if relation_model['name']
value_str[1] = relation_model['name']
elsif relation_model.respond_to?('fullname')
value_str[1] = relation_model.send('fullname')
end
end
end
end
end
data = {
history_attribute: attribute_name,
value_from: value_str[0].to_s,
value_to: value_str[1].to_s,
id_from: value_id[0],
id_to: value_id[1],
}
#logger.info "HIST NEW #{self.class.to_s}.find(#{self.id}) #{data.inspect}"
history_log('updated', updated_by_id, data)
}
end
=begin
delete object history, will be executed automatically
model = Model.find(123)
model.history_destroy
=end
def history_destroy
History.remove(self.class.to_s, id)
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
serve methode to ignore model attributes in historization
class Model < ApplicationModel
include Historisable
history_attributes_ignored :create_article_type_id, :preferences
end
=end
def history_attributes_ignored(*attributes)
@history_attributes_ignored = attributes
end
end
end

View file

@ -0,0 +1,19 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module LatestChangeObserved
extend ActiveSupport::Concern
included do
after_create :latest_change_set_from_observer
after_update :latest_change_set_from_observer
after_touch :latest_change_set_from_observer
after_destroy :latest_change_set_from_observer_destroy
end
def latest_change_set_from_observer
self.class.latest_change_set(updated_at)
end
def latest_change_set_from_observer_destroy
self.class.latest_change_set(nil)
end
end

View file

@ -0,0 +1,87 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module LogsActivityStream
extend ActiveSupport::Concern
included do
after_create :activity_stream_create
after_update :activity_stream_update
before_destroy :activity_stream_destroy
end
=begin
log object create activity stream, if configured - will be executed automatically
model = Model.find(123)
model.activity_stream_create
=end
def activity_stream_create
activity_stream_log('create', self['created_by_id'])
end
=begin
log object update activity stream, if configured - will be executed automatically
model = Model.find(123)
model.activity_stream_update
=end
def activity_stream_update
return if !changed?
ignored_attributes = self.class.instance_variable_get(:@activity_stream_attributes_ignored) || []
ignored_attributes += %i(created_at updated_at created_by_id updated_by_id)
log = false
changes.each { |key, _value|
next if ignored_attributes.include?(key.to_sym)
log = true
}
return if !log
activity_stream_log('update', self['updated_by_id'])
end
=begin
delete object activity stream, will be executed automatically
model = Model.find(123)
model.activity_stream_destroy
=end
def activity_stream_destroy
ActivityStream.remove(self.class.to_s, id)
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
serve methode to ignore model attributes in activity stream and/or limit activity stream permission
class Model < ApplicationModel
include LogsActivityStream
activity_stream_permission 'admin.user'
activity_stream_attributes_ignored :create_article_type_id, :preferences
end
=end
def activity_stream_attributes_ignored(*attributes)
@activity_stream_attributes_ignored = attributes
end
def activity_stream_permission(permission)
@activity_stream_permission = permission
end
end
end

View file

@ -0,0 +1,138 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module NotifiesClients
extend ActiveSupport::Concern
included do
after_create :notify_clients_after_create
after_update :notify_clients_after_update
after_touch :notify_clients_after_touch
after_destroy :notify_clients_after_destroy
end
=begin
notify_clients_after_create after model got created
used as callback in model file
class OwnModel < ApplicationModel
after_create :notify_clients_after_create
after_update :notify_clients_after_update
after_touch :notify_clients_after_touch
after_destroy :notify_clients_after_destroy
[...]
=end
def notify_clients_after_create
# return if we run import mode
return if Setting.get('import_mode')
logger.debug "#{self.class.name}.find(#{id}) notify created " + created_at.to_s
class_name = self.class.name
class_name.gsub!(/::/, '')
PushMessages.send(
message: {
event: class_name + ':create',
data: { id: id, updated_at: updated_at }
},
type: 'authenticated',
)
end
=begin
notify_clients_after_update after model got updated
used as callback in model file
class OwnModel < ApplicationModel
after_create :notify_clients_after_create
after_update :notify_clients_after_update
after_touch :notify_clients_after_touch
after_destroy :notify_clients_after_destroy
[...]
=end
def notify_clients_after_update
# return if we run import mode
return if Setting.get('import_mode')
logger.debug "#{self.class.name}.find(#{id}) notify UPDATED " + updated_at.to_s
class_name = self.class.name
class_name.gsub!(/::/, '')
PushMessages.send(
message: {
event: class_name + ':update',
data: { id: id, updated_at: updated_at }
},
type: 'authenticated',
)
end
=begin
notify_clients_after_touch after model got touched
used as callback in model file
class OwnModel < ApplicationModel
after_create :notify_clients_after_create
after_update :notify_clients_after_update
after_touch :notify_clients_after_touch
after_destroy :notify_clients_after_destroy
[...]
=end
def notify_clients_after_touch
# return if we run import mode
return if Setting.get('import_mode')
logger.debug "#{self.class.name}.find(#{id}) notify TOUCH " + updated_at.to_s
class_name = self.class.name
class_name.gsub!(/::/, '')
PushMessages.send(
message: {
event: class_name + ':touch',
data: { id: id, updated_at: updated_at }
},
type: 'authenticated',
)
end
=begin
notify_clients_after_destroy after model got destroyed
used as callback in model file
class OwnModel < ApplicationModel
after_create :notify_clients_after_create
after_update :notify_clients_after_update
after_touch :notify_clients_after_touch
after_destroy :notify_clients_after_destroy
[...]
=end
def notify_clients_after_destroy
# return if we run import mode
return if Setting.get('import_mode')
logger.debug "#{self.class.name}.find(#{id}) notify DESTOY " + updated_at.to_s
class_name = self.class.name
class_name.gsub!(/::/, '')
PushMessages.send(
message: {
event: class_name + ':destroy',
data: { id: id, updated_at: updated_at }
},
type: 'authenticated',
)
end
end

View file

@ -0,0 +1,138 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module SearchIndexed
extend ActiveSupport::Concern
included do
after_create :search_index_update
after_update :search_index_update
after_touch :search_index_update
after_destroy :search_index_destroy
end
=begin
update search index, if configured - will be executed automatically
model = Model.find(123)
model.search_index_update
=end
def search_index_update
return if ignore_search_indexing?(:update)
# start background job to transfer data to search index
return if !SearchIndexBackend.enabled?
Delayed::Job.enqueue(BackgroundJobSearchIndex.new(self.class.to_s, id))
end
=begin
delete search index object, will be executed automatically
model = Model.find(123)
model.search_index_destroy
=end
def search_index_destroy
return if ignore_search_indexing?(:destroy)
SearchIndexBackend.remove(self.class.to_s, id)
end
=begin
collect data to index and send to backend
ticket = Ticket.find(123)
result = ticket.search_index_update_backend
returns
result = true # false
=end
def search_index_update_backend
# fill up with search data
attributes = search_index_attribute_lookup
return if !attributes
# update backend
SearchIndexBackend.add(self.class.to_s, attributes)
end
=begin
get data to store in search index
ticket = Ticket.find(123)
result = ticket.search_index_data
returns
result = {
attribute1: 'some value',
attribute2: ['value 1', 'value 2'],
...
}
=end
def search_index_data
attributes = {}
%w(name note).each { |key|
next if !self[key]
next if self[key].respond_to?('empty?') && self[key].empty?
attributes[key] = self[key]
}
return if attributes.empty?
attributes
end
def ignore_search_indexing?(_action)
false
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
serve methode to ignore model attributes in search index
class Model < ApplicationModel
include SearchIndexed
search_index_attributes_ignored :password, :image
end
=end
def search_index_attributes_ignored(*attributes)
@search_index_attributes_ignored = attributes
end
=begin
reload search index with full data
Model.search_index_reload
=end
def search_index_reload
tolerance = 5
tolerance_count = 0
all.order('created_at DESC').each { |item|
next if item.ignore_search_indexing?(:destroy)
begin
item.search_index_update_backend
rescue => e
logger.error "Unable to send #{item.class}.find(#{item.id}) backend: #{e.inspect}"
tolerance_count += 1
raise "Unable to send #{item.class}.find(#{item.id}) backend: #{e.inspect}" if tolerance_count == tolerance
end
}
end
end
end

View file

@ -0,0 +1,31 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module UniqNamed
extend ActiveSupport::Concern
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
generate uniq name (will check name of model and generates _1 sequenze)
Used as before_update callback, no own use needed
name = Model.generate_uniq_name('some name')
returns
result = 'some name_X'
=end
def generate_uniq_name(name)
return name if !exists?(name: name)
(1..100).each { |counter|
name = "#{name}_#{counter}"
break if !exists?(name: name)
}
name
end
end
end

View file

@ -1,6 +1,8 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class EmailAddress < ApplicationModel
include LatestChangeObserved
has_many :groups, after_add: :cache_update, after_remove: :cache_update
belongs_to :channel
validates :realname, presence: true
@ -12,8 +14,6 @@ class EmailAddress < ApplicationModel
after_update :update_email_address_id
after_destroy :delete_group_reference
latest_change_support
=begin
check and if channel not exists reset configured channels for email addresses

View file

@ -1,13 +1,15 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Group < ApplicationModel
include LogsActivityStream
include NotifiesClients
include LatestChangeObserved
include Historisable
has_and_belongs_to_many :users, after_add: :cache_update, after_remove: :cache_update
belongs_to :email_address
belongs_to :signature
validates :name, presence: true
activity_stream_support permission: 'admin.group'
history_support
notify_clients_support
latest_change_support
activity_stream_permission 'admin.group'
end

View file

@ -308,17 +308,6 @@ returns
history_attribute
end
private
=begin
nothing to do on destroying history entries
=end
def destroy_dependencies
end
class Object < ApplicationModel
end

View file

@ -1,6 +1,8 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Job < ApplicationModel
include NotifiesClients
load 'job/assets.rb'
include Job::Assets
@ -12,8 +14,6 @@ class Job < ApplicationModel
before_create :updated_matching, :update_next_run_at
before_update :updated_matching, :update_next_run_at
notify_clients_support
def self.run
jobs = Job.where(active: true, running: false)
jobs.each do |job|

View file

@ -32,7 +32,7 @@ returns
data[ User.to_app_model ] = {}
end
if !data[ app_model ][ id ]
data[ app_model ][ id ] = attributes_with_associations
data[ app_model ][ id ] = attributes_with_association_ids
data = assets_of_selector('condition', data)
data = assets_of_selector('perform', data)
end

View file

@ -1,10 +1,9 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Macro < ApplicationModel
include NotifiesClients
include LatestChangeObserved
store :perform
validates :name, presence: true
notify_clients_support
latest_change_support
end

View file

@ -1,13 +1,14 @@
class ObjectManager::Attribute < ApplicationModel
include NotifiesClients
self.table_name = 'object_manager_attributes'
belongs_to :object_lookup, class_name: 'ObjectLookup'
validates :name, presence: true
store :screens
store :data_option
store :data_option_new
notify_clients_support
=begin
list of all attributes

View file

@ -1,6 +1,12 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Organization < ApplicationModel
include LogsActivityStream
include NotifiesClients
include LatestChangeObserved
include Historisable
include SearchIndexed
load 'organization/permission.rb'
include Organization::Permission
load 'organization/assets.rb'
@ -16,11 +22,7 @@ class Organization < ApplicationModel
before_create :domain_cleanup
before_update :domain_cleanup
activity_stream_support permission: 'admin.role'
history_support
search_index_support
notify_clients_support
latest_change_support
activity_stream_permission 'admin.role'
private

View file

@ -1,6 +1,9 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Overview < ApplicationModel
include NotifiesClients
include LatestChangeObserved
load 'overview/assets.rb'
include Overview::Assets
@ -13,9 +16,6 @@ class Overview < ApplicationModel
before_create :fill_link_on_create, :fill_prio
before_update :fill_link_on_update
notify_clients_support
latest_change_support
private
def fill_prio

View file

@ -33,7 +33,7 @@ returns
data[ app_model_user ] = {}
end
if !data[ app_model_overview ][ id ]
data[ app_model_overview ][ id ] = attributes_with_associations
data[ app_model_overview ][ id ] = attributes_with_association_ids
if user_ids
user_ids.each { |local_user_id|
next if data[ app_model_user ][ local_user_id ]

View file

@ -1,11 +1,12 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Permission < ApplicationModel
include NotifiesClients
include LatestChangeObserved
has_and_belongs_to_many :roles
validates :name, presence: true
store :preferences
notify_clients_support
latest_change_support
=begin

View file

@ -1,6 +1,10 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Role < ApplicationModel
include LogsActivityStream
include NotifiesClients
include LatestChangeObserved
has_and_belongs_to_many :users, after_add: :cache_update, after_remove: :cache_update
has_and_belongs_to_many :permissions, after_add: :cache_update, after_remove: :cache_update
validates :name, presence: true
@ -9,10 +13,9 @@ class Role < ApplicationModel
before_create :validate_permissions
before_update :validate_permissions
attributes_with_associations_support ignore: { user_ids: true }
activity_stream_support permission: 'admin.role'
notify_clients_support
latest_change_support
association_attributes_ignored :user_ids
activity_stream_permission 'admin.role'
=begin

View file

@ -1,7 +1,8 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Signature < ApplicationModel
has_many :groups, after_add: :cache_update, after_remove: :cache_update
validates :name, presence: true
latest_change_support
include LatestChangeObserved
has_many :groups, after_add: :cache_update, after_remove: :cache_update
validates :name, presence: true
end

View file

@ -1,6 +1,8 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Sla < ApplicationModel
include NotifiesClients
load 'sla/assets.rb'
include Sla::Assets
@ -8,7 +10,4 @@ class Sla < ApplicationModel
store :data
validates :name, presence: true
belongs_to :calendar
notify_clients_support
end

View file

@ -33,7 +33,7 @@ returns
data[ app_model_user ] = {}
end
if !data[ app_model_sla ][ id ]
data[ app_model_sla ][ id ] = attributes_with_associations
data[ app_model_sla ][ id ] = attributes_with_association_ids
data = assets_of_selector('condition', data)
if calendar_id
calendar = Calendar.lookup(id: calendar_id)

View file

@ -1,7 +1,8 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Template < ApplicationModel
store :options
validates :name, presence: true
notify_clients_support
include NotifiesClients
store :options
validates :name, presence: true
end

View file

@ -1,9 +1,10 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class TextModule < ApplicationModel
validates :name, presence: true
validates :content, presence: true
notify_clients_support
include NotifiesClients
validates :name, presence: true
validates :content, presence: true
=begin

View file

@ -1,68 +1,57 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Ticket < ApplicationModel
include LogsActivityStream
include NotifiesClients
include LatestChangeObserved
include Historisable
include SearchIndexed
include Ticket::Escalation
include Ticket::Subject
load 'ticket/permission.rb'
include Ticket::Permission
load 'ticket/assets.rb'
include Ticket::Assets
load 'ticket/history_log.rb'
include Ticket::HistoryLog
load 'ticket/activity_stream_log.rb'
include Ticket::ActivityStreamLog
load 'ticket/search_index.rb'
include Ticket::SearchIndex
extend Ticket::Search
store :preferences
before_create :check_generate, :check_defaults, :check_title, :check_escalation_update
before_update :check_defaults, :check_title, :reset_pending_time, :check_escalation_update
before_destroy :destroy_dependencies
store :preferences
before_create :check_generate, :check_defaults, :check_title, :check_escalation_update
before_update :check_defaults, :check_title, :reset_pending_time, :check_escalation_update
before_destroy :destroy_dependencies
validates :group_id, presence: true
validates :priority_id, presence: true
validates :state_id, presence: true
notify_clients_support
activity_stream_permission 'ticket.agent'
latest_change_support
activity_stream_attributes_ignored :organization_id, # organization_id will channge automatically on user update
:create_article_type_id,
:create_article_sender_id,
:article_count,
:first_response_at,
:first_response_escalation_at,
:first_response_in_min,
:first_response_diff_in_min,
:close_at,
:close_escalation_at,
:close_in_min,
:close_diff_in_min,
:update_escalation_at,
:update_in_min,
:update_diff_in_min,
:last_contact_at,
:last_contact_agent_at,
:last_contact_customer_at,
:preferences
activity_stream_support(
permission: 'ticket.agent',
ignore_attributes: {
organization_id: true, # organization_id will channge automatically on user update
create_article_type_id: true,
create_article_sender_id: true,
article_count: true,
first_response_at: true,
first_response_escalation_at: true,
first_response_in_min: true,
first_response_diff_in_min: true,
close_at: true,
close_escalation_at: true,
close_in_min: true,
close_diff_in_min: true,
update_escalation_at: true,
update_in_min: true,
update_diff_in_min: true,
last_contact_at: true,
last_contact_agent_at: true,
last_contact_customer_at: true,
preferences: true,
}
)
history_support(
ignore_attributes: {
create_article_type_id: true,
create_article_sender_id: true,
article_count: true,
preferences: true,
}
)
search_index_support
history_attributes_ignored :create_article_type_id,
:create_article_sender_id,
:article_count,
:preferences
belongs_to :group, class_name: 'Group'
has_many :articles, class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update
@ -861,6 +850,27 @@ result
Ticket::Article.where(ticket_id: id).order(:created_at, :id)
end
def history_get(fulldata = false)
list = History.list(self.class.name, self['id'], 'Ticket::Article')
return list if !fulldata
# get related objects
assets = {}
list.each { |item|
record = Kernel.const_get(item['object']).find(item['o_id'])
assets = record.assets(assets)
if item['related_object']
record = Kernel.const_get(item['related_object']).find( item['related_o_id'])
assets = record.assets(assets)
end
}
{
history: list,
assets: assets,
}
end
private
def check_generate
@ -915,5 +925,4 @@ result
# destroy online notifications
OnlineNotification.remove(self.class.to_s, id)
end
end

View file

@ -1,37 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Ticket::ActivityStreamLog
=begin
log activity for this object
ticket = Ticket.find(123)
result = ticket.activity_stream_log('create', user_id)
returns
result = true # false
=end
def activity_stream_log(type, user_id)
# return if we run import mode
return if Setting.get('import_mode')
# return if we run on init mode
return if !Setting.get('system_init_done')
return if !self.class.activity_stream_support_config
permission = self.class.activity_stream_support_config[:permission]
ActivityStream.add(
o_id: self['id'],
type: type,
object: self.class.name,
group_id: self['group_id'],
permission: permission,
created_at: updated_at,
created_by_id: user_id,
)
end
end

View file

@ -1,11 +1,11 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Ticket::Article < ApplicationModel
include LogsActivityStream
include NotifiesClients
include Historisable
load 'ticket/article/assets.rb'
include Ticket::Article::Assets
load 'ticket/article/history_log.rb'
include Ticket::Article::HistoryLog
load 'ticket/article/activity_stream_log.rb'
include Ticket::Article::ActivityStreamLog
belongs_to :ticket
belongs_to :type, class_name: 'Ticket::Article::Type'
@ -16,28 +16,19 @@ class Ticket::Article < ApplicationModel
before_create :check_subject, :check_message_id_md5
before_update :check_subject, :check_message_id_md5
notify_clients_support
activity_stream_permission 'ticket.agent'
activity_stream_support(
permission: 'ticket.agent',
ignore_attributes: {
type_id: true,
sender_id: true,
preferences: true,
}
)
activity_stream_attributes_ignored :type_id,
:sender_id,
:preferences
history_support(
ignore_attributes: {
type_id: true,
sender_id: true,
preferences: true,
message_id: true,
from: true,
to: true,
cc: true,
}
)
history_attributes_ignored :type_id,
:sender_id,
:preferences,
:message_id,
:from,
:to,
:cc
# fillup md5 of message id to search easier on very long message ids
def check_message_id_md5
@ -220,16 +211,32 @@ returns:
subject.gsub!(/\s|\t|\r/, ' ')
end
def history_log_attributes
{
related_o_id: self['ticket_id'],
related_history_object: 'Ticket',
}
end
# callback function to overwrite
# default history stream log attributes
# gets called from activity_stream_log
def activity_stream_log_attributes
{
group_id: Ticket.find(ticket_id).group_id,
}
end
class Flag < ApplicationModel
end
class Sender < ApplicationModel
include LatestChangeObserved
validates :name, presence: true
latest_change_support
end
class Type < ApplicationModel
include LatestChangeObserved
validates :name, presence: true
latest_change_support
end
end

View file

@ -1,38 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Ticket::Article::ActivityStreamLog
=begin
log activity for this object
article = Ticket::Article.find(123)
result = article.activity_stream_log('create', user_id)
returns
result = true # false
=end
def activity_stream_log(type, user_id)
# return if we run import mode
return if Setting.get('import_mode')
# return if we run on init mode
return if !Setting.get('system_init_done')
return if !self.class.activity_stream_support_config
permission = self.class.activity_stream_support_config[:permission]
ticket = Ticket.lookup(id: ticket_id)
ActivityStream.add(
o_id: self['id'],
type: type,
object: self.class.name,
group_id: ticket.group_id,
permission: permission,
created_at: updated_at,
created_by_id: user_id,
)
end
end

View file

@ -1,32 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Ticket::Article::HistoryLog
=begin
create log activity for this article
article = Ticket::Article.find(123)
result = article.history_create( 'created', user_id )
returns
result = true # false
=end
def history_log (type, user_id, data = {})
# if Ticketdata[:data[:Article has changed, remember related ticket to be able
# to show article changes in ticket history
data[:o_id] = self['id']
data[:history_type] = type
data[:history_object] = self.class.name
data[:related_o_id] = self['ticket_id']
data[:related_history_object] = 'Ticket'
data[:created_by_id] = user_id
data[:updated_at] = updated_at
data[:created_at] = updated_at
History.add(data)
end
end

View file

@ -29,7 +29,7 @@ returns
data[ app_model_ticket ] = {}
end
if !data[ app_model_ticket ][ id ]
data[ app_model_ticket ][ id ] = attributes_with_associations
data[ app_model_ticket ][ id ] = attributes_with_association_ids
end
%w(created_by_id updated_by_id owner_id customer_id).each { |local_user_id|
next if !self[ local_user_id ]

View file

@ -1,81 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Ticket::HistoryLog
=begin
create log activity for this ticket
ticket = Ticket.find(123)
result = ticket.history_create('created', user_id)
returns
result = true # false
=end
def history_log(type, user_id, data = {})
data[:o_id] = self['id']
data[:history_type] = type
data[:history_object] = self.class.name
data[:related_o_id] = nil
data[:related_history_object] = nil
data[:created_by_id] = user_id
data[:updated_at] = updated_at
data[:created_at] = updated_at
History.add(data)
end
=begin
get log activity for this ticket
ticket = Ticket.find(123)
result = ticket.history_get()
returns
result = [
{
type: 'created',
object: 'Ticket',
created_by_id: 3,
created_at: "2013-08-19 20:41:33",
},
{
type: 'updated',
object: 'Ticket',
attribute: 'priority',
o_id: 1,
id_to: 3,
id_from: 2,
value_from: "low",
value_to: "high",
created_by_id: 3,
created_at: "2013-08-19 20:41:33",
},
]
=end
def history_get(fulldata = false)
list = History.list(self.class.name, self['id'], 'Ticket::Article')
return list if !fulldata
# get related objects
assets = {}
list.each { |item|
record = Kernel.const_get(item['object']).find(item['o_id'])
assets = record.assets(assets)
if item['related_object']
record = Kernel.const_get(item['related_object']).find( item['related_o_id'])
assets = record.assets(assets)
end
}
{
history: list,
assets: assets,
}
end
end

View file

@ -1,11 +1,11 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Ticket::State < ApplicationModel
include LatestChangeObserved
belongs_to :state_type, class_name: 'Ticket::StateType'
belongs_to :next_state, class_name: 'Ticket::State'
validates :name, presence: true
latest_change_support
=begin
list tickets by customer

View file

@ -1,6 +1,7 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Ticket::StateType < ApplicationModel
has_many :states, class_name: 'Ticket::State'
validates :name, presence: true
latest_change_support
include LatestChangeObserved
has_many :states, class_name: 'Ticket::State'
validates :name, presence: true
end

View file

@ -24,6 +24,11 @@ require 'digest/md5'
# @property active [Boolean] The flag that shows the active state of the User.
# @property note [String] The note or comment stored to the User.
class User < ApplicationModel
include LogsActivityStream
include NotifiesClients
include Historisable
include SearchIndexed
load 'user/permission.rb'
include User::Permission
load 'user/assets.rb'
@ -38,7 +43,6 @@ class User < ApplicationModel
after_create :avatar_for_email_check
after_update :avatar_for_email_check
after_destroy :avatar_destroy
notify_clients_support
has_and_belongs_to_many :groups, after_add: :cache_update, after_remove: :cache_update, class_name: 'Group'
has_and_belongs_to_many :roles, after_add: [:cache_update, :check_notifications], after_remove: :cache_update, class_name: 'Role'
@ -50,35 +54,31 @@ class User < ApplicationModel
store :preferences
activity_stream_support(
permission: 'admin.user',
ignore_attributes: {
last_login: true,
login_failed: true,
image: true,
image_source: true,
preferences: true,
}
)
history_support(
ignore_attributes: {
password: true,
image: true,
image_source: true,
preferences: true,
}
)
search_index_support(
ignore_attributes: {
password: true,
image: true,
image_source: true,
source: true,
login_failed: true,
preferences: true,
},
ignore_ids: [1],
)
activity_stream_permission 'admin.user'
activity_stream_attributes_ignored :last_login,
:login_failed,
:image,
:image_source,
:preferences
history_attributes_ignored :password,
:image,
:image_source,
:preferences
search_index_attributes_ignored :password,
:image,
:image_source,
:source,
:login_failed,
:preferences
def ignore_search_indexing?(_action)
# ignore internal user
return true if id == 1
false
end
=begin

View file

@ -29,7 +29,7 @@ returns
data[ app_model ] = {}
end
if !data[ app_model ][ id ]
local_attributes = attributes_with_associations
local_attributes = attributes_with_association_ids
# do not transfer crypted pw
local_attributes['password'] = ''

View file

@ -0,0 +1,21 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class BackgroundJobSearchIndex
def initialize(object, o_id)
@object = object
@o_id = o_id
end
def perform
record = @object.constantize.lookup(id: @o_id)
return if !exists?(record)
record.search_index_update_backend
end
private
def exists?(record)
return true if record
Rails.logger.info "Can't index #{@object}.lookup(id: #{@o_id}), no such record found"
false
end
end

View file

@ -32,6 +32,8 @@ returns
next if entry =~ %r{channel/}i
next if entry =~ %r{observer/}i
next if entry =~ %r{store/provider/}i
next if entry =~ %r{models/concerns/}i
entry.gsub!(dir, '')
entry = entry.to_classname
model_class = load_adapter(entry)

View file

@ -1,4 +1,5 @@
class Sessions::Backend::Base
def initialize(user, asset_lookup, client, client_id, ttl = 30)
@user = user
@client = client

View file

@ -47,7 +47,7 @@ class Sessions::Backend::Collections::Base < Sessions::Backend::Base
# get relations of data
all = []
items.each { |item|
all.push item.attributes_with_associations
all.push item.attributes_with_association_ids
}
# collect assets

View file

@ -55,12 +55,12 @@ class AssetsTest < ActiveSupport::TestCase
assets = user3.assets({})
org1 = Organization.find(org1.id)
attributes = org1.attributes_with_associations
attributes = org1.attributes_with_association_ids
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org1.id]), 'check assets')
user1 = User.find(user1.id)
attributes = user1.attributes_with_associations
attributes = user1.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -68,7 +68,7 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][user1.id]), 'check assets' )
user2 = User.find(user2.id)
attributes = user2.attributes_with_associations
attributes = user2.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -76,7 +76,7 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][user2.id]), 'check assets' )
user3 = User.find(user3.id)
attributes = user3.attributes_with_associations
attributes = user3.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -89,12 +89,12 @@ class AssetsTest < ActiveSupport::TestCase
org2.note = "some note...#{rand(9_999_999_999_999)}"
org2.save
attributes = org2.attributes_with_associations
attributes = org2.attributes_with_association_ids
attributes.delete('user_ids')
assert( !diff(attributes, assets[:Organization][org2.id]), 'check assets' )
user1_new = User.find(user1.id)
attributes = user1_new.attributes_with_associations
attributes = user1_new.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -103,12 +103,12 @@ class AssetsTest < ActiveSupport::TestCase
# check new assets lookup
assets = user3.assets({})
attributes = org2.attributes_with_associations
attributes = org2.attributes_with_association_ids
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org1.id]), 'check assets')
user1 = User.find(user1.id)
attributes = user1.attributes_with_associations
attributes = user1.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -116,7 +116,7 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][user1.id]), 'check assets' )
user2 = User.find(user2.id)
attributes = user2.attributes_with_associations
attributes = user2.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -124,7 +124,7 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][user2.id]), 'check assets' )
user3 = User.find(user3.id)
attributes = user3.attributes_with_associations
attributes = user3.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -202,12 +202,12 @@ class AssetsTest < ActiveSupport::TestCase
org = Organization.find(org.id)
assets = org.assets({})
attributes = org.attributes_with_associations
attributes = org.attributes_with_association_ids
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org.id]), 'check assets' )
admin1 = User.find(admin1.id)
attributes = admin1.attributes_with_associations
attributes = admin1.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -215,7 +215,7 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][admin1.id]), 'check assets' )
user1 = User.find(user1.id)
attributes = user1.attributes_with_associations
attributes = user1.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -223,7 +223,7 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][user1.id]), 'check assets' )
user2 = User.find(user2.id)
attributes = user2.attributes_with_associations
attributes = user2.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -231,7 +231,7 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][user2.id]), 'check assets' )
user3 = User.find(user3.id)
attributes = user3.attributes_with_associations
attributes = user3.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -245,11 +245,11 @@ class AssetsTest < ActiveSupport::TestCase
user_new_2.save
org_new = Organization.find(org.id)
attributes = org_new.attributes_with_associations
attributes = org_new.attributes_with_association_ids
attributes.delete('user_ids')
assert( !diff(attributes, assets[:Organization][org_new.id]), 'check assets' )
attributes = user_new_2.attributes_with_associations
attributes = user_new_2.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')
@ -258,11 +258,11 @@ class AssetsTest < ActiveSupport::TestCase
# check new assets lookup
assets = org_new.assets({})
attributes = org_new.attributes_with_associations
attributes = org_new.attributes_with_association_ids
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org_new.id]), 'check assets' )
attributes = user_new_2.attributes_with_associations
attributes = user_new_2.attributes_with_association_ids
attributes['accounts'] = {}
attributes['password'] = ''
attributes.delete('token_ids')