FEATURE - allow category group moderators to split/merge topics (#10351)

This commit is contained in:
jbrw 2020-08-05 10:33:25 -04:00 committed by GitHub
parent a68533b394
commit 67e8bc5342
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 488 additions and 286 deletions

View File

@ -122,6 +122,7 @@ export default function transformPost(
currentUser && (currentUser.id === post.user_id || currentUser.staff);
postAtts.canArchiveTopic = !!details.can_archive_topic;
postAtts.canCloseTopic = !!details.can_close_topic;
postAtts.canSplitMergeTopic = !!details.can_split_merge_topic;
postAtts.canEditStaffNotes = !!details.can_edit_staff_notes;
postAtts.canReplyAsNewTopic = !!details.can_reply_as_new_topic;
postAtts.canReviewTopic = !!details.can_review_topic;

View File

@ -94,7 +94,7 @@ export default createWidget("post-stream", {
transformed.canCreatePost = attrs.canCreatePost;
transformed.mobileView = mobileView;
if (transformed.canManage) {
if (transformed.canManage || transformed.canSplitMergeTopic) {
transformed.multiSelect = attrs.multiSelect;
if (attrs.multiSelect) {

View File

@ -130,7 +130,10 @@ export default createWidget("topic-admin-menu", {
const visible = topic.get("visible");
// Admin actions
if (this.currentUser && this.currentUser.get("canManageTopic")) {
if (
this.get("currentUser.canManageTopic") ||
details.can_split_merge_topic
) {
this.addActionButton({
className: "topic-admin-multi-select",
buttonClass: "popup-menu-btn",
@ -138,7 +141,9 @@ export default createWidget("topic-admin-menu", {
icon: "tasks",
label: "actions.multi_select"
});
}
if (this.get("currentUser.canManageTopic")) {
if (details.get("can_delete")) {
this.addActionButton({
className: "topic-admin-delete",
@ -180,7 +185,7 @@ export default createWidget("topic-admin-menu", {
}
}
if (this.currentUser && this.currentUser.get("canManageTopic")) {
if (this.get("currentUser.canManageTopic")) {
this.addActionButton({
className: "topic-admin-status-update",
buttonClass: "popup-menu-btn",
@ -230,7 +235,7 @@ export default createWidget("topic-admin-menu", {
}
}
if (this.currentUser && this.currentUser.get("canManageTopic")) {
if (this.get("currentUser.canManageTopic")) {
this.addActionButton({
className: "topic-admin-visible",
buttonClass: "popup-menu-btn",

View File

@ -711,6 +711,9 @@ class TopicsController < ApplicationController
topic = Topic.find_by(id: topic_id)
guardian.ensure_can_move_posts!(topic)
destination_topic = Topic.find_by(id: destination_topic_id)
guardian.ensure_can_create_post_on_topic!(destination_topic)
args = {}
args[:destination_topic_id] = destination_topic_id.to_i
@ -736,9 +739,15 @@ class TopicsController < ApplicationController
topic = Topic.with_deleted.find_by(id: topic_id)
guardian.ensure_can_move_posts!(topic)
# when creating a new topic, ensure the 1st post is a regular post
if params[:title].present? && Post.where(topic: topic, id: post_ids).order(:post_number).pluck_first(:post_type) != Post.types[:regular]
return render_json_error("When moving posts to a new topic, the first post must be a regular post.")
if params[:title].present?
# when creating a new topic, ensure the 1st post is a regular post
if Post.where(topic: topic, id: post_ids).order(:post_number).pluck_first(:post_type) != Post.types[:regular]
return render_json_error("When moving posts to a new topic, the first post must be a regular post.")
end
if params[:category_id].present?
guardian.ensure_can_create_topic_on_category!(params[:category_id])
end
end
destination_topic = move_posts_to_destination(topic)

View File

@ -19,6 +19,7 @@ class TopicViewDetailsSerializer < ApplicationSerializer
:can_publish_page,
:can_close_topic,
:can_archive_topic,
:can_split_merge_topic,
:can_edit_staff_notes]
end
@ -142,6 +143,7 @@ class TopicViewDetailsSerializer < ApplicationSerializer
end
alias :include_can_close_topic? :can_perform_action_available_to_group_moderators?
alias :include_can_archive_topic? :can_perform_action_available_to_group_moderators?
alias :include_can_split_merge_topic? :can_perform_action_available_to_group_moderators?
alias :include_can_edit_staff_notes? :can_perform_action_available_to_group_moderators?
def include_can_publish_page?

View File

@ -172,7 +172,6 @@ class Guardian
def can_moderate?(obj)
obj && authenticated? && !is_silenced? && (is_staff? || (obj.is_a?(Topic) && @user.has_trust_level?(TrustLevel[4])))
end
alias :can_move_posts? :can_moderate?
alias :can_see_flags? :can_moderate?
def can_tag?(topic)

View File

@ -210,6 +210,12 @@ module TopicGuardian
end
alias :can_archive_topic? :can_perform_action_available_to_group_moderators?
alias :can_close_topic? :can_perform_action_available_to_group_moderators?
alias :can_split_merge_topic? :can_perform_action_available_to_group_moderators?
alias :can_edit_staff_notes? :can_perform_action_available_to_group_moderators?
def can_move_posts?(topic)
return false if is_silenced?
can_perform_action_available_to_group_moderators?(topic)
end
end

View File

@ -99,6 +99,8 @@ RSpec.describe TopicsController do
end
context 'success' do
fab!(:category) { Fabricate(:category) }
before { sign_in(admin) }
it "returns success" do
@ -106,7 +108,7 @@ RSpec.describe TopicsController do
post "/t/#{topic.id}/move-posts.json", params: {
title: 'Logan is a good movie',
post_ids: [p2.id],
category_id: 123,
category_id: category.id,
tags: ["tag1", "tag2"]
}
end.to change { Topic.count }.by(1)
@ -130,7 +132,7 @@ RSpec.describe TopicsController do
post "/t/#{topic.id}/move-posts.json", params: {
title: 'Logan is a good movie',
post_ids: [p2.id],
category_id: 123
category_id: category.id
}
end.to change { Topic.count }.by(1)
@ -185,6 +187,59 @@ RSpec.describe TopicsController do
end
end
describe "moving to a new topic as a group moderator" do
fab!(:group_user) { Fabricate(:group_user) }
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
fab!(:topic) { Fabricate(:topic, category: category) }
fab!(:p1) { Fabricate(:post, user: group_user.user, post_number: 1, topic: topic) }
fab!(:p2) { Fabricate(:post, user: group_user.user, post_number: 2, topic: topic) }
let(:user) { group_user.user }
before do
sign_in(user)
SiteSetting.enable_category_group_moderation = true
end
it "moves the posts" do
expect do
post "/t/#{topic.id}/move-posts.json", params: {
title: 'Logan is a good movie',
post_ids: [p2.id],
category_id: category.id
}
end.to change { Topic.count }.by(1)
expect(response.status).to eq(200)
result = response.parsed_body
expect(result['success']).to eq(true)
expect(result['url']).to eq(Topic.last.relative_url)
end
it "does not allow posts to be moved to a private category" do
staff_category = Fabricate(:category)
staff_category.set_permissions(staff: :full)
staff_category.save!
post "/t/#{topic.id}/move-posts.json", params: {
title: 'Logan is a good movie',
post_ids: [p2.id],
category_id: staff_category.id
}
expect(response).to be_forbidden
end
it "does not allow posts outside of the category to be moved" do
topic.update!(category: nil)
post "/t/#{topic.id}/move-posts.json", params: {
title: 'blah', post_ids: [p1.post_number, p2.post_number]
}
expect(response).to be_forbidden
end
end
describe 'moving to an existing topic' do
let!(:user) { sign_in(moderator) }
let(:p1) { Fabricate(:post, user: user) }
@ -246,6 +301,59 @@ RSpec.describe TopicsController do
end
end
describe "moving to an existing topic as a group moderator" do
fab!(:group_user) { Fabricate(:group_user) }
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
fab!(:topic) { Fabricate(:topic, category: category) }
fab!(:p1) { Fabricate(:post, user: group_user.user, post_number: 1, topic: topic) }
fab!(:p2) { Fabricate(:post, user: group_user.user, post_number: 2, topic: topic) }
fab!(:dest_topic) { Fabricate(:topic) }
let(:user) { group_user.user }
before do
sign_in(user)
SiteSetting.enable_category_group_moderation = true
end
it "moves the posts" do
post "/t/#{topic.id}/move-posts.json", params: {
post_ids: [p2.id],
destination_topic_id: dest_topic.id
}
expect(response.status).to eq(200)
result = response.parsed_body
expect(result['success']).to eq(true)
expect(result['url']).to be_present
end
it "does not allow posts to be moved to a private category" do
staff_category = Fabricate(:category)
staff_category.set_permissions(staff: :full)
staff_category.save!
dest_topic.update!(category: staff_category)
post "/t/#{topic.id}/move-posts.json", params: {
post_ids: [p2.id],
destination_topic_id: dest_topic.id
}
expect(response).to be_forbidden
end
it "does not allow posts outside of the category to be moved" do
topic.update!(category: nil)
post "/t/#{topic.id}/move-posts.json", params: {
post_ids: [p1.post_number, p2.post_number],
destination_topic_id: dest_topic.id
}
expect(response).to be_forbidden
end
end
describe 'moving to a new message' do
let!(:message) { Fabricate(:private_message_topic) }
let!(:p1) { Fabricate(:post, user: user, post_number: 1, topic: message) }
@ -417,6 +525,55 @@ RSpec.describe TopicsController do
end
end
describe "merging into another topic as a group moderator" do
fab!(:group_user) { Fabricate(:group_user) }
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
fab!(:topic) { Fabricate(:topic, category: category) }
fab!(:p1) { Fabricate(:post, post_number: 1, topic: topic) }
fab!(:p2) { Fabricate(:post, post_number: 2, topic: topic) }
fab!(:dest_topic) { Fabricate(:topic) }
let(:user) { group_user.user }
before do
sign_in(user)
SiteSetting.enable_category_group_moderation = true
end
it "moves the posts" do
post "/t/#{topic.id}/merge-topic.json", params: {
destination_topic_id: dest_topic.id
}
expect(response.status).to eq(200)
result = response.parsed_body
expect(result['success']).to eq(true)
expect(result['url']).to be_present
end
it "does not allow posts to be moved to a private category" do
staff_category = Fabricate(:category)
staff_category.set_permissions(staff: :full)
staff_category.save!
dest_topic.update!(category: staff_category)
post "/t/#{topic.id}/merge-topic.json", params: {
destination_topic_id: dest_topic.id
}
expect(response).to be_forbidden
end
it "does not allow posts outside of the category to be moved" do
topic.update!(category: nil)
post "/t/#{topic.id}/merge-topic.json", params: {
destination_topic_id: dest_topic.id
}
expect(response).to be_forbidden
end
end
describe 'merging into another message' do
let(:message) { Fabricate(:private_message_topic, user: user) }
let!(:p1) { Fabricate(:post, topic: message, user: trust_level_4) }
@ -708,9 +865,7 @@ RSpec.describe TopicsController do
fab!(:group_user) { Fabricate(:group_user) }
fab!(:category) { Fabricate(:category, reviewable_by_group: group_user.group) }
fab!(:topic) { Fabricate(:topic, category: category) }
let(:user) { group_user.user }
let(:group) { group_user.group }
before do
sign_in(user)
@ -743,7 +898,6 @@ RSpec.describe TopicsController do
expect(response.status).to eq(403)
expect(topic.reload.pinned_at).to eq(nil)
end
end
end

View File

@ -120,3 +120,27 @@ QUnit.test("moving posts from personal message", async assert => {
"it shows an option to move to existing message"
);
});
QUnit.test("group moderator moving posts", async assert => {
await visit("/t/topic-for-group-moderators/2480");
await click(".toggle-admin-menu");
await click(".topic-admin-multi-select .btn");
await click("#post_2 .select-below");
assert.equal(
find(".selected-posts .move-to-topic")
.text()
.trim(),
I18n.t("topic.move_to.action"),
"it should show the move to button"
);
await click(".selected-posts .move-to-topic");
assert.ok(
find(".choose-topic-modal .title")
.html()
.includes(I18n.t("topic.move_to.title")),
"it opens move to modal"
);
});

View File

@ -5077,340 +5077,342 @@ export default {
pm_with_non_human_user: false
},
"/t/2480/1.json": {
"post_stream": {
"posts": [
post_stream: {
posts: [
{
"id": 41,
"name": "",
"username": "group_moderator",
"avatar_template": "/images/avatar.png",
"created_at": "2020-07-24T17:48:55.419Z",
"cooked": "<p>Here is my new topic. I am a group category moderator!</p>",
"post_number": 1,
"post_type": 1,
"updated_at": "2020-07-24T17:48:55.419Z",
"reply_count": 0,
"reply_to_post_number": null,
"quote_count": 0,
"incoming_link_count": 0,
"reads": 2,
"readers_count": 1,
"score": 0,
"yours": true,
"topic_id": 2480,
"topic_slug": "a-topic-with-group-category-moderators",
"display_username": "",
"primary_group_name": "group_moderators",
"primary_group_flair_url": "cheese",
"primary_group_flair_bg_color": "ff0",
"primary_group_flair_color": "",
"version": 1,
"can_edit": true,
"can_delete": false,
"can_recover": false,
"can_wiki": false,
"read": true,
"user_title": "a title",
"title_is_group": false,
"bookmarked": false,
"actions_summary": [
id: 41,
name: "",
username: "group_moderator",
avatar_template: "/images/avatar.png",
created_at: "2020-07-24T17:48:55.419Z",
cooked: "<p>Here is my new topic. I am a group category moderator!</p>",
post_number: 1,
post_type: 1,
updated_at: "2020-07-24T17:48:55.419Z",
reply_count: 0,
reply_to_post_number: null,
quote_count: 0,
incoming_link_count: 0,
reads: 2,
readers_count: 1,
score: 0,
yours: true,
topic_id: 2480,
topic_slug: "a-topic-with-group-category-moderators",
display_username: "",
primary_group_name: "group_moderators",
primary_group_flair_url: "cheese",
primary_group_flair_bg_color: "ff0",
primary_group_flair_color: "",
version: 1,
can_edit: true,
can_delete: false,
can_recover: false,
can_wiki: false,
read: true,
user_title: "a title",
title_is_group: false,
bookmarked: false,
actions_summary: [
{
"id": 3,
"can_act": true
id: 3,
can_act: true
},
{
"id": 4,
"can_act": true
id: 4,
can_act: true
},
{
"id": 8,
"can_act": true
id: 8,
can_act: true
},
{
"id": 7,
"can_act": true
id: 7,
can_act: true
}
],
"moderator": false,
"admin": false,
"staff": false,
"group_moderator": true,
"user_id": 3,
"hidden": false,
"trust_level": 1,
"deleted_at": null,
"user_deleted": false,
"edit_reason": null,
"can_view_edit_history": true,
"wiki": false,
"reviewable_id": 0,
"reviewable_score_count": 0,
"reviewable_score_pending_count": 0
moderator: false,
admin: false,
staff: false,
group_moderator: true,
user_id: 3,
hidden: false,
trust_level: 1,
deleted_at: null,
user_deleted: false,
edit_reason: null,
can_view_edit_history: true,
wiki: false,
reviewable_id: 0,
reviewable_score_count: 0,
reviewable_score_pending_count: 0
},
{
"id": 42,
"name": "",
"username": "normal_user",
"avatar_template": "/images/avatar.png",
"created_at": "2020-07-24T17:50:01.263Z",
"cooked": "<p>A fascinating topic worthy of discussion.</p>",
"post_number": 2,
"post_type": 1,
"updated_at": "2020-07-24T17:50:01.263Z",
"reply_count": 0,
"reply_to_post_number": null,
"quote_count": 0,
"incoming_link_count": 0,
"reads": 2,
"readers_count": 1,
"score": 0,
"yours": false,
"topic_id": 2480,
"topic_slug": "a-topic-with-group-category-moderators",
"display_username": "",
"primary_group_name": null,
"primary_group_flair_url": null,
"primary_group_flair_bg_color": null,
"primary_group_flair_color": null,
"version": 1,
"can_edit": false,
"can_delete": false,
"can_recover": false,
"can_wiki": false,
"read": true,
"user_title": null,
"bookmarked": false,
"actions_summary": [
id: 42,
name: "",
username: "normal_user",
avatar_template: "/images/avatar.png",
created_at: "2020-07-24T17:50:01.263Z",
cooked: "<p>A fascinating topic worthy of discussion.</p>",
post_number: 2,
post_type: 1,
updated_at: "2020-07-24T17:50:01.263Z",
reply_count: 0,
reply_to_post_number: null,
quote_count: 0,
incoming_link_count: 0,
reads: 2,
readers_count: 1,
score: 0,
yours: false,
topic_id: 2480,
topic_slug: "a-topic-with-group-category-moderators",
display_username: "",
primary_group_name: null,
primary_group_flair_url: null,
primary_group_flair_bg_color: null,
primary_group_flair_color: null,
version: 1,
can_edit: false,
can_delete: false,
can_recover: false,
can_wiki: false,
read: true,
user_title: null,
bookmarked: false,
actions_summary: [
{
"id": 2,
"can_act": true
id: 2,
can_act: true
},
{
"id": 3,
"can_act": true
id: 3,
can_act: true
},
{
"id": 4,
"can_act": true
id: 4,
can_act: true
},
{
"id": 8,
"can_act": true
id: 8,
can_act: true
},
{
"id": 6,
"can_act": true
id: 6,
can_act: true
},
{
"id": 7,
"can_act": true
id: 7,
can_act: true
}
],
"moderator": false,
"admin": false,
"staff": false,
"user_id": 2,
"hidden": false,
"trust_level": 1,
"deleted_at": null,
"user_deleted": false,
"edit_reason": null,
"can_view_edit_history": true,
"wiki": false,
"reviewable_id": 0,
"reviewable_score_count": 0,
"reviewable_score_pending_count": 0
moderator: false,
admin: false,
staff: false,
user_id: 2,
hidden: false,
trust_level: 1,
deleted_at: null,
user_deleted: false,
edit_reason: null,
can_view_edit_history: true,
wiki: false,
reviewable_id: 0,
reviewable_score_count: 0,
reviewable_score_pending_count: 0
},
{
"id": 43,
"name": "",
"username": "group_moderator",
"avatar_template": "/images/avatar.png",
"created_at": "2020-07-24T17:50:17.274Z",
"cooked": "<p>Thank you for your reply!</p>",
"post_number": 3,
"post_type": 1,
"updated_at": "2020-07-24T17:50:17.274Z",
"reply_count": 0,
"reply_to_post_number": null,
"quote_count": 0,
"incoming_link_count": 0,
"reads": 2,
"readers_count": 1,
"score": 0,
"yours": true,
"topic_id": 2480,
"topic_slug": "a-topic-with-group-category-moderators",
"display_username": "",
"primary_group_name": "group_moderators",
"primary_group_flair_url": "cheese",
"primary_group_flair_bg_color": "ff0",
"primary_group_flair_color": "",
"version": 1,
"can_edit": true,
"can_delete": true,
"can_recover": false,
"can_wiki": false,
"read": true,
"user_title": "a title",
"title_is_group": false,
"bookmarked": false,
"actions_summary": [
id: 43,
name: "",
username: "group_moderator",
avatar_template: "/images/avatar.png",
created_at: "2020-07-24T17:50:17.274Z",
cooked: "<p>Thank you for your reply!</p>",
post_number: 3,
post_type: 1,
updated_at: "2020-07-24T17:50:17.274Z",
reply_count: 0,
reply_to_post_number: null,
quote_count: 0,
incoming_link_count: 0,
reads: 2,
readers_count: 1,
score: 0,
yours: true,
topic_id: 2480,
topic_slug: "a-topic-with-group-category-moderators",
display_username: "",
primary_group_name: "group_moderators",
primary_group_flair_url: "cheese",
primary_group_flair_bg_color: "ff0",
primary_group_flair_color: "",
version: 1,
can_edit: true,
can_delete: true,
can_recover: false,
can_wiki: false,
read: true,
user_title: "a title",
title_is_group: false,
bookmarked: false,
actions_summary: [
{
"id": 3,
"can_act": true
id: 3,
can_act: true
},
{
"id": 4,
"can_act": true
id: 4,
can_act: true
},
{
"id": 8,
"can_act": true
id: 8,
can_act: true
},
{
"id": 7,
"can_act": true
id: 7,
can_act: true
}
],
"moderator": false,
"admin": false,
"staff": false,
"group_moderator": true,
"user_id": 3,
"hidden": false,
"trust_level": 1,
"deleted_at": null,
"user_deleted": false,
"edit_reason": null,
"can_view_edit_history": true,
"wiki": false,
"reviewable_id": 0,
"reviewable_score_count": 0,
"reviewable_score_pending_count": 0
moderator: false,
admin: false,
staff: false,
group_moderator: true,
user_id: 3,
hidden: false,
trust_level: 1,
deleted_at: null,
user_deleted: false,
edit_reason: null,
can_view_edit_history: true,
wiki: false,
reviewable_id: 0,
reviewable_score_count: 0,
reviewable_score_pending_count: 0
}
],
"stream": [
stream: [
41,
42,
43
]
},
"timeline_lookup": [
timeline_lookup: [
[
1,
0
]
],
"id": 2480,
"title": "A Topic with Group Category Moderators",
"fancy_title": "A Topic with Group Category Moderators",
"posts_count": 3,
"created_at": "2020-07-24T17:48:54.986Z",
"views": 2,
"reply_count": 0,
"like_count": 0,
"last_posted_at": "2020-07-24T17:50:17.274Z",
"visible": true,
"closed": false,
"archived": false,
"has_summary": false,
"archetype": "regular",
"slug": "a-topic-with-group-category-moderators",
"category_id": 5,
"word_count": 22,
"deleted_at": null,
"user_id": 3,
"featured_link": null,
"pinned_globally": false,
"pinned_at": null,
"pinned_until": null,
"image_url": null,
"draft": null,
"draft_key": "topic_2480",
"draft_sequence": 1,
"posted": true,
"unpinned": null,
"pinned": false,
"current_post_number": 3,
"highest_post_number": 3,
"last_read_post_number": 3,
"last_read_post_id": 43,
"deleted_by": null,
"actions_summary": [
id: 2480,
title: "A Topic with Group Category Moderators",
fancy_title: "A Topic with Group Category Moderators",
posts_count: 3,
created_at: "2020-07-24T17:48:54.986Z",
views: 2,
reply_count: 0,
like_count: 0,
last_posted_at: "2020-07-24T17:50:17.274Z",
visible: true,
closed: false,
archived: false,
has_summary: false,
archetype: "regular",
slug: "a-topic-with-group-category-moderators",
category_id: 5,
word_count: 22,
deleted_at: null,
user_id: 3,
featured_link: null,
pinned_globally: false,
pinned_at: null,
pinned_until: null,
image_url: null,
draft: null,
draft_key: "topic_2480",
draft_sequence: 1,
posted: true,
unpinned: null,
pinned: false,
current_post_number: 3,
highest_post_number: 3,
last_read_post_number: 3,
last_read_post_id: 43,
deleted_by: null,
actions_summary: [
{
"id": 4,
"count": 0,
"hidden": false,
"can_act": true
id: 4,
count: 0,
hidden: false,
can_act: true
},
{
"id": 8,
"count": 0,
"hidden": false,
"can_act": true
id: 8,
count: 0,
hidden: false,
can_act: true
},
{
"id": 7,
"count": 0,
"hidden": false,
"can_act": true
id: 7,
count: 0,
hidden: false,
can_act: true
}
],
"chunk_size": 20,
"bookmarked": false,
"topic_timer": null,
"private_topic_timer": null,
"message_bus_last_id": 4,
"participant_count": 2,
"show_read_indicator": false,
"thumbnails": null,
"details": {
"notification_level": 3,
"notifications_reason_id": 1,
"can_edit": true,
"can_create_post": true,
"can_reply_as_new_topic": true,
"can_flag_topic": true,
"can_review_topic": true,
"can_close_topic": true,
"can_archive_topic": true,
"can_edit_staff_notes": true,
"participants": [
chunk_size: 20,
bookmarked: false,
topic_timer: null,
private_topic_timer: null,
message_bus_last_id: 4,
participant_count: 2,
show_read_indicator: false,
thumbnails: null,
details: {
notification_level: 3,
notifications_reason_id: 1,
can_edit: true,
can_create_post: true,
can_move_posts: true,
can_reply_as_new_topic: true,
can_flag_topic: true,
can_review_topic: true,
can_close_topic: true,
can_archive_topic: true,
can_split_merge_topic: true,
can_edit_staff_notes: true,
participants: [
{
"id": 3,
"username": "group_moderator",
"name": "",
"avatar_template": "/images/avatar.png",
"post_count": 2,
"primary_group_name": "group_moderators",
"primary_group_flair_url": "cheese",
"primary_group_flair_color": "",
"primary_group_flair_bg_color": "ff0"
id: 3,
username: "group_moderator",
name: "",
avatar_template: "/images/avatar.png",
post_count: 2,
primary_group_name: "group_moderators",
primary_group_flair_url: "cheese",
primary_group_flair_color: "",
primary_group_flair_bg_color: "ff0"
},
{
"id": 2,
"username": "normal_user",
"name": "",
"avatar_template": "/images/avatar.png",
"post_count": 1,
"primary_group_name": null,
"primary_group_flair_url": null,
"primary_group_flair_color": null,
"primary_group_flair_bg_color": null
id: 2,
username: "normal_user",
name: "",
avatar_template: "/images/avatar.png",
post_count: 1,
primary_group_name: null,
primary_group_flair_url: null,
primary_group_flair_color: null,
primary_group_flair_bg_color: null
}
],
"created_by": {
"id": 3,
"username": "group_moderator",
"name": "",
"avatar_template": "/images/avatar.png"
created_by: {
id: 3,
username: "group_moderator",
name: "",
avatar_template: "/images/avatar.png"
},
"last_poster": {
"id": 3,
"username": "group_moderator",
"name": "",
"avatar_template": "/images/avatar.png"
last_poster: {
id: 3,
username: "group_moderator",
name: "",
avatar_template: "/images/avatar.png"
}
}
}