mirror of
https://github.com/discourse/discourse.git
synced 2024-12-19 23:33:59 +08:00
ad7d5426d8
⚠️ This commit is a revert of a revert due to a migration which was causing `{}` metadata to be transformed into `{"value": [null]}`. The new migration shouldn't cause this and will also clean the existing errors, there shouldn't be any data loss given the affected fields where not containing actual data. We might want to stop storing these empty fields in the future.
To achieve it, this commit does the following:
- create a new `groups field`, ideally we would have reused the existing group field, but many automations now have the expectation that this field will return a group id and not an array of group ids, which makes it a dangerous change
- alter the code in `post_created_edited` to use this new groups field and change the logic to use an array
- migrate the existing group fields post_created_edited automations to change name from `restricted_group` to `restricted_groups`, the component from `group` to `groups` and the metadata from `{"value": integer}` to `{"value": [integer]}`
<!-- NOTE: All pull requests should have tests (rspec in Ruby, qunit in JavaScript). If your code does not include test coverage, please include an explanation of why it was omitted. -->
366 lines
13 KiB
Ruby
366 lines
13 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module DiscourseAutomation
|
|
module EventHandlers
|
|
def self.handle_post_created_edited(post, action)
|
|
return if post.post_type != Post.types[:regular] || post.user_id < 0
|
|
topic = post.topic
|
|
return if topic.blank?
|
|
|
|
name = DiscourseAutomation::Triggers::POST_CREATED_EDITED
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: name, enabled: true)
|
|
.find_each do |automation|
|
|
original_post_only = automation.trigger_field("original_post_only")
|
|
if original_post_only["value"]
|
|
next if topic.posts_count > 1
|
|
end
|
|
|
|
first_post_only = automation.trigger_field("first_post_only")
|
|
if first_post_only["value"]
|
|
next if post.user.user_stat.post_count != 1
|
|
end
|
|
|
|
first_topic_only = automation.trigger_field("first_topic_only")
|
|
if first_topic_only["value"]
|
|
next if post.post_number != 1
|
|
next if post.user.user_stat.topic_count != 1
|
|
end
|
|
|
|
skip_via_email = automation.trigger_field("skip_via_email")
|
|
if skip_via_email["value"]
|
|
next if post.via_email?
|
|
end
|
|
|
|
valid_trust_levels = automation.trigger_field("valid_trust_levels")
|
|
if valid_trust_levels["value"]
|
|
next if valid_trust_levels["value"].exclude?(post.user.trust_level)
|
|
end
|
|
|
|
restricted_category = automation.trigger_field("restricted_category")
|
|
if restricted_category["value"]
|
|
category_ids =
|
|
if topic.category_id.blank?
|
|
[]
|
|
else
|
|
[topic.category_id, topic.category.parent_category_id]
|
|
end
|
|
next if !category_ids.include?(restricted_category["value"])
|
|
end
|
|
|
|
restricted_tags = automation.trigger_field("restricted_tags")
|
|
if restricted_tags["value"]
|
|
next if (restricted_tags["value"] & topic.tags.map(&:name)).empty?
|
|
end
|
|
|
|
restricted_group_ids = automation.trigger_field("restricted_groups")["value"]
|
|
if restricted_group_ids.present?
|
|
next if !topic.private_message?
|
|
|
|
target_group_ids = topic.allowed_groups.pluck(:id)
|
|
next if (restricted_group_ids & target_group_ids).empty?
|
|
|
|
ignore_group_members = automation.trigger_field("ignore_group_members")["value"]
|
|
next if ignore_group_members && post.user.in_any_groups?(restricted_group_ids)
|
|
end
|
|
|
|
ignore_automated = automation.trigger_field("ignore_automated")
|
|
next if ignore_automated["value"] && post.incoming_email&.is_auto_generated?
|
|
|
|
action_type = automation.trigger_field("action_type")
|
|
selected_action = action_type["value"]&.to_sym
|
|
|
|
if selected_action
|
|
next if selected_action == :created && action != :create
|
|
next if selected_action == :edited && action != :edit
|
|
end
|
|
|
|
automation.trigger!(
|
|
"kind" => name,
|
|
"action" => action,
|
|
"post" => post,
|
|
"placeholders" => {
|
|
"topic_url" => topic.relative_url,
|
|
"topic_title" => topic.title,
|
|
},
|
|
)
|
|
end
|
|
end
|
|
|
|
def self.handle_user_updated(user, new_user: false)
|
|
return if user.id < 0
|
|
|
|
name = DiscourseAutomation::Triggers::USER_UPDATED
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: name, enabled: true)
|
|
.find_each do |automation|
|
|
once_per_user = automation.trigger_field("once_per_user")["value"]
|
|
if once_per_user &&
|
|
user.custom_fields[
|
|
DiscourseAutomation::AUTOMATION_IDS_CUSTOM_FIELD
|
|
].presence&.include?(automation.id)
|
|
next
|
|
end
|
|
|
|
new_users_only = automation.trigger_field("new_users_only")["value"]
|
|
|
|
new_user_custom_field = automation.new_user_custom_field_name
|
|
new_user ||= user.custom_fields[new_user_custom_field].present?
|
|
|
|
next if new_users_only && !new_user
|
|
|
|
required_custom_fields = automation.trigger_field("custom_fields")
|
|
user_data = {}
|
|
user_custom_fields_data = DB.query <<-SQL
|
|
SELECT uf.name AS field_name, ucf.value AS field_value
|
|
FROM user_fields uf
|
|
JOIN user_custom_fields ucf ON CONCAT('user_field_', uf.id) = ucf.name
|
|
WHERE ucf.user_id = #{user.id};
|
|
SQL
|
|
|
|
user_custom_fields_data =
|
|
user_custom_fields_data.each_with_object({}) do |obj, hash|
|
|
field_name = obj.field_name
|
|
field_value = obj.field_value
|
|
hash[field_name] = field_value
|
|
end
|
|
|
|
if required_custom_fields["value"]
|
|
if required_custom_fields["value"].any? { |field|
|
|
user_custom_fields_data[field].blank?
|
|
}
|
|
if new_users_only
|
|
user.custom_fields[new_user_custom_field] = "1"
|
|
user.save_custom_fields
|
|
end
|
|
next
|
|
end
|
|
user_data[:custom_fields] = user_custom_fields_data
|
|
end
|
|
|
|
required_user_profile_fields = automation.trigger_field("user_profile")
|
|
user_profile_data = UserProfile.find(user.id).attributes
|
|
if required_user_profile_fields["value"]
|
|
if required_user_profile_fields["value"].any? { |field|
|
|
user_profile_data[field].blank?
|
|
}
|
|
if new_users_only
|
|
user.custom_fields[new_user_custom_field] = "1"
|
|
user.save_custom_fields
|
|
end
|
|
next
|
|
end
|
|
user_data[:profile_data] = user_profile_data
|
|
end
|
|
|
|
if new_users_only && once_per_user
|
|
user.custom_fields.delete(new_user_custom_field)
|
|
user.save_custom_fields
|
|
end
|
|
|
|
automation.add_id_to_custom_field(user, DiscourseAutomation::AUTOMATION_IDS_CUSTOM_FIELD)
|
|
|
|
automation.trigger!("kind" => name, "user" => user, "user_data" => user_data)
|
|
end
|
|
end
|
|
|
|
def self.handle_category_created_edited(category, action)
|
|
name = DiscourseAutomation::Triggers::CATEGORY_CREATED_EDITED
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: name, enabled: true)
|
|
.find_each do |automation|
|
|
restricted_category = automation.trigger_field("restricted_category")
|
|
if restricted_category["value"].present?
|
|
next if restricted_category["value"] != category.parent_category_id
|
|
end
|
|
|
|
automation.trigger!("kind" => name, "action" => action, "category" => category)
|
|
end
|
|
end
|
|
|
|
def self.handle_pm_created(topic)
|
|
return if topic.user_id < 0
|
|
|
|
user = topic.user
|
|
target_usernames = topic.allowed_users.pluck(:username) - [user.username]
|
|
target_group_ids = topic.allowed_groups.pluck(:id)
|
|
return if (target_usernames.length + target_group_ids.length) > 1
|
|
|
|
name = DiscourseAutomation::Triggers::PM_CREATED
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: name, enabled: true)
|
|
.find_each do |automation|
|
|
restricted_username = automation.trigger_field("restricted_user")["value"]
|
|
next if restricted_username.present? && restricted_username != target_usernames.first
|
|
|
|
restricted_group_id = automation.trigger_field("restricted_group")["value"]
|
|
next if restricted_group_id.present? && restricted_group_id != target_group_ids.first
|
|
|
|
ignore_staff = automation.trigger_field("ignore_staff")
|
|
next if ignore_staff["value"] && user.staff?
|
|
|
|
ignore_group_members = automation.trigger_field("ignore_group_members")
|
|
next if ignore_group_members["value"] && user.in_any_groups?([restricted_group_id])
|
|
|
|
ignore_automated = automation.trigger_field("ignore_automated")
|
|
next if ignore_automated["value"] && topic.first_post.incoming_email&.is_auto_generated?
|
|
|
|
valid_trust_levels = automation.trigger_field("valid_trust_levels")
|
|
if valid_trust_levels["value"]
|
|
next if !valid_trust_levels["value"].include?(user.trust_level)
|
|
end
|
|
|
|
automation.trigger!("kind" => name, "post" => topic.first_post)
|
|
end
|
|
end
|
|
|
|
def self.handle_topic_tags_changed(topic, old_tag_names, new_tag_names, user)
|
|
name = DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: name, enabled: true)
|
|
.find_each do |automation|
|
|
watching_categories = automation.trigger_field("watching_categories")
|
|
if watching_categories["value"]
|
|
next if !watching_categories["value"].include?(topic.category_id)
|
|
end
|
|
|
|
removed_tags = old_tag_names - new_tag_names
|
|
added_tags = new_tag_names - old_tag_names
|
|
|
|
watching_tags = automation.trigger_field("watching_tags")
|
|
|
|
if watching_tags["value"]
|
|
should_skip = false
|
|
watching_tags["value"].each do |tag|
|
|
should_skip = true if !removed_tags.empty? && !removed_tags.include?(tag)
|
|
should_skip = true if !added_tags.empty? && !added_tags.include?(tag)
|
|
end
|
|
next if should_skip
|
|
end
|
|
|
|
automation.trigger!(
|
|
"kind" => name,
|
|
"topic" => topic,
|
|
"removed_tags" => removed_tags,
|
|
"added_tags" => added_tags,
|
|
"user" => user,
|
|
"placeholders" => {
|
|
"topic_url" => topic.relative_url,
|
|
"topic_title" => topic.title,
|
|
},
|
|
)
|
|
end
|
|
end
|
|
|
|
def self.handle_after_post_cook(post, cooked)
|
|
return cooked if post.post_type != Post.types[:regular] || post.post_number > 1
|
|
|
|
name = DiscourseAutomation::Triggers::AFTER_POST_COOK
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: name, enabled: true)
|
|
.find_each do |automation|
|
|
valid_trust_levels = automation.trigger_field("valid_trust_levels")
|
|
if valid_trust_levels["value"]
|
|
next if valid_trust_levels["value"].exclude?(post.user.trust_level)
|
|
end
|
|
|
|
restricted_category = automation.trigger_field("restricted_category")
|
|
if restricted_category["value"]
|
|
category_ids = [post.topic&.category&.parent_category&.id, post.topic&.category&.id]
|
|
next if !category_ids.compact.include?(restricted_category["value"])
|
|
end
|
|
|
|
restricted_tags = automation.trigger_field("restricted_tags")
|
|
if tag_names = restricted_tags["value"]
|
|
found = false
|
|
next if !post.topic
|
|
|
|
post.topic.tags.each do |tag|
|
|
found ||= tag_names.include?(tag.name)
|
|
break if found
|
|
end
|
|
|
|
next if !found
|
|
end
|
|
|
|
if new_cooked = automation.trigger!("kind" => name, "post" => post, "cooked" => cooked)
|
|
cooked = new_cooked
|
|
end
|
|
end
|
|
|
|
cooked
|
|
end
|
|
|
|
def self.handle_user_promoted(user_id, new_trust_level, old_trust_level)
|
|
trigger = DiscourseAutomation::Triggers::USER_PROMOTED
|
|
user = User.find_by(id: user_id)
|
|
return if user.blank?
|
|
|
|
# don't want to do anything if the user is demoted. this should probably
|
|
# be a separate event in core
|
|
return if new_trust_level < old_trust_level
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: trigger, enabled: true)
|
|
.find_each do |automation|
|
|
trust_level_code_all = DiscourseAutomation::USER_PROMOTED_TRUST_LEVEL_CHOICES.first[:id]
|
|
|
|
restricted_group_id = automation.trigger_field("restricted_group")["value"]
|
|
trust_level_transition = automation.trigger_field("trust_level_transition")["value"]
|
|
trust_level_transition = trust_level_transition || trust_level_code_all
|
|
|
|
if restricted_group_id.present? &&
|
|
!GroupUser.exists?(user_id: user_id, group_id: restricted_group_id)
|
|
next
|
|
end
|
|
|
|
transition_code = "TL#{old_trust_level}#{new_trust_level}"
|
|
if trust_level_transition == trust_level_code_all ||
|
|
trust_level_transition == transition_code
|
|
automation.trigger!(
|
|
"kind" => trigger,
|
|
"usernames" => [user.username],
|
|
"placeholders" => {
|
|
"trust_level_transition" =>
|
|
I18n.t(
|
|
"discourse_automation.triggerables.user_promoted.transition_placeholder",
|
|
from_level_name: TrustLevel.name(old_trust_level),
|
|
to_level_name: TrustLevel.name(new_trust_level),
|
|
),
|
|
},
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.handle_stalled_topic(post)
|
|
return if post.topic.blank?
|
|
return if post.user_id != post.topic.user_id
|
|
|
|
DiscourseAutomation::Automation
|
|
.where(trigger: DiscourseAutomation::Triggers::STALLED_TOPIC)
|
|
.where(enabled: true)
|
|
.find_each do |automation|
|
|
fields = automation.serialized_fields
|
|
|
|
categories = fields.dig("categories", "value")
|
|
next if categories && !categories.include?(post.topic.category_id)
|
|
|
|
tags = fields.dig("tags", "value")
|
|
next if tags&.any? && (tags & post.topic.tags.map(&:name)).empty?
|
|
|
|
DiscourseAutomation::UserGlobalNotice
|
|
.where(identifier: automation.id)
|
|
.where(user_id: post.user_id)
|
|
.destroy_all
|
|
end
|
|
end
|
|
end
|
|
end
|