diff --git a/Sources/WordPressData/Objective-C/AbstractPost.m b/Sources/WordPressData/Objective-C/AbstractPost.m index fee78a210cb7..a54b0058ec06 100644 --- a/Sources/WordPressData/Objective-C/AbstractPost.m +++ b/Sources/WordPressData/Objective-C/AbstractPost.m @@ -28,13 +28,6 @@ @implementation AbstractPost @dynamic permalinkTemplateURL; @synthesize voiceContent; -#pragma mark - Life Cycle Methods - -- (void)save -{ - [[ContextManager sharedInstance] saveContext:self.managedObjectContext]; -} - #pragma mark - #pragma mark Revision management @@ -58,45 +51,6 @@ - (AbstractPost *)original #pragma mark - Helpers -- (BOOL)dateCreatedIsNilOrEqualToDateModified -{ - return self.date_created_gmt == nil || [self.date_created_gmt isEqualToDate:self.dateModified]; -} - -- (BOOL)hasPhoto -{ - if ([self.media count] == 0) { - return NO; - } - - if (self.featuredImage != nil) { - return YES; - } - - for (Media *media in self.media) { - if (media.mediaType == MediaTypeImage) { - return YES; - } - } - - return NO; -} - -- (BOOL)hasVideo -{ - if ([self.media count] == 0) { - return NO; - } - - for (Media *media in self.media) { - if (media.mediaType == MediaTypeVideo) { - return YES; - } - } - - return NO; -} - - (BOOL)hasCategories { return NO; @@ -107,93 +61,4 @@ - (BOOL)hasTags return NO; } -- (BOOL)hasRemote -{ - return ((self.postID != nil) && ([self.postID longLongValue] > 0)); -} - -#pragma mark - Convenience methods - -// If the post has a scheduled status. -- (BOOL)isScheduled -{ - return ([self.status isEqualToString:PostStatusScheduled]); -} - -- (BOOL)isDraft -{ - return [self.status isEqualToString:PostStatusDraft]; -} - -- (BOOL)isPublished -{ - return [self.status isEqualToString:PostStatusPublish]; -} - -- (BOOL)originalIsDraft -{ - if ([self.status isEqualToString:PostStatusDraft]) { - return YES; - } else if (self.isRevision && [self.original.status isEqualToString:PostStatusDraft]) { - return YES; - } - return NO; -} - -- (BOOL)shouldPublishImmediately -{ - /// - warning: Yes, this is WordPress logic and it matches the behavior on - /// the web. If `dateCreated` is the same as `dateModified`, the system - /// uses it to represent a "no publish date selected" scenario. - return [self originalIsDraft] && [self dateCreatedIsNilOrEqualToDateModified]; -} - -- (NSURL *)blogURL -{ - return [NSURL URLWithString:self.blog.url]; -} - -- (BOOL)isPrivateAtWPCom -{ - return self.blog.isPrivateAtWPCom; -} - -#pragma mark - Post - -- (void)updatePathForDisplayImageBasedOnContent -{ - // First lets check the post content for a suitable image - NSString *result = [DisplayableImageHelper searchPostContentForImageToDisplay:self.content]; - if (result.length > 0) { - self.pathForDisplayImage = result; - } - // If none found let's see if some galleries are available - NSSet *mediaIDs = [DisplayableImageHelper searchPostContentForAttachmentIdsInGalleries:self.content]; - for (Media *media in self.blog.media) { - NSNumber *mediaID = media.mediaID; - if (mediaID && [mediaIDs containsObject:mediaID]) { - result = media.remoteURL; - } - } - self.pathForDisplayImage = result; -} - -- (void)setParsedOtherTerms:(NSDictionary *> *)data -{ - if (data == nil) { - self.rawOtherTerms = nil; - } else { - self.rawOtherTerms = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil]; - } -} - -- (NSDictionary *> *)parseOtherTerms -{ - if (self.rawOtherTerms == nil) { - return [NSDictionary dictionary]; - } - - return [NSJSONSerialization JSONObjectWithData:self.rawOtherTerms options:0 error:nil] ?: [NSDictionary dictionary]; -} - @end diff --git a/Sources/WordPressData/Objective-C/include/AbstractPost.h b/Sources/WordPressData/Objective-C/include/AbstractPost.h index 6d091cb2cc6b..24833ca1ef5b 100644 --- a/Sources/WordPressData/Objective-C/include/AbstractPost.h +++ b/Sources/WordPressData/Objective-C/include/AbstractPost.h @@ -62,54 +62,9 @@ typedef NS_ENUM(NSUInteger, AbstractPostRemoteStatus) { @property (nonatomic, strong, nullable) NSString *voiceContent; -- (BOOL)hasPhoto; -- (BOOL)hasVideo; - (BOOL)hasCategories; - (BOOL)hasTags; - -#pragma mark - Conveniece Methods -- (BOOL)shouldPublishImmediately; -- (BOOL)isPrivateAtWPCom; - - -#pragma mark - Unsaved Changes - -/** - Returns YES if the post is has a `future` post status - */ -- (BOOL)isScheduled; - -/** - Returns YES if the post is a draft - */ -- (BOOL)isDraft; - -/** - Returns YES if the post is a published. - */ -- (BOOL)isPublished; - -/** - Returns YES if the original post is a draft - */ -- (BOOL)originalIsDraft; - -// Does the post exist on the blog? -- (BOOL)hasRemote; - -// Save changes to disk -- (void)save; - -/** - * Updates the path for the display image by looking at the post content and trying to find an good image to use. - * If no appropiated image is found the path is set to nil. - */ -- (void)updatePathForDisplayImageBasedOnContent; - -- (void)setParsedOtherTerms:(NSDictionary *> *)data; -- (NSDictionary *> *)parseOtherTerms; - @end @interface AbstractPost (CoreDataGeneratedAccessors) diff --git a/Sources/WordPressData/Swift/AbstractPost.swift b/Sources/WordPressData/Swift/AbstractPost.swift index 63be32391b3a..454a34eaaec7 100644 --- a/Sources/WordPressData/Swift/AbstractPost.swift +++ b/Sources/WordPressData/Swift/AbstractPost.swift @@ -44,7 +44,6 @@ public extension AbstractPost { statuses.contains(status ?? .draft) } - @objc var remoteStatus: AbstractPostRemoteStatus { get { guard let remoteStatusNumber = remoteStatusNumber?.uintValue, @@ -72,7 +71,6 @@ public extension AbstractPost { /// /// - returns: The localized title for the specified status, or the status if a title was not found. /// - @objc static func title(forStatus status: String) -> String { switch status { case PostStatusDraft: @@ -243,7 +241,7 @@ public extension AbstractPost { func dateStringForDisplay() -> String? { if self.originalIsDraft() || self.status == .pending { return dateModified?.toMediumString() - } else if self.isScheduled() { + } else if self.status == .scheduled { return self.dateCreated?.mediumStringWithTime() } else if self.shouldPublishImmediately() { return NSLocalizedString("Publish Immediately", comment: "A short phrase indicating a post is due to be immedately published.") @@ -274,7 +272,6 @@ public extension AbstractPost { } } - @objc func createRevision() -> AbstractPost { precondition(managedObjectContext != nil) precondition(revision == nil, "This post must not already have a revision") @@ -287,7 +284,6 @@ public extension AbstractPost { return post } - @objc func deleteRevision() { guard let revision, let context = managedObjectContext else { return @@ -301,7 +297,6 @@ public extension AbstractPost { } } - @objc func applyRevision() { guard isOriginal(), let revision else { return @@ -309,23 +304,86 @@ public extension AbstractPost { clone(from: revision) } - @objc func isRevision() -> Bool { !isOriginal() } - @objc func isOriginal() -> Bool { original == nil } - @objc func latest() -> AbstractPost { revision?.latest() ?? self } - @objc func hasRevision() -> Bool { revision != nil } + + /// Returns YES if the original post is a draft + func originalIsDraft() -> Bool { + if status == .draft { + return true + } else if isRevision(), original?.status == .draft { + return true + } + return false + } + + func shouldPublishImmediately() -> Bool { + // - warning: Yes, this is WordPress logic and it matches the behavior on + // the web. If `dateCreated` is the same as `dateModified`, the system + // uses it to represent a "no publish date selected" scenario. + originalIsDraft() && (date_created_gmt == nil || date_created_gmt == dateModified) + } + + /// Does the post exist on the blog? + func hasRemote() -> Bool { + (postID?.int64Value ?? 0) > 0 + } + + @objc + var parsedOtherTerms: [String: [String]] { + get { + guard let rawOtherTerms else { + return [:] + } + + return (try? JSONSerialization.jsonObject(with: rawOtherTerms) as? [String: [String]]) ?? [:] + } + set { + rawOtherTerms = try? JSONSerialization.data(withJSONObject: newValue) + } + } + + /// Updates the path for the display image by looking at the post content and trying to find an good image to use. + /// If no appropiated image is found the path is set to nil. + @objc + func updatePathForDisplayImageBasedOnContent() { + guard let content else { + return + } + + if let result = DisplayableImageHelper.searchPostContentForImage(toDisplay: content), !result.isEmpty { + pathForDisplayImage = result + return + } + + guard let allMedia = blog.media, !allMedia.isEmpty else { return } + + let mediaIDs = DisplayableImageHelper.searchPostContentForAttachmentIds(inGalleries: content) as? Set ?? [] + for media in allMedia { + guard let media = media as? Media else { continue } + + guard let mediaID = media.mediaID, + mediaIDs.contains(mediaID) else { + continue + } + + if let remoteURL = media.remoteURL { + pathForDisplayImage = remoteURL + break + } + } + } } diff --git a/Sources/WordPressData/Swift/RemotePostCreateParameters+Helpers.swift b/Sources/WordPressData/Swift/RemotePostCreateParameters+Helpers.swift index b4465f326300..cbd0434a230d 100644 --- a/Sources/WordPressData/Swift/RemotePostCreateParameters+Helpers.swift +++ b/Sources/WordPressData/Swift/RemotePostCreateParameters+Helpers.swift @@ -20,7 +20,7 @@ extension RemotePostCreateParameters { excerpt = post.mt_excerpt slug = post.wp_slug featuredImageID = post.featuredImage?.mediaID?.intValue - otherTerms = post.parseOtherTerms() + otherTerms = post.parsedOtherTerms switch post { case let page as Page: parentPageID = page.parentID?.intValue diff --git a/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift b/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift index d269f5219993..f185fc957d65 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift @@ -139,7 +139,7 @@ class EditorMediaUtility { let requestURL: URL if url.isFileURL { requestURL = url - } else if post.isPrivateAtWPCom() && url.isHostedAtWPCom { + } else if post.blog.isPrivateAtWPCom() && url.isHostedAtWPCom { // private wpcom image needs special handling. // the size that WPImageHelper expects is pixel size size.width = size.width * scale diff --git a/WordPress/Classes/ViewRelated/Post/EditPostViewController.swift b/WordPress/Classes/ViewRelated/Post/EditPostViewController.swift index 128c82f8ed63..83bf5d9a6a96 100644 --- a/WordPress/Classes/ViewRelated/Post/EditPostViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/EditPostViewController.swift @@ -241,7 +241,7 @@ class EditPostViewController: UIViewController { } self.afterDismiss?() guard let post = self.post?.getOriginal(), - post.isPublished(), + post.status == .publish, !self.editingExistingPost, let controller = presentingController else { return diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift b/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift index c802674ef6fa..cfa378204bf6 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift @@ -368,3 +368,25 @@ private struct MediaUploadingAlert { static let title = NSLocalizedString("Uploading media", comment: "Title for alert when trying to save/exit a post before media upload process is complete.") static let message = NSLocalizedString("You are currently uploading media. Please wait until this completes.", comment: "This is a notification the user receives if they are trying to save a post (or exit) before the media upload process is complete.") } + +private extension AbstractPost { + func hasPhoto() -> Bool { + if media.isEmpty { + return false + } + + if featuredImage != nil { + return true + } + + return media.contains { $0.mediaType == .image } + } + + func hasVideo() -> Bool { + if media.isEmpty { + return false + } + + return media.contains { $0.mediaType == .video } + } +} diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift index 6758d5f530b4..797c9a1519ad 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift @@ -54,7 +54,7 @@ struct PostSettings: Hashable { } featuredImageID = post.featuredImage?.mediaID?.intValue - otherTerms = post.parseOtherTerms() + otherTerms = post.parsedOtherTerms metadata = PostMetadata(post) @@ -112,8 +112,8 @@ struct PostSettings: Hashable { post.featuredImage = nil } - if !RemotePost.compare(otherTerms: post.parseOtherTerms(), withAnother: otherTerms) { - post.setParsedOtherTerms(otherTerms) + if !RemotePost.compare(otherTerms: post.parsedOtherTerms, withAnother: otherTerms) { + post.parsedOtherTerms = otherTerms } var postMetadataContainer = PostMetadataContainer(post) diff --git a/WordPress/Classes/ViewRelated/Post/Preview/PreviewWebKitViewController.swift b/WordPress/Classes/ViewRelated/Post/Preview/PreviewWebKitViewController.swift index 21423e5b68e4..58c9f429f659 100644 --- a/WordPress/Classes/ViewRelated/Post/Preview/PreviewWebKitViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/Preview/PreviewWebKitViewController.swift @@ -49,7 +49,7 @@ class PreviewWebKitViewController: WebKitViewController { self.post = post - canPublish = post.isDraft() + canPublish = post.status == .draft guard let url = PreviewNonceHandler.nonceURL(post: post, previewURL: previewURL) else { super.init(configuration: WebViewControllerConfiguration(url: Constants.blankURL))