mirror of
https://github.com/discourse/discourse.git
synced 2025-04-30 14:24:40 +08:00

This feature allow admins to personalize their communities by associating emojis or icons with their site categories. There are now 3 style types for categories: - Square (the default) - Emoji - Icon ### How it looks 🎨 Adding an icon: <img width="502" alt="Category with an icon" src="https://github.com/user-attachments/assets/8f711340-166e-4781-a7b7-7267469dbabd" /> Adding an emoji: <img width="651" alt="Category with an emoji" src="https://github.com/user-attachments/assets/588c38ce-c719-4ed5-83f9-f1e1cb52c929" /> Sidebar: <img width="248" alt="Sidebar with emojis" src="https://github.com/user-attachments/assets/cd03d591-6170-4515-998c-0cec20118568" /> Category menus: <img width="621" alt="Screenshot 2025-03-13 at 10 32 30 AM" src="https://github.com/user-attachments/assets/7d89797a-f69f-45e5-bf64-a92d4cff8753" /> Within posts/topics: <img width="382" alt="Screenshot 2025-03-13 at 10 33 41 AM" src="https://github.com/user-attachments/assets/b7b1a951-44c6-4a4f-82ad-8ee31ddd6061" /> Chat messages: <img width="392" alt="Screenshot 2025-03-13 at 10 30 20 AM" src="https://github.com/user-attachments/assets/126f8076-0ea3-4f19-8452-1041fd2af29f" /> Autocomplete: <img width="390" alt="Screenshot 2025-03-13 at 10 29 53 AM" src="https://github.com/user-attachments/assets/cad75669-225f-4b8e-a7b5-ae5aa8f1bcad" /> --------- Co-authored-by: Martin Brennan <martin@discourse.org> Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
268 lines
5.2 KiB
Ruby
268 lines
5.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module ImportExport
|
|
class BaseExporter
|
|
attr_reader :export_data
|
|
|
|
CATEGORY_ATTRS = %i[
|
|
id
|
|
name
|
|
color
|
|
created_at
|
|
user_id
|
|
slug
|
|
description
|
|
text_color
|
|
style_type
|
|
icon
|
|
emoji
|
|
auto_close_hours
|
|
position
|
|
parent_category_id
|
|
auto_close_based_on_last_post
|
|
topic_template
|
|
all_topics_wiki
|
|
permissions_params
|
|
]
|
|
|
|
GROUP_ATTRS = %i[
|
|
id
|
|
name
|
|
created_at
|
|
automatic_membership_email_domains
|
|
primary_group
|
|
title
|
|
grant_trust_level
|
|
incoming_email
|
|
bio_raw
|
|
allow_membership_requests
|
|
full_name
|
|
default_notification_level
|
|
visibility_level
|
|
public_exit
|
|
public_admission
|
|
membership_request_template
|
|
messageable_level
|
|
mentionable_level
|
|
members_visibility_level
|
|
publish_read_state
|
|
]
|
|
|
|
USER_ATTRS = %i[
|
|
id
|
|
email
|
|
username
|
|
name
|
|
created_at
|
|
trust_level
|
|
active
|
|
last_emailed_at
|
|
custom_fields
|
|
]
|
|
|
|
TOPIC_ATTRS = %i[id title created_at views category_id closed archived archetype]
|
|
|
|
POST_ATTRS = %i[
|
|
id
|
|
user_id
|
|
post_number
|
|
raw
|
|
created_at
|
|
reply_to_post_number
|
|
hidden
|
|
hidden_reason_id
|
|
wiki
|
|
]
|
|
|
|
def categories
|
|
@categories ||= Category.all.to_a
|
|
end
|
|
|
|
def export_categories
|
|
data = []
|
|
|
|
categories.each do |cat|
|
|
data << CATEGORY_ATTRS.inject({}) do |h, a|
|
|
h[a] = cat.public_send(a)
|
|
h
|
|
end
|
|
end
|
|
|
|
data
|
|
end
|
|
|
|
def export_categories!
|
|
@export_data[:categories] = export_categories
|
|
|
|
self
|
|
end
|
|
|
|
def export_groups(group_names)
|
|
data = []
|
|
groups = Group.all
|
|
groups = groups.where(name: group_names) if group_names.present?
|
|
|
|
groups.find_each do |group|
|
|
attrs =
|
|
GROUP_ATTRS.inject({}) do |h, a|
|
|
h[a] = group.public_send(a)
|
|
h
|
|
end
|
|
attrs[:user_ids] = group.users.pluck(:id)
|
|
data << attrs
|
|
end
|
|
|
|
data
|
|
end
|
|
|
|
def export_groups!
|
|
@export_data[:groups] = export_groups([])
|
|
|
|
self
|
|
end
|
|
|
|
def export_category_groups
|
|
groups = []
|
|
group_names = []
|
|
auto_group_names = Group::AUTO_GROUPS.keys.map(&:to_s)
|
|
|
|
@export_data[:categories].each do |c|
|
|
c[:permissions_params].each do |group_name, _|
|
|
group_names << group_name if auto_group_names.exclude?(group_name.to_s)
|
|
end
|
|
end
|
|
|
|
group_names.uniq!
|
|
return [] if group_names.empty?
|
|
|
|
export_groups(group_names)
|
|
end
|
|
|
|
def export_category_groups!
|
|
@export_data[:groups] = export_category_groups
|
|
|
|
self
|
|
end
|
|
|
|
def export_group_users
|
|
user_ids = []
|
|
|
|
@export_data[:groups].each { |g| user_ids += g[:user_ids] }
|
|
|
|
user_ids.uniq!
|
|
return User.none if user_ids.empty?
|
|
|
|
users = User.where(id: user_ids)
|
|
export_users(users)
|
|
end
|
|
|
|
def export_group_users!
|
|
@export_data[:users] = export_group_users
|
|
|
|
self
|
|
end
|
|
|
|
def export_topics
|
|
data = []
|
|
|
|
@topics.each do |topic|
|
|
puts topic.title
|
|
|
|
topic_data =
|
|
TOPIC_ATTRS.inject({}) do |h, a|
|
|
h[a] = topic.public_send(a)
|
|
h
|
|
end
|
|
|
|
topic_data[:posts] = []
|
|
|
|
topic.ordered_posts.find_each do |post|
|
|
attributes =
|
|
POST_ATTRS.inject({}) do |h, a|
|
|
h[a] = post.public_send(a)
|
|
h
|
|
end
|
|
|
|
attributes[:raw] = attributes[:raw].gsub(
|
|
'src="/uploads',
|
|
"src=\"#{Discourse.base_url_no_prefix}/uploads",
|
|
)
|
|
|
|
topic_data[:posts] << attributes
|
|
end
|
|
|
|
data << topic_data
|
|
end
|
|
|
|
data
|
|
end
|
|
|
|
def export_topics!
|
|
@export_data[:topics] = export_topics
|
|
|
|
self
|
|
end
|
|
|
|
def export_topic_users
|
|
return if @export_data[:topics].blank?
|
|
topic_ids = @export_data[:topics].pluck(:id)
|
|
|
|
users = User.joins(:posts).where("posts.topic_id IN (?)", topic_ids).distinct
|
|
|
|
export_users(users)
|
|
end
|
|
|
|
def export_topic_users!
|
|
@export_data[:users] = export_topic_users
|
|
|
|
self
|
|
end
|
|
|
|
def export_users(users)
|
|
data = []
|
|
|
|
users.find_each do |u|
|
|
next if u.id == Discourse::SYSTEM_USER_ID
|
|
|
|
x =
|
|
USER_ATTRS.inject({}) do |h, a|
|
|
h[a] = u.public_send(a)
|
|
h
|
|
end
|
|
|
|
x.merge(
|
|
bio_raw: u.user_profile.bio_raw,
|
|
website: u.user_profile.website,
|
|
location: u.user_profile.location,
|
|
)
|
|
data << x
|
|
end
|
|
|
|
data
|
|
end
|
|
|
|
def export_translation_overrides
|
|
@export_data[:translation_overrides] = TranslationOverride.all.select(
|
|
:locale,
|
|
:translation_key,
|
|
:value,
|
|
)
|
|
|
|
self
|
|
end
|
|
|
|
def default_filename_prefix
|
|
raise "Overwrite me!"
|
|
end
|
|
|
|
def save_to_file(filename = nil)
|
|
output_basename =
|
|
filename ||
|
|
File.join("#{default_filename_prefix}-#{Time.now.strftime("%Y-%m-%d-%H%M%S")}.json")
|
|
File.open(output_basename, "w:UTF-8") { |f| f.write(@export_data.to_json) }
|
|
puts "Export saved to #{output_basename}"
|
|
output_basename
|
|
end
|
|
end
|
|
end
|