Browse Source

Streamline of rest api for tickets, users and organisations. Added pagination for search and index. Added auto lookup for id values (e. g. use group: 'some name', backend will automatically replace "group: 'some name'" by "group_id: 123").

Martin Edenhofer 8 years ago
parent
commit
54f6c456b7

+ 1 - 1
app/assets/javascripts/app/controllers/ticket_zoom.coffee

@@ -144,7 +144,7 @@ class App.TicketZoom extends App.Controller
     @ajax(
       id:    "ticket_zoom_#{ticket_id}"
       type:  'GET'
-      url:   "#{@apiPath}/ticket_full/#{ticket_id}"
+      url:   "#{@apiPath}/tickets/#{ticket_id}?full=true"
       processData: true
       success: (data, status, xhr) =>
 

+ 16 - 5
app/controllers/application_controller.rb

@@ -381,8 +381,11 @@ class ApplicationController < ActionController::Base
   # model helper
   def model_create_render(object, params)
 
+    clean_params = object.param_association_lookup(params)
+    clean_params = object.param_cleanup(clean_params, true)
+
     # create object
-    generic_object = object.new(object.param_cleanup(params[object.to_app_model_url], true ))
+    generic_object = object.new(clean_params)
 
     # save object
     generic_object.save!
@@ -406,8 +409,11 @@ class ApplicationController < ActionController::Base
     # find object
     generic_object = object.find(params[:id])
 
+    clean_params = object.param_association_lookup(params)
+    clean_params = object.param_cleanup(clean_params, true)
+
     # save object
-    generic_object.update_attributes!(object.param_cleanup(params[object.to_app_model_url]))
+    generic_object.update_attributes!(clean_params)
 
     # set relations
     generic_object.param_set_associations(params)
@@ -458,12 +464,17 @@ class ApplicationController < ActionController::Base
   end
 
   def model_index_render(object, params)
+    offset = 0
+    per_page = 1000
     if params[:page] && params[:per_page]
       offset = (params[:page].to_i - 1) * params[:per_page].to_i
-      generic_objects = object.limit(params[:per_page]).offset(offset)
-    else
-      generic_objects = object.all
+      limit = params[:per_page].to_i
     end
+    generic_objects = if offset > 0
+                        object.limit(params[:per_page]).offset(offset).limit(limit)
+                      else
+                        object.all.offset(offset).limit(limit)
+                      end
 
     if params[:full]
       assets = {}

+ 86 - 2
app/controllers/organizations_controller.rb

@@ -47,16 +47,38 @@ curl http://localhost/api/v1/organizations -v -u #{login}:#{password}
 =end
 
   def index
+    offset = 0
+    per_page = 1000
+
+    if params[:page] && params[:per_page]
+      offset = (params[:page].to_i - 1) * params[:per_page].to_i
+      per_page = params[:per_page].to_i
+    end
 
     # only allow customer to fetch his own organization
     organizations = []
     if role?(Z_ROLENAME_CUSTOMER) && !role?(Z_ROLENAME_ADMIN) && !role?(Z_ROLENAME_AGENT)
       if current_user.organization_id
-        organizations = Organization.where( id: current_user.organization_id )
+        organizations = Organization.where(id: current_user.organization_id).offset(offset).limit(per_page)
       end
     else
-      organizations = Organization.all
+      organizations = Organization.all.offset(offset).limit(per_page)
+    end
+
+    if params[:full]
+      assets = {}
+      item_ids = []
+      organizations.each {|item|
+        item_ids.push item.id
+        assets = item.assets(assets)
+      }
+      render json: {
+        record_ids: item_ids,
+        assets: assets,
+      }, status: :ok
+      return
     end
+
     render json: organizations
   end
 
@@ -178,6 +200,68 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co
     model_destory_render(Organization, params)
   end
 
+  def search
+
+    if role?(Z_ROLENAME_CUSTOMER) && !role?(Z_ROLENAME_ADMIN) && !role?(Z_ROLENAME_AGENT)
+      response_access_deny
+      return
+    end
+
+    # set limit for pagination if needed
+    if params[:page] && params[:per_page]
+      params[:limit] = params[:page].to_i * params[:per_page].to_i
+    end
+
+    query_params = {
+      query: params[:term],
+      limit: params[:limit],
+      current_user: current_user,
+    }
+    if params[:role_ids] && !params[:role_ids].empty?
+      query_params[:role_ids] = params[:role_ids]
+    end
+
+    # do query
+    organization_all = Organization.search(query_params)
+
+    # do pagination if needed
+    if params[:page] && params[:per_page]
+      offset = (params[:page].to_i - 1) * params[:per_page].to_i
+      organization_all = organization_all.slice(offset, params[:per_page].to_i) || []
+    end
+
+    if params[:expand]
+      render json: organization_all
+      return
+    end
+
+    # build result list
+    if !params[:full]
+      organizations = []
+      organization_all.each { |organization|
+        a = { id: organization.id, label: organization.name }
+        organizations.push a
+      }
+
+      # return result
+      render json: organizations
+      return
+    end
+
+    organization_ids = []
+    assets = {}
+    organization_all.each { |organization|
+      assets = organization.assets(assets)
+      organization_ids.push organization.id
+    }
+
+    # return result
+    render json: {
+      assets: assets,
+      organization_ids: organization_ids.uniq,
+    }
+  end
+
   # GET /api/v1/organizations/history/1
   def history
 

+ 110 - 71
app/controllers/tickets_controller.rb

@@ -3,23 +3,60 @@
 class TicketsController < ApplicationController
   before_action :authentication_check
 
+  # GET /api/v1/tickets
+  def index
+    offset = 0
+    per_page = 100
+
+    if params[:page] && params[:per_page]
+      offset = (params[:page].to_i - 1) * params[:per_page].to_i
+      per_page = params[:per_page].to_i
+    end
+
+    access_condition = Ticket.access_condition(current_user)
+    tickets = Ticket.where(access_condition).offset(offset).limit(per_page)
+
+    if params[:full]
+      assets = {}
+      item_ids = []
+      tickets.each {|item|
+        item_ids.push item.id
+        assets = item.assets(assets)
+      }
+      render json: {
+        record_ids: item_ids,
+        assets: assets,
+      }, status: :ok
+      return
+    end
+
+    render json: tickets
+  end
+
   # GET /api/v1/tickets/1
   def show
-    @ticket = Ticket.find( params[:id] )
 
     # permission check
-    return if !ticket_permission(@ticket)
+    ticket = Ticket.find(params[:id])
+    return if !ticket_permission(ticket)
+
+    if params[:full]
+      render json: ticket_full(ticket)
+      return
+    end
 
-    render json: @ticket
+    render json: ticket
   end
 
   # POST /api/v1/tickets
   def create
-    ticket = Ticket.new( Ticket.param_validation( params[:ticket] ) )
+    clean_params = Ticket.param_association_lookup(params)
+    clean_params = Ticket.param_cleanup(clean_params, true)
+    ticket = Ticket.new(clean_params)
 
     # check if article is given
     if !params[:article]
-      render json: 'article hash is missing', status: :unprocessable_entity
+      render json: { error: 'article hash is missing' }, status: :unprocessable_entity
       return
     end
 
@@ -52,12 +89,15 @@ class TicketsController < ApplicationController
 
   # PUT /api/v1/tickets/1
   def update
-    ticket = Ticket.find(params[:id])
 
     # permission check
+    ticket = Ticket.find(params[:id])
     return if !ticket_permission(ticket)
 
-    if ticket.update_attributes(Ticket.param_validation(params[:ticket]))
+    clean_params = Ticket.param_association_lookup(params)
+    clean_params = Ticket.param_cleanup(clean_params, true)
+
+    if ticket.update_attributes(clean_params)
 
       if params[:article]
         article_create(ticket, params[:article])
@@ -71,9 +111,9 @@ class TicketsController < ApplicationController
 
   # DELETE /api/v1/tickets/1
   def destroy
-    ticket = Ticket.find(params[:id])
 
     # permission check
+    ticket = Ticket.find(params[:id])
     return if !ticket_permission(ticket)
 
     ticket.destroy
@@ -204,68 +244,6 @@ class TicketsController < ApplicationController
     }
   end
 
-  # GET /api/v1/ticket_full/1
-  def ticket_full
-
-    # permission check
-    ticket = Ticket.find(params[:id])
-    return if !ticket_permission(ticket)
-
-    # get attributes to update
-    attributes_to_change = Ticket::ScreenOptions.attributes_to_change(user: current_user, ticket: ticket)
-
-    # get related users
-    assets = attributes_to_change[:assets]
-    assets = ticket.assets(assets)
-
-    # get related articles
-    articles = Ticket::Article.where(ticket_id: params[:id]).order('created_at ASC, id ASC')
-
-    # get related users
-    article_ids = []
-    articles.each {|article|
-
-      # ignore internal article if customer is requesting
-      next if article.internal == true && role?(Z_ROLENAME_CUSTOMER)
-
-      # load article ids
-      article_ids.push article.id
-
-      # load assets
-      assets = article.assets(assets)
-    }
-
-    # get links
-    links = Link.list(
-      link_object: 'Ticket',
-      link_object_value: ticket.id,
-    )
-    link_list = []
-    links.each { |item|
-      link_list.push item
-      if item['link_object'] == 'Ticket'
-        linked_ticket = Ticket.lookup(id: item['link_object_value'])
-        assets = linked_ticket.assets(assets)
-      end
-    }
-
-    # get tags
-    tags = Tag.tag_list(
-      object: 'Ticket',
-      o_id: ticket.id,
-    )
-
-    # return result
-    render json: {
-      ticket_id: ticket.id,
-      ticket_article_ids: article_ids,
-      assets: assets,
-      links: link_list,
-      tags: tags,
-      form_meta: attributes_to_change[:form_meta],
-    }
-  end
-
   # GET /api/v1/ticket_split
   def ticket_split
 
@@ -528,7 +506,10 @@ class TicketsController < ApplicationController
     # create article if given
     form_id = params[:form_id]
     params.delete(:form_id)
-    article = Ticket::Article.new(Ticket::Article.param_validation( params ))
+
+    clean_params = Ticket::Article.param_association_lookup(params)
+    clean_params = Ticket::Article.param_cleanup(clean_params, true)
+    article = Ticket::Article.new(clean_params)
     article.ticket_id = ticket.id
 
     # store dataurl images to store
@@ -573,4 +554,62 @@ class TicketsController < ApplicationController
       o_id: form_id,
     )
   end
+
+  def ticket_full(ticket)
+
+    # get attributes to update
+    attributes_to_change = Ticket::ScreenOptions.attributes_to_change(user: current_user, ticket: ticket)
+
+    # get related users
+    assets = attributes_to_change[:assets]
+    assets = ticket.assets(assets)
+
+    # get related articles
+    articles = Ticket::Article.where(ticket_id: ticket.id).order('created_at ASC, id ASC')
+
+    # get related users
+    article_ids = []
+    articles.each {|article|
+
+      # ignore internal article if customer is requesting
+      next if article.internal == true && role?(Z_ROLENAME_CUSTOMER)
+
+      # load article ids
+      article_ids.push article.id
+
+      # load assets
+      assets = article.assets(assets)
+    }
+
+    # get links
+    links = Link.list(
+      link_object: 'Ticket',
+      link_object_value: ticket.id,
+    )
+    link_list = []
+    links.each { |item|
+      link_list.push item
+      if item['link_object'] == 'Ticket'
+        linked_ticket = Ticket.lookup(id: item['link_object_value'])
+        assets = linked_ticket.assets(assets)
+      end
+    }
+
+    # get tags
+    tags = Tag.tag_list(
+      object: 'Ticket',
+      o_id: ticket.id,
+    )
+
+    # return result
+    {
+      ticket_id: ticket.id,
+      ticket_article_ids: article_ids,
+      assets: assets,
+      links: link_list,
+      tags: tags,
+      form_meta: attributes_to_change[:form_meta],
+    }
+  end
+
 end

+ 53 - 10
app/controllers/users_controller.rb

@@ -13,13 +13,34 @@ class UsersController < ApplicationController
   # @response_message 200 [Array<User>] List of matching User records.
   # @response_message 401               Invalid session.
   def index
+    offset = 0
+    per_page = 1000
+    if params[:page] && params[:per_page]
+      offset = (params[:page].to_i - 1) * params[:per_page].to_i
+      per_page = params[:per_page].to_i
+    end
 
     # only allow customer to fetch him self
     users = if role?(Z_ROLENAME_CUSTOMER) && !role?(Z_ROLENAME_ADMIN) && !role?('Agent')
-              User.where(id: current_user.id)
+              User.where(id: current_user.id).offset(offset).limit(per_page)
             else
-              User.all
+              User.all.offset(offset).limit(per_page)
             end
+
+    if params[:full]
+      assets = {}
+      item_ids = []
+      users.each {|item|
+        item_ids.push item.id
+        assets = item.assets(assets)
+      }
+      render json: {
+        record_ids: item_ids,
+        assets: assets,
+      }, status: :ok
+      return
+    end
+
     users_all = []
     users.each {|user|
       users_all.push User.lookup(id: user.id).attributes_with_associations
@@ -69,7 +90,10 @@ class UsersController < ApplicationController
     # in case of authentication, set current_user to access later
     authentication_check_only({})
 
-    user = User.new(User.param_cleanup(params, true))
+    clean_params = User.param_association_lookup(params)
+    clean_params = User.param_cleanup(clean_params, true)
+    user = User.new(clean_params)
+    user.param_set_associations(params)
 
     begin
       # check if it's first user
@@ -194,27 +218,30 @@ class UsersController < ApplicationController
     return if !permission_check
 
     user = User.find(params[:id])
+    clean_params = User.param_association_lookup(params)
+    clean_params = User.param_cleanup(clean_params, true)
 
     begin
 
       # permission check by role
       return if !permission_check_by_role(params)
-
-      user.update_attributes( User.param_cleanup(params) )
+      user.update_attributes(clean_params)
 
       # only allow Admin's and Agent's
-      if role?(Z_ROLENAME_ADMIN) && role?('Agent') && params[:role_ids]
+      if role?(Z_ROLENAME_ADMIN) && role?('Agent') && (params[:role_ids] || params[:roles])
         user.role_ids = params[:role_ids]
+        user.param_set_associations({ role_ids: params[:role_ids], roles: params[:roles] })
       end
 
       # only allow Admin's
-      if role?(Z_ROLENAME_ADMIN) && params[:group_ids]
+      if role?(Z_ROLENAME_ADMIN) && (params[:group_ids] || params[:groups])
         user.group_ids = params[:group_ids]
+        user.param_set_associations({ group_ids: params[:group_ids], groups: params[:groups] })
       end
 
       # only allow Admin's and Agent's
-      if role?(Z_ROLENAME_ADMIN) && role?('Agent') && params[:organization_ids]
-        user.organization_ids = params[:organization_ids]
+      if role?(Z_ROLENAME_ADMIN) && role?('Agent') && (params[:organization_ids] || params[:organizations])
+        user.param_set_associations({ organization_ids: params[:organization_ids], organizations: params[:organizations] })
       end
 
       # get new data
@@ -262,11 +289,16 @@ class UsersController < ApplicationController
   # @response_message 401               Invalid session.
   def search
 
-    if role?(Z_ROLENAME_CUSTOMER) && !role?(Z_ROLENAME_ADMIN) && !role?('Agent')
+    if role?(Z_ROLENAME_CUSTOMER) && !role?(Z_ROLENAME_ADMIN) && !role?(Z_ROLENAME_AGENT)
       response_access_deny
       return
     end
 
+    # set limit for pagination if needed
+    if params[:page] && params[:per_page]
+      params[:limit] = params[:page].to_i * params[:per_page].to_i
+    end
+
     query_params = {
       query: params[:term],
       limit: params[:limit],
@@ -279,6 +311,17 @@ class UsersController < ApplicationController
     # do query
     user_all = User.search(query_params)
 
+    # do pagination if needed
+    if params[:page] && params[:per_page]
+      offset = (params[:page].to_i - 1) * params[:per_page].to_i
+      user_all = user_all.slice(offset, params[:per_page].to_i) || []
+    end
+
+    if params[:expand]
+      render json: user_all
+      return
+    end
+
     # build result list
     if !params[:full]
       users = []

+ 133 - 31
app/models/application_model.rb

@@ -68,26 +68,35 @@ returns
 
 =end
 
-  def self.param_cleanup(params, newObject = false)
+  def self.param_cleanup(params, new_object = false)
+
+    if params.respond_to?('permit!')
+      params.permit!
+    end
 
     if params.nil?
       raise "No params for #{self}!"
     end
 
+    data = {}
+    params.each {|key, value|
+      data[key.to_sym] = value
+    }
+
     # ignore id for new objects
-    if newObject && params[:id]
-      params[:id] = nil
+    if new_object && params[:id]
+      data.delete(:id)
     end
 
     # only use object attributes
-    data = {}
-    new.attributes.each {|item|
-      next if !params.key?(item[0])
-      data[item[0].to_sym] = params[item[0]]
+    clean_params = {}
+    new.attributes.each {|attribute, _value|
+      next if !data.key?(attribute.to_sym)
+      clean_params[attribute.to_sym] = data[attribute.to_sym]
     }
 
     # we do want to set this via database
-    param_validation(data)
+    param_validation(clean_params)
   end
 
 =begin
@@ -105,22 +114,62 @@ returns
 
   def param_set_associations(params)
 
-    # set relations
+    # set relations by id/verify if ref exists
     self.class.reflect_on_all_associations.map { |assoc|
-      real_key = assoc.name.to_s[0, assoc.name.to_s.length - 1] + '_ids'
+      real_ids = assoc.name.to_s[0, assoc.name.to_s.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].class != 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)
 
-      next if !params.key?(real_key.to_sym)
+        # complain if we found no reference
+        if !lookup
+          raise "No value found for '#{assoc.name}' with id #{item_id.inspect}"
+        end
+        list.push item_id
+      }
+      #p "SEND #{real_ids} = #{list.inspect}"
+      send("#{real_ids}=", list)
+    }
 
-      list_of_items = params[ real_key.to_sym ]
-      if params[ real_key.to_sym ].class != Array
-        list_of_items = [ params[ real_key.to_sym ] ]
-      end
+    # set relations by name/lookup
+    self.class.reflect_on_all_associations.map { |assoc|
+      real_ids = assoc.name.to_s[0, assoc.name.to_s.length - 1] + '_ids'
+      next if !respond_to?(real_ids)
+      real_values = assoc.name.to_s[0, assoc.name.to_s.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].class != Array
       list = []
-      list_of_items.each {|item|
-        next if !item
-        list.push(assoc.klass.find(item))
+      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 "No lookup value found for '#{assoc.name}': #{value.inspect}"
+        end
+        list.push lookup.id
       }
-      send(assoc.name.to_s + '=', list)
+      #p "SEND #{real_ids} = #{list.inspect}"
+      send("#{real_ids}=", list)
     }
   end
 
@@ -139,13 +188,12 @@ returns
 
   def attributes_with_associations
 
-    # set relations
+    # get relations
     attributes = self.attributes
     self.class.reflect_on_all_associations.map { |assoc|
-      real_key = assoc.name.to_s[0, assoc.name.to_s.length - 1] + '_ids'
-      if respond_to?(real_key)
-        attributes[ real_key ] = send(real_key)
-      end
+      real_ids = assoc.name.to_s[0, assoc.name.to_s.length - 1] + '_ids'
+      next if !respond_to?(real_ids)
+      attributes[real_ids] = send(real_ids)
     }
     attributes
   end
@@ -165,13 +213,67 @@ returns
   def self.param_validation(data)
 
     # we do want to set this via database
-    data.delete(:updated_at)
-    data.delete(:created_at)
-    data.delete(:updated_by_id)
-    data.delete(:created_by_id)
-    if data.respond_to?('permit!')
-      data.permit!
-    end
+    [:action, :controller, :updated_at, :created_at, :updated_by_id, :created_by_id, :updated_by, :created_by].each {|key|
+      data.delete(key)
+    }
+
+    data
+  end
+
+=begin
+
+do name/login/email based lookup for associations
+
+  attributes = Model.param_association_lookup(params)
+
+returns
+
+  attributes = params # params with possible lookups
+
+=end
+
+  def self.param_association_lookup(params)
+
+    data = {}
+    params.each {|key, value|
+      data[key.to_sym] = value
+    }
+
+    data.symbolize_keys!
+    available_attributes = attribute_names
+    reflect_on_all_associations.map { |assoc|
+      value = data[assoc.name.to_sym]
+      next if !value # next if we do not have a value
+      ref_name = "#{assoc.name}_id"
+      next if !available_attributes.include?(ref_name) # next if we do not 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 !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 "No lookup value found for '#{assoc.name}': #{value.inspect}"
+      end
+
+      # release data value
+      data.delete(assoc.name.to_sym)
+
+      # remember id reference
+      data[ref_name.to_sym] = lookup.id
+    }
+
     data
   end
 

+ 1 - 1
app/models/organization/search.rb

@@ -88,7 +88,7 @@ returns
 
           # get model with full data
           if !organization_exists
-            organizations.push Organization.find(organization_by_user)
+            organizations.push Organization.find(organization_by_user.id)
           end
         }
       end

+ 2 - 2
app/models/ticket.rb

@@ -55,9 +55,9 @@ class Ticket < ApplicationModel
 
   search_index_support
 
-  belongs_to    :group
+  belongs_to    :group,                 class_name: 'Group'
   has_many      :articles,              class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update
-  belongs_to    :organization
+  belongs_to    :organization,          class_name: 'Organization'
   belongs_to    :state,                 class_name: 'Ticket::State'
   belongs_to    :priority,              class_name: 'Ticket::Priority'
   belongs_to    :owner,                 class_name: 'User'

+ 7 - 6
app/models/ticket/state.rb

@@ -1,6 +1,7 @@
 # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
 class Ticket::State < ApplicationModel
   belongs_to    :state_type, class_name: 'Ticket::StateType'
+  belongs_to    :next_state, class_name: 'Ticket::State'
   validates     :name, presence: true
 
   latest_change_support
@@ -20,27 +21,27 @@ returns:
   def self.by_category(category)
     if category == 'open'
       return Ticket::State.where(
-        state_type_id: Ticket::StateType.where( name: ['new', 'open', 'pending reminder', 'pending action'] )
+        state_type_id: Ticket::StateType.where(name: ['new', 'open', 'pending reminder', 'pending action'])
       )
     elsif category == 'pending_reminder'
       return Ticket::State.where(
-        state_type_id: Ticket::StateType.where( name: ['pending reminder'] )
+        state_type_id: Ticket::StateType.where(name: ['pending reminder'])
       )
     elsif category == 'pending_action'
       return Ticket::State.where(
-        state_type_id: Ticket::StateType.where( name: ['pending action'] )
+        state_type_id: Ticket::StateType.where(name: ['pending action'])
       )
     elsif category == 'work_on'
       return Ticket::State.where(
-        state_type_id: Ticket::StateType.where( name: %w(new open) )
+        state_type_id: Ticket::StateType.where(name: %w(new open))
       )
     elsif category == 'work_on_all'
       return Ticket::State.where(
-        state_type_id: Ticket::StateType.where( name: ['new', 'open', 'pending reminder'] )
+        state_type_id: Ticket::StateType.where(name: ['new', 'open', 'pending reminder'])
       )
     elsif category == 'closed'
       return Ticket::State.where(
-        state_type_id: Ticket::StateType.where( name: %w(closed) )
+        state_type_id: Ticket::StateType.where(name: %w(closed))
       )
     end
     raise "Unknown category '#{category}'"

+ 3 - 3
app/models/user.rb

@@ -36,9 +36,9 @@ class User < ApplicationModel
   after_destroy   :avatar_destroy
   notify_clients_support
 
-  has_and_belongs_to_many :groups,          after_add: :cache_update, after_remove: :cache_update
-  has_and_belongs_to_many :roles,           after_add: [:cache_update, :check_notifications], after_remove: :cache_update
-  has_and_belongs_to_many :organizations,   after_add: :cache_update, after_remove: :cache_update
+  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'
+  has_and_belongs_to_many :organizations,   after_add: :cache_update, after_remove: :cache_update, class_name: 'Organization'
   has_many                :tokens,          after_add: :cache_update, after_remove: :cache_update
   has_many                :authorizations,  after_add: :cache_update, after_remove: :cache_update
   belongs_to              :organization,    class_name: 'Organization'

Some files were not shown because too many files changed in this diff