Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add category moderation groups and more attributes to generic importer #32561

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

RubenOussoren
Copy link
Contributor

This PR improves the generic bulk import and base scripts by adding support for several previously unimplemented features and attributes:

  • Category Moderation Groups: Implements the import of group-based moderation settings for categories.
  • Group Attributes: Adds import support for public_admission, public_exit, allow_membership_requests, and assignable_level.
  • User Attributes: Adds import support for trust_level and primary_group_id.
  • User Option: Adds import support for the hide_profile_and_presence user option.
  • Multiple Tag Groups per Tag: Updates tag import logic to handle tags belonging to multiple tag groups.

@RubenOussoren RubenOussoren requested a review from s3lase May 2, 2025 15:25
@RubenOussoren RubenOussoren self-assigned this May 2, 2025
@RubenOussoren RubenOussoren requested a review from a team as a code owner May 2, 2025 15:25
@github-actions github-actions bot added the migrations-tooling PR which includes changes to migrations tooling label May 2, 2025
@@ -508,6 +511,7 @@ def discourse_reaction_id_from_original_id(id)
messageable_level
created_at
updated_at
assignable_level
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this currently work? I don't see assignable_level in the groups table

Comment on lines +502 to +504
public_admission
public_exit
allow_membership_requests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also extend the process_group method to set a default value for these if they're not present? In its current form, it’ll fail for intermediate DBs generated by other converters that don’t explicitly set these values.

next if existing_group_user_ids.include?([group_id, user_id])

{ group_id: group_id, user_id: user_id }
{ group_id: group_id, user_id: user_id, owner: row["owner"] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's extend process_group_user to set default value for owner

@@ -562,6 +631,7 @@ def import_user_options
email_level: row["email_level"],
email_messages_level: row["email_messages_level"],
email_digests: row["email_digests"],
hide_profile_and_presence: row["hide_profile_and_presence"] || false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be some work to make this setting more granular by splitting it into hide_profile and hide_presence, but hide_profile_and_presence still appears to be kept in sync as follows:

def update_hide_profile_and_presence
if hide_profile_changed? || hide_presence_changed?
self.hide_profile_and_presence = hide_profile || hide_presence
elsif hide_profile_and_presence_changed?
self.hide_profile = hide_profile_and_presence
self.hide_presence = hide_profile_and_presence
end
end

Simply setting hide_profile_and_presence might be problematic. You could:

  1. Set both hide_profile and hide_presence to the value of hide_profile_and_presence, if it’s explicitly provided in process_user_options
  2. Update the converter framework to support to also support hide_profile and hide_presence and replicate the logic above to keep them in sync

group_id = group_id_from_imported_id(row["group_id"])

next unless category_id && group_id
next if existing_moderation_groups.include?([category_id, group_id])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
next if existing_moderation_groups.include?([category_id, group_id])
next unless existing_moderation_groups.add?([category_id, group_id])

@@ -77,6 +77,8 @@ def execute
import_category_tag_groups
import_category_permissions
import_category_users
import_category_moderation_groups
update_read_restricted_flags
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny nit, for clarity, could the method name explicitly mention category, even though it's colocated? Maybe update_category_read_restricted?. Makes it more obvious at first glance and consistent with other similar steps

.find_each do |category|
processed_count += 1

permissions = category.permissions_params
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way permissions_params is currently implemented triggers additional DB queries even when both category_groups and group are preloaded. Since it's a short method, it wouldn’t hurt to inline it.

Category.includes(category_groups: :group).find_each do |category|
  # ... Do stuff

  permissions = {}

  category.category_groups.each do |category_group|
    group = category_group.group
    permissions[category_group.group_name] = category_group.permission_type if group.present?
  end

  # ... Do more stuff
end

@@ -247,7 +249,6 @@ def import_categories
parent_category_id:
row["parent_category_id"] ? category_id_from_imported_id(row["parent_category_id"]) : nil,
slug: row["slug"],
read_restricted: row["read_restricted"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still referenced in here and there. It's redundant since we're moving to computing the value instead

Comment on lines +2026 to +2040
if row["tag_group_id"] && !row["tag_group_id"].empty?
intermediate_group_ids = JSON.parse(row["tag_group_id"])

intermediate_group_ids.each do |intermediate_group_id|
discourse_tag_group_id = @tag_group_mapping[intermediate_group_id]

if discourse_tag_group_id
TagGroupMembership.find_or_create_by!(
tag_id: tag.id,
tag_group_id: discourse_tag_group_id,
)
else
puts "Warning: Intermediate tag group ID #{intermediate_group_id} from row not found in @tag_group_mapping for tag '#{tag.name}'"
end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you update tag_group_id in the converter to a JSON column?

You’d typically expect tag_group_id to be an integer. If we want to support multiple tag group IDs, it might be better to add a separate tag_group_ids column and leave tag_group_id as is since we don’t have time to update all converters right now. We can just handle both columns here for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
migrations-tooling PR which includes changes to migrations tooling
Development

Successfully merging this pull request may close these issues.

2 participants