diff --git a/README.md b/README.md index 05c30f50..ead8c0b1 100644 --- a/README.md +++ b/README.md @@ -167,29 +167,32 @@ Notes: ## Endpoint Coverage and Gaps -WordPressPCL currently provides dedicated clients for the most common `wp/v2` endpoints: +WordPressPCL provides dedicated clients for the standard `wp/v2` endpoints: - Posts, Pages, Comments, Categories, Tags, Users and Media - Taxonomies, Post Types, Post Statuses and Settings -- Post revisions via `client.Posts.Revisions(postId)` - Plugins and Themes +- Search (`client.Search`) +- Block types (`client.BlockTypes`), reusable blocks (`client.Blocks`), templates (`client.Templates`), template parts (`client.TemplateParts`) and global styles (`client.GlobalStyles`) +- Navigation (`client.Navigation`), sidebars (`client.Sidebars`), widgets (`client.Widgets`) and widget types (`client.WidgetTypes`) +- URL details (`client.UrlDetails`) +- Post revisions via `client.Posts.Revisions(postId)` and page revisions via `client.Pages.Revisions(pageId)` +- Post autosaves via `client.Posts.Autosaves(postId)` and page autosaves via `client.Pages.Autosaves(pageId)` -The standard WordPress REST API reference also includes endpoints that do not yet have first-class wrappers in this library, including: +The following endpoints do not yet have first-class wrappers but can be called via `CustomRequest`: -- `wp/v2/search` -- newer block editor endpoints such as block types, blocks, block rendering, templates, template parts and global styles -- navigation, sidebars, widgets and widget types -- `wp/v2/url-details` -- autosaves and page revisions +- `wp/v2/block-renderer` (server-side block rendering) +- `wp/v2/navigation-fallback` +- font families, font faces and pattern directory endpoints -You can still work with unsupported standard endpoints, plugin namespaces and site-specific custom endpoints by using `CustomRequest`. +You can also work with plugin namespaces and site-specific custom endpoints using `CustomRequest`. ```csharp // Discover the namespaces and routes exposed by a site dynamic apiIndex = await client.CustomRequest.GetAsync("", ignoreDefaultPath: true); // Call a standard wp/v2 endpoint that does not have a dedicated client -dynamic searchResults = await client.CustomRequest.GetAsync("search?search=hello", ignoreDefaultPath: false); +dynamic rendered = await client.CustomRequest.PostAsync("block-renderer/core%2Fparagraph", body, ignoreDefaultPath: false); // Call a plugin or custom namespace route dynamic customEndpoint = await client.CustomRequest.GetAsync("wc/v3/products", useAuth: true); diff --git a/docs/v3/endpoint-coverage.md b/docs/v3/endpoint-coverage.md index 7599959e..b3861258 100644 --- a/docs/v3/endpoint-coverage.md +++ b/docs/v3/endpoint-coverage.md @@ -7,8 +7,11 @@ WordPressPCL focuses on the most commonly used WordPress REST API endpoints and | WordPress endpoint | WordPressPCL entry point | Notes | |--------------------|--------------------------|-------| | `wp/v2/posts` | `client.Posts` | CRUD, count, sticky/filter helpers | -| `wp/v2/posts//revisions` | `client.Posts.Revisions(postId)` | Post revisions only | +| `wp/v2/posts//revisions` | `client.Posts.Revisions(postId)` | Post revisions | +| `wp/v2/posts//autosaves` | `client.Posts.Autosaves(postId)` | Post autosaves | | `wp/v2/pages` | `client.Pages` | CRUD | +| `wp/v2/pages//revisions` | `client.Pages.Revisions(pageId)` | Page revisions | +| `wp/v2/pages//autosaves` | `client.Pages.Autosaves(pageId)` | Page autosaves | | `wp/v2/comments` | `client.Comments` | CRUD | | `wp/v2/categories` | `client.Categories` | CRUD | | `wp/v2/tags` | `client.Tags` | CRUD | @@ -20,17 +23,27 @@ WordPressPCL focuses on the most commonly used WordPress REST API endpoints and | `wp/v2/settings` | `client.Settings` | Read/update only | | `wp/v2/plugins` | `client.Plugins` | Install, activate, deactivate, delete | | `wp/v2/themes` | `client.Themes` | Read/query only | +| `wp/v2/search` | `client.Search` | Read/query only | +| `wp/v2/block-types` | `client.BlockTypes` | Read only | +| `wp/v2/blocks` | `client.Blocks` | CRUD (reusable blocks) | +| `wp/v2/templates` | `client.Templates` | CRUD (requires auth) | +| `wp/v2/template-parts` | `client.TemplateParts` | CRUD (requires auth) | +| `wp/v2/global-styles/` | `client.GlobalStyles` | Read/update by ID or theme | +| `wp/v2/navigation` | `client.Navigation` | CRUD | +| `wp/v2/sidebars` | `client.Sidebars` | Read/update only | +| `wp/v2/widgets` | `client.Widgets` | CRUD | +| `wp/v2/widget-types` | `client.WidgetTypes` | Read only | +| `wp/v2/url-details` | `client.UrlDetails` | Read by URL (requires auth) | ## Standard endpoints without dedicated wrappers -The official WordPress REST API reference includes additional standard endpoints that currently do not have dedicated WordPressPCL clients. The main gaps are: +The following standard WordPress REST API endpoints do not yet have dedicated WordPressPCL clients: -- `wp/v2/search` -- block editor endpoints such as `wp/v2/block-types`, `wp/v2/blocks`, `wp/v2/block-renderer`, `wp/v2/templates`, `wp/v2/template-parts` and `wp/v2/global-styles` -- navigation and site editing endpoints such as `wp/v2/navigation` and `wp/v2/navigation-fallback` -- `wp/v2/sidebars`, `wp/v2/widgets` and `wp/v2/widget-types` -- `wp/v2/url-details` -- autosaves and page revisions +- `wp/v2/block-renderer` (POST to render a block server-side) +- `wp/v2/navigation-fallback` (GET the navigation fallback menu) +- `wp/v2/global-styles//revisions` (global styles revision history) +- font families and font faces (`wp/v2/font-families`, `wp/v2/font-faces`) +- pattern directory (`wp/v2/pattern-directory/patterns`) As WordPress adds more standard endpoints, the authoritative way to see what a site exposes is its API index at `/wp-json/`. @@ -47,8 +60,8 @@ These examples use `dynamic` for brevity. If you already know the response shape Use `CustomRequest` for standard endpoints that do not yet have dedicated wrappers: ```csharp -dynamic searchResults = await client.CustomRequest.GetAsync("search?search=hello", ignoreDefaultPath: false); -dynamic pageRevisions = await client.CustomRequest.GetAsync("pages/42/revisions", useAuth: true, ignoreDefaultPath: false); +dynamic rendered = await client.CustomRequest.PostAsync("block-renderer/core%2Fparagraph", body, ignoreDefaultPath: false); +dynamic navFallback = await client.CustomRequest.GetAsync("navigation-fallback", useAuth: true, ignoreDefaultPath: false); ``` Use `CustomRequest` for plugin namespaces and custom site routes: @@ -61,4 +74,9 @@ dynamic products = await client.CustomRequest.GetAsync("wc/v3/products" - Themes are available through `client.Themes`, but they are read/query only. - Post revisions are available through `client.Posts.Revisions(postId)`. -- Pages do not currently have a dedicated revisions or autosaves helper. +- Page revisions are available through `client.Pages.Revisions(pageId)`. +- Post autosaves are available through `client.Posts.Autosaves(postId)`. +- Page autosaves are available through `client.Pages.Autosaves(pageId)`. +- Templates and template parts use compound string IDs (e.g. `"twentytwentyfour//index"`). +- Sidebars, widgets and widget types use string IDs (e.g. `"sidebar-1"`, `"block-3"`). +- `client.GlobalStyles` exposes `GetByIdAsync(id)`, `GetThemeStylesAsync(stylesheet)` and `UpdateAsync(entity)`. diff --git a/src/WordPressPCL/Client/BlockTypes.cs b/src/WordPressPCL/Client/BlockTypes.cs new file mode 100644 index 00000000..1b11c90d --- /dev/null +++ b/src/WordPressPCL/Client/BlockTypes.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Block Types endpoint WP REST API (wp/v2/block-types). +/// +public class BlockTypes +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "block-types"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public BlockTypes(HttpHelper httpHelper) + { + _httpHelper = httpHelper; + } + + /// + /// Get all registered block types. + /// + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// List of block types + public Task> GetAsync(bool embed = false, bool useAuth = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>(_methodPath, embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Get a specific block type by its namespaced name (e.g. "core/paragraph"). + /// + /// Block namespace (e.g. "core") + /// Block name (e.g. "paragraph") + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// The requested block type + public Task GetByNameAsync(string @namespace, string name, bool embed = false, bool useAuth = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"{_methodPath}/{@namespace}/{name}", embed, useAuth, cancellationToken: cancellationToken); + } +} diff --git a/src/WordPressPCL/Client/Blocks.cs b/src/WordPressPCL/Client/Blocks.cs new file mode 100644 index 00000000..3f4b8e99 --- /dev/null +++ b/src/WordPressPCL/Client/Blocks.cs @@ -0,0 +1,20 @@ +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Reusable Blocks endpoint WP REST API (wp/v2/blocks). +/// +public class Blocks : CRUDOperation +{ + private const string _methodPath = "blocks"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public Blocks(HttpHelper httpHelper) : base(httpHelper, _methodPath) + { + } +} diff --git a/src/WordPressPCL/Client/GlobalStylesClient.cs b/src/WordPressPCL/Client/GlobalStylesClient.cs new file mode 100644 index 00000000..9f5c6ed8 --- /dev/null +++ b/src/WordPressPCL/Client/GlobalStylesClient.cs @@ -0,0 +1,67 @@ +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Global Styles endpoint WP REST API (wp/v2/global-styles/{id}). +/// Global styles objects are identified by an integer ID obtained from the site's API index or theme endpoint. +/// +public class GlobalStylesClient +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "global-styles"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public GlobalStylesClient(HttpHelper httpHelper) + { + _httpHelper = httpHelper; + } + + /// + /// Get the global styles object for a given ID. + /// + /// The global styles post ID + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// The global styles object + public Task GetByIdAsync(int id, bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"{_methodPath}/{id}", embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Get the global styles for a specific theme by its stylesheet slug. + /// + /// The theme stylesheet slug (e.g. "twentytwentyfour") + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// The theme's global styles object + public Task GetThemeStylesAsync(string stylesheet, bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"{_methodPath}/themes/{stylesheet}", embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Update an existing global styles object. + /// + /// Global styles object with updated values + /// Cancellation token + /// Updated global styles object + public async Task UpdateAsync(Models.GlobalStyles entity, CancellationToken cancellationToken = default) + { + string json = JsonSerializer.Serialize(entity, _httpHelper.JsonSerializerOptions); + using StringContent postBody = new(json, Encoding.UTF8, "application/json"); + return (await _httpHelper.PostRequestAsync($"{_methodPath}/{entity.Id}", postBody, cancellationToken: cancellationToken).ConfigureAwait(false)).Item1; + } +} diff --git a/src/WordPressPCL/Client/NavigationClient.cs b/src/WordPressPCL/Client/NavigationClient.cs new file mode 100644 index 00000000..835db69e --- /dev/null +++ b/src/WordPressPCL/Client/NavigationClient.cs @@ -0,0 +1,20 @@ +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Navigation endpoint WP REST API (wp/v2/navigation). +/// +public class NavigationClient : CRUDOperation +{ + private const string _methodPath = "navigation"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public NavigationClient(HttpHelper httpHelper) : base(httpHelper, _methodPath) + { + } +} diff --git a/src/WordPressPCL/Client/PageAutosaves.cs b/src/WordPressPCL/Client/PageAutosaves.cs new file mode 100644 index 00000000..7ffc5f11 --- /dev/null +++ b/src/WordPressPCL/Client/PageAutosaves.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Page Autosaves endpoint WP REST API +/// (wp/v2/pages/{pageId}/autosaves). +/// +public class PageAutosaves +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "autosaves"; + private readonly int _pageId; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + /// ID of the parent page + public PageAutosaves(HttpHelper httpHelper, int pageId) + { + _httpHelper = httpHelper; + _pageId = pageId; + } + + /// + /// Get the available autosaves for the page. + /// + /// Include embed info + /// Cancellation token + /// List of autosaves + public Task> GetAsync(bool embed = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>($"pages/{_pageId}/{_methodPath}", embed, true, cancellationToken: cancellationToken); + } + + /// + /// Get a specific autosave by ID. + /// + /// Autosave ID + /// Include embed info + /// Cancellation token + /// The requested autosave + public Task GetByIdAsync(int id, bool embed = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"pages/{_pageId}/{_methodPath}/{id}", embed, true, cancellationToken: cancellationToken); + } + + /// + /// Create an autosave for the page. + /// + /// The page to autosave + /// Cancellation token + /// The created autosave + public async Task CreateAsync(Page page, CancellationToken cancellationToken = default) + { + string json = JsonSerializer.Serialize(page, _httpHelper.JsonSerializerOptions); + using StringContent postBody = new(json, Encoding.UTF8, "application/json"); + return (await _httpHelper.PostRequestAsync($"pages/{_pageId}/{_methodPath}", postBody, cancellationToken: cancellationToken).ConfigureAwait(false)).Item1; + } +} diff --git a/src/WordPressPCL/Client/PageRevisions.cs b/src/WordPressPCL/Client/PageRevisions.cs new file mode 100644 index 00000000..21d1d545 --- /dev/null +++ b/src/WordPressPCL/Client/PageRevisions.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Interfaces; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Page Revisions endpoint WP REST API +/// (wp/v2/pages/{pageId}/revisions). +/// +public class PageRevisions : IReadOperation, IDeleteOperation +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "revisions"; + private readonly int _pageId; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + /// ID of the parent page + public PageRevisions(HttpHelper httpHelper, int pageId) + { + _httpHelper = httpHelper; + _pageId = pageId; + } + + /// + /// Delete a revision by ID. + /// + /// Revision Id + /// Cancellation token + /// Result of operation + public Task DeleteAsync(int ID, CancellationToken cancellationToken = default) + { + return _httpHelper.DeleteRequestAsync($"pages/{_pageId}/{_methodPath}/{ID}?force=true", cancellationToken: cancellationToken); + } + + /// + /// Get the most recent revisions. + /// + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// List of page revisions + public Task> GetAsync(bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>($"pages/{_pageId}/{_methodPath}", embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Get all revisions. + /// + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// List of all page revisions + public Task> GetAllAsync(bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return GetAsync(embed, useAuth, cancellationToken); + } + + /// + /// Get a revision by ID. + /// + /// Revision ID + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// The requested page revision + public Task GetByIdAsync(object id, bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"pages/{_pageId}/{_methodPath}/{id}", embed, useAuth, cancellationToken: cancellationToken); + } +} diff --git a/src/WordPressPCL/Client/Pages.cs b/src/WordPressPCL/Client/Pages.cs index e4a0a09e..0dab07a5 100644 --- a/src/WordPressPCL/Client/Pages.cs +++ b/src/WordPressPCL/Client/Pages.cs @@ -70,5 +70,25 @@ public Task DeleteAsync(int ID, bool force = false, CancellationToken canc return HttpHelper.DeleteRequestAsync($"{_methodPath}/{ID}?force={force.ToString().ToLower(CultureInfo.InvariantCulture)}", cancellationToken: cancellationToken); } + /// + /// Get an object to interact with revisions for a specific page. + /// + /// ID of the parent Page + /// Page revisions object + public PageRevisions Revisions(int pageId) + { + return new PageRevisions(HttpHelper, pageId); + } + + /// + /// Get an object to interact with autosaves for a specific page. + /// + /// ID of the parent Page + /// Page autosaves object + public PageAutosaves Autosaves(int pageId) + { + return new PageAutosaves(HttpHelper, pageId); + } + #endregion Custom } diff --git a/src/WordPressPCL/Client/PostAutosaves.cs b/src/WordPressPCL/Client/PostAutosaves.cs new file mode 100644 index 00000000..73621503 --- /dev/null +++ b/src/WordPressPCL/Client/PostAutosaves.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Post Autosaves endpoint WP REST API +/// (wp/v2/posts/{postId}/autosaves). +/// +public class PostAutosaves +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "autosaves"; + private readonly int _postId; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + /// ID of the parent post + public PostAutosaves(HttpHelper httpHelper, int postId) + { + _httpHelper = httpHelper; + _postId = postId; + } + + /// + /// Get the available autosaves for the post. + /// + /// Include embed info + /// Cancellation token + /// List of autosaves + public Task> GetAsync(bool embed = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>($"posts/{_postId}/{_methodPath}", embed, true, cancellationToken: cancellationToken); + } + + /// + /// Get a specific autosave by ID. + /// + /// Autosave ID + /// Include embed info + /// Cancellation token + /// The requested autosave + public Task GetByIdAsync(int id, bool embed = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"posts/{_postId}/{_methodPath}/{id}", embed, true, cancellationToken: cancellationToken); + } + + /// + /// Create an autosave for the post. + /// + /// The post to autosave + /// Cancellation token + /// The created autosave + public async Task CreateAsync(Post post, CancellationToken cancellationToken = default) + { + string json = JsonSerializer.Serialize(post, _httpHelper.JsonSerializerOptions); + using StringContent postBody = new(json, Encoding.UTF8, "application/json"); + return (await _httpHelper.PostRequestAsync($"posts/{_postId}/{_methodPath}", postBody, cancellationToken: cancellationToken).ConfigureAwait(false)).Item1; + } +} diff --git a/src/WordPressPCL/Client/Posts.cs b/src/WordPressPCL/Client/Posts.cs index 219e6233..52ba0d1d 100644 --- a/src/WordPressPCL/Client/Posts.cs +++ b/src/WordPressPCL/Client/Posts.cs @@ -152,5 +152,15 @@ public PostRevisions Revisions(int postId) return new PostRevisions(HttpHelper, postId); } + /// + /// Get an object to interact with autosaves for a specific post. + /// + /// ID of parent Post + /// Post autosaves object + public PostAutosaves Autosaves(int postId) + { + return new PostAutosaves(HttpHelper, postId); + } + #endregion Custom } diff --git a/src/WordPressPCL/Client/Search.cs b/src/WordPressPCL/Client/Search.cs new file mode 100644 index 00000000..9617f3f8 --- /dev/null +++ b/src/WordPressPCL/Client/Search.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Search endpoint WP REST API (wp/v2/search). +/// +public class Search +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "search"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public Search(HttpHelper httpHelper) + { + _httpHelper = httpHelper; + } + + /// + /// Search for content matching a given term. + /// + /// Text to search for + /// Send request with authentication header + /// Cancellation token + /// List of matching search results + public Task> SearchAsync(string searchTerm, bool useAuth = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>(_methodPath.SetQueryParam("search", searchTerm), false, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Execute a parametrized search query. + /// + /// Query builder with specific parameters + /// Send request with authentication header + /// Cancellation token + /// List of matching search results + public Task> QueryAsync(SearchQueryBuilder queryBuilder, bool useAuth = false, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>($"{_methodPath}{queryBuilder.BuildQuery()}", false, useAuth, cancellationToken: cancellationToken); + } +} diff --git a/src/WordPressPCL/Client/Sidebars.cs b/src/WordPressPCL/Client/Sidebars.cs new file mode 100644 index 00000000..e9a93710 --- /dev/null +++ b/src/WordPressPCL/Client/Sidebars.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Sidebars endpoint WP REST API (wp/v2/sidebars). +/// Sidebars support read and update operations only; they cannot be created or deleted via the API. +/// +public class Sidebars +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "sidebars"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public Sidebars(HttpHelper httpHelper) + { + _httpHelper = httpHelper; + } + + /// + /// Get all registered sidebars. + /// + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// List of sidebars + public Task> GetAsync(bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>(_methodPath, embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Get a specific sidebar by its string ID (e.g. "sidebar-1"). + /// + /// Sidebar ID + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// The requested sidebar + public Task GetByIdAsync(string id, bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"{_methodPath}/{id}", embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Update an existing sidebar (e.g. to re-assign its widgets). + /// + /// Sidebar object with updated values + /// Cancellation token + /// Updated sidebar + public async Task UpdateAsync(Sidebar entity, CancellationToken cancellationToken = default) + { + string json = JsonSerializer.Serialize(entity, _httpHelper.JsonSerializerOptions); + using StringContent postBody = new(json, Encoding.UTF8, "application/json"); + return (await _httpHelper.PostRequestAsync($"{_methodPath}/{entity.Id}", postBody, cancellationToken: cancellationToken).ConfigureAwait(false)).Item1; + } +} diff --git a/src/WordPressPCL/Client/TemplateParts.cs b/src/WordPressPCL/Client/TemplateParts.cs new file mode 100644 index 00000000..304a7a6e --- /dev/null +++ b/src/WordPressPCL/Client/TemplateParts.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Block Template Parts endpoint WP REST API (wp/v2/template-parts). +/// Template parts use compound string identifiers such as twentytwentyfour//header. +/// +public class TemplateParts +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "template-parts"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public TemplateParts(HttpHelper httpHelper) + { + _httpHelper = httpHelper; + } + + /// + /// Get all template parts. + /// + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// List of template parts + public Task> GetAsync(bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>(_methodPath, embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Get a template part by its compound string ID (e.g. "twentytwentyfour//header"). + /// + /// Template part ID + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// The requested template part + public Task GetByIdAsync(string id, bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync($"{_methodPath}/{id}", embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Execute a parametrized query. + /// + /// Query builder with specific parameters + /// Send request with authentication header + /// Cancellation token + /// List of matching template parts + public Task> QueryAsync(TemplatePartsQueryBuilder queryBuilder, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>($"{_methodPath}{queryBuilder.BuildQuery()}", false, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Create a new template part. + /// + /// Template part object to create + /// Cancellation token + /// Created template part + public async Task CreateAsync(TemplatePart entity, CancellationToken cancellationToken = default) + { + string json = JsonSerializer.Serialize(entity, _httpHelper.JsonSerializerOptions); + using StringContent postBody = new(json, Encoding.UTF8, "application/json"); + return (await _httpHelper.PostRequestAsync(_methodPath, postBody, cancellationToken: cancellationToken).ConfigureAwait(false)).Item1; + } + + /// + /// Update an existing template part. + /// + /// Template part object with updated values + /// Cancellation token + /// Updated template part + public async Task UpdateAsync(TemplatePart entity, CancellationToken cancellationToken = default) + { + string json = JsonSerializer.Serialize(entity, _httpHelper.JsonSerializerOptions); + using StringContent postBody = new(json, Encoding.UTF8, "application/json"); + return (await _httpHelper.PostRequestAsync($"{_methodPath}/{entity.Id}", postBody, cancellationToken: cancellationToken).ConfigureAwait(false)).Item1; + } + + /// + /// Delete a template part by its compound string ID. + /// + /// Template part ID + /// Cancellation token + /// True if successfully deleted + public Task DeleteAsync(string id, CancellationToken cancellationToken = default) + { + return _httpHelper.DeleteRequestAsync($"{_methodPath}/{id}?force=true", cancellationToken: cancellationToken); + } +} diff --git a/src/WordPressPCL/Client/Templates.cs b/src/WordPressPCL/Client/Templates.cs new file mode 100644 index 00000000..a84d565f --- /dev/null +++ b/src/WordPressPCL/Client/Templates.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client; + +/// +/// Client class for interaction with the Block Templates endpoint WP REST API (wp/v2/templates). +/// Templates use compound string identifiers such as twentytwentyfour//index. +/// +public class Templates +{ + private readonly HttpHelper _httpHelper; + private const string _methodPath = "templates"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public Templates(HttpHelper httpHelper) + { + _httpHelper = httpHelper; + } + + /// + /// Get all templates. + /// + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// List of templates + public Task> GetAsync(bool embed = false, bool useAuth = true, CancellationToken cancellationToken = default) + { + return _httpHelper.GetRequestAsync>(_methodPath, embed, useAuth, cancellationToken: cancellationToken); + } + + /// + /// Get a template by its compound string ID (e.g. "twentytwentyfour//index"). + /// + /// Template ID + /// Include embed info + /// Send request with authentication header + /// Cancellation token + /// The requested template + public Task