diff --git a/Sources/WordPressKit/Models/RemoteBlogOptionsHelper.swift b/Sources/WordPressKit/Models/RemoteBlogOptionsHelper.swift index 184960f36..d8fac632e 100644 --- a/Sources/WordPressKit/Models/RemoteBlogOptionsHelper.swift +++ b/Sources/WordPressKit/Models/RemoteBlogOptionsHelper.swift @@ -67,7 +67,7 @@ import Foundation } public class func remoteBlogSettings(fromXMLRPCDictionaryOptions options: NSDictionary) -> RemoteBlogSettings { - let remoteSettings = RemoteBlogSettings() + let remoteSettings = RemoteBlogSettings(jsonDictionary: [:]) remoteSettings.name = options.string(forKeyPath: "blog_title.value")?.stringByDecodingXMLCharacters() remoteSettings.tagline = options.string(forKeyPath: "blog_tagline.value")?.stringByDecodingXMLCharacters() if options["blog_public"] != nil { diff --git a/Sources/WordPressKit/Models/RemoteBlogSettings.swift b/Sources/WordPressKit/Models/RemoteBlogSettings.swift index bd395c7b1..8685f9d7d 100644 --- a/Sources/WordPressKit/Models/RemoteBlogSettings.swift +++ b/Sources/WordPressKit/Models/RemoteBlogSettings.swift @@ -186,6 +186,182 @@ public class RemoteBlogSettings: NSObject { /// @objc public var sharingDisabledReblogs: NSNumber? + // Defined as CodingKey-conforming already to simplify Codable support in the future. + enum CodingKeys: String, CodingKey { + case name = "name" + case tagline = "description" + case privacy = "blog_public" + case languageID = "lang_id" + case iconMediaID = "site_icon" + case gmtOffset = "gmt_offset" + case timezoneString = "timezone_string" + case settings = "settings" + case defaultCategory = "default_category" + case defaultPostFormat = "default_post_format" + case dateFormat = "date_format" + case timeFormat = "time_format" + case startOfWeek = "start_of_week" + case postsPerPage = "posts_per_page" + case commentsAllowed = "default_comment_status" + case commentsBlocklistKeys = "blacklist_keys" + case commentsCloseAutomatically = "close_comments_for_old_posts" + case commentsCloseAutomaticallyAfterDays = "close_comments_days_old" + case commentsKnownUsersAllowlist = "comment_whitelist" + case commentsMaxLinks = "comment_max_links" + case commentsModerationKeys = "moderation_keys" + case commentsPagingEnabled = "page_comments" + case commentsPageSize = "comments_per_page" + case commentsRequireModeration = "comment_moderation" + case commentsRequireNameAndEmail = "require_name_email" + case commentsRequireRegistration = "comment_registration" + case commentsSortOrder = "comment_order" + case commentsThreadingEnabled = "thread_comments" + case commentsThreadingDepth = "thread_comments_depth" + case pingbackOutbound = "default_pingback_flag" + case pingbackInbound = "default_ping_status" + case relatedPostsAllowed = "jetpack_relatedposts_allowed" + case relatedPostsEnabled = "jetpack_relatedposts_enabled" + case relatedPostsShowHeadline = "jetpack_relatedposts_show_headline" + case relatedPostsShowThumbnails = "jetpack_relatedposts_show_thumbnails" + case ampSupported = "amp_is_supported" + case ampEnabled = "amp_is_enabled" + + case sharingButtonStyle = "sharing_button_style" + case sharingLabel = "sharing_label" + case sharingTwitterName = "twitter_via" + case sharingCommentLikesEnabled = "jetpack_comment_likes_enabled" + case sharingDisabledLikes = "disabled_likes" + case sharingDisabledReblogs = "disabled_reblogs" + } + + /// Parses details from a JSON dictionary, as returned by the WordPress.com REST API. + @objc + public init(jsonDictionary json: [String: Any]) { + let rawSettings = json[CodingKeys.settings.rawValue] as? [String: Any] ?? [:] + + name = json[CodingKeys.name.rawValue] as? String + tagline = json[CodingKeys.tagline.rawValue] as? String + privacy = rawSettings[CodingKeys.privacy.rawValue] as? NSNumber + // The value here can be a String, so we need a custom conversion + languageID = rawSettings.number(forKey: CodingKeys.languageID.rawValue) + iconMediaID = rawSettings[CodingKeys.iconMediaID.rawValue] as? NSNumber + // The value here can be a String, so we need a custom conversion + gmtOffset = rawSettings.number(forKey: CodingKeys.gmtOffset.rawValue) + timezoneString = rawSettings[CodingKeys.timezoneString.rawValue] as? String + + defaultCategoryID = rawSettings[CodingKeys.defaultCategory.rawValue] as? NSNumber ?? 1 + + // Note: the backend might send '0' as a number, OR a string value. + // See https://github.com/wordpress-mobile/WordPress-iOS/issues/4187 + let defaultPostFormatValue = rawSettings[CodingKeys.defaultPostFormat.rawValue] + if let defaultPostFormatNumber = defaultPostFormatValue as? NSNumber, defaultPostFormatNumber == 0 || + defaultPostFormatValue as? String == "0" { + defaultPostFormat = "standard" + } else { + defaultPostFormat = defaultPostFormatValue as? String + } + + dateFormat = rawSettings[CodingKeys.dateFormat.rawValue] as? String + timeFormat = rawSettings[CodingKeys.timeFormat.rawValue] as? String + startOfWeek = rawSettings[CodingKeys.startOfWeek.rawValue] as? String + postsPerPage = rawSettings[CodingKeys.postsPerPage.rawValue] as? NSNumber + + commentsAllowed = rawSettings[CodingKeys.commentsAllowed.rawValue] as? NSNumber + commentsBlocklistKeys = rawSettings[CodingKeys.commentsBlocklistKeys.rawValue] as? String + commentsCloseAutomatically = rawSettings[CodingKeys.commentsCloseAutomatically.rawValue] as? NSNumber + commentsCloseAutomaticallyAfterDays = rawSettings[CodingKeys.commentsCloseAutomaticallyAfterDays.rawValue] as? NSNumber + commentsFromKnownUsersAllowlisted = rawSettings[CodingKeys.commentsKnownUsersAllowlist.rawValue] as? NSNumber + commentsMaximumLinks = rawSettings[CodingKeys.commentsMaxLinks.rawValue] as? NSNumber + commentsModerationKeys = rawSettings[CodingKeys.commentsModerationKeys.rawValue] as? String + commentsPagingEnabled = rawSettings[CodingKeys.commentsPagingEnabled.rawValue] as? NSNumber + commentsPageSize = rawSettings[CodingKeys.commentsPageSize.rawValue] as? NSNumber + commentsRequireManualModeration = rawSettings[CodingKeys.commentsRequireModeration.rawValue] as? NSNumber + commentsRequireNameAndEmail = rawSettings[CodingKeys.commentsRequireNameAndEmail.rawValue] as? NSNumber + commentsRequireRegistration = rawSettings[CodingKeys.commentsRequireRegistration.rawValue] as? NSNumber + commentsSortOrder = rawSettings[CodingKeys.commentsSortOrder.rawValue] as? String + commentsThreadingEnabled = rawSettings[CodingKeys.commentsThreadingEnabled.rawValue] as? NSNumber + commentsThreadingDepth = rawSettings[CodingKeys.commentsThreadingDepth.rawValue] as? NSNumber + pingbackOutboundEnabled = rawSettings[CodingKeys.pingbackOutbound.rawValue] as? NSNumber + pingbackInboundEnabled = rawSettings[CodingKeys.pingbackInbound.rawValue] as? NSNumber + + relatedPostsAllowed = rawSettings[CodingKeys.relatedPostsAllowed.rawValue] as? NSNumber + relatedPostsEnabled = rawSettings[CodingKeys.relatedPostsEnabled.rawValue] as? NSNumber + relatedPostsShowHeadline = rawSettings[CodingKeys.relatedPostsShowHeadline.rawValue] as? NSNumber + relatedPostsShowThumbnails = rawSettings[CodingKeys.relatedPostsShowThumbnails.rawValue] as? NSNumber + + ampSupported = rawSettings[CodingKeys.ampSupported.rawValue] as? NSNumber + ampEnabled = rawSettings[CodingKeys.ampEnabled.rawValue] as? NSNumber + + sharingButtonStyle = rawSettings[CodingKeys.sharingButtonStyle.rawValue] as? String + sharingLabel = rawSettings[CodingKeys.sharingLabel.rawValue] as? String + sharingTwitterName = rawSettings[CodingKeys.sharingTwitterName.rawValue] as? String + sharingCommentLikesEnabled = rawSettings[CodingKeys.sharingCommentLikesEnabled.rawValue] as? NSNumber + sharingDisabledLikes = rawSettings[CodingKeys.sharingDisabledLikes.rawValue] as? NSNumber + sharingDisabledReblogs = rawSettings[CodingKeys.sharingDisabledReblogs.rawValue] as? NSNumber + } + + @objc + public var dictionaryRepresentation: [String: Any] { + var parameters: [String: Any] = [:] + + // name and tagline/description use different keys... + parameters["blogname"] = name + parameters["blogdescription"] = tagline + + parameters[CodingKeys.privacy.rawValue] = privacy + parameters[CodingKeys.languageID.rawValue] = languageID + parameters[CodingKeys.iconMediaID.rawValue] = iconMediaID + parameters[CodingKeys.gmtOffset.rawValue] = gmtOffset + parameters[CodingKeys.timezoneString.rawValue] = timezoneString + + parameters[CodingKeys.defaultCategory.rawValue] = defaultCategoryID + parameters[CodingKeys.defaultPostFormat.rawValue] = defaultPostFormat + parameters[CodingKeys.dateFormat.rawValue] = dateFormat + parameters[CodingKeys.timeFormat.rawValue] = timeFormat + parameters[CodingKeys.startOfWeek.rawValue] = startOfWeek + parameters[CodingKeys.postsPerPage.rawValue] = postsPerPage + + parameters[CodingKeys.commentsAllowed.rawValue] = commentsAllowed + parameters[CodingKeys.commentsBlocklistKeys.rawValue] = commentsBlocklistKeys + parameters[CodingKeys.commentsCloseAutomatically.rawValue] = commentsCloseAutomatically + parameters[CodingKeys.commentsCloseAutomaticallyAfterDays.rawValue] = commentsCloseAutomaticallyAfterDays + parameters[CodingKeys.commentsKnownUsersAllowlist.rawValue] = commentsFromKnownUsersAllowlisted + parameters[CodingKeys.commentsMaxLinks.rawValue] = commentsMaximumLinks + parameters[CodingKeys.commentsModerationKeys.rawValue] = commentsModerationKeys + parameters[CodingKeys.commentsPagingEnabled.rawValue] = commentsPagingEnabled + parameters[CodingKeys.commentsPageSize.rawValue] = commentsPageSize + parameters[CodingKeys.commentsRequireModeration.rawValue] = commentsRequireManualModeration + parameters[CodingKeys.commentsRequireNameAndEmail.rawValue] = commentsRequireNameAndEmail + parameters[CodingKeys.commentsRequireRegistration.rawValue] = commentsRequireRegistration + parameters[CodingKeys.commentsSortOrder.rawValue] = commentsSortOrder + parameters[CodingKeys.commentsThreadingEnabled.rawValue] = commentsThreadingEnabled + parameters[CodingKeys.commentsThreadingDepth.rawValue] = commentsThreadingDepth + + parameters[CodingKeys.pingbackOutbound.rawValue] = pingbackOutboundEnabled + parameters[CodingKeys.pingbackInbound.rawValue] = pingbackInboundEnabled + + // Note: releatedPostsAllowed was not set in the Objective-C implementation. + // There was no comment about it, so I assumed it was simply something that was never noticed. + parameters[CodingKeys.relatedPostsAllowed.rawValue] = relatedPostsAllowed + parameters[CodingKeys.relatedPostsEnabled.rawValue] = relatedPostsEnabled + parameters[CodingKeys.relatedPostsShowHeadline.rawValue] = relatedPostsShowHeadline + parameters[CodingKeys.relatedPostsShowThumbnails.rawValue] = relatedPostsShowThumbnails + + // Note: ampSupported was not set in the Objective-C implementation. + // There was no comment about it, so I assumed it was simply something that was never noticed. + parameters[CodingKeys.ampSupported.rawValue] = ampSupported + parameters[CodingKeys.ampEnabled.rawValue] = ampEnabled + + parameters[CodingKeys.sharingButtonStyle.rawValue] = sharingButtonStyle + parameters[CodingKeys.sharingLabel.rawValue] = sharingLabel + parameters[CodingKeys.sharingTwitterName.rawValue] = sharingTwitterName + parameters[CodingKeys.sharingCommentLikesEnabled.rawValue] = sharingCommentLikesEnabled + parameters[CodingKeys.sharingDisabledLikes.rawValue] = sharingDisabledLikes + parameters[CodingKeys.sharingDisabledReblogs.rawValue] = sharingDisabledReblogs + + return parameters + } + // MARK: - Helpers /// Computed property, meant to help conversion from Remote / String-Based values, into their Integer counterparts @@ -204,3 +380,20 @@ public class RemoteBlogSettings: NSObject { private static let AscendingStringValue = "asc" private static let DescendingStringValue = "desc" } + +extension Dictionary where Key == String { + + func number(forKey key: String) -> NSNumber? { + guard let value = self[key] else { return .none } + + if let value = value as? NSNumber { + return value + } else if let value = value as? String { + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .none + return numberFormatter.number(from: value) + } else { + return .none + } + } +} diff --git a/Sources/WordPressKit/Services/BlogServiceRemoteREST.m b/Sources/WordPressKit/Services/BlogServiceRemoteREST.m index b8b3f140e..b27106920 100644 --- a/Sources/WordPressKit/Services/BlogServiceRemoteREST.m +++ b/Sources/WordPressKit/Services/BlogServiceRemoteREST.m @@ -7,68 +7,11 @@ @import WordPressShared; #pragma mark - Parsing Keys -static NSString * const RemoteBlogNameKey = @"name"; -static NSString * const RemoteBlogTaglineKey = @"description"; -static NSString * const RemoteBlogPrivacyKey = @"blog_public"; -static NSString * const RemoteBlogLanguageKey = @"lang_id"; -static NSString * const RemoteBlogIconKey = @"site_icon"; -static NSString * const RemoteBlogGMTOffsetKey = @"gmt_offset"; -static NSString * const RemoteBlogTimezoneStringKey = @"timezone_string"; - -static NSString * const RemoteBlogSettingsKey = @"settings"; -static NSString * const RemoteBlogDefaultCategoryKey = @"default_category"; -static NSString * const RemoteBlogDefaultPostFormatKey = @"default_post_format"; -static NSString * const RemoteBlogDateFormatKey = @"date_format"; -static NSString * const RemoteBlogTimeFormatKey = @"time_format"; -static NSString * const RemoteBlogStartOfWeekKey = @"start_of_week"; -static NSString * const RemoteBlogPostsPerPageKey = @"posts_per_page"; -static NSString * const RemoteBlogCommentsAllowedKey = @"default_comment_status"; -static NSString * const RemoteBlogCommentsBlocklistKeys = @"blacklist_keys"; -static NSString * const RemoteBlogCommentsCloseAutomaticallyKey = @"close_comments_for_old_posts"; -static NSString * const RemoteBlogCommentsCloseAutomaticallyAfterDaysKey = @"close_comments_days_old"; -static NSString * const RemoteBlogCommentsKnownUsersAllowlistKey = @"comment_whitelist"; -static NSString * const RemoteBlogCommentsMaxLinksKey = @"comment_max_links"; -static NSString * const RemoteBlogCommentsModerationKeys = @"moderation_keys"; -static NSString * const RemoteBlogCommentsPagingEnabledKey = @"page_comments"; -static NSString * const RemoteBlogCommentsPageSizeKey = @"comments_per_page"; -static NSString * const RemoteBlogCommentsRequireModerationKey = @"comment_moderation"; -static NSString * const RemoteBlogCommentsRequireNameAndEmailKey = @"require_name_email"; -static NSString * const RemoteBlogCommentsRequireRegistrationKey = @"comment_registration"; -static NSString * const RemoteBlogCommentsSortOrderKey = @"comment_order"; -static NSString * const RemoteBlogCommentsThreadingEnabledKey = @"thread_comments"; -static NSString * const RemoteBlogCommentsThreadingDepthKey = @"thread_comments_depth"; -static NSString * const RemoteBlogCommentsPingbackOutboundKey = @"default_pingback_flag"; -static NSString * const RemoteBlogCommentsPingbackInboundKey = @"default_ping_status"; -static NSString * const RemoteBlogRelatedPostsAllowedKey = @"jetpack_relatedposts_allowed"; -static NSString * const RemoteBlogRelatedPostsEnabledKey = @"jetpack_relatedposts_enabled"; -static NSString * const RemoteBlogRelatedPostsShowHeadlineKey = @"jetpack_relatedposts_show_headline"; -static NSString * const RemoteBlogRelatedPostsShowThumbnailsKey = @"jetpack_relatedposts_show_thumbnails"; -static NSString * const RemoteBlogAmpSupportedKey = @"amp_is_supported"; -static NSString * const RemoteBlogAmpEnabledKey = @"amp_is_enabled"; - -static NSString * const RemoteBlogSharingButtonStyle = @"sharing_button_style"; -static NSString * const RemoteBlogSharingLabel = @"sharing_label"; -static NSString * const RemoteBlogSharingTwitterName = @"twitter_via"; -static NSString * const RemoteBlogSharingCommentLikesEnabled = @"jetpack_comment_likes_enabled"; -static NSString * const RemoteBlogSharingDisabledLikes = @"disabled_likes"; -static NSString * const RemoteBlogSharingDisabledReblogs = @"disabled_reblogs"; - static NSString * const RemotePostTypesKey = @"post_types"; static NSString * const RemotePostTypeNameKey = @"name"; static NSString * const RemotePostTypeLabelKey = @"label"; static NSString * const RemotePostTypeQueryableKey = @"api_queryable"; -#pragma mark - Keys used for Update Calls -// Note: Only god knows why these don't match the "Parsing Keys" -static NSString * const RemoteBlogNameForUpdateKey = @"blogname"; -static NSString * const RemoteBlogTaglineForUpdateKey = @"blogdescription"; - -#pragma mark - Defaults -static NSString * const RemoteBlogDefaultPostFormat = @"standard"; -static NSInteger const RemoteBlogUncategorizedCategory = 1; - - - @implementation BlogServiceRemoteREST - (void)getAllAuthorsWithSuccess:(UsersHandler)success @@ -219,7 +162,7 @@ - (void)syncBlogSettingsWithSuccess:(SettingsHandler)success } return; } - RemoteBlogSettings *remoteSettings = [self remoteBlogSettingFromJSONDictionary:responseObject]; + RemoteBlogSettings *remoteSettings = [[RemoteBlogSettings alloc] initWithJsonDictionary:responseObject]; if (success) { success(remoteSettings); } @@ -236,7 +179,7 @@ - (void)updateBlogSettings:(RemoteBlogSettings *)settings { NSParameterAssert(settings); - NSDictionary *parameters = [self remoteSettingsToDictionary:settings]; + NSDictionary *parameters = [settings dictionaryRepresentation]; NSString *path = [NSString stringWithFormat:@"sites/%@/settings?context=edit", self.siteID]; NSString *requestUrl = [self pathForEndpoint:path withVersion:WordPressComRESTAPIVersion_1_1]; @@ -374,132 +317,4 @@ - (RemotePostType *)remotePostTypeWithDictionary:(NSDictionary *)json return postType; } -- (RemoteBlogSettings *)remoteBlogSettingFromJSONDictionary:(NSDictionary *)json -{ - NSAssert([json isKindOfClass:[NSDictionary class]], @"Invalid Settings Kind"); - - RemoteBlogSettings *settings = [RemoteBlogSettings new]; - NSDictionary *rawSettings = [json dictionaryForKey:RemoteBlogSettingsKey]; - - // General - settings.name = [json stringForKey:RemoteBlogNameKey]; - settings.tagline = [json stringForKey:RemoteBlogTaglineKey]; - settings.privacy = [rawSettings numberForKey:RemoteBlogPrivacyKey]; - settings.languageID = [rawSettings numberForKey:RemoteBlogLanguageKey]; - settings.iconMediaID = [rawSettings numberForKey:RemoteBlogIconKey]; - settings.gmtOffset = [rawSettings numberForKey:RemoteBlogGMTOffsetKey]; - settings.timezoneString = [rawSettings stringForKey:RemoteBlogTimezoneStringKey]; - - // Writing - settings.defaultCategoryID = [rawSettings numberForKey:RemoteBlogDefaultCategoryKey] ?: @(RemoteBlogUncategorizedCategory); - - // Note: the backend might send '0' as a number, OR a string value. Ref. Issue #4187 - if ([[rawSettings numberForKey:RemoteBlogDefaultPostFormatKey] isEqualToNumber:@(0)] || - [[rawSettings stringForKey:RemoteBlogDefaultPostFormatKey] isEqualToString:@"0"]) - { - settings.defaultPostFormat = RemoteBlogDefaultPostFormat; - } else { - settings.defaultPostFormat = [rawSettings stringForKey:RemoteBlogDefaultPostFormatKey]; - } - settings.dateFormat = [rawSettings stringForKey:RemoteBlogDateFormatKey]; - settings.timeFormat = [rawSettings stringForKey:RemoteBlogTimeFormatKey]; - settings.startOfWeek = [rawSettings stringForKey:RemoteBlogStartOfWeekKey]; - settings.postsPerPage = [rawSettings numberForKey:RemoteBlogPostsPerPageKey]; - - // Discussion - settings.commentsAllowed = [rawSettings numberForKey:RemoteBlogCommentsAllowedKey]; - settings.commentsBlocklistKeys = [rawSettings stringForKey:RemoteBlogCommentsBlocklistKeys]; - settings.commentsCloseAutomatically = [rawSettings numberForKey:RemoteBlogCommentsCloseAutomaticallyKey]; - settings.commentsCloseAutomaticallyAfterDays = [rawSettings numberForKey:RemoteBlogCommentsCloseAutomaticallyAfterDaysKey]; - settings.commentsFromKnownUsersAllowlisted = [rawSettings numberForKey:RemoteBlogCommentsKnownUsersAllowlistKey]; - settings.commentsMaximumLinks = [rawSettings numberForKey:RemoteBlogCommentsMaxLinksKey]; - settings.commentsModerationKeys = [rawSettings stringForKey:RemoteBlogCommentsModerationKeys]; - settings.commentsPagingEnabled = [rawSettings numberForKey:RemoteBlogCommentsPagingEnabledKey]; - settings.commentsPageSize = [rawSettings numberForKey:RemoteBlogCommentsPageSizeKey]; - settings.commentsRequireManualModeration = [rawSettings numberForKey:RemoteBlogCommentsRequireModerationKey]; - settings.commentsRequireNameAndEmail = [rawSettings numberForKey:RemoteBlogCommentsRequireNameAndEmailKey]; - settings.commentsRequireRegistration = [rawSettings numberForKey:RemoteBlogCommentsRequireRegistrationKey]; - settings.commentsSortOrder = [rawSettings stringForKey:RemoteBlogCommentsSortOrderKey]; - settings.commentsThreadingEnabled = [rawSettings numberForKey:RemoteBlogCommentsThreadingEnabledKey]; - settings.commentsThreadingDepth = [rawSettings numberForKey:RemoteBlogCommentsThreadingDepthKey]; - settings.pingbackOutboundEnabled = [rawSettings numberForKey:RemoteBlogCommentsPingbackOutboundKey]; - settings.pingbackInboundEnabled = [rawSettings numberForKey:RemoteBlogCommentsPingbackInboundKey]; - - // Related Posts - settings.relatedPostsAllowed = [rawSettings numberForKey:RemoteBlogRelatedPostsAllowedKey]; - settings.relatedPostsEnabled = [rawSettings numberForKey:RemoteBlogRelatedPostsEnabledKey]; - settings.relatedPostsShowHeadline = [rawSettings numberForKey:RemoteBlogRelatedPostsShowHeadlineKey]; - settings.relatedPostsShowThumbnails = [rawSettings numberForKey:RemoteBlogRelatedPostsShowThumbnailsKey]; - - // AMP - settings.ampSupported = [rawSettings numberForKey:RemoteBlogAmpSupportedKey]; - settings.ampEnabled = [rawSettings numberForKey:RemoteBlogAmpEnabledKey]; - - // Sharing - settings.sharingButtonStyle = [rawSettings stringForKey:RemoteBlogSharingButtonStyle]; - settings.sharingLabel = [rawSettings stringForKey:RemoteBlogSharingLabel]; - settings.sharingTwitterName = [rawSettings stringForKey:RemoteBlogSharingTwitterName]; - settings.sharingCommentLikesEnabled = [rawSettings numberForKey:RemoteBlogSharingCommentLikesEnabled]; - settings.sharingDisabledLikes = [rawSettings numberForKey:RemoteBlogSharingDisabledLikes]; - settings.sharingDisabledReblogs = [rawSettings numberForKey:RemoteBlogSharingDisabledReblogs]; - - return settings; -} - -- (NSDictionary *)remoteSettingsToDictionary:(RemoteBlogSettings *)settings -{ - NSParameterAssert(settings); - NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; - - [parameters setValueIfNotNil:settings.name forKey:RemoteBlogNameForUpdateKey]; - [parameters setValueIfNotNil:settings.tagline forKey:RemoteBlogTaglineForUpdateKey]; - [parameters setValueIfNotNil:settings.privacy forKey:RemoteBlogPrivacyKey]; - [parameters setValueIfNotNil:settings.languageID forKey:RemoteBlogLanguageKey]; - [parameters setValueIfNotNil:settings.iconMediaID forKey:RemoteBlogIconKey]; - [parameters setValueIfNotNil:settings.gmtOffset forKey:RemoteBlogGMTOffsetKey]; - [parameters setValueIfNotNil:settings.timezoneString forKey:RemoteBlogTimezoneStringKey]; - - [parameters setValueIfNotNil:settings.defaultCategoryID forKey:RemoteBlogDefaultCategoryKey]; - [parameters setValueIfNotNil:settings.defaultPostFormat forKey:RemoteBlogDefaultPostFormatKey]; - [parameters setValueIfNotNil:settings.dateFormat forKey:RemoteBlogDateFormatKey]; - [parameters setValueIfNotNil:settings.timeFormat forKey:RemoteBlogTimeFormatKey]; - [parameters setValueIfNotNil:settings.startOfWeek forKey:RemoteBlogStartOfWeekKey]; - [parameters setValueIfNotNil:settings.postsPerPage forKey:RemoteBlogPostsPerPageKey]; - - [parameters setValueIfNotNil:settings.commentsAllowed forKey:RemoteBlogCommentsAllowedKey]; - [parameters setValueIfNotNil:settings.commentsBlocklistKeys forKey:RemoteBlogCommentsBlocklistKeys]; - [parameters setValueIfNotNil:settings.commentsCloseAutomatically forKey:RemoteBlogCommentsCloseAutomaticallyKey]; - [parameters setValueIfNotNil:settings.commentsCloseAutomaticallyAfterDays forKey:RemoteBlogCommentsCloseAutomaticallyAfterDaysKey]; - [parameters setValueIfNotNil:settings.commentsFromKnownUsersAllowlisted forKey:RemoteBlogCommentsKnownUsersAllowlistKey]; - [parameters setValueIfNotNil:settings.commentsMaximumLinks forKey:RemoteBlogCommentsMaxLinksKey]; - [parameters setValueIfNotNil:settings.commentsModerationKeys forKey:RemoteBlogCommentsModerationKeys]; - [parameters setValueIfNotNil:settings.commentsPagingEnabled forKey:RemoteBlogCommentsPagingEnabledKey]; - [parameters setValueIfNotNil:settings.commentsPageSize forKey:RemoteBlogCommentsPageSizeKey]; - [parameters setValueIfNotNil:settings.commentsRequireManualModeration forKey:RemoteBlogCommentsRequireModerationKey]; - [parameters setValueIfNotNil:settings.commentsRequireNameAndEmail forKey:RemoteBlogCommentsRequireNameAndEmailKey]; - [parameters setValueIfNotNil:settings.commentsRequireRegistration forKey:RemoteBlogCommentsRequireRegistrationKey]; - [parameters setValueIfNotNil:settings.commentsSortOrder forKey:RemoteBlogCommentsSortOrderKey]; - [parameters setValueIfNotNil:settings.commentsThreadingEnabled forKey:RemoteBlogCommentsThreadingEnabledKey]; - [parameters setValueIfNotNil:settings.commentsThreadingDepth forKey:RemoteBlogCommentsThreadingDepthKey]; - - [parameters setValueIfNotNil:settings.pingbackOutboundEnabled forKey:RemoteBlogCommentsPingbackOutboundKey]; - [parameters setValueIfNotNil:settings.pingbackInboundEnabled forKey:RemoteBlogCommentsPingbackInboundKey]; - - [parameters setValueIfNotNil:settings.relatedPostsEnabled forKey:RemoteBlogRelatedPostsEnabledKey]; - [parameters setValueIfNotNil:settings.relatedPostsShowHeadline forKey:RemoteBlogRelatedPostsShowHeadlineKey]; - [parameters setValueIfNotNil:settings.relatedPostsShowThumbnails forKey:RemoteBlogRelatedPostsShowThumbnailsKey]; - - [parameters setValueIfNotNil:settings.ampEnabled forKey:RemoteBlogAmpEnabledKey]; - - // Sharing - [parameters setValueIfNotNil:settings.sharingButtonStyle forKey:RemoteBlogSharingButtonStyle]; - [parameters setValueIfNotNil:settings.sharingLabel forKey:RemoteBlogSharingLabel]; - [parameters setValueIfNotNil:settings.sharingTwitterName forKey:RemoteBlogSharingTwitterName]; - [parameters setValueIfNotNil:settings.sharingCommentLikesEnabled forKey:RemoteBlogSharingCommentLikesEnabled]; - [parameters setValueIfNotNil:settings.sharingDisabledLikes forKey:RemoteBlogSharingDisabledLikes]; - [parameters setValueIfNotNil:settings.sharingDisabledReblogs forKey:RemoteBlogSharingDisabledReblogs]; - - return parameters; -} - @end diff --git a/Tests/WordPressKitTests/Tests/RemoteBlogSettingsTests.swift b/Tests/WordPressKitTests/Tests/RemoteBlogSettingsTests.swift new file mode 100644 index 000000000..c1a9ac6c9 --- /dev/null +++ b/Tests/WordPressKitTests/Tests/RemoteBlogSettingsTests.swift @@ -0,0 +1,121 @@ +@testable import WordPressKit +import XCTest + +// The aim of these tests is not to be comprehensive, but to help with migrating the decoding and encoding logic from Objective-C to Swift without regression. +// (The logic was originally part of BlogServiceRemoteREST). +// They remain in the suite after the conversion to Swift to prevent future regression and provide a ready made harness where to add more tests. +final class RemoteBlogSettingsTests: XCTestCase { + + func testInitWithJSON() throws { + let json = try loadJSONSettings() + let settings = RemoteBlogSettings(jsonDictionary: json) + + XCTAssertEqual(settings.name, "My Epic Blog") + XCTAssertEqual(settings.tagline, "Definitely, the best blog out there") + XCTAssertEqual(settings.privacy, 1) + XCTAssertNil(settings.iconMediaID) + XCTAssertEqual(settings.timezoneString, "") + XCTAssertEqual(settings.defaultCategoryID, 8) + XCTAssertEqual(settings.dateFormat, "m/d/Y") + XCTAssertEqual(settings.timeFormat, "g:i a") + XCTAssertEqual(settings.startOfWeek, "0") + XCTAssertEqual(settings.postsPerPage, 12) + XCTAssertEqual(settings.commentsAllowed, true) + XCTAssertEqual(settings.commentsBlocklistKeys, "some evil keywords") + XCTAssertEqual(settings.commentsCloseAutomatically, false) + XCTAssertEqual(settings.commentsCloseAutomaticallyAfterDays, 3000) + XCTAssertEqual(settings.commentsFromKnownUsersAllowlisted, true) + XCTAssertEqual(settings.commentsMaximumLinks, 42) + XCTAssertEqual(settings.commentsModerationKeys, "moderation keys") + XCTAssertEqual(settings.commentsPagingEnabled, true) + XCTAssertEqual(settings.commentsPageSize, 5) + XCTAssertEqual(settings.commentsRequireManualModeration, true) + XCTAssertEqual(settings.commentsRequireNameAndEmail, false) + XCTAssertEqual(settings.commentsRequireRegistration, true) + XCTAssertEqual(settings.commentsSortOrder, "desc") + XCTAssertEqual(settings.commentsThreadingDepth, 5) + XCTAssertEqual(settings.commentsThreadingEnabled, true) + XCTAssertEqual(settings.pingbackInboundEnabled, true) + XCTAssertEqual(settings.pingbackOutboundEnabled, true) + XCTAssertEqual(settings.relatedPostsAllowed, true) + XCTAssertEqual(settings.relatedPostsEnabled, false) + XCTAssertEqual(settings.relatedPostsShowHeadline, true) + XCTAssertEqual(settings.relatedPostsShowThumbnails, false) + XCTAssertEqual(settings.ampSupported, true) + XCTAssertEqual(settings.ampEnabled, false) + XCTAssertEqual(settings.sharingButtonStyle, "icon-text") + XCTAssertEqual(settings.sharingLabel, "Share this:") + XCTAssertEqual(settings.sharingTwitterName, "gcorne") + XCTAssertEqual(settings.sharingCommentLikesEnabled, true) + XCTAssertEqual(settings.sharingDisabledLikes, false) + XCTAssertEqual(settings.sharingDisabledReblogs, false) + + // These properties have custom decoding. + // It would be appropriate to add additional tests to check all its paths. + XCTAssertEqual(settings.defaultPostFormat, "standard") + XCTAssertEqual(settings.languageID, 31337) + XCTAssertEqual(settings.gmtOffset, 0) + } + + func testToDictionary() throws { + // Rather than creating an object and checking the resulting NSDictionary, + // let's load one, convert it, then compare the source and converted dictionaries + let json = try loadJSONSettings() + let settings = try XCTUnwrap(RemoteBlogSettings(jsonDictionary: json)) + + let dictionary = try XCTUnwrap(settings.dictionaryRepresentation) + + XCTAssertEqual(dictionary["blogname"] as? String, settings.name) // "name" in JSON + XCTAssertEqual(dictionary["blogdescription"] as? String, settings.tagline) // "description" in JSON + XCTAssertEqual(dictionary["blog_public"] as? NSNumber, settings.privacy) + XCTAssertEqual(dictionary["site_icon"] as? NSNumber, settings.iconMediaID) + XCTAssertEqual(dictionary["timezone_string"] as? String, settings.timezoneString) + XCTAssertEqual(dictionary["default_category"] as? NSNumber, settings.defaultCategoryID) + XCTAssertEqual(dictionary["date_format"] as? String, settings.dateFormat) + XCTAssertEqual(dictionary["time_format"] as? String, settings.timeFormat) + XCTAssertEqual(dictionary["start_of_week"] as? String, settings.startOfWeek) + XCTAssertEqual(dictionary["posts_per_page"] as? NSNumber, settings.postsPerPage) + XCTAssertEqual(dictionary["default_comment_status"] as? NSNumber, settings.commentsAllowed) + XCTAssertEqual(dictionary["blacklist_keys"] as? String, settings.commentsBlocklistKeys) + XCTAssertEqual(dictionary["close_comments_for_old_posts"] as? NSNumber, settings.commentsCloseAutomatically) + XCTAssertEqual(dictionary["close_comments_days_old"] as? NSNumber, settings.commentsCloseAutomaticallyAfterDays) + XCTAssertEqual(dictionary["comment_whitelist"] as? NSNumber, settings.commentsFromKnownUsersAllowlisted) + XCTAssertEqual(dictionary["comment_max_links"] as? NSNumber, settings.commentsMaximumLinks) + XCTAssertEqual(dictionary["moderation_keys"] as? String, settings.commentsModerationKeys) + XCTAssertEqual(dictionary["page_comments"] as? NSNumber, settings.commentsPagingEnabled) + XCTAssertEqual(dictionary["comments_per_page"] as? NSNumber, settings.commentsPageSize) + XCTAssertEqual(dictionary["comment_moderation"] as? NSNumber, settings.commentsRequireManualModeration) + XCTAssertEqual(dictionary["require_name_email"] as? NSNumber, settings.commentsRequireNameAndEmail) + XCTAssertEqual(dictionary["comment_registration"] as? NSNumber, settings.commentsRequireRegistration) + XCTAssertEqual(dictionary["comment_order"] as? String, settings.commentsSortOrder) + XCTAssertEqual(dictionary["thread_comments"] as? NSNumber, settings.commentsThreadingEnabled) + XCTAssertEqual(dictionary["thread_comments_depth"] as? NSNumber, settings.commentsThreadingDepth) + XCTAssertEqual(dictionary["jetpack_relatedposts_allowed"] as? NSNumber, settings.relatedPostsAllowed) + XCTAssertEqual(dictionary["jetpack_relatedposts_enabled"] as? NSNumber, settings.relatedPostsEnabled) + XCTAssertEqual(dictionary["jetpack_relatedposts_show_headline"] as? NSNumber, settings.relatedPostsShowHeadline) + XCTAssertEqual(dictionary["jetpack_relatedposts_show_thumbnails"] as? NSNumber, settings.relatedPostsShowThumbnails) + XCTAssertEqual(dictionary["amp_is_supported"] as? NSNumber, settings.ampSupported) + XCTAssertEqual(dictionary["amp_is_enabled"] as? NSNumber, settings.ampEnabled) + XCTAssertEqual(dictionary["sharing_button_style"] as? String, settings.sharingButtonStyle) + XCTAssertEqual(dictionary["sharing_label"] as? String, settings.sharingLabel) + XCTAssertEqual(dictionary["twitter_via"] as? String, settings.sharingTwitterName) + XCTAssertEqual(dictionary["jetpack_comment_likes_enabled"] as? NSNumber, settings.sharingCommentLikesEnabled) + XCTAssertEqual(dictionary["disabled_likes"] as? NSNumber, settings.sharingDisabledLikes) + XCTAssertEqual(dictionary["disabled_reblogs"] as? NSNumber, settings.sharingDisabledReblogs) + + // These properties have custom decoding. + // Note that here we're obviously testing only one of the possible paths. + XCTAssertEqual(dictionary["lang_id"] as? NSNumber, settings.languageID) + XCTAssertEqual(dictionary["gmt_offset"] as? NSNumber, settings.gmtOffset) + XCTAssertEqual(dictionary["default_post_format"] as? String, settings.defaultPostFormat) + } + + func loadJSONSettings() throws -> [String: Any] { + let jsonPath = try XCTUnwrap( + Bundle(for: type(of: self)) + .path(forResource: "rest-site-settings", ofType: "json") + ) + let json = try XCTUnwrap(JSONLoader().loadFile(jsonPath)) + return json + } +} diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index 393460ce4..7e0f8d5a2 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -72,6 +72,7 @@ 3FE2E94F2BB29A1B002CA2E1 /* FilePart.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FE2E94D2BB29A1B002CA2E1 /* FilePart.m */; }; 3FE2E9502BB29A1B002CA2E1 /* FilePart.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FE2E94E2BB29A1B002CA2E1 /* FilePart.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3FE2E9672BBEB8D2002CA2E1 /* WordPressComRESTAPIVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FE2E9662BBEB8D2002CA2E1 /* WordPressComRESTAPIVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3FE2E9692BBF7C10002CA2E1 /* RemoteBlogSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE2E9682BBF7C10002CA2E1 /* RemoteBlogSettingsTests.swift */; }; 3FFCC0412BA995290051D229 /* Date+WordPressComTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */; }; 3FFCC0472BAA6EF40051D229 /* NSDate+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */; }; 3FFCC0492BAB98130051D229 /* DateFormatter+WordPressCom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */; }; @@ -817,6 +818,7 @@ 3FE2E94D2BB29A1B002CA2E1 /* FilePart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FilePart.m; sourceTree = ""; }; 3FE2E94E2BB29A1B002CA2E1 /* FilePart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FilePart.h; sourceTree = ""; }; 3FE2E9662BBEB8D2002CA2E1 /* WordPressComRESTAPIVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WordPressComRESTAPIVersion.h; sourceTree = ""; }; + 3FE2E9682BBF7C10002CA2E1 /* RemoteBlogSettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteBlogSettingsTests.swift; sourceTree = ""; }; 3FFCC0402BA995290051D229 /* Date+WordPressComTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+WordPressComTests.swift"; sourceTree = ""; }; 3FFCC0462BAA6EF40051D229 /* NSDate+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDate+WordPressCom.swift"; sourceTree = ""; }; 3FFCC0482BAB98130051D229 /* DateFormatter+WordPressCom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WordPressCom.swift"; sourceTree = ""; }; @@ -1878,6 +1880,7 @@ FA1D0F8D299534FF0025D76C /* BlazeServiceRemoteTests.swift */, 465F88A6263B371D00F4C950 /* BlockEditorSettingsServiceRemoteTests.swift */, 74B5F0DD1EF82A9600B411E7 /* BlogServiceRemoteRESTTests.m */, + 3FE2E9682BBF7C10002CA2E1 /* RemoteBlogSettingsTests.swift */, FEE48EF52A4B3602008A48E0 /* BlogServiceRemote+ActiveFeaturesTests.swift */, FEEFD8B6280EC91B00A3E261 /* BloggingPromptsServiceRemoteTests.swift */, ABD95B7E25DD6C4B00735BEE /* CommentServiceRemoteRESTLikesTests.swift */, @@ -3537,6 +3540,7 @@ FFA4D4AA2423B10A00BF5180 /* WordPressOrgRestApiTests.swift in Sources */, 74A44DD51F13C6D8006CD8F4 /* PushAuthenticationServiceRemoteTests.swift in Sources */, 4AE278482B2FBF1100E4D9B1 /* HTTPBodyEncodingTests.swift in Sources */, + 3FE2E9692BBF7C10002CA2E1 /* RemoteBlogSettingsTests.swift in Sources */, 731BA83621DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift in Sources */, 7EC60EBE22DC4F9000FB0336 /* EditorServiceRemoteTests.swift in Sources */, 931924241F1662FA0069CBCC /* JSONLoader.swift in Sources */,