diff --git a/app/controllers/flags_controller.rb b/app/controllers/flags_controller.rb index a83947d37..f505f08fc 100644 --- a/app/controllers/flags_controller.rb +++ b/app/controllers/flags_controller.rb @@ -41,7 +41,7 @@ def new end def history - @user = User.find(params[:id]) + @user = helpers.user_with_me params[:id] unless @user == current_user || (current_user.is_admin || current_user.is_moderator) not_found return diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 62d2c93d2..973a8eb59 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -201,6 +201,10 @@ def posts end end + def my_network + redirect_to network_path(current_user) + end + def network @communities = Community.all render layout: 'without_sidebar' @@ -632,7 +636,12 @@ def filter_params end def set_user - @user = user_scope.find_by(id: params[:id]) + user_id = if params[:id] == 'me' && user_signed_in? + current_user.id + else + params[:id] + end + @user = user_scope.find_by(id: user_id) not_found if @user.nil? end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 79ae8ac3d..a689060d0 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -79,4 +79,17 @@ def sso_sign_in_enabled? def devise_sign_in_enabled? SiteSetting['MixedSignIn'] || !sso_sign_in_enabled? end + + ## + # Returns a user corresponding to the ID provided, with the caveat that if +user_id+ is 'me' and there is a user + # signed in, the signed in user will be returned. Use for /users/me links. + # @param [String] user_id The user ID to find, from +params+ + # @return [User] The User object + def user_with_me(user_id) + if user_id == 'me' && user_signed_in? + current_user + else + User.find(user_id) + end + end end diff --git a/app/models/community_user.rb b/app/models/community_user.rb index 43e8fa1e3..a62926dcc 100644 --- a/app/models/community_user.rb +++ b/app/models/community_user.rb @@ -24,11 +24,6 @@ def suspended? false end - # All undeleted posts on this community by this user. - def post_count - Post.unscoped.where(community_id: community_id).where(user: user).undeleted.count - end - # Calculation functions for privilege scores # These are quite expensive, so we'll cache them for a while def post_score diff --git a/app/models/post.rb b/app/models/post.rb index de81fb53e..54d1c1e5b 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -25,6 +25,7 @@ class Post < ApplicationRecord has_many :reactions counter_culture :parent, column_name: proc { |model| model.deleted? ? nil : 'answer_count' } + counter_culture [:user, :community_user], column_name: proc { |model| model.deleted? ? nil : 'post_count' } serialize :tags_cache, Array @@ -200,13 +201,21 @@ def reaction_list private - # Updates the tags association from the tags_cache. + ## + # Before-validation callback. Update the tags association from the tags_cache. def update_tag_associations tags_cache.each do |tag_name| - tag = Tag.find_or_create_by name: tag_name, tag_set: category.tag_set + tag, name_used = Tag.find_or_create_synonymized name: tag_name, tag_set: category.tag_set unless tags.include? tag tags << tag end + + # If the tags_cache doesn't include name_used then tag_name was a synonym - remove the synonym from tags_cache + # and add the primary for it instead. + unless tags_cache.include? name_used + tags_cache.delete tag_name + tags_cache << name_used + end end tags.each do |tag| unless tags_cache.include? tag.name @@ -215,10 +224,12 @@ def update_tag_associations end end + ## + # Helper method for #check_attribution_notice validator. Produces a text-only attribution notice either based on + # values given or the current state of the post for use in post histories. # @param source [String, Nil] where the post originally came from # @param name [String, Nil] the name of the license # @param url [String, Nil] the url of the license - # # @return [String] an attribution notice corresponding to this post def attribution_text(source = nil, name = nil, url = nil) "Source: #{source || att_source}\nLicense name: #{name || att_license_name}\n" \ diff --git a/app/models/tag.rb b/app/models/tag.rb index 3fc1dff71..873a0ea95 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -38,6 +38,36 @@ def self.search(term) .distinct end + ## + # Find or create a tag within a given tag set, considering synonyms. If a synonym is given as +name+ then the primary + # tag for it is returned instead. + # @param name [String] A tag name to find or create. + # @param tag_set [TagSet] The tag set within which to search for or create the tag. + # @return [Array(Tag, String)] The found or created tag, and the final name used. If a synonymized name was given as + # +name+ then this will be the primary tag name. + # + # @example +name+ does not yet exist: a new Tag is created + # Tag.find_or_create_synonymized name: 'new-tag', tag_set: ... + # # => [Tag, 'new-tag'] + # + # @example +name+ already exists: the existing Tag is returned + # Tag.find_or_create_synonymized name: 'existing-tag', tag_set: ... + # # => [Tag, 'existing-tag'] + # + # @example +name+ is a synonym of 'other-tag': the Tag for 'other-tag' is returned + # Tag.find_or_create_synonymized name: 'synonym', tag_set: ... + # # => [Tag, 'other-tag'] + def self.find_or_create_synonymized(name:, tag_set:) + existing = Tag.find_by(name: name, tag_set: tag_set) + if existing.nil? + synonyms = TagSynonym.joins(:tag).where(name: name, tags: { tag_set: tag_set }) + synonymized_name = synonyms.exists? ? synonyms.first.tag.name : name + [Tag.find_or_create_by(name: synonymized_name, tag_set: tag_set), synonymized_name] + else + [existing, name] + end + end + def all_children query = File.read(Rails.root.join('db/scripts/tag_children.sql')) query = query.gsub('$ParentId', id.to_s) diff --git a/db/migrate/20241020193053_add_post_count_to_community_users.rb b/db/migrate/20241020193053_add_post_count_to_community_users.rb new file mode 100644 index 000000000..a6c54aa17 --- /dev/null +++ b/db/migrate/20241020193053_add_post_count_to_community_users.rb @@ -0,0 +1,5 @@ +class AddPostCountToCommunityUsers < ActiveRecord::Migration[7.0] + def change + add_column :community_users, :post_count, :integer, default: 0, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index baac35e3e..d71e83045 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_04_05_113618) do +ActiveRecord::Schema[7.0].define(version: 2024_10_20_193053) do create_table "abilities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "community_id" t.string "name" @@ -226,6 +226,7 @@ t.boolean "deleted", default: false, null: false t.datetime "deleted_at", precision: nil t.bigint "deleted_by_id" + t.integer "post_count", default: 0, null: false t.index ["community_id"], name: "index_community_users_on_community_id" t.index ["deleted_by_id"], name: "index_community_users_on_deleted_by_id" t.index ["user_id"], name: "index_community_users_on_user_id"