Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* [*] [internal] In-app updates: guard the flexible update notice against double-posting when two update checks race [#25666]
* [*] [build tooling] Add a String Catalog localization pipeline (plurals + build-free catalog generation) with a CI coverage gate [#25688]
* [*] [internal] Stats widgets: migrate the site picker from SiriKit to App Intents [#25753]
* [*] Fix categories and tags not working on self-hosted sites with XML-RPC disabled [#25767]
* [*] Fix Blogging Reminders text color in dark mode [#25780]

27.0
Expand Down
12 changes: 10 additions & 2 deletions WordPress/Classes/Services/PostCategoryService.m
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,16 @@ - (void)mergeCategories:(NSArray <RemotePostCategory *> *)remoteCategories forBl
if (blog.wordPressComRestApi) {
return [[TaxonomyServiceRemoteREST alloc] initWithWordPressComRestApi:blog.wordPressComRestApi siteID:blog.dotComID];
}
} else if (blog.xmlrpcApi) {
return [[TaxonomyServiceRemoteXMLRPC alloc] initWithApi:blog.xmlrpcApi username:blog.username password:blog.password];
} else {
// Prefer the core REST API (wp/v2) for self-hosted sites. Some hosts
// block the XML-RPC term methods, which breaks categories (#25758).
TaxonomyServiceRemoteCoreREST *coreREST = [[TaxonomyServiceRemoteCoreREST alloc] initWithBlog:blog];
if (coreREST) {
return coreREST;
}
if (blog.xmlrpcApi) {
return [[TaxonomyServiceRemoteXMLRPC alloc] initWithApi:blog.xmlrpcApi username:blog.username password:blog.password];
}
}
return nil;
}
Expand Down
80 changes: 65 additions & 15 deletions WordPress/Classes/Services/TaxonomyServiceRemoteCoreREST.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import WordPressAPI
self.client = client
}

public func createCategory(_ category: RemotePostCategory, success: ((RemotePostCategory) -> Void)?, failure: ((any Error) -> Void)? = nil) {
public func createCategory(
_ category: RemotePostCategory,
success: ((RemotePostCategory) -> Void)?,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let params = TermCreateParams(
Expand All @@ -33,10 +37,13 @@ import WordPressAPI
}
}

public func getCategoriesWithSuccess(_ success: @escaping ([RemotePostCategory]) -> Void, failure: ((any Error) -> Void)? = nil) {
public func getCategoriesWithSuccess(
_ success: @escaping ([RemotePostCategory]) -> Void,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let sequence = await client.api.terms.sequenceWithEditContext(
let sequence = await client.api.terms.sequenceWithViewContext(
type: .categories,
params: TermListParams(perPage: 100)
)
Expand All @@ -51,7 +58,11 @@ import WordPressAPI
}
}

public func getCategoriesWith(_ paging: RemoteTaxonomyPaging, success: @escaping ([RemotePostCategory]) -> Void, failure: ((any Error) -> Void)? = nil) {
public func getCategoriesWith(
_ paging: RemoteTaxonomyPaging,
success: @escaping ([RemotePostCategory]) -> Void,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let params = TermListParams(
Expand All @@ -61,7 +72,7 @@ import WordPressAPI
order: WpApiParamOrder(paging.order),
orderby: WpApiParamTermsOrderBy(paging.orderBy)
)
let response = try await client.api.terms.listWithEditContext(
let response = try await client.api.terms.listWithViewContext(
termEndpointType: .categories,
params: params
)
Expand All @@ -73,11 +84,15 @@ import WordPressAPI
}
}

public func searchCategories(withName nameQuery: String, success: @escaping ([RemotePostCategory]) -> Void, failure: ((any Error) -> Void)? = nil) {
public func searchCategories(
withName nameQuery: String,
success: @escaping ([RemotePostCategory]) -> Void,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let params = TermListParams(search: nameQuery)
let response = try await client.api.terms.listWithEditContext(
let response = try await client.api.terms.listWithViewContext(
termEndpointType: .categories,
params: params
)
Expand All @@ -89,7 +104,11 @@ import WordPressAPI
}
}

public func createTag(_ tag: RemotePostTag, success: ((RemotePostTag) -> Void)?, failure: ((any Error) -> Void)? = nil) {
public func createTag(
_ tag: RemotePostTag,
success: ((RemotePostTag) -> Void)?,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let params = TermCreateParams(
Expand All @@ -109,7 +128,11 @@ import WordPressAPI
}
}

public func update(_ tag: RemotePostTag, success: ((RemotePostTag) -> Void)?, failure: ((any Error) -> Void)? = nil) {
public func update(
_ tag: RemotePostTag,
success: ((RemotePostTag) -> Void)?,
failure: ((any Error) -> Void)? = nil
) {
guard let tagID = tag.tagID else {
failure?(URLError(.unknown))
return
Expand Down Expand Up @@ -151,10 +174,13 @@ import WordPressAPI
}
}

public func getTagsWithSuccess(_ success: @escaping ([RemotePostTag]) -> Void, failure: ((any Error) -> Void)? = nil) {
public func getTagsWithSuccess(
_ success: @escaping ([RemotePostTag]) -> Void,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let response = try await client.api.terms.listWithEditContext(
let response = try await client.api.terms.listWithViewContext(
termEndpointType: .tags,
params: TermListParams()
)
Expand All @@ -166,7 +192,11 @@ import WordPressAPI
}
}

public func getTagsWith(_ paging: RemoteTaxonomyPaging, success: @escaping ([RemotePostTag]) -> Void, failure: ((any Error) -> Void)? = nil) {
public func getTagsWith(
_ paging: RemoteTaxonomyPaging,
success: @escaping ([RemotePostTag]) -> Void,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let params = TermListParams(
Expand All @@ -176,7 +206,7 @@ import WordPressAPI
order: WpApiParamOrder(paging.order),
orderby: WpApiParamTermsOrderBy(paging.orderBy)
)
let response = try await client.api.terms.listWithEditContext(
let response = try await client.api.terms.listWithViewContext(
termEndpointType: .tags,
params: params
)
Expand All @@ -188,10 +218,14 @@ import WordPressAPI
}
}

public func searchTags(withName nameQuery: String, success: @escaping ([RemotePostTag]) -> Void, failure: ((any Error) -> Void)? = nil) {
public func searchTags(
withName nameQuery: String,
success: @escaping ([RemotePostTag]) -> Void,
failure: ((any Error) -> Void)? = nil
) {
Task { @MainActor in
do {
let response = try await client.api.terms.listWithEditContext(
let response = try await client.api.terms.listWithViewContext(
termEndpointType: .tags,
params: TermListParams(search: nameQuery)
)
Expand All @@ -205,6 +239,13 @@ import WordPressAPI
}

private extension RemotePostCategory {
convenience init(category: AnyTermWithViewContext) {
self.init()
self.categoryID = NSNumber(value: category.id)
self.name = category.name
self.parentID = NSNumber(value: category.parent ?? 0)
}

convenience init(category: AnyTermWithEditContext) {
self.init()
self.categoryID = NSNumber(value: category.id)
Expand All @@ -214,6 +255,15 @@ private extension RemotePostCategory {
}

private extension RemotePostTag {
convenience init(tag: AnyTermWithViewContext) {
self.init()
self.tagID = NSNumber(value: tag.id)
self.name = tag.name
self.slug = tag.slug
self.tagDescription = tag.description
self.postCount = NSNumber(value: tag.count)
}

convenience init(tag: AnyTermWithEditContext) {
self.init()
self.tagID = NSNumber(value: tag.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ extension SiteSettingsViewController {
@objc public func showCustomTaxonomies() {
let viewController: UIViewController
if let client = try? WordPressClientFactory.shared.instance(for: .init(blog: blog)) {
let rootView = SiteCustomTaxonomiesView(blog: self.blog, client: client)
let rootView = SiteCustomTaxonomiesView(client: client)
viewController = UIHostingController(rootView: rootView)
} else {
let feature = NSLocalizedString(
Expand All @@ -76,7 +76,7 @@ extension SiteSettingsViewController {
source: "taxonomies",
presentingViewController: self
) { client in
SiteCustomTaxonomiesView(blog: self.blog, client: client)
SiteCustomTaxonomiesView(client: client)
}
viewController = UIHostingController(rootView: rootView)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ struct PostSettingsFormContentView<ViewModel: PostSettingsViewModelProtocol>: Vi
ForEach(viewModel.customTaxonomies, id: \.slug) { taxonomy in
NavigationLink {
PostTagsView(
blog: viewModel.blog,
client: client,
taxonomy: taxonomy,
selectedTerms: viewModel.settings.getTerms(forTaxonomySlug: taxonomy.slug)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM
Task { [weak self] in
guard let self else { return }

let service = TagsService(blog: blog)
let service = TagsViewModel.makeTagsService(for: blog)
let resolved = await service.resolveTerms(named: pendingNames)
for (name, existing) in resolved {
if let index = settings.tags.firstIndex(where: { $0.name == name }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ struct PostTagsView: View {
@State private var isKeyboardPresented = false

init(blog: Blog, selectedTags: [TagsViewModel.SelectedTerm], onSelectionChanged: @escaping ([TagsViewModel.SelectedTerm]) -> Void) {
let viewModel = TagsViewModel(blog: blog, selectedTags: selectedTags, mode: .selection(onSelectedTagsChanged: { tags in
let viewModel = TagsViewModel.tags(for: blog, selectedTerms: selectedTags, mode: .selection(onSelectedTagsChanged: { tags in
onSelectionChanged(tags)
}))
self._viewModel = StateObject(wrappedValue: viewModel)
}

init(blog: Blog, client: WordPressClient, taxonomy: SiteTaxonomy, selectedTerms: [TagsViewModel.SelectedTerm] = [], onSelectionChanged: @escaping ([TagsViewModel.SelectedTerm]) -> Void) {
let viewModel = TagsViewModel(blog: blog, client: client, taxonomy: taxonomy, selectedTerms: selectedTerms, mode: .selection(onSelectedTagsChanged: { tags in
init(client: WordPressClient, taxonomy: SiteTaxonomy, selectedTerms: [TagsViewModel.SelectedTerm] = [], onSelectionChanged: @escaping ([TagsViewModel.SelectedTerm]) -> Void) {
let viewModel = TagsViewModel.taxonomy(taxonomy, client: client, selectedTerms: selectedTerms, mode: .selection(onSelectedTagsChanged: { tags in
onSelectionChanged(tags)
}))
self._viewModel = StateObject(wrappedValue: viewModel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,21 @@ import WordPressShared
import WordPressUI

struct SiteCustomTaxonomiesView: View {
let blog: Blog
let client: WordPressClient

@State private var isLoading: Bool = false
@State private var taxonomies: [SiteTaxonomy]? = nil
@State private var error: Error?

init(blog: Blog, client: WordPressClient) {
self.blog = blog
init(client: WordPressClient) {
self.client = client
}

var body: some View {
List {
ForEach(taxonomies ?? [], id: \.slug) { taxonomy in
NavigationLink {
SiteTagsView(viewModel: .init(blog: blog, client: client, taxonomy: taxonomy, mode: .browse))
SiteTagsView(viewModel: .taxonomy(taxonomy, client: client, mode: .browse))
} label: {
Text(taxonomy.localizedName)
}
Expand Down
2 changes: 1 addition & 1 deletion WordPress/Classes/ViewRelated/Tags/SiteTagsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class SiteTagsViewController: UIHostingController<SiteTagsView> {
let viewModel: TagsViewModel

init(blog: Blog) {
viewModel = TagsViewModel(blog: blog, mode: .browse)
viewModel = TagsViewModel.tags(for: blog, mode: .browse)
super.init(rootView: .init(viewModel: viewModel))
}

Expand Down
14 changes: 9 additions & 5 deletions WordPress/Classes/ViewRelated/Tags/TagsService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import WordPressAPI
import WordPressAPIInternal

protocol TaxonomyServiceProtocol {
func getTags(page: Int, recentlyUsed: Bool) async throws -> [AnyTermWithViewContext]
func getTags(page: Int, recentlyUsed: Bool) async throws -> (terms: [AnyTermWithViewContext], hasMore: Bool)
func searchTags(with query: String) async throws -> [AnyTermWithViewContext]
func createTag(name: String, description: String) async throws -> AnyTermWithViewContext
func updateTag(_ term: AnyTermWithViewContext, name: String, description: String) async throws -> AnyTermWithViewContext
Expand Down Expand Up @@ -38,7 +38,7 @@ class TagsService: TaxonomyServiceProtocol {
func getTags(
page: Int = 0,
recentlyUsed: Bool = false
) async throws -> [AnyTermWithViewContext] {
) async throws -> (terms: [AnyTermWithViewContext], hasMore: Bool) {
guard let remote else {
throw TagsServiceError.noRemoteService
}
Expand All @@ -52,7 +52,8 @@ class TagsService: TaxonomyServiceProtocol {

return try await withCheckedThrowingContinuation { continuation in
remote.getTagsWith(paging, success: { remoteTags in
continuation.resume(returning: remoteTags.map { AnyTermWithViewContext(tag: $0) })
let terms = remoteTags.map { AnyTermWithViewContext(tag: $0) }
continuation.resume(returning: (terms, terms.count == pageSize))
}, failure: { error in
continuation.resume(throwing: error)
})
Expand Down Expand Up @@ -254,7 +255,10 @@ class AnyTermService: TaxonomyServiceProtocol {
self.client = client
}

func getTags(page: Int = 0, recentlyUsed: Bool = false) async throws -> [AnyTermWithViewContext] {
func getTags(
page: Int = 0,
recentlyUsed: Bool = false
) async throws -> (terms: [AnyTermWithViewContext], hasMore: Bool) {
let perPage: UInt32 = 100
let params = TermListParams(
page: UInt32(page + 1),
Expand All @@ -268,7 +272,7 @@ class AnyTermService: TaxonomyServiceProtocol {
params: params
)

return response.data
return (response.data, response.nextPageParams != nil)
}

func searchTags(with query: String) async throws -> [AnyTermWithViewContext] {
Expand Down
Loading