From 31cba5f5490a74b348e3ed862a4bd1950e4d5505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Fri, 26 May 2023 11:42:34 +0200 Subject: [PATCH 1/8] Wordpress plugins: install, activate, deactivate, delete --- README.md | 9 + .../Plugins_Tests.cs | 61 ++++++ WordPressPCL/Client/Plugins.cs | 103 ++++++++++ WordPressPCL/Models/Enums.cs | 19 ++ WordPressPCL/Models/Plugin.cs | 109 +++++++++++ WordPressPCL/Utility/PluginsQueryBuilder.cs | 57 ++++++ WordPressPCL/WordPressClient.cs | 6 + WordPressPCL/WordPressPCL.xml | 184 ++++++++++++++++++ 8 files changed, 548 insertions(+) create mode 100644 WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs create mode 100644 WordPressPCL/Client/Plugins.cs create mode 100644 WordPressPCL/Models/Plugin.cs create mode 100644 WordPressPCL/Utility/PluginsQueryBuilder.cs diff --git a/README.md b/README.md index 14a439a8..4af697dc 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,13 @@ var comments = await client.Comments.GetAllAsync(); var commentbyid = await client.Comments.GetByIdAsync(id); var commentsbypost = await client.Comments.GetCommentsForPostAsync(postid, true, false); +// Plugins +var plugins = await client.Plugins.GetAllAsync(); +var installedplugin = await client.Plugins.InstallAsync("akismet"); +var activateplugin = await client.Plugins.ActivateAsync(installedplugin); +var deactivateplugin = await client.Plugins.DeactivateAsync(installedplugin); +var deleteplugin = await client.Plugins.DeleteAsync(installedplugin); + // Authentication var client = new WordPressClient(ApiCredentials.WordPressUri); @@ -104,6 +111,8 @@ var response = client.Posts.DeleteAsync(postId); | **Post Types** | --- | yes | --- | --- | | **Post Statuses** | --- | yes | --- | --- | | **Settings** | --- | yes | yes | --- | +| **Plugins** | yes | yes | yes | yes | + ## Additional Features diff --git a/WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs b/WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs new file mode 100644 index 00000000..fae3f09a --- /dev/null +++ b/WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs @@ -0,0 +1,61 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using WordPressPCL; +using WordPressPCL.Client; +using WordPressPCL.Models; +using WordPressPCL.Tests.Selfhosted.Utility; +using WordPressPCL.Utility; + +namespace WordPressPCL.Tests.Selfhosted; + +[TestClass] +public class Plugins_Tests +{ + private static WordPressClient _clientAuth; + private static string PluginId= "wordpress-seo"; + + [ClassInitialize] + public static async Task Init(TestContext testContext) + { + _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); + } + + [TestMethod] + public async Task Plugins_Install_Activate_Deactivate_Delete() + { + var plugin = await _clientAuth.Plugins.InstallAsync(PluginId); + Assert.IsNotNull(plugin); + Assert.AreEqual(PluginId, plugin.Id); + + + //Activate plugin + var activePlugin = await _clientAuth.Plugins.ActivateAsync(plugin); + Assert.AreEqual(activePlugin.Status, ActivationStatus.Active); + Assert.AreEqual(activePlugin.Id, plugin.Id); + + //Deactivate plugin + var deactivatedPlugin = await _clientAuth.Plugins.DeactivateAsync(plugin); + Assert.AreEqual(deactivatedPlugin.Status, ActivationStatus.Inactive); + Assert.AreEqual(deactivatedPlugin.Id, plugin.Id); + + //Delete plugin + var response = await _clientAuth.Plugins.DeleteAsync(plugin); + Assert.IsTrue(response); + var plugins = await _clientAuth.Plugins.GetAllAsync(useAuth: true); + var c = plugins.Where(x => x.Id == plugin.Id).ToList(); + Assert.AreEqual(c.Count, 0); + } + + [TestMethod] + public async Task Plugins_Get() + { + var plugins = await _clientAuth.Plugins.GetAsync (useAuth: true); + Assert.IsNotNull(plugins); + Assert.AreNotEqual(plugins.Count(), 0); + CollectionAssert.AllItemsAreUnique(plugins.Select(tag => tag.Id).ToList()); + } + +} diff --git a/WordPressPCL/Client/Plugins.cs b/WordPressPCL/Client/Plugins.cs new file mode 100644 index 00000000..3570d73c --- /dev/null +++ b/WordPressPCL/Client/Plugins.cs @@ -0,0 +1,103 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Runtime; +using System.Text; +using System.Threading.Tasks; +using WordPressPCL.Interfaces; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client +{ + /// + /// Client class for interaction with Plugins endpoint WP REST API + /// + public class Plugins : CRUDOperation + { + #region Init + + private const string _methodPath = "plugins"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public Plugins(HttpHelper HttpHelper) : base(HttpHelper, _methodPath) + { + } + + #endregion Init + + #region Custom + + /// + /// Installs a plugin based on the Plugin + /// + /// + /// + public async Task InstallAsync(Plugin Plugin) + { + + using var postBody = new StringContent(JsonConvert.SerializeObject(new { slug = Plugin.Id }), Encoding.UTF8, "application/json"); + var (plugin, _) = await _httpHelper.PostRequestAsync("plugins", postBody).ConfigureAwait(false); + return plugin; + + } + + /// + /// Installs a plugin based on the Id + /// + /// + /// + public async Task InstallAsync(string Id) + { + + using var postBody = new StringContent(JsonConvert.SerializeObject(new { slug=Id }), Encoding.UTF8, "application/json"); + var (plugin, _) = await _httpHelper.PostRequestAsync("plugins", postBody).ConfigureAwait(false); + return plugin; + } + + /// + /// Activates an existing plugin + /// + /// + /// + public async Task ActivateAsync(Plugin Plugin) + { + using var postBody = new StringContent(JsonConvert.SerializeObject(new { status = "active" }), Encoding.UTF8, "application/json"); + var (plugin, _) = await _httpHelper.PostRequestAsync($"plugins/{Plugin.PluginFile}", postBody).ConfigureAwait(false); + return plugin; + } + + /// + /// Deactivates an existing plugin + /// + /// + /// + public async Task DeactivateAsync(Plugin Plugin) + { + + using var postBody = new StringContent(JsonConvert.SerializeObject(new { status = "inactive" }), Encoding.UTF8, "application/json"); + var (plugin, _) = await _httpHelper.PostRequestAsync($"plugins/{Plugin.PluginFile}", postBody).ConfigureAwait(false); + return plugin; + } + + + /// + /// Deletes an existing plugin + /// + /// + /// + public Task DeleteAsync(Plugin Plugin) + { +#pragma warning disable CA1507 // Use nameof to express symbol names + return HttpHelper.DeleteRequestAsync($"{_methodPath}/{Plugin.PluginFile}"); +#pragma warning restore CA1507 // Use nameof to express symbol names + } + + #endregion Custom + } +} \ No newline at end of file diff --git a/WordPressPCL/Models/Enums.cs b/WordPressPCL/Models/Enums.cs index 7094cfda..c31c0b32 100644 --- a/WordPressPCL/Models/Enums.cs +++ b/WordPressPCL/Models/Enums.cs @@ -66,6 +66,25 @@ public enum OpenStatus Closed, } + /// + /// Status of Plugin + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum ActivationStatus + { + /// + /// Status is active + /// + [EnumMember(Value = "active")] + Active, + + /// + /// Status is inactive + /// + [EnumMember(Value = "inactive")] + Inactive, + } + /// /// Status of Comments /// diff --git a/WordPressPCL/Models/Plugin.cs b/WordPressPCL/Models/Plugin.cs new file mode 100644 index 00000000..4c8bc171 --- /dev/null +++ b/WordPressPCL/Models/Plugin.cs @@ -0,0 +1,109 @@ +using Newtonsoft.Json; + +namespace WordPressPCL.Models +{ + /// + /// WordPress main settings + /// + public class Plugin + { + /// + /// The plugin file. unique identifier. + /// + [JsonProperty("plugin")] + public string PluginFile + { + get + { + return _PluginFile; + } + set + { + _PluginFile = value; + if (value.Contains("/")) + Id = value.Substring(0, value.IndexOf("/")); + else + Id = value; + } + } + private string _PluginFile; + + /// + /// First part of the pluginfile: wordpress-seo/wp-seo => wordpress-seo + /// Id is needed to install, PluginFile is needed to activate, deactivate, delete + /// + public string Id { get ; set; } + + /// + /// The plugin activation status. + /// + [JsonProperty("status")] + public ActivationStatus Status { get; set; } + + /// + /// The plugin name. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// The plugin's website address. + /// + [JsonProperty("plugin_uri")] + public string PluginUri { get; set; } + + /// + /// The plugin author. + /// + [JsonProperty("author")] + public string Author { get; set; } + + /// + /// Plugin author's website address. + /// + [JsonProperty("author_uri")] + public string AuthorUri { get; set; } + + /// + /// The plugin description. + /// + [JsonProperty("description")] + public Description Description { get; set; } + + /// + /// The plugin version number. + /// + [JsonProperty("version")] + public string Version { get; set; } + + /// + /// Whether the plugin can only be activated network-wide. + /// + [JsonProperty("network_only")] + public bool NetworkOnly { get; set; } + + /// + /// Minimum required version of WordPress. + /// + [JsonProperty("requires_wp")] + public string RequiresWordPress { get; set; } + + /// + /// Minimum required version of PHP. + /// + [JsonProperty("requires_php")] + public string RequiresPHP { get; set; } + + /// + /// The plugin's text domain. + /// + [JsonProperty("textdomain")] + public string TextDomain { get; set; } + + /// + /// The plugin's links. + /// + [JsonProperty("_links")] + public Links Links { get; set; } + } +} \ No newline at end of file diff --git a/WordPressPCL/Utility/PluginsQueryBuilder.cs b/WordPressPCL/Utility/PluginsQueryBuilder.cs new file mode 100644 index 00000000..037bb944 --- /dev/null +++ b/WordPressPCL/Utility/PluginsQueryBuilder.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using WordPressPCL.Models; + +namespace WordPressPCL.Utility +{ + /// + /// Plugins Query Builder class to construct queries with valid parameters + /// + public class PluginsQueryBuilder : QueryBuilder + { + /// + /// Current page of the collection. + /// + /// Default: 1 + [QueryText("page")] + public int Page { get; set; } + + /// + /// Maximum number of items to be returned in result set (1-100). + /// + /// Default: 10 + [QueryText("per_page")] + public int PerPage { get; set; } + + /// + /// Limit results to those matching a string. + /// + [QueryText("search")] + public string Search { get; set; } + + /// + /// Ensure result set excludes specific IDs. + /// + [QueryText("exclude")] + public List Exclude { get; set; } + + /// + /// Limit result set to specific IDs. + /// + [QueryText("include")] + public List Include { get; set; } + + /// + /// Offset the result set by a specific number of items. + /// + [QueryText("offset")] + public int Offset { get; set; } + + + /// + /// Limit result set to users with a specific slug. + /// + [QueryText("slug")] + public List Slugs { get; set; } + + } +} \ No newline at end of file diff --git a/WordPressPCL/WordPressClient.cs b/WordPressPCL/WordPressClient.cs index 489d37be..7b076c86 100644 --- a/WordPressPCL/WordPressClient.cs +++ b/WordPressPCL/WordPressClient.cs @@ -105,6 +105,11 @@ public JsonSerializerSettings JsonSerializerSettings /// public Settings Settings { get; private set; } + /// + /// Plugins client interaction object + /// + public Plugins Plugins { get; private set; } + /// /// The WordPressClient holds all connection infos and provides methods to call WordPress APIs. /// @@ -159,6 +164,7 @@ private void SetupSubClients(HttpHelper httpHelper) { PostStatuses = new PostStatuses(httpHelper); CustomRequest = new CustomRequest(httpHelper); Settings = new Settings(httpHelper); + Plugins = new Plugins(httpHelper); } } diff --git a/WordPressPCL/WordPressPCL.xml b/WordPressPCL/WordPressPCL.xml index 5d32a19c..dc2b3a7b 100644 --- a/WordPressPCL/WordPressPCL.xml +++ b/WordPressPCL/WordPressPCL.xml @@ -383,6 +383,52 @@ force deletion Result of operation + + + Client class for interaction with Plugins endpoint WP REST API + + + + + Constructor + + reference to HttpHelper class for interaction with HTTP + + + + Installs a plugin based on the Plugin + + + + + + + Installs a plugin based on the Id + + + + + + + Activates an existing plugin + + + + + + + Deactivates an existing plugin + + + + + + + Deletes an existing plugin + + + + Client class for interaction with Post revisions endpoint WP REST API @@ -1221,6 +1267,21 @@ Status is closed + + + Status of Plugin + + + + + Status is active + + + + + Status is inactive + + Status of Comments @@ -2304,6 +2365,82 @@ Parameterless constructor + + + WordPress main settings + + + + + The plugin file. unique identifier. + + + + + First part of the pluginfile: wordpress-seo/wp-seo => wordpress-seo + Id is needed to install, PluginFile is needed to activate, deactivate, delete + + + + + The plugin activation status. + + + + + The plugin name. + + + + + The plugin's website address. + + + + + The plugin author. + + + + + Plugin author's website address. + + + + + The plugin description. + + + + + The plugin version number. + + + + + Whether the plugin can only be activated network-wide. + + + + + Minimum required version of WordPress. + + + + + Minimum required version of PHP. + + + + + The plugin's text domain. + + + + + The plugin's links. + + Type represents Entity Post of WP REST API @@ -3827,6 +3964,48 @@ Use WP Query arguments to modify the response; private query vars require appropriate authorization. + + + Plugins Query Builder class to construct queries with valid parameters + + + + + Current page of the collection. + + Default: 1 + + + + Maximum number of items to be returned in result set (1-100). + + Default: 10 + + + + Limit results to those matching a string. + + + + + Ensure result set excludes specific IDs. + + + + + Limit result set to specific IDs. + + + + + Offset the result set by a specific number of items. + + + + + Limit result set to users with a specific slug. + + Post Query Builder class to construct queries with valid parameters @@ -4236,6 +4415,11 @@ Settings client interaction object + + + Plugins client interaction object + + The WordPressClient holds all connection infos and provides methods to call WordPress APIs. From 56b1e73fb864f44ee39b6c2159057f0683ec87d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Fri, 26 May 2023 11:48:00 +0200 Subject: [PATCH 2/8] Manage Wordpress plugins --- WordPressPCL/Client/Plugins.cs | 2 ++ WordPressPCL/Models/Plugin.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/WordPressPCL/Client/Plugins.cs b/WordPressPCL/Client/Plugins.cs index 3570d73c..d4cc51e5 100644 --- a/WordPressPCL/Client/Plugins.cs +++ b/WordPressPCL/Client/Plugins.cs @@ -14,6 +14,8 @@ namespace WordPressPCL.Client { /// /// Client class for interaction with Plugins endpoint WP REST API + /// Date: 26 May 2023 + /// Creator: Gregory Liénard /// public class Plugins : CRUDOperation { diff --git a/WordPressPCL/Models/Plugin.cs b/WordPressPCL/Models/Plugin.cs index 4c8bc171..7bb277c8 100644 --- a/WordPressPCL/Models/Plugin.cs +++ b/WordPressPCL/Models/Plugin.cs @@ -3,7 +3,9 @@ namespace WordPressPCL.Models { /// - /// WordPress main settings + /// WordPress Plugins + /// Date: 26 May 2023 + /// Creator: Gregory Liénard /// public class Plugin { From 2e970f081b5b5998d1a37633d95d4d527613ad09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Fri, 26 May 2023 11:55:23 +0200 Subject: [PATCH 3/8] plugins update: use authentication --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4af697dc..8398021d 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ var commentbyid = await client.Comments.GetByIdAsync(id); var commentsbypost = await client.Comments.GetCommentsForPostAsync(postid, true, false); // Plugins -var plugins = await client.Plugins.GetAllAsync(); +var plugins = await client.Plugins.GetAllAsync(useAuth:true); var installedplugin = await client.Plugins.InstallAsync("akismet"); var activateplugin = await client.Plugins.ActivateAsync(installedplugin); var deactivateplugin = await client.Plugins.DeactivateAsync(installedplugin); From aff20c2984206ed00da61e91a1612cfd54158893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Thu, 8 Jun 2023 20:49:52 +0200 Subject: [PATCH 4/8] Include Themes, finetune plugins queries --- .../Plugins_Tests.cs | 27 ++ WordPressPCL.Tests.Selfhosted/Themes_Tests.cs | 49 ++++ WordPressPCL/Client/Plugins.cs | 26 ++ WordPressPCL/Client/Themes.cs | 57 +++++ WordPressPCL/Models/Subclasses.cs | 158 ++++++++++++ WordPressPCL/Models/Theme.cs | 106 ++++++++ WordPressPCL/Utility/PluginsQueryBuilder.cs | 38 +-- WordPressPCL/Utility/ThemesQueryBuilder.cs | 19 ++ WordPressPCL/WordPressClient.cs | 6 + WordPressPCL/WordPressPCL.xml | 234 +++++++++++++++--- 10 files changed, 655 insertions(+), 65 deletions(-) create mode 100644 WordPressPCL.Tests.Selfhosted/Themes_Tests.cs create mode 100644 WordPressPCL/Client/Themes.cs create mode 100644 WordPressPCL/Models/Theme.cs create mode 100644 WordPressPCL/Utility/ThemesQueryBuilder.cs diff --git a/WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs b/WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs index fae3f09a..fe95bc72 100644 --- a/WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs +++ b/WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs @@ -36,6 +36,7 @@ public async Task Plugins_Install_Activate_Deactivate_Delete() Assert.AreEqual(activePlugin.Status, ActivationStatus.Active); Assert.AreEqual(activePlugin.Id, plugin.Id); + //Deactivate plugin var deactivatedPlugin = await _clientAuth.Plugins.DeactivateAsync(plugin); Assert.AreEqual(deactivatedPlugin.Status, ActivationStatus.Inactive); @@ -49,6 +50,25 @@ public async Task Plugins_Install_Activate_Deactivate_Delete() Assert.AreEqual(c.Count, 0); } + [TestMethod] + public async Task Plugins_GetActive() + { + //Active plugin + var plugins = await _clientAuth.Plugins.QueryAsync(new PluginsQueryBuilder { Status = ActivationStatus.Active }, useAuth:true); + Assert.IsNotNull(plugins); + Assert.AreNotEqual(plugins.Count(), 0); + + } + [TestMethod] + public async Task Plugins_Search () + { + //Active plugin + var plugins = await _clientAuth.Plugins.QueryAsync(new PluginsQueryBuilder { Search="jwt" }, useAuth:true); + Assert.IsNotNull(plugins); + Assert.AreNotEqual(plugins.Count(), 0); + + } + [TestMethod] public async Task Plugins_Get() { @@ -58,4 +78,11 @@ public async Task Plugins_Get() CollectionAssert.AllItemsAreUnique(plugins.Select(tag => tag.Id).ToList()); } + [TestMethod] + public async Task Plugins_GetByID() + { + var plugin = await _clientAuth.Plugins.GetByIDAsync("jwt-auth/jwt-auth", useAuth: true); + Assert.IsNotNull(plugin); + } + } diff --git a/WordPressPCL.Tests.Selfhosted/Themes_Tests.cs b/WordPressPCL.Tests.Selfhosted/Themes_Tests.cs new file mode 100644 index 00000000..f5abe875 --- /dev/null +++ b/WordPressPCL.Tests.Selfhosted/Themes_Tests.cs @@ -0,0 +1,49 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using WordPressPCL; +using WordPressPCL.Client; +using WordPressPCL.Models; +using WordPressPCL.Tests.Selfhosted.Utility; +using WordPressPCL.Utility; + +namespace WordPressPCL.Tests.Selfhosted; + +[TestClass] +public class Themes_Tests +{ + private static WordPressClient _clientAuth; + + [ClassInitialize] + public static async Task Init(TestContext testContext) + { + _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); + } + + [TestMethod] + public async Task Themes_GetActive() + { + var themes = await _clientAuth.Themes.QueryAsync(new ThemesQueryBuilder { Status = ActivationStatus.Active }, useAuth:true); + Assert.IsNotNull(themes); + Assert.AreNotEqual(themes.Count(), 0); + + } + [TestMethod] + public async Task Themes_Get() + { + var themes = await _clientAuth.Themes.GetAsync (useAuth: true); + Assert.IsNotNull(themes); + Assert.AreNotEqual(themes.Count(), 0); + CollectionAssert.AllItemsAreUnique(themes.Select(tag => tag.Stylesheet).ToList()); + } + + [TestMethod] + public async Task Themes_GetByID() + { + var theme = await _clientAuth.Themes.GetByIDAsync("twentytwentythree", useAuth: true); + Assert.IsNotNull(theme); + } + +} diff --git a/WordPressPCL/Client/Plugins.cs b/WordPressPCL/Client/Plugins.cs index d4cc51e5..dbe97829 100644 --- a/WordPressPCL/Client/Plugins.cs +++ b/WordPressPCL/Client/Plugins.cs @@ -87,6 +87,32 @@ public async Task DeactivateAsync(Plugin Plugin) return plugin; } + /// + /// Get plugins by search term + /// + /// Search term + /// include embed info + /// List of posts + public Task> GetPluginsBySearchAsync(string searchTerm, bool embed = false) + { + // default values + // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date + return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("search", searchTerm), embed,true); + } + + /// + /// Get plugins by search term + /// + /// active or inactive + /// include embed info + /// List of posts + public Task> GetPluginsByActivationStatusAsync(ActivationStatus activationStatus, bool embed = false) + { + // default values + // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date + return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("status", activationStatus.ToString()), embed, true); + } + /// /// Deletes an existing plugin diff --git a/WordPressPCL/Client/Themes.cs b/WordPressPCL/Client/Themes.cs new file mode 100644 index 00000000..09308dc8 --- /dev/null +++ b/WordPressPCL/Client/Themes.cs @@ -0,0 +1,57 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Runtime; +using System.Text; +using System.Threading.Tasks; +using WordPressPCL.Interfaces; +using WordPressPCL.Models; +using WordPressPCL.Utility; + +namespace WordPressPCL.Client +{ + /// + /// Client class for interaction with Themes endpoint WP REST API + /// Date: 26 May 2023 + /// Creator: Gregory Liénard + /// + public class Themes : CRUDOperation + { + #region Init + + private const string _methodPath = "themes"; + + /// + /// Constructor + /// + /// reference to HttpHelper class for interaction with HTTP + public Themes(HttpHelper HttpHelper) : base(HttpHelper, _methodPath) + { + } + + #endregion Init + + #region Custom + + + + /// + /// Get themes by search term + /// + /// active or inactive + /// include embed info + /// List of posts + public Task> GetThemesByActivationStatusAsync(ActivationStatus activationStatus, bool embed = false) + { + // default values + // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date + return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("status", activationStatus.ToString()), embed, true); + } + + + + #endregion Custom + } +} \ No newline at end of file diff --git a/WordPressPCL/Models/Subclasses.cs b/WordPressPCL/Models/Subclasses.cs index 2c1069df..c32430b6 100644 --- a/WordPressPCL/Models/Subclasses.cs +++ b/WordPressPCL/Models/Subclasses.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using System.Collections.Generic; namespace WordPressPCL.Models { @@ -20,6 +21,24 @@ public abstract class RenderedRawBase public string Raw { get; set; } } + /// + /// Adds a "Rendered" and a "Raw" property to all classes derived from this one + /// + public abstract class RenderedArrayRawBase + { + /// + /// Rendered text + /// + [JsonProperty("rendered")] + public string Rendered { get; set; } + + /// + /// Raw HTML text + /// + [JsonProperty("raw")] + public string [] Raw { get; set; } + } + /// /// Adds an Href property to all classes derived from this one /// @@ -119,6 +138,145 @@ public class Guid : RenderedRawBase { } + /// + /// The title for the object. + /// + /// Context: view, edit, embed + public class Tags : RenderedArrayRawBase + { + /// + /// Constructor + /// + public Tags() + { + } + + /// + /// Constructor with same Raw and rendered author + /// + /// Text for author + public Tags(string [] Tag) : this(Tag, string.Join(",",Tag)) + { + } + + /// + /// Constructor with Raw and rendered text + /// + /// Raw HTML text for author + /// Rendered text for author + public Tags(string[] TagsRaw, string TagsRendered) + { + Raw = TagsRaw; + Rendered = TagsRendered; + } + } + + public class ThemeSupports + { + [JsonProperty("align-wide")] + public bool AlignWide { get; set; } + + [JsonProperty("automatic-feed-links")] + public bool AutomaticFeedLinks { get; set; } + + [JsonProperty("block-templates")] + public bool BlockTemplates { get; set; } + + [JsonProperty("block-template-parts")] + public bool BlockTemplateParts { get; set; } + + [JsonProperty("custom-background")] + public bool CustomBackground { get; set; } + + [JsonProperty("custom-header")] + public bool CustomHeader { get; set; } + + [JsonProperty("custom-logo")] + public bool CustomLogo { get; set; } + + [JsonProperty("customize-selective-refresh-widgets")] + public bool CustomizeSelectiveRefreshWidgets { get; set; } + + [JsonProperty("dark-editor-style")] + public bool DarkEditorStyle { get; set; } + + [JsonProperty("disable-custom-colors")] + public bool DisableCustomColors { get; set; } + + [JsonProperty("disable-custom-font-sizes")] + public bool DisableCustomFontSizes { get; set; } + + [JsonProperty("disable-custom-gradients")] + public bool DisableCustomGradients { get; set; } + + [JsonProperty("disable-layout-styles")] + public bool DisableLayoutStyles { get; set; } + + [JsonProperty("editor-color-palette")] + public bool EditorColorPalette { get; set; } + + [JsonProperty("editor-font-sizes")] + public bool EditorFontSizes { get; set; } + + [JsonProperty("editor-gradient-presets")] + public bool EditorGradientPresets { get; set; } + + [JsonProperty("editor-styles")] + public bool EditorStyles { get; set; } + + [JsonProperty("html5")] + public List Html5 { get; set; } + + [JsonProperty("formats")] + public List Formats { get; set; } + + [JsonProperty("post-thumbnails")] + public bool PostThumbnails { get; set; } + + [JsonProperty("responsive-embeds")] + public bool ResponsiveEmbeds { get; set; } + + [JsonProperty("title-tag")] + public bool TitleTag { get; set; } + + [JsonProperty("wp-block-styles")] + public bool WpBlockStyles { get; set; } + } + + + /// + /// The tags for the object. + /// + /// Context: view, edit, embed + public class Rendered : RenderedRawBase + { + /// + /// Constructor + /// + public Rendered() + { + } + + /// + /// Constructor with same Raw and rendered author + /// + /// Text for author + public Rendered(string Author) : this(Author, Author) + { + } + + /// + /// Constructor with Raw and rendered text + /// + /// Raw HTML text for author + /// Rendered text for author + public Rendered(string AuthorRaw, string AuthorRendered) + { + Raw = AuthorRaw; + Rendered = AuthorRendered; + } + } + /// /// The title for the object. /// diff --git a/WordPressPCL/Models/Theme.cs b/WordPressPCL/Models/Theme.cs new file mode 100644 index 00000000..4304f417 --- /dev/null +++ b/WordPressPCL/Models/Theme.cs @@ -0,0 +1,106 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace WordPressPCL.Models +{ + /// + /// WordPress Themes + /// Date: 8 June 2023 + /// Creator: Gregory Liénard + /// + public class Theme + { + /// + /// The theme's stylesheet. This uniquely identifies the theme. + /// + [JsonProperty("stylesheet")] + public string Stylesheet { get; set; } + + /// + /// The theme's template. If this is a child theme, this refers to the parent theme. + /// + [JsonProperty("template")] + public string Template { get; set; } + + /// + /// The theme author. + /// + [JsonProperty("author")] + public Rendered Author { get; set; } + + /// + /// The website of the theme author. + /// + [JsonProperty("author_uri")] + public Rendered AuthorUri { get; set; } + + /// + /// A description of the theme. + /// + [JsonProperty("description")] + public Description Description { get; set; } + + /// + /// The name of the theme. + /// + [JsonProperty("name")] + public Rendered Name { get; set; } + + /// + /// The minimum PHP version required for the theme to work. + /// + [JsonProperty("requires_php")] + public string RequiresPhp { get; set; } + + /// + /// The minimum WordPress version required for the theme to work. + /// + [JsonProperty("requires_wp")] + public string RequiresWp { get; set; } + + /// + /// The theme's screenshot URL. + /// + [JsonProperty("screenshot")] + public Uri Screenshot { get; set; } + + /// + /// Tags indicating styles and features of the theme. + /// + [JsonProperty("tags")] + public Tags Tags { get; set; } + + /// + /// The theme's text domain. + /// + [JsonProperty("textdomain")] + public string Textdomain { get; set; } + + /// + /// Features supported by this theme. + /// + [JsonProperty("theme_supports")] + public ThemeSupports ThemeSupports { get; set; } + + /// + /// The URI of the theme's webpage. + /// + [JsonProperty("theme_uri")] + public Rendered ThemeUri { get; set; } + + /// + /// The theme's current version. + /// + [JsonProperty("version")] + public Version Version { get; set; } + + /// + /// A named status for the theme. + /// + [JsonProperty("status")] + public ActivationStatus Status { get; set; } + } + +} diff --git a/WordPressPCL/Utility/PluginsQueryBuilder.cs b/WordPressPCL/Utility/PluginsQueryBuilder.cs index 037bb944..7005c82a 100644 --- a/WordPressPCL/Utility/PluginsQueryBuilder.cs +++ b/WordPressPCL/Utility/PluginsQueryBuilder.cs @@ -8,20 +8,6 @@ namespace WordPressPCL.Utility /// public class PluginsQueryBuilder : QueryBuilder { - /// - /// Current page of the collection. - /// - /// Default: 1 - [QueryText("page")] - public int Page { get; set; } - - /// - /// Maximum number of items to be returned in result set (1-100). - /// - /// Default: 10 - [QueryText("per_page")] - public int PerPage { get; set; } - /// /// Limit results to those matching a string. /// @@ -29,29 +15,11 @@ public class PluginsQueryBuilder : QueryBuilder public string Search { get; set; } /// - /// Ensure result set excludes specific IDs. + /// Limit results to specific status /// - [QueryText("exclude")] - public List Exclude { get; set; } + [QueryText("status")] + public ActivationStatus Status { get; set; } - /// - /// Limit result set to specific IDs. - /// - [QueryText("include")] - public List Include { get; set; } - - /// - /// Offset the result set by a specific number of items. - /// - [QueryText("offset")] - public int Offset { get; set; } - - - /// - /// Limit result set to users with a specific slug. - /// - [QueryText("slug")] - public List Slugs { get; set; } } } \ No newline at end of file diff --git a/WordPressPCL/Utility/ThemesQueryBuilder.cs b/WordPressPCL/Utility/ThemesQueryBuilder.cs new file mode 100644 index 00000000..df920eb5 --- /dev/null +++ b/WordPressPCL/Utility/ThemesQueryBuilder.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using WordPressPCL.Models; + +namespace WordPressPCL.Utility +{ + /// + /// Themes Query Builder class to construct queries with valid parameters + /// + public class ThemesQueryBuilder : QueryBuilder + { + /// + /// Limit results to specific status + /// + [QueryText("status")] + public ActivationStatus Status { get; set; } + + + } +} \ No newline at end of file diff --git a/WordPressPCL/WordPressClient.cs b/WordPressPCL/WordPressClient.cs index 7b076c86..970ddc1d 100644 --- a/WordPressPCL/WordPressClient.cs +++ b/WordPressPCL/WordPressClient.cs @@ -110,6 +110,11 @@ public JsonSerializerSettings JsonSerializerSettings /// public Plugins Plugins { get; private set; } + /// + /// Plugins client interaction object + /// + public Themes Themes { get; private set; } + /// /// The WordPressClient holds all connection infos and provides methods to call WordPress APIs. /// @@ -165,6 +170,7 @@ private void SetupSubClients(HttpHelper httpHelper) { CustomRequest = new CustomRequest(httpHelper); Settings = new Settings(httpHelper); Plugins = new Plugins(httpHelper); + Themes = new Themes(httpHelper); } } diff --git a/WordPressPCL/WordPressPCL.xml b/WordPressPCL/WordPressPCL.xml index dc2b3a7b..2003b9c7 100644 --- a/WordPressPCL/WordPressPCL.xml +++ b/WordPressPCL/WordPressPCL.xml @@ -386,6 +386,8 @@ Client class for interaction with Plugins endpoint WP REST API + Date: 26 May 2023 + Creator: Gregory Liénard @@ -422,6 +424,22 @@ + + + Get plugins by search term + + Search term + include embed info + List of posts + + + + Get plugins by search term + + active or inactive + include embed info + List of posts + Deletes an existing plugin @@ -700,6 +718,27 @@ Send request with authentication header List of filtered result + + + Client class for interaction with Themes endpoint WP REST API + Date: 26 May 2023 + Creator: Gregory Liénard + + + + + Constructor + + reference to HttpHelper class for interaction with HTTP + + + + Get themes by search term + + active or inactive + include embed info + List of posts + Client class for interaction with Users endpoint WP REST API @@ -2367,7 +2406,9 @@ - WordPress main settings + WordPress Plugins + Date: 26 May 2023 + Creator: Gregory Liénard @@ -2957,6 +2998,21 @@ Raw HTML text + + + Adds a "Rendered" and a "Raw" property to all classes derived from this one + + + + + Rendered text + + + + + Raw HTML text + + Adds an Href property to all classes derived from this one @@ -3032,6 +3088,54 @@ Context: view, edit + + + The title for the object. + + Context: view, edit, embed + + + + Constructor + + + + + Constructor with same Raw and rendered author + + Text for author + + + + Constructor with Raw and rendered text + + Raw HTML text for author + Rendered text for author + + + + The tags for the object. + + Context: view, edit, embed + + + + Constructor + + + + + Constructor with same Raw and rendered author + + Text for author + + + + Constructor with Raw and rendered text + + Raw HTML text for author + Rendered text for author + The title for the object. @@ -3360,6 +3464,88 @@ parameterless constructor + + + WordPress Themes + Date: 8 June 2023 + Creator: Gregory Liénard + + + + + The theme's stylesheet. This uniquely identifies the theme. + + + + + The theme's template. If this is a child theme, this refers to the parent theme. + + + + + The theme author. + + + + + The website of the theme author. + + + + + A description of the theme. + + + + + The name of the theme. + + + + + The minimum PHP version required for the theme to work. + + + + + The minimum WordPress version required for the theme to work. + + + + + The theme's screenshot URL. + + + + + Tags indicating styles and features of the theme. + + + + + The theme's text domain. + + + + + Features supported by this theme. + + + + + The URI of the theme's webpage. + + + + + The theme's current version. + + + + + A named status for the theme. + + Type represents Entity User of WP REST API @@ -3969,41 +4155,14 @@ Plugins Query Builder class to construct queries with valid parameters - - - Current page of the collection. - - Default: 1 - - - - Maximum number of items to be returned in result set (1-100). - - Default: 10 - Limit results to those matching a string. - + - Ensure result set excludes specific IDs. - - - - - Limit result set to specific IDs. - - - - - Offset the result set by a specific number of items. - - - - - Limit result set to users with a specific slug. + Limit results to specific status @@ -4227,6 +4386,16 @@ Limit results to taxonomies associated with a specific post type. + + + Themes Query Builder class to construct queries with valid parameters + + + + + Limit results to specific status + + Helper class to create threaded comment views @@ -4420,6 +4589,11 @@ Plugins client interaction object + + + Plugins client interaction object + + The WordPressClient holds all connection infos and provides methods to call WordPress APIs. From d8f2921d38c72232fc3d8686a42fef8543cd36af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Thu, 8 Jun 2023 21:13:12 +0200 Subject: [PATCH 5/8] Remove ThemeSupports --- WordPressPCL/Models/Theme.cs | 5 ----- WordPressPCL/WordPressPCL.xml | 5 ----- 2 files changed, 10 deletions(-) diff --git a/WordPressPCL/Models/Theme.cs b/WordPressPCL/Models/Theme.cs index 4304f417..3018b12b 100644 --- a/WordPressPCL/Models/Theme.cs +++ b/WordPressPCL/Models/Theme.cs @@ -78,11 +78,6 @@ public class Theme [JsonProperty("textdomain")] public string Textdomain { get; set; } - /// - /// Features supported by this theme. - /// - [JsonProperty("theme_supports")] - public ThemeSupports ThemeSupports { get; set; } /// /// The URI of the theme's webpage. diff --git a/WordPressPCL/WordPressPCL.xml b/WordPressPCL/WordPressPCL.xml index 2003b9c7..5f62022a 100644 --- a/WordPressPCL/WordPressPCL.xml +++ b/WordPressPCL/WordPressPCL.xml @@ -3526,11 +3526,6 @@ The theme's text domain. - - - Features supported by this theme. - - The URI of the theme's webpage. From 7f08b9cedbd8e1ac976c4e40ccf801208b83e61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Thu, 8 Jun 2023 22:51:39 +0200 Subject: [PATCH 6/8] activation status bug --- WordPressPCL/Client/Plugins.cs | 2 +- WordPressPCL/Client/Themes.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WordPressPCL/Client/Plugins.cs b/WordPressPCL/Client/Plugins.cs index dbe97829..e2f840c9 100644 --- a/WordPressPCL/Client/Plugins.cs +++ b/WordPressPCL/Client/Plugins.cs @@ -110,7 +110,7 @@ public Task> GetPluginsByActivationStatusAsync(ActivationSta { // default values // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date - return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("status", activationStatus.ToString()), embed, true); + return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("status", activationStatus.ToString().ToLower()), embed, true); } diff --git a/WordPressPCL/Client/Themes.cs b/WordPressPCL/Client/Themes.cs index 09308dc8..5ab11966 100644 --- a/WordPressPCL/Client/Themes.cs +++ b/WordPressPCL/Client/Themes.cs @@ -47,7 +47,7 @@ public Task> GetThemesByActivationStatusAsync(ActivationStatu { // default values // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date - return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("status", activationStatus.ToString()), embed, true); + return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("status", activationStatus.ToString().ToLower()), embed, true); } From 7a9e27368da38d67240c888b132a8e6b9212c661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Tue, 14 Nov 2023 14:14:03 +0100 Subject: [PATCH 7/8] add site logo and icon to site settings --- WordPressPCL/Models/Settings.cs | 13 +++++++++++++ WordPressPCL/WordPressPCL.xml | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/WordPressPCL/Models/Settings.cs b/WordPressPCL/Models/Settings.cs index 10c0f4bc..94aa5775 100644 --- a/WordPressPCL/Models/Settings.cs +++ b/WordPressPCL/Models/Settings.cs @@ -96,5 +96,18 @@ public class Settings /// [JsonProperty("default_comment_status")] public OpenStatus? DefaultCommentStatus { get; set; } + + + /// + /// Media ID of the site logo + /// + [JsonProperty("site_logo")] + public int? SiteLogo { get; set; } + + /// + /// Media ID of the site icon + /// + [JsonProperty("site_icon")] + public int? SiteIcon { get; set; } } } \ No newline at end of file diff --git a/WordPressPCL/WordPressPCL.xml b/WordPressPCL/WordPressPCL.xml index 5f62022a..8d683aae 100644 --- a/WordPressPCL/WordPressPCL.xml +++ b/WordPressPCL/WordPressPCL.xml @@ -2983,6 +2983,16 @@ Default Comment Status + + + Media ID of the site logo + + + + + Media ID of the site icon + + Adds a "Rendered" and a "Raw" property to all classes derived from this one From 76d74b5afa0615abde2c48561b3106ac505caf2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregory=20Li=C3=A9nard?= Date: Thu, 16 Nov 2023 18:57:39 +0100 Subject: [PATCH 8/8] Cannot use null value when saving Site Icon or Site Logo --- WordPressPCL/Models/Settings.cs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/WordPressPCL/Models/Settings.cs b/WordPressPCL/Models/Settings.cs index 94aa5775..8d7fbb0f 100644 --- a/WordPressPCL/Models/Settings.cs +++ b/WordPressPCL/Models/Settings.cs @@ -98,16 +98,37 @@ public class Settings public OpenStatus? DefaultCommentStatus { get; set; } + private int? _SiteLogo; /// /// Media ID of the site logo /// [JsonProperty("site_logo")] - public int? SiteLogo { get; set; } - + public int? SiteLogo + { + get + { + return _SiteLogo ?? 0; + } + set + { + _SiteLogo = value ?? 0; + } + } + + private int? _SiteIcon; /// /// Media ID of the site icon /// [JsonProperty("site_icon")] - public int? SiteIcon { get; set; } + public int? SiteIcon { + get + { + return _SiteIcon ?? 0; + } + set + { + _SiteIcon = value ?? 0; + } + } } } \ No newline at end of file