Skip to content

Commit 585a552

Browse files
authored
Add "Discussion" to "Post Settings" (#24948)
* Add discussion settings * Add dotcom encoder * Add initial Discussion Settings implementation * Update the Dotcom encoder * Update PostDiscussionSettingsView design * Add support for self-hosted sites * Update release notes * Show discussion settings before format * Cleanup * Move RemotePostDiscussionState to WordPressKit * Fix remotePostFromXMLRPCDictionary missing comments and pings status
1 parent e317c87 commit 585a552

File tree

8 files changed

+202
-0
lines changed

8 files changed

+202
-0
lines changed

Modules/Sources/WordPressKit/RemotePostParameters.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public struct RemotePostCreateParameters: Equatable {
2323
public var tags: [String] = []
2424
public var categoryIDs: [Int] = []
2525
public var metadata: Set<RemotePostMetadataItem> = []
26+
public var discussion: RemotePostDiscussionSettings = .default
2627

2728
public init(type: String, status: String) {
2829
self.type = type
@@ -53,10 +54,20 @@ public struct RemotePostUpdateParameters: Equatable {
5354
public var tags: [String]?
5455
public var categoryIDs: [Int]?
5556
public var metadata: Set<RemotePostMetadataItem>?
57+
public var discussion: RemotePostDiscussionSettings?
5658

5759
public init() {}
5860
}
5961

62+
public enum RemotePostDiscussionState: String, CaseIterable {
63+
case open = "open"
64+
case closed = "closed"
65+
66+
init(isOpen: Bool) {
67+
self = isOpen ? .open : .closed
68+
}
69+
}
70+
6071
public struct RemotePostMetadataItem: Hashable {
6172
public var id: String?
6273
public var key: String?
@@ -69,6 +80,18 @@ public struct RemotePostMetadataItem: Hashable {
6980
}
7081
}
7182

83+
public struct RemotePostDiscussionSettings: Hashable {
84+
public var allowComments: Bool
85+
public var allowPings: Bool
86+
87+
public static let `default` = RemotePostDiscussionSettings(allowComments: true, allowPings: true)
88+
89+
public init(allowComments: Bool, allowPings: Bool) {
90+
self.allowComments = allowComments
91+
self.allowPings = allowPings
92+
}
93+
}
94+
7295
// MARK: - Diff
7396

7497
extension RemotePostCreateParameters {
@@ -118,6 +141,9 @@ extension RemotePostCreateParameters {
118141
if Set(previous.categoryIDs) != Set(categoryIDs) {
119142
changes.categoryIDs = categoryIDs
120143
}
144+
if previous.discussion != discussion {
145+
changes.discussion = discussion
146+
}
121147
if previous.metadata != metadata {
122148
changes.metadata = metadata
123149
}
@@ -168,6 +194,9 @@ extension RemotePostCreateParameters {
168194
if let categoryIDs = changes.categoryIDs {
169195
self.categoryIDs = categoryIDs
170196
}
197+
if let discussion = changes.discussion {
198+
self.discussion = discussion
199+
}
171200
if let metadata = changes.metadata {
172201
self.metadata = metadata
173202
}
@@ -193,6 +222,7 @@ private enum RemotePostWordPressComCodingKeys: String, CodingKey {
193222
case format
194223
case isSticky = "sticky"
195224
case categoryIDs = "categories_by_id"
225+
case discussion
196226
case metadata
197227

198228
static let postTags = "post_tag"
@@ -232,6 +262,9 @@ struct RemotePostCreateParametersWordPressComEncoder: Encodable {
232262
if parameters.isSticky {
233263
try container.encode(parameters.isSticky, forKey: .isSticky)
234264
}
265+
if parameters.discussion != .default {
266+
try container.encode(RemotePostDiscussionSettingsWordPressComEncoder(discussion: parameters.discussion), forKey: .discussion)
267+
}
235268
}
236269
}
237270

@@ -259,6 +292,21 @@ struct RemotePostUpdateParametersWordPressComMetadata: Encodable {
259292
}
260293
}
261294

295+
struct RemotePostDiscussionSettingsWordPressComEncoder: Encodable {
296+
var discussion: RemotePostDiscussionSettings
297+
298+
private enum CodingKeys: String, CodingKey {
299+
case commenets = "comments_open"
300+
case pings = "pings_open"
301+
}
302+
303+
func encode(to encoder: any Encoder) throws {
304+
var container = encoder.container(keyedBy: CodingKeys.self)
305+
try container.encodeIfPresent(discussion.allowComments, forKey: .commenets)
306+
try container.encodeIfPresent(discussion.allowPings, forKey: .pings)
307+
}
308+
}
309+
262310
struct RemotePostUpdateParametersWordPressComEncoder: Encodable {
263311
let parameters: RemotePostUpdateParameters
264312

@@ -294,6 +342,9 @@ struct RemotePostUpdateParametersWordPressComEncoder: Encodable {
294342
}
295343
try container.encodeIfPresent(parameters.categoryIDs, forKey: .categoryIDs)
296344
try container.encodeIfPresent(parameters.isSticky, forKey: .isSticky)
345+
if let discussion = parameters.discussion {
346+
try container.encode(RemotePostDiscussionSettingsWordPressComEncoder(discussion: discussion), forKey: .discussion)
347+
}
297348
}
298349
}
299350

@@ -317,6 +368,8 @@ private enum RemotePostXMLRPCCodingKeys: String, CodingKey {
317368
case format = "post_format"
318369
case isSticky = "sticky"
319370
case metadata = "custom_fields"
371+
case commentStatus = "comment_status"
372+
case pingStatus = "ping_status"
320373

321374
static let taxonomyTag = "post_tag"
322375
static let taxonomyCategory = "category"
@@ -356,6 +409,10 @@ struct RemotePostCreateParametersXMLRPCEncoder: Encodable {
356409
if parameters.isSticky {
357410
try container.encode(parameters.isSticky, forKey: .isSticky)
358411
}
412+
if parameters.discussion != .default {
413+
try container.encode(RemotePostDiscussionState(isOpen: parameters.discussion.allowComments).rawValue, forKey: .commentStatus)
414+
try container.encode(RemotePostDiscussionState(isOpen: parameters.discussion.allowPings).rawValue, forKey: .pingStatus)
415+
}
359416
}
360417
}
361418

@@ -395,6 +452,10 @@ struct RemotePostUpdateParametersXMLRPCEncoder: Encodable {
395452
try container.encode([RemotePostXMLRPCCodingKeys.taxonomyCategory: categoryIDs], forKey: .terms)
396453
}
397454
try container.encodeIfPresent(parameters.isSticky, forKey: .isSticky)
455+
if let discussion = parameters.discussion {
456+
try container.encode(RemotePostDiscussionState(isOpen: discussion.allowComments).rawValue, forKey: .commentStatus)
457+
try container.encode(RemotePostDiscussionState(isOpen: discussion.allowPings).rawValue, forKey: .pingStatus)
458+
}
398459
}
399460
}
400461

Modules/Sources/WordPressKitObjC/PostServiceRemoteXMLRPC.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,9 @@ + (RemotePost *)remotePostFromXMLRPCDictionary:(NSDictionary *)xmlrpcDictionary
334334
post.tags = [self tagsFromXMLRPCTermsArray:terms];
335335
post.categories = [self remoteCategoriesFromXMLRPCTermsArray:terms];
336336

337+
post.commentsStatus = [xmlrpcDictionary stringForKey:@"comment_status"];
338+
post.pingsStatus = [xmlrpcDictionary stringForKey:@"ping_status"];
339+
337340
post.isStickyPost = [xmlrpcDictionary numberForKeyPath:@"sticky"];
338341

339342
// Pick an image to use for display

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* [*] Add "Publish Date" back to "Post Settings" for draft and pending posts [#24939]
55
* [*] Use "Menlo" font for the password field to make it easier to distinguish between characters like `0` and `0` [#24939]
66
* [*] Add "Access" section to "Post Settings" [#24942]
7+
* [*] Add "Discussion" to "Post Settings" [#24948]
78
* [*] Add "File Size" to Site Media Details [#24947]
89
* [*] Add "Email to Subscribers" row to "Publishing" sheet [#24946]
910

Sources/WordPressData/Swift/Post+CoreDataProperties.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import CoreData
3+
import WordPressKit
34

45
public extension Post {
56

@@ -27,3 +28,15 @@ public extension Post {
2728
@NSManaged func addCategories(_ values: Set<PostCategory>)
2829
@NSManaged func removeCategories(_ values: Set<PostCategory>)
2930
}
31+
32+
extension Post {
33+
public var allowComments: Bool {
34+
get { commentsStatus != RemotePostDiscussionState.closed.rawValue }
35+
set { commentsStatus = (newValue ? RemotePostDiscussionState.open : .closed).rawValue }
36+
}
37+
38+
public var allowPings: Bool {
39+
get { pingsStatus != RemotePostDiscussionState.closed.rawValue }
40+
set { pingsStatus = (newValue ? RemotePostDiscussionState.open : .closed).rawValue }
41+
}
42+
}

Sources/WordPressData/Swift/RemotePostCreateParameters+Helpers.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ extension RemotePostCreateParameters {
3333
metadata = Set(Self.generateRemoteMetadata(for: post).compactMap { dictionary -> RemotePostMetadataItem? in
3434
return PostHelper.mapDictionaryToMetadataItems(dictionary)
3535
})
36+
discussion = RemotePostDiscussionSettings(
37+
allowComments: post.allowComments,
38+
allowPings: post.allowPings
39+
)
3640
default:
3741
break
3842
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import SwiftUI
2+
3+
struct PostDiscussionSettingsView: View {
4+
@Binding var postSettings: PostSettings
5+
6+
var body: some View {
7+
Form {
8+
// Comments Section
9+
Section {
10+
Toggle(Strings.allowCommentsLabel, isOn: $postSettings.allowComments)
11+
.accessibilityIdentifier("post_discussion_allow_comments_toggle")
12+
} footer: {
13+
Text(commentsFooterText)
14+
}
15+
16+
// Pingbacks Section
17+
Section {
18+
Toggle(Strings.allowPingsLabel, isOn: $postSettings.allowPings)
19+
.accessibilityIdentifier("post_discussion_allow_pings_toggle")
20+
} footer: {
21+
Link(destination: Strings.pingbacksLearnMoreURL) {
22+
(Text(Strings.learnMorePingbacksText) + Text(" ") + Text(Image(systemName: "link")))
23+
.font(.footnote)
24+
}
25+
.accessibilityIdentifier("post_discussion_pingbacks_learn_more_button")
26+
}
27+
}
28+
.navigationTitle(Strings.discussionTitle)
29+
.navigationBarTitleDisplayMode(.inline)
30+
}
31+
32+
private var commentsFooterText: String {
33+
if postSettings.allowComments {
34+
return Strings.commentsEnabledFooter
35+
} else {
36+
return Strings.commentsDisabledFooter
37+
}
38+
}
39+
}
40+
41+
private enum Strings {
42+
static let discussionTitle = NSLocalizedString(
43+
"postDiscussion.title",
44+
value: "Discussion",
45+
comment: "Navigation title for post discussion settings"
46+
)
47+
48+
static let allowCommentsLabel = NSLocalizedString(
49+
"postDiscussion.allowComments.label",
50+
value: "Allow Comments",
51+
comment: "Toggle label for allowing comments on post"
52+
)
53+
54+
static let allowPingsLabel = NSLocalizedString(
55+
"postDiscussion.allowPings.label",
56+
value: "Allow Pingbacks",
57+
comment: "Toggle label for allowing pings/trackbacks on post"
58+
)
59+
60+
static let commentsEnabledFooter = NSLocalizedString(
61+
"postDiscussion.comments.enabled.footer",
62+
value: "Visitors can add new comments and replies.",
63+
comment: "Footer text when comments are enabled"
64+
)
65+
66+
static let commentsDisabledFooter = NSLocalizedString(
67+
"postDiscussion.comments.disabled.footer",
68+
value: "Visitors cannot add new comments or replies. Existing comments remain visible.",
69+
comment: "Footer text when comments are disabled"
70+
)
71+
72+
static let pingbacksLearnMoreURL = URL(string: "https://wordpress.org/documentation/article/trackbacks-and-pingbacks/")!
73+
74+
static let learnMorePingbacksText = NSLocalizedString(
75+
"postDiscussion.pingbacks.learnMore.text",
76+
value: "Learn more about pingbacks & trackbacks",
77+
comment: "Link text for learning more about pingbacks and trackbacks"
78+
)
79+
}

WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ struct PostSettings: Hashable {
2727
var postFormat: String?
2828
var isStickyPost = false
2929
var sharing: PostSocialSharingSettings?
30+
var accessLevel: JetpackPostAccessLevel?
31+
var allowComments = true
32+
var allowPings = true
3033

3134
// MARK: - Page-specific
3235
var parentPageID: Int?
@@ -61,6 +64,10 @@ struct PostSettings: Hashable {
6164
categoryIDs = Set((post.categories ?? []).compactMap {
6265
$0.categoryID?.intValue
6366
})
67+
sharing = PostSocialSharingSettings.make(for: post)
68+
accessLevel = metadata.accessLevel ?? .everybody
69+
allowComments = post.allowComments
70+
allowPings = post.allowPings
6471
case let page as Page:
6572
parentPageID = page.parentID?.intValue
6673
default:
@@ -144,6 +151,14 @@ struct PostSettings: Hashable {
144151
post.isStickyPost = isStickyPost
145152
}
146153

154+
// Update discussion settings
155+
if post.allowComments != allowComments {
156+
post.allowComments = allowComments
157+
}
158+
if post.allowPings != allowPings {
159+
post.allowPings = allowPings
160+
}
161+
147162
if let sharing {
148163
for connection in sharing.services.flatMap(\.connections) {
149164
let keyringID = NSNumber(value: connection.keyringID)

WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsView.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ struct PostSettingsFormContentView: View {
371371
pendingReviewRow
372372
}
373373
if viewModel.isPost {
374+
discussionRow
374375
postFormatRow
375376
}
376377
if !viewModel.isPost {
@@ -392,6 +393,14 @@ struct PostSettingsFormContentView: View {
392393
}
393394
}
394395

396+
private var discussionRow: some View {
397+
NavigationLink {
398+
PostDiscussionSettingsView(postSettings: $viewModel.settings)
399+
} label: {
400+
SettingsRow(Strings.discussionLabel, value: viewModel.settings.allowComments ? Strings.discussionOpen : Strings.discussionClosed)
401+
}
402+
}
403+
395404
private var parentPageRow: some View {
396405
NavigationLink {
397406
if let page = viewModel.post as? Page {
@@ -587,6 +596,23 @@ private enum Strings {
587596
value: "Post Format",
588597
comment: "Label for the post format field. Should be the same as WP core."
589598
)
599+
static let discussionLabel = NSLocalizedString(
600+
"postSettings.discussion.label",
601+
value: "Discussion",
602+
comment: "Label for the discussion settings field in Post Settings"
603+
)
604+
605+
static let discussionOpen = NSLocalizedString(
606+
"postSettings.discussion.open",
607+
value: "Open",
608+
comment: "Status text when discussion (comments) is enabled"
609+
)
610+
611+
static let discussionClosed = NSLocalizedString(
612+
"postSettings.discussion.closed",
613+
value: "Closed",
614+
comment: "Status text when discussion (comments) is disabled"
615+
)
590616

591617
static let parentPageLabel = NSLocalizedString(
592618
"postSettings.parentPage.label",

0 commit comments

Comments
 (0)