diff --git a/app/models/post.rb b/app/models/post.rb index c7c2df8759f631149eebc9f376882027d85a6210..cfd4aeef1a46ce5ad9be7e9daae35e8361fc2fa7 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -56,6 +56,8 @@ class Post < ApplicationRecord after_save :update_category_activity, if: -> { post_type.has_category && !destroyed? } after_save :recalc_score + # @param term [String] the search term + # @return [ActiveRecord::Relation<Post>] def self.search(term) match_search term, posts: :body_markdown end @@ -74,14 +76,18 @@ class Post < ApplicationRecord end end + # @return [TagSet] def tag_set parent.nil? ? category.tag_set : parent.category.tag_set end + # @return [Boolean] def meta? false end + # Used in the transfer of content from SE to reassign the owner of a post to the given user. + # @param new_user [User] def reassign_user(new_user) new_user.ensure_community_user! @@ -92,10 +98,13 @@ class Post < ApplicationRecord update!(deleted: false, deleted_at: nil, deleted_by: nil) end + # Removes the attribution notice from this post + # @return [Boolean] whether the action was successful def remove_attribution_notice! update(att_source: nil, att_license_link: nil, att_license_name: nil) end + # @return [String] the type of the last activity on this post def last_activity_type case last_activity when closed_at @@ -119,30 +128,37 @@ class Post < ApplicationRecord end end + # @return [String] the body with all markdown stripped def body_plain ApplicationController.helpers.strip_markdown(body_markdown) end + # @return [Boolean] whether this post is a question def question? post_type_id == Question.post_type_id end + # @return [Boolean] whether this post is an answer def answer? post_type_id == Answer.post_type_id end + # @return [Boolean] whether this post is an article def article? post_type_id == Article.post_type_id end + # @return [Boolean] whether there is a suggested edit pending for this post def pending_suggested_edit? SuggestedEdit.where(post_id: id, active: true).any? end + # @return [SuggestedEdit, Nil] the suggested edit pending for this post (if any) def pending_suggested_edit SuggestedEdit.where(post_id: id, active: true).last end + # Recalculates the score of this post based on its up and downvotes def recalc_score variable = SiteSetting['ScoringVariable'] || 2 sql = 'UPDATE posts SET score = (upvote_count + ?) / (upvote_count + downvote_count + (2 * ?)) WHERE id = ?' @@ -150,6 +166,8 @@ class Post < ApplicationRecord ActiveRecord::Base.connection.execute sanitized end + # This method will update the locked status of this post if locked_until is in the past. + # @return [Boolean] whether this post is locked def locked? return true if locked && locked_until.nil? # permanent lock return true if locked && !locked_until.past? @@ -159,12 +177,15 @@ class Post < ApplicationRecord end end + # @param user [User, Nil] + # @return [Boolean] whether the given user can view this post def can_access?(user) (!deleted? || user&.has_post_privilege?('flag_curate', self)) && (!category.present? || !category.min_view_trust_level.present? || category.min_view_trust_level <= (user&.trust_level || 0)) end + # @return [Hash] a hash with as key the reaction type and value the amount of reactions for that type def reaction_list reactions.includes(:reaction_type).group_by(&:reaction_type_id) .to_h { |_k, v| [v.first.reaction_type, v] } @@ -172,6 +193,7 @@ class Post < ApplicationRecord private + # Updates 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 @@ -186,11 +208,18 @@ class Post < ApplicationRecord end end + # @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" \ "License URL: #{url || att_license_link}" end + # Intended to be called as callback after a save. + # If changes were made to the licensing of this post, this will insert the correct history items. def check_attribution_notice sc = saved_changes attributes = ['att_source', 'att_license_name', 'att_license_link'] @@ -208,6 +237,8 @@ class Post < ApplicationRecord end end + # Intended to be called as callback after a save. + # If the last activity of this post was changed and it has a parent, also updates the parent activity def copy_last_activity_to_parent sc = saved_changes if parent.present? && (sc.include?('last_activity') || sc.include?('last_activity_by_id')) \ @@ -216,6 +247,8 @@ class Post < ApplicationRecord end end + # Intended to be called as callback after a save. + # If this deletion status of this post was changed, then remove or re-add the reputation. def modify_author_reputation sc = saved_changes if sc.include?('deleted') && sc['deleted'][0] != sc['deleted'][1] && created_at >= 60.days.ago @@ -228,10 +261,14 @@ class Post < ApplicationRecord end end + # Intended to be called as callback after a save. + # @return [PostHistory] creates an initial revision for this post def create_initial_revision PostHistory.initial_revision(self, user, after: body_markdown, after_title: title, after_tags: tags) end + # Intended to be used as validation. + # Will add an error if this post's post type is not allowed in the associated category. def category_allows_post_type return if category.nil? @@ -240,6 +277,8 @@ class Post < ApplicationRecord end end + # Intended to be called as callback after a save. + # Deletes this posts description from the cache such that it will be regenerated next time it is needed. def break_description_cache Rails.cache.delete "posts/#{id}/description" if parent_id.present? @@ -247,6 +286,8 @@ class Post < ApplicationRecord end end + # Intended to be used as a validation. + # Checks whether the associated license is present and enabled. def license_valid # Don't validate license on edits return unless id.nil? @@ -261,6 +302,9 @@ class Post < ApplicationRecord end end + # Intended to be used as validation. + # Checks whether there are any moderator tags present added, and if so whether the current user is allowed to add + # those. def moderator_tags mod_tags = category&.moderator_tags&.map(&:name) return unless mod_tags.present? && !mod_tags.empty? @@ -274,6 +318,8 @@ class Post < ApplicationRecord end end + # Intended to be used as callback after a save. + # Updates the category activity indicator if the last activity of this post changed. def update_category_activity if saved_changes.include? 'last_activity' category.update_activity(last_activity)