summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/award_emoji.rb17
-rw-r--r--app/models/concerns/optionally_search.rb19
-rw-r--r--app/models/project.rb21
-rw-r--r--app/models/user.rb45
4 files changed, 101 insertions, 1 deletions
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index 99c7866d636..ddc516ccb60 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -28,6 +28,23 @@ class AwardEmoji < ActiveRecord::Base
.where('name IN (?) AND awardable_type = ? AND awardable_id IN (?)', [DOWNVOTE_NAME, UPVOTE_NAME], type, ids)
.group('name', 'awardable_id')
end
+
+ # Returns the top 100 emoji awarded by the given user.
+ #
+ # The returned value is a Hash mapping emoji names to the number of times
+ # they were awarded:
+ #
+ # { 'thumbsup' => 2, 'thumbsdown' => 1 }
+ #
+ # user - The User to get the awards for.
+ # limt - The maximum number of emoji to return.
+ def award_counts_for_user(user, limit = 100)
+ limit(limit)
+ .where(user: user)
+ .group(:name)
+ .order('count_all DESC, name ASC')
+ .count
+ end
end
def downvote?
diff --git a/app/models/concerns/optionally_search.rb b/app/models/concerns/optionally_search.rb
new file mode 100644
index 00000000000..dec97b7dee8
--- /dev/null
+++ b/app/models/concerns/optionally_search.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module OptionallySearch
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def search(*)
+ raise(
+ NotImplementedError,
+ 'Your model must implement the "search" class method'
+ )
+ end
+
+ # Optionally limits a result set to those matching the given search query.
+ def optionally_search(query = nil)
+ query.present? ? search(query) : all
+ end
+ end
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 94c1d60f071..15336ec2ea2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -28,6 +28,7 @@ class Project < ActiveRecord::Base
include WithUploads
include BatchDestroyDependentAssociations
include FeatureGate
+ include OptionallySearch
extend Gitlab::Cache::RequestCache
extend Gitlab::ConfigHelper
@@ -384,6 +385,26 @@ class Project < ActiveRecord::Base
only_integer: true,
message: 'needs to be beetween 10 minutes and 1 month' }
+ # Paginates a collection using a `WHERE id < ?` condition.
+ #
+ # before - A project ID to use for filtering out projects with an equal or
+ # greater ID. If no ID is given, all projects are included.
+ #
+ # limit - The maximum number of rows to include.
+ def self.paginate_in_descending_order_using_id(
+ before: nil,
+ limit: Kaminari.config.default_per_page
+ )
+ relation = order_id_desc.limit(limit)
+ relation = relation.where('projects.id < ?', before) if before
+
+ relation
+ end
+
+ def self.eager_load_namespace_and_owner
+ includes(namespace: :owner)
+ end
+
# Returns a collection of projects that is either public or visible to the
# logged in user.
def self.public_or_visible_to_user(user = nil)
diff --git a/app/models/user.rb b/app/models/user.rb
index 13b04270a4a..a6ba90794d6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -19,6 +19,7 @@ class User < ActiveRecord::Base
include BulkMemberAccessLoad
include BlocksJsonSerialization
include WithUploads
+ include OptionallySearch
DEFAULT_NOTIFICATION_LEVEL = :participating
@@ -253,11 +254,41 @@ class User < ActiveRecord::Base
scope :external, -> { where(external: true) }
scope :active, -> { with_state(:active).non_internal }
scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) }
- scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) }
scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'DESC')) }
scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
+ # Limits the users to those that have TODOs, optionally in the given state.
+ #
+ # user - The user to get the todos for.
+ #
+ # with_todos - If we should limit the result set to users that are the
+ # authors of todos.
+ #
+ # todo_state - An optional state to require the todos to be in.
+ def self.limit_to_todo_authors(user: nil, with_todos: false, todo_state: nil)
+ if user && with_todos
+ where(id: Todo.where(user: user, state: todo_state).select(:author_id))
+ else
+ all
+ end
+ end
+
+ # Returns a relation that optionally includes the given user.
+ #
+ # user_id - The ID of the user to include.
+ def self.union_with_user(user_id = nil)
+ if user_id.present?
+ union = Gitlab::SQL::Union.new([all, User.unscoped.where(id: user_id)])
+
+ # We use "unscoped" here so that any inner conditions are not repeated for
+ # the outer query, which would be redundant.
+ User.unscoped.from("(#{union.to_sql}) #{User.table_name}")
+ else
+ all
+ end
+ end
+
def self.with_two_factor_indistinct
joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
.where("u2f.id IS NOT NULL OR users.otp_required_for_login = ?", true)
@@ -365,6 +396,18 @@ class User < ActiveRecord::Base
).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
end
+ # Limits the result set to users _not_ in the given query/list of IDs.
+ #
+ # users - The list of users to ignore. This can be an
+ # `ActiveRecord::Relation`, or an Array.
+ def where_not_in(users = nil)
+ users ? where.not(id: users) : all
+ end
+
+ def reorder_by_name
+ reorder(:name)
+ end
+
# searches user by given pattern
# it compares name, email, username fields and user's secondary emails with given pattern
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.