Merge branch 'develop' of github.com:martini/zammad into develop

This commit is contained in:
Felix Niklas 2015-07-01 10:18:50 +02:00
commit a872f78e24
35 changed files with 568 additions and 626 deletions

View file

@ -118,4 +118,21 @@ return all activity entries of an user
list list
end end
=begin
cleanup old stream messages
ActivityStream.cleanup
optional you can parse the max oldest stream entries
ActivityStream.cleanup(3.months)
=end
def self.cleanup(diff = 3.months)
ActivityStream.where('created_at < ?', Time.zone.now - diff).delete_all
true
end
end end

View file

@ -3,6 +3,7 @@
class OnlineNotification < ApplicationModel class OnlineNotification < ApplicationModel
belongs_to :type_lookup, class_name: 'TypeLookup' belongs_to :type_lookup, class_name: 'TypeLookup'
belongs_to :object_lookup, class_name: 'ObjectLookup' belongs_to :object_lookup, class_name: 'ObjectLookup'
belongs_to :user
after_create :notify_clients_after_change after_create :notify_clients_after_change
after_update :notify_clients_after_change after_update :notify_clients_after_change
@ -188,4 +189,32 @@ returns:
) )
end end
=begin
cleanup old online notifications
OnlineNotification.cleanup
=end
def self.cleanup
OnlineNotification.where('created_at < ?', Time.zone.now - 12.months).delete_all
OnlineNotification.where('seen = ? AND created_at < ?', true, Time.zone.now - 1.days).delete_all
# notify all agents
users = Ticket::ScreenOptions.agents
users.each {|user|
Sessions.send_to(
user.id,
{
event: 'OnlineNotification::changed',
data: {}
}
)
sleep 2 # slow down client requests
}
true
end
end end

View file

@ -98,4 +98,22 @@ class RecentView < ApplicationModel
return if !record.respond_to?(:permission) return if !record.respond_to?(:permission)
record.permission( current_user: user ) record.permission( current_user: user )
end end
=begin
cleanup old entries
RecentView.cleanup
optional you can parse the max oldest entries
RecentView.cleanup(1.month)
=end
def self.cleanup(diff = 1.month)
RecentView.where('created_at < ?', Time.zone.now - diff).delete_all
true
end
end end

View file

@ -26,7 +26,7 @@ class Scheduler < ApplicationModel
end end
# read/load jobs and check if it is alredy started # read/load jobs and check if it is alredy started
jobs = Scheduler.where( 'active = ?', true ) jobs = Scheduler.where( 'active = ?', true ).order('prio ASC')
jobs.each {|job| jobs.each {|job|
# ignore job is still running # ignore job is still running
@ -37,14 +37,14 @@ class Scheduler < ApplicationModel
# run job as own thread # run job as own thread
@@jobs_started[ job.id ] = true @@jobs_started[ job.id ] = true
start_job( job ) start_job(job)
sleep 10
} }
sleep 60 sleep 60
end end
end end
def self.start_job( job ) def self.start_job( job )
sleep 4
Thread.new { Thread.new {
@ -53,8 +53,8 @@ class Scheduler < ApplicationModel
# start loop for periods under 5 minutes # start loop for periods under 5 minutes
if job.period && job.period <= 300 if job.period && job.period <= 300
loop do loop do
_start_job( job ) _start_job(job)
job = Scheduler.lookup( id: job.id ) job = Scheduler.lookup(id: job.id)
# exit is job got deleted # exit is job got deleted
break if !job break if !job
@ -69,7 +69,7 @@ class Scheduler < ApplicationModel
sleep job.period sleep job.period
end end
else else
_start_job( job ) _start_job(job)
end end
job.pid = '' job.pid = ''
job.save job.save
@ -82,7 +82,6 @@ class Scheduler < ApplicationModel
end end
def self._start_job( job, try_count = 0, try_run_time = Time.zone.now ) def self._start_job( job, try_count = 0, try_run_time = Time.zone.now )
sleep 5
begin begin
job.last_run = Time.zone.now job.last_run = Time.zone.now
job.pid = Thread.current.object_id job.pid = Thread.current.object_id

View file

@ -6,7 +6,7 @@ module Ticket::ScreenOptions
list of active agents list of active agents
result = Ticket::ScreenOptions.agents() result = Ticket::ScreenOptions.agents
returns returns
@ -23,21 +23,21 @@ returns
list attributes list attributes
result = Ticket::ScreenOptions.attributes_to_change( result = Ticket::ScreenOptions.attributes_to_change(
:ticket_id => 123, ticket_id: 123,
:article_id => 123, article_id: 123,
:ticket => ticket_model, ticket: ticket_model,
) )
returns returns
result = { result = {
:type_id => type_ids, type_id: type_ids,
:state_id => state_ids, state_id: state_ids,
:priority_id => priority_ids, priority_id: priority_ids,
:owner_id => owner_ids, owner_id: owner_ids,
:group_id => group_ids, group_id: group_ids,
:group_id__owner_id => groups_users, group_id__owner_id: groups_users,
} }
=end =end
@ -126,16 +126,16 @@ returns
list tickets by customer groupd in state categroie open and closed list tickets by customer groupd in state categroie open and closed
result = Ticket::ScreenOptions.list_by_customer( result = Ticket::ScreenOptions.list_by_customer(
:customer_id => 123, customer_id: 123,
:limit => 15, # optional, default 15 limit: 15, # optional, default 15
) )
returns returns
result = { result = {
:ticket_ids_open => tickets_open, ticket_ids_open: tickets_open,
:ticket_ids_closed => tickets_closed, ticket_ids_closed: tickets_closed,
:assets => { ...list of assets... }, assets: { ...list of assets... },
} }
=end =end

View file

@ -7,9 +7,9 @@ module Ticket::Search
search tickets via search index search tickets via search index
result = Ticket.search( result = Ticket.search(
:current_user => User.find(123), current_user: User.find(123),
:query => 'search something', query: 'search something',
:limit => 15, limit: 15,
) )
returns returns
@ -19,10 +19,10 @@ returns
search tickets via search index search tickets via search index
result = Ticket.search( result = Ticket.search(
:current_user => User.find(123), current_user: User.find(123),
:query => 'search something', query: 'search something',
:limit => 15, limit: 15,
:full => false, full: false,
) )
returns returns
@ -32,8 +32,8 @@ returns
search tickets via database search tickets via database
result = Ticket.search( result = Ticket.search(
:current_user => User.find(123), current_user: User.find(123),
:condition => { condition: {
'tickets.owner_id' => user.id, 'tickets.owner_id' => user.id,
'tickets.state_id' => Ticket::State.where( 'tickets.state_id' => Ticket::State.where(
state_type_id: Ticket::StateType.where( state_type_id: Ticket::StateType.where(
@ -44,8 +44,8 @@ search tickets via database
), ),
), ),
}, },
:limit => 15, limit: 15,
:full => false, full: false,
) )
returns returns

View file

@ -1,4 +0,0 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class TimeAccounting < ActiveRecord::Base
end

View file

@ -27,6 +27,7 @@ class CreateBase < ActiveRecord::Migration
t.column :zip, :string, limit: 100, null: true, default: '' t.column :zip, :string, limit: 100, null: true, default: ''
t.column :city, :string, limit: 100, null: true, default: '' t.column :city, :string, limit: 100, null: true, default: ''
t.column :country, :string, limit: 100, null: true, default: '' t.column :country, :string, limit: 100, null: true, default: ''
t.column :vip, :boolean, default: false
t.column :verified, :boolean, null: false, default: false t.column :verified, :boolean, null: false, default: false
t.column :active, :boolean, null: false, default: true t.column :active, :boolean, null: false, default: true
t.column :note, :string, limit: 250, null: true, default: '' t.column :note, :string, limit: 250, null: true, default: ''
@ -41,6 +42,7 @@ class CreateBase < ActiveRecord::Migration
add_index :users, [:login], unique: true add_index :users, [:login], unique: true
add_index :users, [:email] add_index :users, [:email]
#add_index :users, [:email], unique: => true #add_index :users, [:email], unique: => true
add_index :users, [:organization_id]
add_index :users, [:image] add_index :users, [:image]
add_index :users, [:department] add_index :users, [:department]
add_index :users, [:phone] add_index :users, [:phone]
@ -111,16 +113,22 @@ class CreateBase < ActiveRecord::Migration
t.integer :user_id t.integer :user_id
t.integer :role_id t.integer :role_id
end end
add_index :roles_users, [:user_id]
add_index :roles_users, [:role_id]
create_table :groups_users, id: false do |t| create_table :groups_users, id: false do |t|
t.integer :user_id t.integer :user_id
t.integer :group_id t.integer :group_id
end end
add_index :groups_users, [:user_id]
add_index :groups_users, [:group_id]
create_table :organizations_users, id: false do |t| create_table :organizations_users, id: false do |t|
t.integer :user_id t.integer :user_id
t.integer :organization_id t.integer :organization_id
end end
add_index :organizations_users, [:user_id]
add_index :organizations_users, [:organization_id]
create_table :authorizations do |t| create_table :authorizations do |t|
t.string :provider, limit: 250, null: false t.string :provider, limit: 250, null: false
@ -309,5 +317,39 @@ class CreateBase < ActiveRecord::Migration
add_index :settings, [:area] add_index :settings, [:area]
add_index :settings, [:frontend] add_index :settings, [:frontend]
create_table :stores do |t|
t.references :store_object, null: false
t.references :store_file, null: false
t.column :o_id, :integer, limit: 8, null: false
t.column :preferences, :string, limit: 2500, null: true
t.column :size, :string, limit: 50, null: true
t.column :filename, :string, limit: 250, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :stores, [:store_object_id, :o_id]
create_table :store_objects do |t|
t.column :name, :string, limit: 250, null: false
t.column :note, :string, limit: 250, null: true
t.timestamps
end
add_index :store_objects, [:name], unique: true
create_table :store_files do |t|
t.column :sha, :string, limit: 128, null: false
t.column :provider, :string, limit: 20, null: true
t.timestamps
end
add_index :store_files, [:sha], unique: true
add_index :store_files, [:provider]
create_table :store_provider_dbs do |t|
t.column :sha, :string, limit: 128, null: false
t.column :data, :binary, limit: 200.megabytes, null: true
t.timestamps
end
add_index :store_provider_dbs, [:sha], unique: true
end end
end end

View file

@ -145,6 +145,7 @@ class CreateTicket < ActiveRecord::Migration
t.column :message_id, :string, limit: 3000, null: true t.column :message_id, :string, limit: 3000, null: true
t.column :message_id_md5, :string, limit: 32, null: true t.column :message_id_md5, :string, limit: 32, null: true
t.column :in_reply_to, :string, limit: 3000, null: true t.column :in_reply_to, :string, limit: 3000, null: true
t.column :content_type, :string, limit: 20, null: false, default: 'text/plain'
t.column :references, :string, limit: 3200, null: true t.column :references, :string, limit: 3200, null: true
t.column :body, :text, limit: 4.megabytes + 1 t.column :body, :text, limit: 4.megabytes + 1
t.column :internal, :boolean, null: false, default: false t.column :internal, :boolean, null: false, default: false
@ -248,9 +249,67 @@ class CreateTicket < ActiveRecord::Migration
t.timestamps t.timestamps
end end
add_index :links, [:link_object_source_id, :link_object_source_value, :link_object_target_id, :link_object_target_value, :link_type_id], unique: true, name: 'links_uniq_total' add_index :links, [:link_object_source_id, :link_object_source_value, :link_object_target_id, :link_object_target_value, :link_type_id], unique: true, name: 'links_uniq_total'
create_table :postmaster_filters do |t|
t.column :name, :string, limit: 250, null: false
t.column :channel, :string, limit: 250, null: false
t.column :match, :string, limit: 5000, null: false
t.column :perform, :string, limit: 5000, null: false
t.column :active, :boolean, null: false, default: true
t.column :note, :string, limit: 250, null: true
t.column :updated_by_id, :integer, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :postmaster_filters, [:channel]
create_table :text_modules do |t|
t.references :user, null: true
t.column :name, :string, limit: 250, null: false
t.column :keywords, :string, limit: 500, null: true
t.column :content, :string, limit: 5000, null: false
t.column :note, :string, limit: 250, null: true
t.column :active, :boolean, null: false, default: true
t.column :updated_by_id, :integer, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :text_modules, [:user_id]
add_index :text_modules, [:name]
create_table :text_modules_groups, id: false do |t|
t.integer :text_module_id
t.integer :group_id
end
add_index :text_modules_groups, [:text_module_id]
add_index :text_modules_groups, [:group_id]
create_table :templates do |t|
t.references :user, null: true
t.column :name, :string, limit: 250, null: false
t.column :options, :string, limit: 2500, null: false
t.column :updated_by_id, :integer, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :templates, [:user_id]
add_index :templates, [:name]
create_table :templates_groups, id: false do |t|
t.integer :template_id
t.integer :group_id
end
add_index :templates_groups, [:template_id]
add_index :templates_groups, [:group_id]
end end
def self.down def self.down
drop_table :templates_groups
drop_table :templates
drop_table :text_modules_groups
drop_table :text_modules
drop_table :postmaster_filters
drop_table :notifications drop_table :notifications
drop_table :triggers drop_table :triggers
drop_table :links drop_table :links

View file

@ -1,44 +0,0 @@
class CreateStorage < ActiveRecord::Migration
def up
create_table :stores do |t|
t.references :store_object, null: false
t.references :store_file, null: false
t.column :o_id, :integer, limit: 8, null: false
t.column :preferences, :string, limit: 2500, null: true
t.column :size, :string, limit: 50, null: true
t.column :filename, :string, limit: 250, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :stores, [:store_object_id, :o_id]
create_table :store_objects do |t|
t.column :name, :string, limit: 250, null: false
t.column :note, :string, limit: 250, null: true
t.timestamps
end
add_index :store_objects, [:name], unique: true
create_table :store_files do |t|
t.column :sha, :string, limit: 128, null: false
t.column :provider, :string, limit: 20, null: true
t.timestamps
end
add_index :store_files, [:sha], unique: true
add_index :store_files, [:provider]
create_table :store_provider_dbs do |t|
t.column :sha, :string, limit: 128, null: false
t.column :data, :binary, limit: 200.megabytes, null: true
t.timestamps
end
add_index :store_provider_dbs, [:sha], unique: true
end
def down
drop_table :stores
drop_table :store_objects
drop_table :store_files
end
end

View file

@ -1,26 +0,0 @@
class CreateTemplate < ActiveRecord::Migration
def up
create_table :templates do |t|
t.references :user, null: true
t.column :name, :string, limit: 250, null: false
t.column :options, :string, limit: 2500, null: false
t.column :updated_by_id, :integer, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :templates, [:user_id]
add_index :templates, [:name]
create_table :templates_groups, id: false do |t|
t.integer :template_id
t.integer :group_id
end
add_index :templates_groups, [:template_id]
add_index :templates_groups, [:group_id]
end
def down
drop_table :templates_groups
drop_table :templates
end
end

View file

@ -1,19 +0,0 @@
class PostmasterFilterCreate < ActiveRecord::Migration
def up
create_table :postmaster_filters do |t|
t.column :name, :string, limit: 250, null: false
t.column :channel, :string, limit: 250, null: false
t.column :match, :string, limit: 5000, null: false
t.column :perform, :string, limit: 5000, null: false
t.column :active, :boolean, null: false, default: true
t.column :note, :string, limit: 250, null: true
t.column :updated_by_id, :integer, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :postmaster_filters, [:channel]
end
def down
end
end

View file

@ -1,30 +0,0 @@
class TextModuleCreate < ActiveRecord::Migration
def up
create_table :text_modules do |t|
t.references :user, null: true
t.column :name, :string, limit: 250, null: false
t.column :keywords, :string, limit: 500, null: true
t.column :content, :string, limit: 5000, null: false
t.column :note, :string, limit: 250, null: true
t.column :active, :boolean, null: false, default: true
t.column :updated_by_id, :integer, null: false
t.column :created_by_id, :integer, null: false
t.timestamps
end
add_index :text_modules, [:user_id]
add_index :text_modules, [:name]
create_table :text_modules_groups, id: false do |t|
t.integer :text_module_id
t.integer :group_id
end
add_index :text_modules_groups, [:text_module_id]
add_index :text_modules_groups, [:group_id]
end
def down
drop_table :text_modules_groups
drop_table :text_modules
end
end

View file

@ -1,34 +0,0 @@
class UpdateTicketNumber < ActiveRecord::Migration
def up
Setting.create_or_update(
title: 'Ticket Number Format',
name: 'ticket_number',
area: 'Ticket::Number',
description: 'Selects the ticket number generator module. "Increment" increments the ticket
number, the SystemID and the counter are used with SystemID.Counter format (e.g. 1010138, 1010139).
With "Date" the ticket numbers will be generated by the current date, the SystemID and the counter.
The format looks like Year.Month.Day.SystemID.counter (e.g. 201206231010138, 201206231010139).
With param "Checksum => true" the counter will be appended as checksum to the string. The format
looks like SystemID.Counter.CheckSum (e. g. 10101384, 10101392) or Year.Month.Day.SystemID.Counter.CheckSum (e.g. 2012070110101520, 2012070110101535).',
options: {
form: [
{
display: '',
null: true,
name: 'ticket_number',
tag: 'select',
options: {
'Ticket::Number::Increment' => 'Increment (SystemID.Counter)',
'Ticket::Number::Date' => 'Date (Year.Month.Day.SystemID.Counter)',
},
},
],
},
state: 'Ticket::Number::Increment',
frontend: false
)
end
def down
end
end

View file

@ -1,53 +0,0 @@
class UpdateAuth < ActiveRecord::Migration
def up
Setting.create_or_update(
title: 'Authentication via OTRS',
name: 'auth_otrs',
area: 'Security::Authentication',
description: 'Enables user authentication via OTRS.',
state: {
adapter: 'Auth::Otrs',
required_group_ro: 'stats',
group_rw_role_map: {
'admin' => 'Admin',
'stats' => 'Report',
},
group_ro_role_map: {
'stats' => 'Report',
},
always_role: {
'Agent' => true,
},
},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via LDAP',
name: 'auth_ldap',
area: 'Security::Authentication',
description: 'Enables user authentication via LDAP.',
state: {
adapter: 'Auth::Ldap',
host: 'localhost',
port: 389,
bind_dn: 'cn=Manager,dc=example,dc=org',
bind_pw: 'example',
uid: 'mail',
base: 'dc=example,dc=org',
always_filter: '',
always_roles: %w(Admin Agent),
always_groups: ['Users'],
sync_params: {
firstname: 'sn',
lastname: 'givenName',
email: 'mail',
login: 'mail',
},
},
frontend: false
)
end
def down
end
end

View file

@ -1,59 +0,0 @@
class AddSearchIndex < ActiveRecord::Migration
def up
Setting.create_or_update(
title: 'Elasticsearch Endpoint URL',
name: 'es_url',
area: 'SearchIndex::Elasticsearch',
description: 'Define endpoint of Elastic Search.',
state: '',
frontend: false
)
Setting.create_or_update(
title: 'Elasticsearch Endpoint User',
name: 'es_user',
area: 'SearchIndex::Elasticsearch',
description: 'Define http basic auth user of Elasticsearch.',
state: '',
frontend: false
)
Setting.create_or_update(
title: 'Elastic Search Endpoint Password',
name: 'es_password',
area: 'SearchIndex::Elasticsearch',
description: 'Define http basic auth password of Elasticsearch.',
state: '',
frontend: false
)
Setting.create_or_update(
title: 'Elastic Search Endpoint Index',
name: 'es_index',
area: 'SearchIndex::Elasticsearch',
description: 'Define Elasticsearch index name.',
state: 'zammad',
frontend: false
)
Setting.create_or_update(
title: 'Elastic Search Attachment Extentions',
name: 'es_attachment_ignore',
area: 'SearchIndex::Elasticsearch',
description: 'Define attachment extentions which are ignored for Elasticsearch.',
state: [ '.png', '.jpg', '.jpeg', '.mpeg', '.mpg', '.mov', '.bin', '.exe', '.box', '.mbox' ],
frontend: false
)
Setting.create_or_update(
title: 'Elastic Search Attachment Size',
name: 'es_attachment_max_size_in_mb',
area: 'SearchIndex::Elasticsearch',
description: 'Define max. attachment size for Elasticsearch.',
state: 50,
frontend: false
)
Ticket.search_index_reload
User.search_index_reload
Organization.search_index_reload
end
def down
end
end

View file

@ -1,51 +0,0 @@
class UpdateSetting1 < ActiveRecord::Migration
def up
Setting.create_if_not_exists(
title: 'Send client stats',
name: 'ui_send_client_stats',
area: 'System::UI',
description: 'Send client stats to central server.',
options: {
form: [
{
display: '',
null: true,
name: 'ui_send_client_stats',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
state: true,
frontend: true
)
Setting.create_if_not_exists(
title: 'Client storage',
name: 'ui_client_storage',
area: 'System::UI',
description: 'Use client storage to cache data to perform speed of application.',
options: {
form: [
{
display: '',
null: true,
name: 'ui_client_storage',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
state: false,
frontend: true
)
end
def down
end
end

View file

@ -1,8 +0,0 @@
class UpdateTicketArticleSize < ActiveRecord::Migration
def up
change_column :ticket_articles, :body, :text, limit: 4.megabytes + 1
end
def down
end
end

View file

@ -1,25 +0,0 @@
class UpdateSetting2 < ActiveRecord::Migration
def up
Setting.create_if_not_exists(
title: 'Logo',
name: 'product_logo',
area: 'System::CI',
description: 'Defines the logo of the application, shown in the web interface.',
options: {
form: [
{
display: '',
null: false,
name: 'product_logo',
tag: 'input',
},
],
},
state: 'logo.svg',
frontend: true
)
end
def down
end
end

View file

@ -1,16 +0,0 @@
class UpdateSetting3 < ActiveRecord::Migration
def up
Setting.create_if_not_exists(
title: 'Online Service',
name: 'system_online_service',
area: 'Core',
description: 'Defines if application is used as online service.',
options: {},
state: false,
frontend: true
)
end
def down
end
end

View file

@ -5,7 +5,7 @@ class CreateAvatar < ActiveRecord::Migration
t.column :object_lookup_id, :integer, null: false t.column :object_lookup_id, :integer, null: false
t.column :default, :boolean, null: false, default: false t.column :default, :boolean, null: false, default: false
t.column :deletable, :boolean, null: false, default: true t.column :deletable, :boolean, null: false, default: true
t.column :inital, :boolean, null: false, default: false t.column :initial, :boolean, null: false, default: false
t.column :store_full_id, :integer, null: true t.column :store_full_id, :integer, null: true
t.column :store_resize_id, :integer, null: true t.column :store_resize_id, :integer, null: true
t.column :store_hash, :string, limit: 32, null: true t.column :store_hash, :string, limit: 32, null: true

View file

@ -487,6 +487,44 @@ class UpdateObjectManager2 < ActiveRecord::Migration
updated_by_id: 1, updated_by_id: 1,
) )
ObjectManager::Attribute.add(
object: 'User',
name: 'vip',
display: 'VIP',
data_type: 'boolean',
data_option: {
null: true,
default: false,
item_class: 'formGroup--halfSize',
options: {
false: 'no',
true: 'yes',
},
translate: true,
},
editable: false,
active: true,
screens: {
edit: {
Admin: {
null: true,
},
Agent: {
null: true,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
pending_migration: false,
position: 1490,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add( ObjectManager::Attribute.add(
object: 'User', object: 'User',
name: 'note', name: 'note',
@ -594,7 +632,6 @@ class UpdateObjectManager2 < ActiveRecord::Migration
display: 'Active', display: 'Active',
data_type: 'active', data_type: 'active',
data_option: { data_option: {
null: true,
default: true, default: true,
}, },
editable: false, editable: false,

View file

@ -1,8 +0,0 @@
class UpdateTicketArticle < ActiveRecord::Migration
def up
add_column :ticket_articles, :content_type, :string, limit: 20, null: false, default: 'text/plain'
end
def down
end
end

View file

@ -1,16 +0,0 @@
class AddDevelopMode < ActiveRecord::Migration
def up
Setting.create_if_not_exists(
title: 'Develop System',
name: 'developer_mode',
area: 'Core::Develop',
description: 'Defines if application is in developer mode (useful for developer, all users have the same password, password reset will work without email delivery).',
options: {},
state: false,
frontend: true
)
end
def down
end
end

View file

@ -1,47 +0,0 @@
class CreateVip < ActiveRecord::Migration
def up
add_column :users, :vip, :boolean, default: false
ObjectManager::Attribute.add(
object: 'User',
name: 'vip',
display: 'VIP',
data_type: 'boolean',
data_option: {
null: true,
default: false,
item_class: 'formGroup--halfSize',
options: {
false: 'no',
true: 'yes',
},
translate: true,
},
editable: false,
active: true,
screens: {
edit: {
Admin: {
null: true,
},
Agent: {
null: true,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
pending_migration: false,
position: 1490,
created_by_id: 1,
updated_by_id: 1,
)
end
def down
end
end

View file

@ -1,100 +0,0 @@
class UpdateObjectManager3 < ActiveRecord::Migration
def up
ObjectManager::Attribute.add(
object: 'User',
name: 'active',
display: 'Active',
data_type: 'active',
data_option: {
default: true,
},
editable: false,
active: true,
screens: {
signup: {},
invite_agent: {},
edit: {
Admin: {
null: false,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
pending_migration: false,
position: 1800,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
object: 'Organization',
name: 'active',
display: 'Active',
data_type: 'active',
data_option: {
default: true,
},
editable: false,
active: true,
screens: {
edit: {
Admin: {
null: false,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
pending_migration: false,
position: 1800,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
object: 'User',
name: 'password',
display: 'Password',
data_type: 'input',
data_option: {
type: 'password',
maxlength: 100,
null: true,
autocomplete: 'off',
item_class: 'formGroup--halfSize',
},
editable: false,
active: true,
screens: {
signup: {
'-all-' => {
null: false,
},
},
invite_agent: {},
edit: {
Admin: {
null: true,
},
},
view: {}
},
pending_migration: false,
position: 1400,
created_by_id: 1,
updated_by_id: 1,
)
end
def down
end
end

View file

@ -1,9 +0,0 @@
class RenameAvatarTypo < ActiveRecord::Migration
def up
rename_column :avatars, :inital, :initial
end
def down
rename_column :avatars, :initial, :inital
end
end

View file

@ -1,30 +0,0 @@
class UpdateGeoIpConfig < ActiveRecord::Migration
def up
Setting.create_or_update(
title: 'Geo IP Backend',
name: 'geo_ip_backend',
area: 'System::Geo',
description: 'Defines the backend for geo ip lookups.',
options: {
form: [
{
display: '',
null: true,
name: 'geo_ip_backend',
tag: 'select',
options: {
'' => '-',
'GeoIp::ZammadGeoIp' => 'Zammad GeoIP Service',
},
},
],
},
state: 'GeoIp::ZammadGeoIp',
frontend: false
)
end
def down
end
end

View file

@ -1,12 +0,0 @@
class AddBaseIndexes < ActiveRecord::Migration
def change
add_index :users, [:organization_id]
add_index :roles_users, [:user_id]
add_index :roles_users, [:role_id]
add_index :groups_users, [:user_id]
add_index :groups_users, [:group_id]
add_index :organizations_users, [:user_id]
add_index :organizations_users, [:organization_id]
end
end

View file

@ -0,0 +1,33 @@
class AddCleanup < ActiveRecord::Migration
def up
# delete old entries
Scheduler.create_or_update(
name: 'Delete old activity stream entries.',
method: 'ActivityStream.cleanup',
period: 1.day,
prio: 2,
active: true,
updated_by_id: 1,
created_by_id: 1,
)
Scheduler.create_or_update(
name: 'Delete old online notification entries.',
method: 'OnlineNotification.cleanup',
period: 1.day,
prio: 2,
active: true,
updated_by_id: 1,
created_by_id: 1,
)
Scheduler.create_or_update(
name: 'Delete old entries.',
method: 'RecentView.cleanup',
period: 1.day,
prio: 2,
active: true,
updated_by_id: 1,
created_by_id: 1,
)
end
end

View file

@ -1130,6 +1130,55 @@ Setting.create_if_not_exists(
frontend: true frontend: true
) )
Setting.create_if_not_exists(
title: 'Elasticsearch Endpoint URL',
name: 'es_url',
area: 'SearchIndex::Elasticsearch',
description: 'Define endpoint of Elastic Search.',
state: '',
frontend: false
)
Setting.create_if_not_exists(
title: 'Elasticsearch Endpoint User',
name: 'es_user',
area: 'SearchIndex::Elasticsearch',
description: 'Define http basic auth user of Elasticsearch.',
state: '',
frontend: false
)
Setting.create_if_not_exists(
title: 'Elastic Search Endpoint Password',
name: 'es_password',
area: 'SearchIndex::Elasticsearch',
description: 'Define http basic auth password of Elasticsearch.',
state: '',
frontend: false
)
Setting.create_if_not_exists(
title: 'Elastic Search Endpoint Index',
name: 'es_index',
area: 'SearchIndex::Elasticsearch',
description: 'Define Elasticsearch index name.',
state: 'zammad',
frontend: false
)
Setting.create_if_not_exists(
title: 'Elastic Search Attachment Extentions',
name: 'es_attachment_ignore',
area: 'SearchIndex::Elasticsearch',
description: 'Define attachment extentions which are ignored for Elasticsearch.',
state: [ '.png', '.jpg', '.jpeg', '.mpeg', '.mpg', '.mov', '.bin', '.exe', '.box', '.mbox' ],
frontend: false
)
Setting.create_if_not_exists(
title: 'Elastic Search Attachment Size',
name: 'es_attachment_max_size_in_mb',
area: 'SearchIndex::Elasticsearch',
description: 'Define max. attachment size for Elasticsearch.',
state: 50,
frontend: false
)
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Import Mode', title: 'Import Mode',
name: 'import_mode', name: 'import_mode',

View file

@ -35,6 +35,21 @@ class String
camel_cased_word.gsub(/::/, '/').downcase camel_cased_word.gsub(/::/, '/').downcase
end end
=begin
filename = 'some/module.rb'.to_classname
returns
'Some::Module'
=end
def to_classname
camel_cased_word = "#{self}"
camel_cased_word.gsub!(/\.rb$/, '')
camel_cased_word.split('/').map(&:camelize).join('::')
end
# because of mysql inno_db limitations, strip 4 bytes utf8 chars (e. g. emojis) # because of mysql inno_db limitations, strip 4 bytes utf8 chars (e. g. emojis)
# unfortunaly UTF8mb4 will raise other limitaions of max varchar and lower index sizes # unfortunaly UTF8mb4 will raise other limitaions of max varchar and lower index sizes
# More details: http://pjambet.github.io/blog/emojis-and-mysql/ # More details: http://pjambet.github.io/blog/emojis-and-mysql/

121
lib/models.rb Normal file
View file

@ -0,0 +1,121 @@
class Models
include ApplicationLib
=begin
get list of models
result = Models.all
returns
{
'Some::Classname1' => {
attributes: ['id', 'name', '...']
reflections: ...model.reflections...
},
'Some::Classname2' => {
attributes: ['id', 'name', '...']
reflections: ...model.reflections...
},
}
=end
def self.all
all = {}
dir = "#{Rails.root}/app/models/"
Dir.glob( "#{dir}**/*.rb" ) do |entry|
next if entry =~ /application_model/i
next if entry =~ /channel\//i
next if entry =~ /observer\//i
next if entry =~ /store\/provider\//i
entry.gsub!(dir, '')
entry = entry.to_classname
model_class = load_adapter(entry)
next if !model_class
next if !model_class.respond_to? :new
model_object = model_class.new
next if !model_object.respond_to? :attributes
all[model_class] = {}
all[model_class][:attributes] = model_class.attribute_names
all[model_class][:reflections] = model_class.reflections
#puts "rrrr #{all[model_class][:attributes]}"
#puts model.class
#puts " #{model.attribute_names.inspect}"
end
all
end
=begin
get reference list of a models
result = Models.references('User', 2)
returns
{
'Some::Classname1' => {
attributes: ['id', 'name', '...']
reflections: ...model.reflections...
},
'Some::Classname2' => {
attributes: ['id', 'name', '...']
reflections: ...model.reflections...
},
}
=end
def self.references(object_name, object_id)
object_model = load_adapter(object_name)
object_model.find(object_id)
list = all
references = {
model: {},
total: 0,
}
# find relations via attributes
list.each {|model_class, model_attributes|
references[:model][model_class.to_s] = 0
next if !model_attributes[:attributes]
['created_by_id', 'updated_by_id'].each {|item|
if model_attributes[:attributes].include?(item)
count = model_class.where("#{item} = ?", object_id).count
next if count == 0
Rails.logger.debug "FOUND (by id) #{model_class}->#{item} #{count}!"
references[:model][model_class.to_s] += count
end
}
}
# find relations via reflections
list.each {|model_class, model_attributes|
next if !model_attributes[:reflections]
model_attributes[:reflections].each{|reflection_key, reflection_value|
next if reflection_value.macro != :belongs_to
if reflection_value.options[:class_name] == object_name
count = model_class.where("#{reflection_value.name}_id = ?", object_id).count
next if count == 0
Rails.logger.debug "FOUND (by ref without class) #{model_class}->#{reflection_value.name} #{count}!"
references[:model][model_class.to_s] += count
end
if !reflection_value.options[:class_name] && reflection_value.name == object_name.downcase.to_sym
count = model_class.where("#{reflection_value.name}_id = ?", object_id).count
next if count == 0
Rails.logger.debug "FOUND (by ref with class) #{model_class}->#{reflection_value.name} #{count}!"
references[:model][model_class.to_s] += count
end
}
}
references[:model].each {|k, v|
next if v == 0
references[:total] += v
}
references
end
end

View file

@ -25,6 +25,36 @@ class AaaStringTest < ActiveSupport::TestCase
assert_equal( result, modul.to_filename ) assert_equal( result, modul.to_filename )
end end
test 'to_classname ref' do
modul = 'test'
result = 'test'
modul.to_filename
assert_equal( result, modul )
modul = 'some/file'
result = 'some/file'
modul.to_filename
assert_equal( result, modul )
end
test 'to_classname function' do
modul = 'test'
result = 'Test'
assert_equal( result, modul.to_classname )
modul = 'some/file'
result = 'Some::File'
assert_equal( result, modul.to_classname )
modul = 'some/files'
result = 'Some::Files'
assert_equal( result, modul.to_classname )
modul = 'some_test/files'
result = 'SomeTest::Files'
assert_equal( result, modul.to_classname )
end
test 'html2text ref' do test 'html2text ref' do
html = 'test' html = 'test'
result = 'test' result = 'test'

84
test/unit/model_test.rb Normal file
View file

@ -0,0 +1,84 @@
# encoding: utf-8
require 'test_helper'
class ModelTest < ActiveSupport::TestCase
test 'references test' do
# create base
groups = Group.where( name: 'Users' )
roles = Role.where( name: ['Agent', 'Admin'] )
agent1 = User.create_or_update(
login: 'model-agent1@example.com',
firstname: 'Model',
lastname: 'Agent1',
email: 'model-agent1@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
updated_at: '2015-02-05 16:37:00',
updated_by_id: 1,
created_by_id: 1,
)
organization1 = Organization.create_if_not_exists(
name: 'Model Org 1',
updated_at: '2015-02-05 16:37:00',
updated_by_id: 1,
created_by_id: 1,
)
organization2 = Organization.create_if_not_exists(
name: 'Model Org 2',
updated_at: '2015-02-05 16:37:00',
updated_by_id: agent1.id,
created_by_id: 1,
)
roles = Role.where( name: 'Customer' )
customer1 = User.create_or_update(
login: 'model-customer1@example.com',
firstname: 'Model',
lastname: 'Customer1',
email: 'model-customer1@example.com',
password: 'customerpw',
active: true,
organization_id: organization1.id,
roles: roles,
updated_at: '2015-02-05 16:37:00',
updated_by_id: 1,
created_by_id: 1,
)
customer2 = User.create_or_update(
login: 'model-customer2@example.com',
firstname: 'Model',
lastname: 'Customer2',
email: 'model-customer2@example.com',
password: 'customerpw',
active: true,
organization_id: nil,
roles: roles,
updated_at: '2015-02-05 16:37:00',
updated_by_id: agent1.id,
created_by_id: 1,
)
customer3 = User.create_or_update(
login: 'model-customer3@example.com',
firstname: 'Model',
lastname: 'Customer3',
email: 'model-customer3@example.com',
password: 'customerpw',
active: true,
organization_id: nil,
roles: roles,
updated_at: '2015-02-05 16:37:00',
updated_by_id: agent1.id,
created_by_id: agent1.id,
)
references = Models.references('User', agent1.id)
assert_equal(references[:model]['User'], 3)
assert_equal(references[:model]['Organization'], 1)
assert_equal(references[:model]['Group'], 0)
assert_equal(references[:total], 6)
end
end