├── dev ├── dockerfile ├── .env ├── install.md ├── .htaccess └── docker-compose.yml ├── WordPressPCL ├── Images │ └── icon.png ├── Interfaces │ ├── ICountOperation.cs │ ├── IDeleteOperation.cs │ ├── ICreateOperation.cs │ ├── IUpdateOperation.cs │ ├── IQueryOperation.cs │ └── IReadOperation.cs ├── Models │ ├── CommentThreaded.cs │ ├── AuthMethod.cs │ ├── Base.cs │ ├── JWTPlugin.cs │ ├── BadRequest.cs │ ├── AvatarURL.cs │ ├── JWTResponse.cs │ ├── JWTData.cs │ ├── JWTUser.cs │ ├── MediaSize.cs │ ├── MediaDetails.cs │ ├── Embedded.cs │ ├── Tag.cs │ ├── ApplicationPassword.cs │ ├── Category.cs │ ├── Term.cs │ ├── Exceptions │ │ ├── WPException.cs │ │ └── WPUnexpectedException.cs │ ├── ImageMeta.cs │ ├── Links.cs │ ├── PostStatus.cs │ ├── PostType.cs │ ├── Taxonomy.cs │ ├── Theme.cs │ ├── Settings.cs │ └── Plugin.cs ├── Utility │ ├── TaxonomiesQueryBuilder.cs │ ├── ThemesQueryBuilder.cs │ ├── PluginsQueryBuilder.cs │ ├── QueryTextAttribute.cs │ ├── ExcludeQueryTextAttribute.cs │ ├── CustomCapabilitiesJsonConverter.cs │ ├── UrlHelper.cs │ ├── TagsQueryBuilder.cs │ ├── UsersQueryBuilder.cs │ └── CategoriesQueryBuilder.cs └── Client │ ├── Tags.cs │ ├── Categories.cs │ ├── Settings.cs │ ├── Themes.cs │ ├── Pages.cs │ ├── PostRevisions.cs │ ├── PostTypes.cs │ ├── PostStatuses.cs │ ├── Comments.cs │ ├── CustomRequest.cs │ └── Taxonomies.cs ├── mkdocs.yml ├── WordPressPCL.Tests.Selfhosted ├── Assets │ ├── cat.jpg │ └── img_exif_error.jpg ├── jwt.runsettings ├── jwtauth.runsettings ├── Utility │ ├── ApiCredentials.cs │ ├── UrlHelper_Tests.cs │ ├── ClientHelper.cs │ └── HttpHelper_Tests.cs ├── MimeTypeHelper_Tests.cs ├── Settings_Tests.cs ├── QueryBuilder_Tests.cs ├── WordPressPCL.Tests.Selfhosted.csproj ├── PostStatuses_Tests.cs ├── PostTypes_Tests.cs ├── Themes_Tests.cs ├── Taxonomies_Tests.cs ├── HttpClient_Tests.cs ├── ListPosts_QueryBuilder_Tests.cs ├── ApplicationPasswords_Tests.cs ├── PostRevisions_Tests.cs ├── Exception_Unexpected_Tests.cs ├── Plugins_Tests.cs ├── ExceptionTests.cs ├── CustomRequests_Tests.cs ├── Pages_Tests.cs ├── Tag_Tests.cs └── Categories_Tests.cs ├── WordPressPCL.Tests.Hosted ├── Utility │ ├── ClientHelper.cs │ ├── ApiCredentials.cs │ └── HttpHelper_Tests.cs ├── WordPressPCL.Tests.Hosted.csproj └── Basic_Tests.cs ├── docs ├── II version 1.x │ ├── entities │ │ ├── settings.md │ │ ├── posttypes.md │ │ ├── taxonomies.md │ │ ├── poststatuses.md │ │ ├── customPostType.md │ │ ├── tags.md │ │ ├── pages.md │ │ ├── categories.md │ │ ├── posts.md │ │ ├── users.md │ │ ├── comments.md │ │ └── media.md │ ├── customization │ │ ├── customHttpClient.md │ │ ├── customJsonSerializationSettings.md │ │ ├── customRequest.md │ │ └── httpResponsePreProcessing.md │ └── troubleshooting.md ├── I version 2.x │ ├── entities │ │ ├── settings.md │ │ ├── posttypes.md │ │ ├── taxonomies.md │ │ ├── poststatuses.md │ │ ├── customPostType.md │ │ ├── tags.md │ │ ├── pages.md │ │ ├── categories.md │ │ ├── media.md │ │ ├── comments.md │ │ ├── posts.md │ │ └── users.md │ ├── breaking-changes.md │ ├── customization │ │ ├── customHttpClient.md │ │ ├── customJsonSerializationSettings.md │ │ ├── customRequest.md │ │ └── httpResponsePreProcessing.md │ └── troubleshooting.md └── index.md ├── .github ├── FUNDING.yml └── workflows │ ├── publish-nuget.yml │ ├── documentation.yml │ ├── integration-tests.yml │ └── codeql-analysis.yml ├── LICENSE ├── .gitattributes └── CODE_OF_CONDUCT.md /dev/dockerfile: -------------------------------------------------------------------------------- 1 | FROM wordpress:latest 2 | 3 | COPY ./.htaccess /var/www/html/ 4 | -------------------------------------------------------------------------------- /WordPressPCL/Images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wp-net/WordPressPCL/master/WordPressPCL/Images/icon.png -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: WordPressPCL 2 | site_url: https://wp-net.github.io/WordPressPCL/ 3 | theme: readthedocs -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Assets/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wp-net/WordPressPCL/master/WordPressPCL.Tests.Selfhosted/Assets/cat.jpg -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Assets/img_exif_error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wp-net/WordPressPCL/master/WordPressPCL.Tests.Selfhosted/Assets/img_exif_error.jpg -------------------------------------------------------------------------------- /dev/.env: -------------------------------------------------------------------------------- 1 | MYSQL_ROOT_PASSWORD=wordpress 2 | MYSQL_DATABASE=wordpress 3 | MYSQL_USER=wordpress 4 | MYSQL_PASSWORD=wordpress 5 | 6 | WORDPRESS_DB_HOST=db:3306 7 | WORDPRESS_DB_USER=wordpress 8 | WORDPRESS_DB_PASSWORD=wordpress 9 | WORDPRESS_DB_NAME=wordpress 10 | WORDPRESS_DEBUG=1 -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/jwt.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/jwtauth.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Utility/ApiCredentials.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Tests.Selfhosted.Utility; 2 | 3 | public class ApiCredentials 4 | { 5 | public static string WordPressUri = "http://localhost:8080/wp-json/"; 6 | public static string Username = "wordpress"; 7 | public static string Password = "wordpress"; 8 | } 9 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Hosted/Utility/ClientHelper.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Tests.Hosted.Utility; 2 | 3 | public static class ClientHelper 4 | { 5 | private static WordPressClient _client; 6 | 7 | public static WordPressClient GetWordPressClient() 8 | { 9 | if(_client == null) 10 | _client = new WordPressClient(ApiCredentials.WordPressUri, ""); 11 | return _client; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Hosted/Utility/ApiCredentials.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Tests.Hosted.Utility 2 | { 3 | public class ApiCredentials 4 | { 5 | public static string SiteUri = "mysite.wordpress.com"; 6 | public static string WordPressUri = $"https://public-api.wordpress.com/wp/v2/sites/{SiteUri}/"; 7 | public static string Username = "Name"; 8 | public static string Password = "password"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /WordPressPCL/Interfaces/ICountOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace WordPressPCL.Interfaces { 4 | 5 | /// 6 | /// Interface for count of Wordpress items 7 | /// 8 | public interface ICountOperation 9 | { 10 | /// 11 | /// Get Count of Wordpress items 12 | /// 13 | /// Result of Operation 14 | Task GetCountAsync(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/II version 1.x/entities/settings.md: -------------------------------------------------------------------------------- 1 | # Settings 2 | 3 | Here is a list of methods and examples of working with Settings 4 | 5 | ## GetSettings() 6 | 7 | ```C# 8 | // returns current settings 9 | var settings = await client.GetSettings(); 10 | ``` 11 | 12 | ## Update Settings 13 | ```C# 14 | //update settings 15 | var settings = await client.GetSettings(); 16 | settings.Description = "New Site Description"; 17 | var updatedSettings = await client.UpdateSettings(settings); 18 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Models/CommentThreaded.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Models 2 | { 3 | /// 4 | /// An extension class for Comment that holds a depth property 5 | /// for displaying threaded comments 6 | /// 7 | public class CommentThreaded : Comment 8 | { 9 | /// 10 | /// The depth of a comment 11 | /// 0 is a top level comments without parent 12 | /// 13 | public int Depth { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /WordPressPCL/Utility/TaxonomiesQueryBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Utility 2 | { 3 | /// 4 | /// Taxonomies Query Builder class to construct queries with valid parameters 5 | /// 6 | public class TaxonomiesQueryBuilder : QueryBuilder 7 | { 8 | /// 9 | /// Limit results to taxonomies associated with a specific post type. 10 | /// 11 | [QueryText("type")] 12 | public string Type { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /WordPressPCL/Utility/ThemesQueryBuilder.cs: -------------------------------------------------------------------------------- 1 | using WordPressPCL.Models; 2 | 3 | namespace WordPressPCL.Utility 4 | { 5 | /// 6 | /// Themes Query Builder class to construct queries with valid parameters 7 | /// 8 | public class ThemesQueryBuilder : QueryBuilder 9 | { 10 | /// 11 | /// Limit results to specific status 12 | /// 13 | [QueryText("status")] 14 | public ActivationStatus Status { get; set; } 15 | 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /docs/I version 2.x/entities/settings.md: -------------------------------------------------------------------------------- 1 | # Settings 2 | 3 | Here is a list of methods and examples of working with Settings 4 | 5 | ## Get Settings 6 | 7 | ```C# 8 | // returns current settings 9 | var settings = await client.Settings.GetSettingsAsync(); 10 | ``` 11 | 12 | ## Update Settings 13 | ```C# 14 | //update settings 15 | var settings = await client.Settings.GetSettingsAsync(); 16 | settings.Description = "New Site Description"; 17 | var updatedSettings = await client.Settings.UpdateSettingsAsync(settings); 18 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Interfaces/IDeleteOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace WordPressPCL.Interfaces 4 | { 5 | /// 6 | /// Interface with required Delete methods 7 | /// 8 | public interface IDeleteOperation 9 | { 10 | /// 11 | /// Delete object by Id 12 | /// 13 | /// ID ob object to delete 14 | /// Result of operation 15 | Task DeleteAsync(int ID); 16 | } 17 | } -------------------------------------------------------------------------------- /WordPressPCL/Models/AuthMethod.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Models 2 | { 3 | /// 4 | /// Authentication Methods 5 | /// JWT - recommended AUTH method 6 | /// 7 | public enum AuthMethod 8 | { 9 | /// 10 | /// Bearer Authentication using token 11 | /// 12 | Bearer, 13 | /// 14 | /// Basic Authentication using Application Passwords introduced in Wordpress 5.6 15 | /// 16 | Basic 17 | } 18 | } -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Utility/UrlHelper_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WordPressPCL.Utility; 3 | 4 | namespace WordPressPCL.Tests.Selfhosted.Utility; 5 | 6 | [TestClass] 7 | public class UrlHelper_Tests 8 | { 9 | 10 | 11 | [TestMethod] 12 | public void SetQueryParam_Test() 13 | { 14 | string test = "test"; 15 | string result = test.SetQueryParam("param", "value"); 16 | 17 | Assert.AreEqual("test", test); 18 | Assert.AreEqual("test?param=value", result); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Base.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// Base class for Models 7 | /// 8 | public class Base 9 | { 10 | /// 11 | /// Unique identifier for the object. 12 | /// 13 | /// 14 | /// Read only 15 | /// Context: view, edit, embed 16 | /// 17 | [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore)] 18 | public int Id { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /WordPressPCL/Interfaces/ICreateOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace WordPressPCL.Interfaces 4 | { 5 | /// 6 | /// Interface with required Create methods 7 | /// 8 | /// return class type 9 | public interface ICreateOperation 10 | { 11 | /// 12 | /// Create object 13 | /// 14 | /// object to create 15 | /// Created object 16 | Task CreateAsync(TClass Entity); 17 | } 18 | } -------------------------------------------------------------------------------- /WordPressPCL/Models/JWTPlugin.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Models { 2 | 3 | /// 4 | /// JWT Plugins supported 5 | /// 6 | public enum JWTPlugin { 7 | 8 | /// 9 | /// JWT Authentication for WP REST API plugin 10 | /// Author - Enrique Chavez 11 | /// 12 | JWTAuthByEnriqueChavez, 13 | /// 14 | /// JWT Auth – WordPress JSON Web Token Authentication plugin 15 | /// Author - Useful Team 16 | /// 17 | JWTAuthByUsefulTeam 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /WordPressPCL/Interfaces/IUpdateOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace WordPressPCL.Interfaces 4 | { 5 | /// 6 | /// Interface with required Update methods 7 | /// 8 | /// return class type 9 | public interface IUpdateOperation 10 | { 11 | /// 12 | /// Update entity method 13 | /// 14 | /// object to update 15 | /// Updated entity 16 | Task UpdateAsync(TClass Entity); 17 | } 18 | } -------------------------------------------------------------------------------- /docs/I version 2.x/breaking-changes.md: -------------------------------------------------------------------------------- 1 | # Breaking Changes in Version 2.x 2 | 3 | - Separate sub client for Authentication accessed via `client.Auth` 4 | - Separate sub client for Settings accessed via `client.Settings` 5 | - Default MIME type for media is **application/octet-stream** instead of text/plain 6 | - Async methods have an Async suffix as per naming C# naming guidelines e.g. `client.Posts.GetAllAsync()` 7 | - MediaQueryBuilder by default will return all types of media instead of just images. 8 | - Model properties that returned arrays are changed to List type e.g. `Tags` in `Post` class is now of type `List` instead of `int[]` type -------------------------------------------------------------------------------- /WordPressPCL.Tests.Hosted/WordPressPCL.Tests.Hosted.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/I version 2.x/customization/customHttpClient.md: -------------------------------------------------------------------------------- 1 | # Custom HttpClient 2 | 3 | You can inject your own instance of an HttpClient into the WordPressClient. This allows you to re-use an existing instance, set desired headers etc. 4 | 5 | ```c# 6 | var httpClient = new HttpClient 7 | { 8 | BaseAddress = new Uri(ApiCredentials.WordPressUri) 9 | }; 10 | httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0"); 11 | httpClient.DefaultRequestHeaders.Add("Referer", "https://github.com/wp-net/WordPressPCL"); 12 | 13 | var client = new WordPressClient(httpClient); 14 | var posts = await client.Posts.GetAllAsync(); 15 | ``` -------------------------------------------------------------------------------- /docs/II version 1.x/customization/customHttpClient.md: -------------------------------------------------------------------------------- 1 | # Custom HttpClient 2 | 3 | You can inject your own instance of an HttpClient into the WordPressClient. This allows you to re-use an existing instance, set desired headers etc. 4 | 5 | ```csharp 6 | var httpClient = new HttpClient 7 | { 8 | BaseAddress = new Uri(ApiCredentials.WordPressUri) 9 | }; 10 | httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0"); 11 | httpClient.DefaultRequestHeaders.Add("Referer", "https://github.com/wp-net/WordPressPCL"); 12 | 13 | var client = new WordPressClient(httpClient); 14 | var posts = await client.Posts.GetAll(); 15 | ``` -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/MimeTypeHelper_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WordPressPCL.Utility; 3 | 4 | namespace WordPressPCL.Tests.Selfhosted; 5 | [TestClass] 6 | public class MimeTypeHelper_Tests { 7 | 8 | [TestMethod] 9 | public void MimeType_Defaults_To_Application_Octet_Stream_For_Unknown_Extension() { 10 | const string unknownExtension = "unknown"; 11 | const string expectedMimeType = "application/octet-stream"; 12 | 13 | string resultMimeType = MimeTypeHelper.GetMIMETypeFromExtension(unknownExtension); 14 | 15 | Assert.AreEqual(expectedMimeType, resultMimeType); 16 | } 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /docs/II version 1.x/entities/posttypes.md: -------------------------------------------------------------------------------- 1 | # PostTypes 2 | 3 | Here is a list of methods and examples of working with PostTypes 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all posttypes 9 | var posttypes = await client.PostTypes.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns posttype by ID 16 | var posttype = await client.PostTypes.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new PostTypesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var posttypes = await client.PostTypes.Query(queryBuilder); 28 | ``` -------------------------------------------------------------------------------- /docs/II version 1.x/entities/taxonomies.md: -------------------------------------------------------------------------------- 1 | # Taxonomies 2 | 3 | Here is a list of methods and examples of working with Taxonomies 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all taxonomies 9 | var taxonomies = await client.Taxonomies.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns taxonomy by ID 16 | var taxonomy = await client.Taxonomies.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new TaxonomiesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var taxonomies = await client.Taxonomies.Query(queryBuilder); 28 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/entities/posttypes.md: -------------------------------------------------------------------------------- 1 | # PostTypes 2 | 3 | Here is a list of methods and examples of working with PostTypes 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // returns all posttypes 9 | var posttypes = await client.PostTypes.GetAllAsync(); 10 | ``` 11 | 12 | ## Get By ID 13 | 14 | ```C# 15 | // returns posttype by ID 16 | var posttype = await client.PostTypes.GetByIDAsync(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new PostTypesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var posttypes = await client.PostTypes.QueryAsync(queryBuilder); 28 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/entities/taxonomies.md: -------------------------------------------------------------------------------- 1 | # Taxonomies 2 | 3 | Here is a list of methods and examples of working with Taxonomies 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // returns all taxonomies 9 | var taxonomies = await client.Taxonomies.GetAllAsync(); 10 | ``` 11 | 12 | ## Get By ID 13 | 14 | ```C# 15 | // returns taxonomy by ID 16 | var taxonomy = await client.Taxonomies.GetByIDAsync(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new TaxonomiesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var taxonomies = await client.Taxonomies.QueryAsync(queryBuilder); 28 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Utility/PluginsQueryBuilder.cs: -------------------------------------------------------------------------------- 1 | using WordPressPCL.Models; 2 | 3 | namespace WordPressPCL.Utility 4 | { 5 | /// 6 | /// Plugins Query Builder class to construct queries with valid parameters 7 | /// 8 | public class PluginsQueryBuilder : QueryBuilder 9 | { 10 | /// 11 | /// Limit results to those matching a string. 12 | /// 13 | [QueryText("search")] 14 | public string Search { get; set; } 15 | 16 | /// 17 | /// Limit results to specific status 18 | /// 19 | [QueryText("status")] 20 | public ActivationStatus Status { get; set; } 21 | 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /docs/II version 1.x/entities/poststatuses.md: -------------------------------------------------------------------------------- 1 | # PostStatuses 2 | 3 | Here is a list of methods and examples of working with PostStatuses 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all poststatuses 9 | var poststatuses = await client.PostStatuses.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns poststatus by ID 16 | var poststatus = await client.PostStatuses.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new PostStatusesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var poststatuses = await client.PostStatuses.Query(queryBuilder); 28 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/customization/customJsonSerializationSettings.md: -------------------------------------------------------------------------------- 1 | 2 | # Custom JsonSerializationSettings 3 | 4 | In some cases, it may be useful to change the default settings for the serialization / deserialization process of the Json.NET library 5 | You can do this in the following way: 6 | ```c# 7 | var client = new WordPressClient("https://site.com/wp-json/"); 8 | client.JsonSerializationSettings = new JsonSerializationSettings() 9 | { 10 | DateFormatHandling=DateFormatHandling.IsoDateFormat, 11 | DateFormatString = "d MMMM YYYY" 12 | }; 13 | // working with library 14 | ``` 15 | For detailed information on the available settings, see the Json.NET documentation https://www.newtonsoft.com/json/help/html/SerializationSettings.htm -------------------------------------------------------------------------------- /docs/I version 2.x/entities/poststatuses.md: -------------------------------------------------------------------------------- 1 | # PostStatuses 2 | 3 | Here is a list of methods and examples of working with PostStatuses 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // returns all poststatuses 9 | var poststatuses = await client.PostStatuses.GetAllAsync(); 10 | ``` 11 | 12 | ## Get By ID 13 | 14 | ```C# 15 | // returns poststatus by ID 16 | var poststatus = await client.PostStatuses.GetByIDAsync(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new PostStatusesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var poststatuses = await client.PostStatuses.QueryAsync(queryBuilder); 28 | ``` -------------------------------------------------------------------------------- /docs/II version 1.x/customization/customJsonSerializationSettings.md: -------------------------------------------------------------------------------- 1 | 2 | # Custom JsonSerializationSettings 3 | 4 | In some cases, it may be useful to change the default settings for the serialization / deserialization process of the Json.NET library 5 | You can do this in the following way: 6 | ```c# 7 | var client = new WordPressClient("https://site.com/wp-json/"); 8 | client.JsonSerializationSettings = new JsonSerializationSettings() 9 | { 10 | DateFormatHandling=DateFormatHandling.IsoDateFormat, 11 | DateFormatString = "d MMMM YYYY" 12 | }; 13 | // working with library 14 | ``` 15 | For detailed information on the available settings, see the Json.NET documentation https://www.newtonsoft.com/json/help/html/SerializationSettings.htm -------------------------------------------------------------------------------- /WordPressPCL/Utility/QueryTextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WordPressPCL.Utility 4 | { 5 | /// 6 | /// Attribute for set Text in querybuilder 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public sealed class QueryTextAttribute : Attribute 10 | { 11 | /// 12 | /// Text property uses in HTTP query string 13 | /// 14 | public string Text { get; set; } 15 | /// 16 | /// Constructor 17 | /// 18 | /// text uses in HTTP query string 19 | public QueryTextAttribute(string text) 20 | { 21 | Text = text; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /WordPressPCL/Models/BadRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// Model for unsuccessful request 7 | /// 8 | public class BadRequest 9 | { 10 | /// 11 | /// Error type 12 | /// 13 | [JsonProperty("code")] 14 | public string Name { get; set; } 15 | 16 | /// 17 | /// Error description 18 | /// 19 | [JsonProperty("message")] 20 | public string Message { get; set; } 21 | 22 | /// 23 | /// Additional info 24 | /// 25 | [JsonProperty("data")] 26 | public dynamic Data { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /WordPressPCL/Client/Tags.cs: -------------------------------------------------------------------------------- 1 | using WordPressPCL.Models; 2 | using WordPressPCL.Utility; 3 | 4 | namespace WordPressPCL.Client 5 | { 6 | /// 7 | /// Client class for interaction with Tags endpoint WP REST API 8 | /// 9 | public class Tags : CRUDOperation 10 | { 11 | #region Init 12 | 13 | private const string _methodPath = "tags"; 14 | 15 | /// 16 | /// Constructor 17 | /// 18 | /// reference to HttpHelper class for interaction with HTTP 19 | public Tags(HttpHelper HttpHelper) : base(HttpHelper, _methodPath, true) 20 | { 21 | } 22 | 23 | #endregion Init 24 | } 25 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ThomasPe] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /dev/install.md: -------------------------------------------------------------------------------- 1 | Run `docker compose up` inside the `dev` folder and it will start a fully setup docker Wordpress instance on port 8080 to run tests. 2 | 3 | - The Wordpress instance will have following already configured plugins: 4 | - [JWT Auth – WordPress JSON Web Token Authentication](https://wordpress.org/plugins/jwt-auth/) 5 | - [Contact Form 7](https://wordpress.org/plugins/contact-form-7/) 6 | - https://github.com/wp-net/wordpress-docker-compose/raw/master/plugins/enable-application-passwords.1.0.zip 7 | 8 | - The "Permanlinks" link structure in Wordpress settings is set to "Post name" 9 | 10 | To destroy the containers simply run `docker compose down` in the terminal 11 | 12 | To run the tests in Visual Studio, make sure to use the `jwtauth.runsettings` test settings. -------------------------------------------------------------------------------- /WordPressPCL/Client/Categories.cs: -------------------------------------------------------------------------------- 1 | using WordPressPCL.Models; 2 | using WordPressPCL.Utility; 3 | 4 | namespace WordPressPCL.Client 5 | { 6 | /// 7 | /// Client class for interaction with Categories endpoint WP REST API 8 | /// 9 | public class Categories : CRUDOperation 10 | { 11 | #region Init 12 | 13 | private const string _methodPath = "categories"; 14 | 15 | /// 16 | /// Constructor 17 | /// 18 | /// reference to HttpHelper class for interaction with HTTP 19 | public Categories(HttpHelper HttpHelper) : base(HttpHelper, _methodPath, true) 20 | { 21 | } 22 | 23 | #endregion Init 24 | } 25 | } -------------------------------------------------------------------------------- /WordPressPCL/Models/AvatarURL.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// Avatar URLs for the users. 7 | /// 8 | /// Default sizes: 24, 48, 96 9 | public class AvatarURL 10 | { 11 | /// 12 | /// Avatar URL 24x24 pixels 13 | /// 14 | [JsonProperty("24")] 15 | public string Size24 { get; set; } 16 | 17 | /// 18 | /// Avatar URL 48x48 pixels 19 | /// 20 | [JsonProperty("48")] 21 | public string Size48 { get; set; } 22 | 23 | /// 24 | /// Avatar URL 96x96 pixels 25 | /// 26 | [JsonProperty("96")] 27 | public string Size96 { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WordPressPCL/Utility/ExcludeQueryTextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WordPressPCL.Utility { 4 | /// 5 | /// Attribute to exclude query text in querybuilder 6 | /// 7 | [AttributeUsage(AttributeTargets.Property)] 8 | public sealed class ExcludeQueryTextAttribute : Attribute { 9 | 10 | /// 11 | /// Value which determines if query text is excluded 12 | /// 13 | public string ExclusionValue { get; } 14 | 15 | /// 16 | /// Constructor 17 | /// 18 | /// value which determines if query text is excluded 19 | public ExcludeQueryTextAttribute(string exclusionValue) { 20 | ExclusionValue = exclusionValue; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Settings_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Models; 4 | using WordPressPCL.Tests.Selfhosted.Utility; 5 | 6 | namespace WordPressPCL.Tests.Selfhosted; 7 | 8 | [TestClass] 9 | public class Settings_Tests 10 | { 11 | private static WordPressClient _clientAuth; 12 | 13 | [ClassInitialize] 14 | public static async Task Init(TestContext testContext) 15 | { 16 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 17 | } 18 | 19 | [TestMethod] 20 | public async Task Get_Settings_Test() 21 | { 22 | Settings settings = await _clientAuth.Settings.GetSettingsAsync(); 23 | Assert.IsNotNull(settings); 24 | Assert.IsNotNull(settings.Title); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WordPressPCL/Models/JWTResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.ComponentModel; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Response class for the JWT Plugin 8 | /// 9 | public class JWTResponse 10 | { 11 | /// 12 | /// Indicates if the call was successful 13 | /// 14 | [JsonProperty("success")] 15 | public bool Success { get; set; } 16 | 17 | /// 18 | /// The response message 19 | /// 20 | [JsonProperty("message")] 21 | public string Message { get; set; } 22 | 23 | /// 24 | /// The JWT Content 25 | /// 26 | [JsonProperty("data")] 27 | [DefaultValue(null)] 28 | public JWTData Data { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /WordPressPCL/Models/JWTData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// JWT Data 7 | /// 8 | public class JWTData 9 | { 10 | /// 11 | /// JWT Token 12 | /// 13 | [JsonProperty("token")] 14 | public string Token { get; set; } 15 | 16 | /// 17 | /// User Display Name 18 | /// 19 | [JsonProperty("displayName")] 20 | public string DisplayName { get; set; } 21 | 22 | /// 23 | /// User Email 24 | /// 25 | [JsonProperty("email")] 26 | public string Email { get; set; } 27 | 28 | /// 29 | /// User Nice Names 30 | /// 31 | [JsonProperty("nicename")] 32 | public string NiceName { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /WordPressPCL/Interfaces/IQueryOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Utility; 4 | 5 | namespace WordPressPCL.Interfaces 6 | { 7 | /// 8 | /// Interface with required Query methods 9 | /// 10 | /// return class type 11 | /// Query Builder class 12 | public interface IQueryOperation where TQueryClass : QueryBuilder 13 | { 14 | /// 15 | /// Execute query 16 | /// 17 | /// query builder with parameters for query 18 | /// Is use auth header 19 | /// list of filtered objects 20 | Task> QueryAsync(TQueryClass queryBuilder, bool useAuth = false); 21 | } 22 | } -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/QueryBuilder_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Tests.Selfhosted; 8 | 9 | [TestClass] 10 | public class QueryBuilder_Tests 11 | { 12 | [TestMethod] 13 | public void Multi_Parameter_Query_Works_Test() 14 | { 15 | // Initialize 16 | PostsQueryBuilder builder = new() { 17 | Page = 1, 18 | PerPage = 15, 19 | OrderBy = PostsOrderBy.Title, 20 | Order = Order.ASC, 21 | Statuses = new List { Status.Publish }, 22 | Embed = true 23 | }; 24 | Console.WriteLine(builder.BuildQuery()); 25 | Assert.AreEqual("?page=1&per_page=15&orderby=title&status=publish&order=asc&_embed=true&context=view", builder.BuildQuery()); 26 | } 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /dev/.htaccess: -------------------------------------------------------------------------------- 1 | # BEGIN WordPress 2 | # The directives (lines) between `BEGIN WordPress` and `END WordPress` are 3 | # dynamically generated, and should only be modified via WordPress filters. 4 | # Any changes to the directives between these markers will be overwritten. 5 | 6 | 7 | RewriteEngine On 8 | RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}] 9 | RewriteBase / 10 | RewriteRule ^index\.php$ - [L] 11 | RewriteCond %{REQUEST_FILENAME} !-f 12 | RewriteCond %{REQUEST_FILENAME} !-d 13 | RewriteRule . /index.php [L] 14 | 15 | 16 | # END WordPress 17 | 18 | SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 19 | 20 | RewriteEngine On 21 | RewriteCond %{HTTP:Authorization} ^(.*) 22 | RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1] 23 | RewriteBase / 24 | RewriteRule ^index\.php$ - [L] 25 | RewriteCond %{REQUEST_FILENAME} !-f 26 | RewriteCond %{REQUEST_FILENAME} !-d 27 | RewriteRule . /index.php [L] 28 | -------------------------------------------------------------------------------- /WordPressPCL/Models/JWTUser.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// User class for working with the JWT plugin 7 | /// 8 | public class JWTUser 9 | { 10 | /// 11 | /// The JWT Token used to authorize requests 12 | /// 13 | [JsonProperty("token")] 14 | public string Token { get; set; } 15 | 16 | /// 17 | /// User Display Name 18 | /// 19 | [JsonProperty("user_display_name")] 20 | public string DisplayName { get; set; } 21 | 22 | /// 23 | /// User Email Address 24 | /// 25 | [JsonProperty("user_email")] 26 | public string Email { get; set; } 27 | 28 | /// 29 | /// User Nice Name 30 | /// 31 | [JsonProperty("user_nicename")] 32 | public string NiceName { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/WordPressPCL.Tests.Selfhosted.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Always 24 | 25 | 26 | Always 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /docs/II version 1.x/entities/customPostType.md: -------------------------------------------------------------------------------- 1 | # Custom Post Type 2 | 3 | To create post of custom post type you should do 2 things 4 | 1. Enable rest api support for custom post type [registering-a-custom-post-type-with-rest-api-support](https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-rest-api-support-for-custom-content-types/#registering-a-custom-post-type-with-rest-api-support) 5 | 2. This is a one of contrintuitive things in WP REST API, but for creating post with custom type you should send requests to custom endpoint. For this task you can use our [Custom Requests](https://github.com/wp-net/WordPressPCL/wiki/CustomRequests) feature 6 | Example: 7 | ```c# 8 | var post = new Post() 9 | { 10 | Title = new Title("Title 1"), 11 | Content = new Content("Content PostCreate"), 12 | Type = "portfolio" //your custom post type 13 | }; 14 | //change portfolio to your custom post type 15 | var createdPost = await _clientAuth.CustomRequest.Create("wp/v2/portfolio",post); 16 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/entities/customPostType.md: -------------------------------------------------------------------------------- 1 | # Custom Post Type 2 | 3 | To create post of custom post type you should do 2 things 4 | 1. Enable rest api support for custom post type [registering-a-custom-post-type-with-rest-api-support](https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-rest-api-support-for-custom-content-types/#registering-a-custom-post-type-with-rest-api-support) 5 | 2. This is a one of the unintuitive things in WP REST API, but for creating post with custom type you should send requests to custom endpoint. For this task you can use our [Custom Requests](https://github.com/wp-net/WordPressPCL/wiki/CustomRequests) feature 6 | 7 | 8 | Example: 9 | 10 | ```c# 11 | var post = new Post() 12 | { 13 | Title = new Title("Title 1"), 14 | Content = new Content("Content PostCreate"), 15 | Type = "portfolio" //your custom post type 16 | }; 17 | //change portfolio to your custom post type 18 | var createdPost = await _clientAuth.CustomRequest.CreateAsync("wp/v2/portfolio", post); 19 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Models/MediaSize.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// Info about Media Size 7 | /// 8 | /// 9 | public class MediaSize 10 | { 11 | /// 12 | /// File 13 | /// 14 | [JsonProperty("file")] 15 | public string File { get; set; } 16 | /// 17 | /// Media Width 18 | /// 19 | [JsonProperty("width")] 20 | public int? Width { get; set; } 21 | /// 22 | /// Media Height 23 | /// 24 | [JsonProperty("height")] 25 | public int? Height { get; set; } 26 | /// 27 | /// Mime Type 28 | /// 29 | [JsonProperty("mime_type")] 30 | public string MimeType { get; set; } 31 | /// 32 | /// Url of source media 33 | /// 34 | [JsonProperty("source_url")] 35 | public string SourceUrl { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /WordPressPCL/Utility/CustomCapabilitiesJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Globalization; 4 | 5 | namespace WordPressPCL.Utility 6 | { 7 | /// 8 | /// Custom JSON converter to convert string values to boolean in capabilities and extra_capabilities properties 9 | /// 10 | /// 11 | /// 12 | public class CustomCapabilitiesJsonConverter : JsonConverter 13 | { 14 | /// 15 | public override bool ReadJson(JsonReader reader, Type objectType, bool existingValue, bool hasExistingValue, JsonSerializer serializer) 16 | { 17 | return Convert.ToBoolean(reader?.ValueType == typeof(string) ? Convert.ToByte(reader.Value, CultureInfo.InvariantCulture) : reader.Value, CultureInfo.InvariantCulture); 18 | } 19 | 20 | /// 21 | public override void WriteJson(JsonWriter writer, bool value, JsonSerializer serializer) 22 | { 23 | writer?.WriteValue(value); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Thomas Pentenrieder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /WordPressPCL/Models/MediaDetails.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Details of media item 8 | /// 9 | /// 10 | public class MediaDetails 11 | { 12 | /// 13 | /// Media width 14 | /// 15 | [JsonProperty("width")] 16 | public int Width { get; set; } 17 | /// 18 | /// Media height 19 | /// 20 | [JsonProperty("height")] 21 | public int Height { get; set; } 22 | /// 23 | /// File 24 | /// 25 | [JsonProperty("file")] 26 | public string File { get; set; } 27 | /// 28 | /// Sizes 29 | /// 30 | [JsonProperty("sizes")] 31 | public IDictionary Sizes { get; set; } 32 | /// 33 | /// Meta info of Image 34 | /// 35 | [JsonProperty("image_meta")] 36 | public ImageMeta ImageMeta { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Utility/ClientHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Threading.Tasks; 4 | using WordPressPCL.Models; 5 | 6 | namespace WordPressPCL.Tests.Selfhosted.Utility; 7 | 8 | public static class ClientHelper 9 | { 10 | public static async Task GetAuthenticatedWordPressClient(TestContext context) 11 | { 12 | WordPressClient clientAuth = new(ApiCredentials.WordPressUri); 13 | 14 | Console.WriteLine($"Auth Plugin: {context?.Properties["authplugin"]}"); 15 | if (context?.Properties["authplugin"]?.ToString() == "jwtAuthByUsefulTeam") 16 | { 17 | clientAuth.Auth.UseBearerAuth(JWTPlugin.JWTAuthByUsefulTeam); 18 | } 19 | else { 20 | clientAuth.Auth.UseBearerAuth(JWTPlugin.JWTAuthByEnriqueChavez); 21 | } 22 | await clientAuth.Auth.RequestJWTokenAsync(ApiCredentials.Username, ApiCredentials.Password); 23 | 24 | return clientAuth; 25 | } 26 | 27 | public static WordPressClient GetWordPressClient() 28 | { 29 | return new WordPressClient(ApiCredentials.WordPressUri); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/PostStatuses_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Tests.Selfhosted.Utility; 6 | 7 | namespace WordPressPCL.Tests.Selfhosted; 8 | 9 | [TestClass] 10 | public class PostStatuses_Tests 11 | { 12 | private static WordPressClient _clientAuth; 13 | 14 | [ClassInitialize] 15 | public static async Task Init(TestContext testContext) 16 | { 17 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 18 | } 19 | 20 | [TestMethod] 21 | public async Task PostStatuses_Read() 22 | { 23 | List poststatuses = await _clientAuth.PostStatuses.GetAllAsync(); 24 | Assert.IsNotNull(poststatuses); 25 | Assert.AreNotEqual(poststatuses.Count, 0); 26 | } 27 | 28 | [TestMethod] 29 | public async Task PostStatuses_Get() 30 | { 31 | List poststatuses = await _clientAuth.PostStatuses.GetAsync(); 32 | Assert.IsNotNull(poststatuses); 33 | Assert.AreNotEqual(poststatuses.Count, 0); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/publish-nuget.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Build & Publish to NuGet 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | # This workflow contains a single job called "build" 13 | build: 14 | # The type of runner that the job will run on 15 | runs-on: ubuntu-latest 16 | 17 | # Steps represent a sequence of tasks that will be executed as part of the job 18 | steps: 19 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 20 | - uses: actions/checkout@v2 21 | - name: Setup .NET SDK 22 | uses: actions/setup-dotnet@v4 23 | with: 24 | dotnet-version: 8.x 25 | - name: Build & Package 26 | run: | 27 | cd WordPressPCL 28 | dotnet pack -c Release -o out 29 | - name: PushNuget 30 | run: dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{secrets.WORDPRESSPCLNUGET}} --skip-duplicate 31 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Embedded.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Embedded Information 8 | /// 9 | public class Embedded 10 | { 11 | /// 12 | /// Post Author 13 | /// 14 | [JsonProperty("author")] 15 | public List Author { get; set; } 16 | 17 | /// 18 | /// Comments on the post 19 | /// 20 | [JsonProperty("replies")] 21 | public List> Replies { get; set; } 22 | 23 | /// 24 | /// Featured images for the post 25 | /// 26 | [JsonProperty("wp:featuredmedia")] 27 | public List WpFeaturedmedia { get; set; } 28 | 29 | /// 30 | /// Terms for the post (categories, tags etc.) 31 | /// 32 | [JsonProperty("wp:term")] 33 | public List> WpTerm { get; set; } 34 | /// 35 | /// Parent page 36 | /// 37 | [JsonProperty("up")] 38 | public List Up { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/PostTypes_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Tests.Selfhosted.Utility; 6 | 7 | namespace WordPressPCL.Tests.Selfhosted; 8 | 9 | [TestClass] 10 | public class PostTypes_Tests 11 | { 12 | private static WordPressClient _client; 13 | private static WordPressClient _clientAuth; 14 | 15 | [ClassInitialize] 16 | public static async Task Init(TestContext testContext) 17 | { 18 | _client = ClientHelper.GetWordPressClient(); 19 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 20 | } 21 | 22 | [TestMethod] 23 | public async Task PostTypes_Read() 24 | { 25 | List posttypes = await _clientAuth.PostTypes.GetAllAsync(); 26 | Assert.IsNotNull(posttypes); 27 | Assert.AreNotEqual(posttypes.Count, 0); 28 | } 29 | 30 | [TestMethod] 31 | public async Task PostTypes_Get() 32 | { 33 | List posttypes = await _clientAuth.PostTypes.GetAsync(); 34 | Assert.IsNotNull(posttypes); 35 | Assert.AreNotEqual(posttypes.Count, 0); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Docs 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - uses: actions/setup-python@v2 28 | - run: pip install --upgrade pip && pip install mkdocs mkdocs-gen-files 29 | - run: git config user.name 'github-actions[bot]' && git config user.email 'github-actions[bot]@users.noreply.github.com' 30 | - name: Publish docs 31 | run: mkdocs gh-deploy 32 | -------------------------------------------------------------------------------- /docs/II version 1.x/entities/tags.md: -------------------------------------------------------------------------------- 1 | # Tags 2 | 3 | Here is a list of methods and examples of working with Tags 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all tags 9 | var tags = await client.Tags.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns tag by ID 16 | var tag = await client.Tags.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new TagsQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var tags = await client.Tags.Query(queryBuilder); 28 | ``` 29 | 30 | ## Create new Tag 31 | 32 | ```C# 33 | // returns created tag 34 | var tag = new Tag() 35 | { 36 | Name = "Name", 37 | Description = "Tag" 38 | }; 39 | if (await client.IsValidJWToken()) 40 | { 41 | var createdtag = await client.Tags.Create(tag); 42 | } 43 | ``` 44 | 45 | ## Update Tag 46 | 47 | ```C# 48 | // returns updated tag 49 | var tag = client.Tags.GetByID(123); 50 | tag.Name = "New Name"; 51 | if (await client.IsValidJWToken()) 52 | { 53 | var updatedTag = await client.Tags.Update(tag); 54 | } 55 | ``` 56 | 57 | ## Delete Tag 58 | 59 | ```C# 60 | // returns result of deletion 61 | if (await client.IsValidJWToken()) 62 | { 63 | var result = await client.Tags.Delete(123); 64 | } 65 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/entities/tags.md: -------------------------------------------------------------------------------- 1 | # Tags 2 | 3 | Here is a list of methods and examples of working with Tags 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // returns all tags 9 | var tags = await client.Tags.GetAllAsync(); 10 | ``` 11 | 12 | ## Get By ID 13 | 14 | ```C# 15 | // returns tag by ID 16 | var tag = await client.Tags.GetByIDAsync(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new TagsQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var tags = await client.Tags.QueryAsync(queryBuilder); 28 | ``` 29 | 30 | ## Create new Tag 31 | 32 | ```C# 33 | // returns created tag 34 | var tag = new Tag() 35 | { 36 | Name = "Name", 37 | Description = "Tag" 38 | }; 39 | if (await client.IsValidJWTokenAsync()) 40 | { 41 | var createdtag = await client.Tags.CreateAsync(tag); 42 | } 43 | ``` 44 | 45 | ## Update Tag 46 | 47 | ```C# 48 | // returns updated tag 49 | var tag = client.Tags.GetByIDAsync(123); 50 | tag.Name = "New Name"; 51 | if (await client.IsValidJWTokenAsync()) 52 | { 53 | var updatedTag = await client.Tags.UpdateAsync(tag); 54 | } 55 | ``` 56 | 57 | ## Delete Tag 58 | 59 | ```C# 60 | // returns result of deletion 61 | if (await client.IsValidJWTokenAsync()) 62 | { 63 | var result = await client.Tags.DeleteAsync(123); 64 | } 65 | ``` -------------------------------------------------------------------------------- /docs/II version 1.x/entities/pages.md: -------------------------------------------------------------------------------- 1 | # Pages 2 | 3 | Here is a list of methods and examples of working with Pages 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all pages 9 | var pages = await client.Pages.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns page by ID 16 | var page = await client.Pages.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new PagesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var pages = await client.Pages.Query(queryBuilder); 28 | ``` 29 | 30 | ## Create new Page 31 | 32 | ```C# 33 | // returns created page 34 | var page = new Page() 35 | { 36 | Title = new Title("Title 1"), 37 | Content = new Content("Content PageCreate") 38 | }; 39 | if (await client.IsValidJWToken()) 40 | { 41 | var createdPage = await client.Pages.Create(page); 42 | } 43 | ``` 44 | 45 | ## Update Page 46 | 47 | ```C# 48 | // returns updated page 49 | var page= client.Pages.GetByID(123); 50 | page.Content.Raw = "New Content"; 51 | if (await client.IsValidJWToken()) 52 | { 53 | var updatedPage = await client.Pages.Update(page); 54 | } 55 | ``` 56 | 57 | ## Delete Page 58 | 59 | ```C# 60 | // returns result of deletion 61 | if (await client.IsValidJWToken()) 62 | { 63 | var result = await client.Pages.Delete(123); 64 | } 65 | ``` -------------------------------------------------------------------------------- /docs/II version 1.x/customization/customRequest.md: -------------------------------------------------------------------------------- 1 | # CustomRequest 2 | 3 | Here is a list of methods and examples of working with Custom Requests 4 | 5 | ## Overview 6 | WP REST Api can be modified and extended by plugins (Woocommerce, Contact Form 7, ACF and others), so Custom requests allow you to create non-standard requests. 7 | Before send requests you must create DTO Model of your request. 8 | Here is an example with Contact Form 7 plugin 9 | 10 | ## DTO Model 11 | ```C# 12 | public class ContactFormItem 13 | { 14 | public int? id; 15 | public string title; 16 | public string slug; 17 | public string locale; 18 | } 19 | ``` 20 | 21 | ## Get 22 | ```C# 23 | var forms = client.CustomRequest.Get>("contact-form-7/v1/contact-forms"); 24 | ``` 25 | 26 | ## Create 27 | ```C# 28 | //requires two T parameters: first - input model, second - output model 29 | var forms = client.CustomRequest.Create("contact-form-7/v1/contact-forms",new ContactFormItem() { title = "test" }); 30 | ``` 31 | 32 | ## Update 33 | ```C# 34 | //requires two T parameters: first - input model, second - output model 35 | var forms = client.CustomRequest.Update("contact-form-7/v1/contact-forms/123",new ContactFormItem() { title = "test" }); 36 | ``` 37 | 38 | ## Delete 39 | ```C# 40 | var forms = client.CustomRequest.Delete("contact-form-7/v1/contact-forms/123"); 41 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/customization/customRequest.md: -------------------------------------------------------------------------------- 1 | # CustomRequest 2 | 3 | Here is a list of methods and examples of working with Custom Requests 4 | 5 | ## Overview 6 | WP REST Api can be modified and extended by plugins (Woocommerce, Contact Form 7, ACF and others), so Custom requests allow you to create non-standard requests. 7 | Before send requests you must create DTO Model of your request. 8 | Here is an example with Contact Form 7 plugin 9 | 10 | ## DTO Model 11 | ```C# 12 | public class ContactFormItem 13 | { 14 | public int? id; 15 | public string title; 16 | public string slug; 17 | public string locale; 18 | } 19 | ``` 20 | 21 | ## Get 22 | ```C# 23 | var forms = client.CustomRequest.GetAsync>("contact-form-7/v1/contact-forms"); 24 | ``` 25 | 26 | ## Create 27 | ```C# 28 | //requires two T parameters: first - input model, second - output model 29 | var forms = client.CustomRequest.CreateAsync("contact-form-7/v1/contact-forms", new ContactFormItem() { title = "test" }); 30 | ``` 31 | 32 | ## Update 33 | ```C# 34 | //requires two T parameters: first - input model, second - output model 35 | var forms = client.CustomRequest.UpdateAsync("contact-form-7/v1/contact-forms/123",new ContactFormItem() { title = "test" }); 36 | ``` 37 | 38 | ## Delete 39 | ```C# 40 | var forms = client.CustomRequest.DeleteAsync("contact-form-7/v1/contact-forms/123"); 41 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Models/Tag.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// Terms of the type tag 7 | /// 8 | public class Tag : Term 9 | { 10 | /// 11 | /// Number of published posts for the term. 12 | /// 13 | /// 14 | /// Read only 15 | /// Context: view, edit 16 | /// 17 | [JsonProperty("count")] 18 | public int Count { get; set; } 19 | 20 | /// 21 | /// HTML description of the term. 22 | /// 23 | /// Context: view, edit 24 | [JsonProperty("description")] 25 | public string Description { get; set; } 26 | /// 27 | /// Meta object 28 | /// 29 | /// Context: view 30 | [JsonProperty("meta", DefaultValueHandling = DefaultValueHandling.Ignore)] 31 | public dynamic Meta { get; set; } 32 | /// 33 | /// Parameterless constructor 34 | /// 35 | public Tag():base() 36 | { 37 | 38 | } 39 | /// 40 | /// Constructor with required parameters 41 | /// 42 | /// Tag name 43 | public Tag(string name):this() 44 | { 45 | Name = name; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docs/I version 2.x/entities/pages.md: -------------------------------------------------------------------------------- 1 | # Pages 2 | 3 | Here is a list of methods and examples of working with Pages 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // returns all pages 9 | var pages = await client.Pages.GetAllAsync(); 10 | ``` 11 | 12 | ## Get By ID 13 | 14 | ```C# 15 | // returns page by ID 16 | var page = await client.Pages.GetByIDAsync(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new PagesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var pages = await client.Pages.QueryAsync(queryBuilder); 28 | ``` 29 | 30 | ## Create new Page 31 | 32 | ```C# 33 | // returns created page 34 | var page = new Page() 35 | { 36 | Title = new Title("Title 1"), 37 | Content = new Content("Content PageCreate") 38 | }; 39 | if (await client.IsValidJWTokenAsync()) 40 | { 41 | var createdPage = await client.Pages.CreateAsync(page); 42 | } 43 | ``` 44 | 45 | ## Update Page 46 | 47 | ```C# 48 | // returns updated page 49 | var page= client.Pages.GetByIDAsync(123); 50 | page.Content.Raw = "New Content"; 51 | if (await client.IsValidJWTokenAsync()) 52 | { 53 | var updatedPage = await client.Pages.UpdateAsync(page); 54 | } 55 | ``` 56 | 57 | ## Delete Page 58 | 59 | ```C# 60 | // returns result of deletion 61 | if (await client.IsValidJWTokenAsync()) 62 | { 63 | var result = await client.Pages.DeleteAsync(123); 64 | } 65 | ``` -------------------------------------------------------------------------------- /.github/workflows/integration-tests.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Integration Tests 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | env: 17 | solution: 'WordPressPCL.sln' 18 | buildPlatform: Any CPU 19 | buildConfiguration: Release 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@master 25 | - name: Setup .NET SDK 26 | uses: actions/setup-dotnet@v4 27 | with: 28 | dotnet-version: 8.x 29 | - name: Dotnet Restore 30 | run: dotnet restore ${{ env.solution }} --disable-parallel 31 | - name: Build Solution 32 | run: dotnet build ${{ env.solution }} -c Release --no-restore 33 | 34 | - name: Create dockerzied WordPress 35 | run: | 36 | cd dev 37 | docker compose up -d 38 | docker ps 39 | sleep 20s 40 | curl --fail http://localhost:8080/wp-json 41 | 42 | - name: 'Run Tests: Selfhosted WordPress with JWT-AUTH' 43 | run: dotnet test -l "console;verbosity=detailed" WordPressPCL.Tests.Selfhosted/WordPressPCL.Tests.Selfhosted.csproj -s WordPressPCL.Tests.Selfhosted/jwtauth.runsettings 44 | -------------------------------------------------------------------------------- /WordPressPCL/Interfaces/IReadOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace WordPressPCL.Interfaces 5 | { 6 | /// 7 | /// Interface with required Get methods 8 | /// 9 | /// return class type 10 | public interface IReadOperation 11 | { 12 | /// 13 | /// Get latest 14 | /// 15 | /// Is use embed info 16 | /// Is use auth header 17 | /// requested object 18 | Task> GetAsync(bool embed = false, bool useAuth = false); 19 | 20 | /// 21 | /// Get object by Id 22 | /// 23 | /// Object Id 24 | /// Is use embed info 25 | /// Is use auth header 26 | /// requested object 27 | Task GetByIDAsync(object ID, bool embed = false, bool useAuth = false); 28 | 29 | /// 30 | /// Get all objects 31 | /// 32 | /// Is use embed info 33 | /// Is use auth header 34 | /// List of objects 35 | Task> GetAllAsync(bool embed = false, bool useAuth = false); 36 | } 37 | } -------------------------------------------------------------------------------- /docs/I version 2.x/customization/httpResponsePreProcessing.md: -------------------------------------------------------------------------------- 1 | # HttpResponsePreProcessing 2 | 3 | Sometimes it's necessary to pre-process the HttpResponseMessage before deserializeing it. 4 | 5 | To do this, you can use the "HttpResponsePreProcessing" to pass a function to the WordPressClient. 6 | This function takes the HttpResponse content string as a parameter, and has to return a string that will be deserialized. 7 | 8 | ```C# 9 | // Add a HttpResponsePreProcessing function : 10 | client.HttpResponsePreProcessing = (response) => 11 | { 12 | string updatedResponse = response; 13 | 14 | // Do something here on the updatedResponse 15 | 16 | return updatedResponse; 17 | }; 18 | ``` 19 | 20 | ## WordPress on Azure / ISS 21 | 22 | When deploying a WordPress website on Microsoft Azure, the REST API will add [unnecessary HTML before every POST request](https://social.msdn.microsoft.com/Forums/azure/en-US/8faac1fa-3d47-4149-aec0-f0a9a09f9744/php-wordpress-rest-api-prepending-html-with-location-header?forum=windowsazurewebsitespreview). 23 | 24 | In order to correct this, you can add a HttpResponsePreProcessing function that will delete the HTML code that is preventing the deserialization of the JSON content: 25 | 26 | ```C# 27 | client.HttpResponsePreProcessing = (responseString) => 28 | { 29 | var clearedString = responseString.Replace("\n", ""); 30 | var regex = @"\"; 31 | return System.Text.RegularExpressions.Regex.Replace(clearedString, regex, ""); 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/II version 1.x/customization/httpResponsePreProcessing.md: -------------------------------------------------------------------------------- 1 | # HttpResponsePreProcessing 2 | 3 | Sometimes it's necessary to pre-process the HttpResponseMessage before deserializeing it. 4 | 5 | To do this, you can use the "HttpResponsePreProcessing" to pass a function to the WordPressClient. 6 | This function takes the HttpResponse content string as a parameter, and has to return a string that will be deserialized. 7 | 8 | ```C# 9 | // Add a HttpResponsePreProcessing function : 10 | client.HttpResponsePreProcessing = (response) => 11 | { 12 | string updatedResponse = response; 13 | 14 | // Do something here on the updatedResponse 15 | 16 | return updatedResponse; 17 | }; 18 | ``` 19 | 20 | ## WordPress on Azure / ISS 21 | 22 | When deploying a WordPress website on Microsoft Azure, the REST API will add [unnecessary HTML before every POST request](https://social.msdn.microsoft.com/Forums/azure/en-US/8faac1fa-3d47-4149-aec0-f0a9a09f9744/php-wordpress-rest-api-prepending-html-with-location-header?forum=windowsazurewebsitespreview). 23 | 24 | In order to correct this, you can add a HttpResponsePreProcessing function that will delete the HTML code that is preventing the deserialization of the JSON content: 25 | 26 | ```C# 27 | client.HttpResponsePreProcessing = (responseString) => 28 | { 29 | var clearedString = responseString.Replace("\n", ""); 30 | var regex = @"\"; 31 | return System.Text.RegularExpressions.Regex.Replace(clearedString, regex, ""); 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/II version 1.x/entities/categories.md: -------------------------------------------------------------------------------- 1 | # Categories 2 | 3 | Here is a list of methods and examples of working with Categories 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all categories 9 | var categories = await client.Categories.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns category by ID 16 | var category = await client.Categories.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new CategoriesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var categories = await client.Categories.Query(queryBuilder); 28 | ``` 29 | 30 | ## Create new Category 31 | 32 | ```C# 33 | // returns created category 34 | var category = new Category() 35 | { 36 | Name = "Title 1", 37 | Description = "Content" 38 | }; 39 | if (await client.IsValidJWToken()) 40 | { 41 | var createdCategory = await client.Categories.Create(category); 42 | } 43 | ``` 44 | 45 | ## Update Category 46 | 47 | ```C# 48 | // returns updated category 49 | var category = client.Categories.GetByID(123); 50 | category.Name = "New Name"; 51 | if (await client.IsValidJWToken()) 52 | { 53 | var updatedCategory = await client.Categories.Update(category); 54 | } 55 | ``` 56 | 57 | ## Delete Category 58 | 59 | ```C# 60 | // returns result of deletion 61 | if (await client.IsValidJWToken()) 62 | { 63 | var result = await client.Categories.Delete(123); 64 | } 65 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/entities/categories.md: -------------------------------------------------------------------------------- 1 | # Categories 2 | 3 | Here is a list of methods and examples of working with Categories 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // returns all categories 9 | var categories = await client.Categories.GetAllAsync(); 10 | ``` 11 | 12 | ## Get by ID 13 | 14 | ```C# 15 | // returns category by ID 16 | var category = await client.Categories.GetByIDAsync(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new CategoriesQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var categories = await client.Categories.QueryAsync(queryBuilder); 28 | ``` 29 | 30 | ## Create new Category 31 | 32 | ```C# 33 | // returns created category 34 | var category = new Category() 35 | { 36 | Name = "Title 1", 37 | Description = "Content" 38 | }; 39 | if (await client.IsValidJWToken()) 40 | { 41 | var createdCategory = await client.Categories.CreateAsync(category); 42 | } 43 | ``` 44 | 45 | ## Update Category 46 | 47 | ```C# 48 | // returns updated category 49 | var category = client.Categories.GetByIDAsync(123); 50 | category.Name = "New Name"; 51 | if (await client.IsValidJWTokenAsync()) 52 | { 53 | var updatedCategory = await client.Categories.UpdateAsync(category); 54 | } 55 | ``` 56 | 57 | ## Delete Category 58 | 59 | ```C# 60 | // returns result of deletion 61 | if (await client.IsValidJWTokenAsync()) 62 | { 63 | var result = await client.Categories.DeleteAsync(123); 64 | } 65 | ``` -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Themes_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using WordPressPCL.Models; 6 | using WordPressPCL.Tests.Selfhosted.Utility; 7 | using WordPressPCL.Utility; 8 | 9 | namespace WordPressPCL.Tests.Selfhosted; 10 | 11 | [TestClass] 12 | public class Themes_Tests 13 | { 14 | private static WordPressClient _clientAuth; 15 | 16 | [ClassInitialize] 17 | public static async Task Init(TestContext testContext) 18 | { 19 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 20 | } 21 | 22 | [TestMethod] 23 | public async Task Themes_GetActive() 24 | { 25 | List themes = await _clientAuth.Themes.QueryAsync(new ThemesQueryBuilder { Status = ActivationStatus.Active }, useAuth:true); 26 | Assert.IsNotNull(themes); 27 | Assert.AreNotEqual(themes.Count, 0); 28 | 29 | } 30 | [TestMethod] 31 | public async Task Themes_Get() 32 | { 33 | List themes = await _clientAuth.Themes.GetAsync (useAuth: true); 34 | Assert.IsNotNull(themes); 35 | Assert.AreNotEqual(themes.Count, 0); 36 | CollectionAssert.AllItemsAreUnique(themes.Select(tag => tag.Stylesheet).ToList()); 37 | } 38 | 39 | [TestMethod] 40 | public async Task Themes_GetByID() 41 | { 42 | Theme theme = await _clientAuth.Themes.GetByIDAsync("twentytwentyfour", useAuth: true); 43 | Assert.IsNotNull(theme); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /docs/II version 1.x/entities/posts.md: -------------------------------------------------------------------------------- 1 | # Posts 2 | 3 | Here is a list of methods and examples of working with Posts 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all posts 9 | var posts = await client.Posts.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns post by ID 16 | var post = await client.Posts.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new PostsQueryBuilder(); 24 | queryBuilder.PerPage=40; 25 | queryBuilder.Page=2; 26 | queryBuilder.Categories= new int[]{1,2,3}; 27 | var posts = await client.Posts.Query(queryBuilder); 28 | ``` 29 | 30 | ## Create new Post 31 | 32 | ```C# 33 | // returns created post 34 | var post = new Post() 35 | { 36 | Title = new Title("Title 1"), 37 | Content = new Content("Content PostCreate") 38 | }; 39 | if (await client.IsValidJWToken()) 40 | { 41 | var createdPost = await client.Posts.Create(post); 42 | } 43 | ``` 44 | 45 | ## Update Post 46 | 47 | ```C# 48 | // returns updated post 49 | var post = client.Posts.GetByID(123); 50 | post.Content.Raw = "New Content"; 51 | if (await client.IsValidJWToken()) 52 | { 53 | var updatedPost = await client.Posts.Update(post); 54 | } 55 | ``` 56 | 57 | ## Delete Post 58 | 59 | ```C# 60 | // returns result of deletion 61 | if (await client.IsValidJWToken()) 62 | { 63 | var result = await client.Posts.Delete(123); 64 | } 65 | ``` 66 | 67 | ## Get Post Revisions 68 | 69 | ```C# 70 | // returns revisions of post 71 | var revisions = await client.Posts.Revisions(123); 72 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Models/ApplicationPassword.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Application Password 8 | /// 9 | public class ApplicationPassword 10 | { 11 | /// 12 | /// Unique ID 13 | /// 14 | [JsonProperty("uuid")] 15 | public string Uuid { get; set; } 16 | 17 | /// 18 | /// App ID 19 | /// 20 | [JsonProperty("app_id")] 21 | public string AppId { get; set; } 22 | 23 | /// 24 | /// App Name 25 | /// 26 | [JsonProperty("name")] 27 | public string Name { get; set; } 28 | 29 | /// 30 | /// Created Timestamp 31 | /// 32 | [JsonProperty("created")] 33 | public DateTime Created { get; set; } 34 | 35 | /// 36 | /// Last Used 37 | /// 38 | [JsonProperty("last_used")] 39 | public object LastUsed { get; set; } 40 | 41 | /// 42 | /// Last IP 43 | /// 44 | [JsonProperty("last_ip")] 45 | public object LastIp { get; set; } 46 | 47 | /// 48 | /// Application Password 49 | /// 50 | [JsonProperty("password")] 51 | public string Password { get; set; } 52 | 53 | /// 54 | /// Links 55 | /// 56 | [JsonProperty("_links")] 57 | public Links Links { get; set; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /docs/II version 1.x/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## WordPressClient throws WPException 4 | This happens when the rest api has received your request but for various reasons can not process it. You can catch this exception and in the additional information you can find out exactly what problems are with your request. 5 | To do this, you can access the **BadRequest** property, which contains the fields: 6 | * Name - the service name of the problem 7 | * Message - description of the problem 8 | * Data is a dynamic object that contains additional information about the exception 9 | Example: 10 | ```C# 11 | try 12 | { 13 | //create empty post is not allowed 14 | var post = await client.Posts.Create(new Post()); 15 | } 16 | catch (WPException wpex) 17 | { 18 | wpex.BadRequest.Name //system name 19 | wpex.BadRequest.Message //description 20 | wpex.BadRequest.Data //dynamic object with any non-structured additional info 21 | } 22 | ``` 23 | 24 | ### WordPressPCL.Models.Exceptions.WPException: No route was found matching the URL and request method 25 | This usually happens when a serverside redirect to retreive a new JWT Token changes the POST request into a GET request (e.g. on www-to-non-www or http-to-https redirects). 26 | 27 | ## WordPressClient returns `null` 28 | * Check your WordPress URL. It should look like this: `https://wordpress-site.com/wp-json/` 29 | * Check your request. If you're making a post query that requires the edit context, or for whatever reason requires auth headers, you need to tell the query method to include the auth headers using an additional parameter, as it will *not* by default. Ex: `_client.Posts.Query(query, true);` -------------------------------------------------------------------------------- /docs/I version 2.x/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## WordPressClient throws WPException 4 | This happens when the rest api has received your request but for various reasons can not process it. You can catch this exception and in the additional information you can find out exactly what problems are with your request. 5 | To do this, you can access the **BadRequest** property, which contains the fields: 6 | * Name - the service name of the problem 7 | * Message - description of the problem 8 | * Data is a dynamic object that contains additional information about the exception 9 | Example: 10 | ```C# 11 | try 12 | { 13 | //create empty post is not allowed 14 | var post = await client.Posts.CreateAsync(new Post()); 15 | } 16 | catch (WPException wpex) 17 | { 18 | wpex.BadRequest.Name //system name 19 | wpex.BadRequest.Message //description 20 | wpex.BadRequest.Data //dynamic object with any non-structured additional info 21 | } 22 | ``` 23 | 24 | ### WordPressPCL.Models.Exceptions.WPException: No route was found matching the URL and request method 25 | This usually happens when a serverside redirect to retreive a new JWT Token changes the POST request into a GET request (e.g. on www-to-non-www or http-to-https redirects). 26 | 27 | ## WordPressClient returns `null` 28 | * Check your WordPress URL. It should look like this: `https://wordpress-site.com/wp-json/` 29 | * Check your request. If you're making a post query that requires the edit context, or for whatever reason requires auth headers, you need to tell the query method to include the auth headers using an additional parameter, as it will *not* by default. Ex: `_client.Posts.QueryAsync(query, true);` -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Taxonomies_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Utility; 4 | using WordPressPCL.Tests.Selfhosted.Utility; 5 | using WordPressPCL.Models; 6 | using System.Collections.Generic; 7 | 8 | namespace WordPressPCL.Tests.Selfhosted; 9 | 10 | [TestClass] 11 | public class Taxonomies_Tests 12 | { 13 | private static WordPressClient _clientAuth; 14 | 15 | [ClassInitialize] 16 | public static async Task Init(TestContext testContext) 17 | { 18 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 19 | } 20 | 21 | [TestMethod] 22 | public async Task Taxonomies_Read() 23 | { 24 | List taxonomies = await _clientAuth.Taxonomies.GetAllAsync(); 25 | Assert.IsNotNull(taxonomies); 26 | Assert.AreNotEqual(taxonomies.Count, 0); 27 | } 28 | 29 | [TestMethod] 30 | public async Task Taxonomies_Get() 31 | { 32 | List taxonomies = await _clientAuth.Taxonomies.GetAsync(); 33 | Assert.IsNotNull(taxonomies); 34 | Assert.AreNotEqual(taxonomies.Count, 0); 35 | } 36 | 37 | [TestMethod] 38 | public async Task Taxonomies_Query() 39 | { 40 | TaxonomiesQueryBuilder queryBuilder = new() 41 | { 42 | Type = "post" 43 | }; 44 | List queryresult = await _clientAuth.Taxonomies.QueryAsync(queryBuilder); 45 | Assert.AreEqual("?type=post&order=desc&context=view", queryBuilder.BuildQuery()); 46 | Assert.IsNotNull(queryresult); 47 | Assert.AreNotSame(queryresult.Count, 0); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /WordPressPCL/Client/Settings.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using Newtonsoft.Json; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Client { 8 | /// 9 | /// Client class for interaction with Settings endpoints of WP REST API 10 | /// 11 | public class Settings { 12 | 13 | private readonly HttpHelper _httpHelper; 14 | 15 | /// 16 | /// Constructor 17 | /// 18 | /// reference to HttpHelper class for interaction with HTTP 19 | public Settings(HttpHelper httpHelper) { 20 | _httpHelper = httpHelper; 21 | } 22 | 23 | /// 24 | /// Get site settings 25 | /// 26 | /// Site settings 27 | public Task GetSettingsAsync() 28 | { 29 | return _httpHelper.GetRequestAsync("settings", false, true); 30 | } 31 | 32 | /// 33 | /// Update site settings 34 | /// 35 | /// Settings object 36 | /// Updated settings 37 | public async Task UpdateSettingsAsync(Models.Settings settings) 38 | { 39 | using var postBody = new StringContent(JsonConvert.SerializeObject(settings), Encoding.UTF8, "application/json"); 40 | var (setting, _) = await _httpHelper.PostRequestAsync("settings", postBody).ConfigureAwait(false); 41 | return setting; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /docs/II version 1.x/entities/users.md: -------------------------------------------------------------------------------- 1 | # Users 2 | 3 | Here is a list of methods and examples of working with Users 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // execute request users without credentials - returns only you 9 | var users = await client.Users.GetAll(); 10 | 11 | // send credentials - list of all users 12 | var users = await client.Users.GetAll(useAuth:true); 13 | ``` 14 | 15 | ## GetByID 16 | 17 | ```C# 18 | // returns user by ID 19 | var user = await client.Users.GetByID(123); 20 | ``` 21 | 22 | ## GetCurrentUser 23 | 24 | ```C# 25 | // returns current user 26 | var user = await client.Users.GetCurrentUser(); 27 | ``` 28 | 29 | ## Query 30 | Create parametrized request 31 | ```C# 32 | // returns result of query 33 | var queryBuilder = new UsersQueryBuilder(); 34 | queryBuilder.PerPage = 40; 35 | queryBuilder.Page = 2; 36 | queryBuilder.Before = DateTime.Now; 37 | var users = await client.Users.Query(queryBuilder); 38 | ``` 39 | 40 | ## Create new User 41 | 42 | ```C# 43 | // returns created user 44 | var user = new User("username","email","password") 45 | { 46 | NickName= "nickname" 47 | }; 48 | if (await client.IsValidJWToken()) 49 | { 50 | var user = await client.Users.Create(user); 51 | } 52 | ``` 53 | 54 | ## Update User 55 | 56 | ```C# 57 | // returns updated user 58 | var user = client.Users.GetByID(123); 59 | user.Name = "New Name"; 60 | if (await client.IsValidJWToken()) 61 | { 62 | var updatedUser = await client.Users.Update(user); 63 | } 64 | ``` 65 | 66 | ## Delete User 67 | 68 | ```C# 69 | // returns result of deletion 70 | if (await client.IsValidJWToken()) 71 | { 72 | //second param - user to reassign all content 73 | var result = await client.Users.Delete(123,321); 74 | } 75 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Client/Themes.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Models; 4 | using WordPressPCL.Utility; 5 | 6 | namespace WordPressPCL.Client 7 | { 8 | /// 9 | /// Client class for interaction with Themes endpoint WP REST API 10 | /// Date: 26 May 2023 11 | /// Creator: Gregory Liénard 12 | /// 13 | public class Themes : CRUDOperation 14 | { 15 | #region Init 16 | 17 | private const string _methodPath = "themes"; 18 | 19 | /// 20 | /// Constructor 21 | /// 22 | /// reference to HttpHelper class for interaction with HTTP 23 | public Themes(HttpHelper HttpHelper) : base(HttpHelper, _methodPath) 24 | { 25 | } 26 | 27 | #endregion Init 28 | 29 | #region Custom 30 | 31 | 32 | 33 | /// 34 | /// Get themes by search term 35 | /// 36 | /// active or inactive 37 | /// include embed info 38 | /// List of posts 39 | public Task> GetThemesByActivationStatusAsync(ActivationStatus activationStatus, bool embed = false) 40 | { 41 | // default values 42 | // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date 43 | return HttpHelper.GetRequestAsync>(_methodPath.SetQueryParam("status", activationStatus.ToString().ToLower()), embed, true); 44 | } 45 | 46 | 47 | 48 | #endregion Custom 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Category.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// Terms of the type category 7 | /// 8 | public class Category : Term 9 | { 10 | /// 11 | /// Number of published posts for the term. 12 | /// 13 | /// 14 | /// Read only 15 | /// Context: view, edit 16 | /// 17 | [JsonProperty("count")] 18 | public int Count { get; set; } 19 | 20 | /// 21 | /// HTML description of the term. 22 | /// 23 | /// Context: view, edit 24 | [JsonProperty("description")] 25 | public string Description { get; set; } 26 | 27 | /// 28 | /// The parent term ID. 29 | /// 30 | /// Context: view, edit 31 | [JsonProperty("parent", NullValueHandling = NullValueHandling.Ignore)] 32 | public int Parent { get; set; } 33 | 34 | /// 35 | /// Meta fields. 36 | /// 37 | /// Context: view, edit 38 | [JsonProperty("meta", DefaultValueHandling = DefaultValueHandling.Ignore)] 39 | public dynamic Meta { get; set; } 40 | 41 | /// 42 | /// Parameterless constructor 43 | /// 44 | public Category() : base() 45 | { 46 | } 47 | 48 | /// 49 | /// Default constructor 50 | /// 51 | /// 52 | public Category(string name) : this() 53 | { 54 | Name = name; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/HttpClient_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | using WordPressPCL.Models; 8 | using WordPressPCL.Tests.Selfhosted.Utility; 9 | 10 | namespace WordPressPCL.Tests.Selfhosted; 11 | 12 | [TestClass] 13 | public class HttpClient_Tests 14 | { 15 | [TestMethod] 16 | public async Task CustomHttpClient_WithBaseAddress() 17 | { 18 | // Initialize 19 | HttpClient httpClient = new() 20 | { 21 | BaseAddress = new Uri(ApiCredentials.WordPressUri) 22 | }; 23 | 24 | await CustomHttpClientBase(httpClient, new WordPressClient(httpClient)); 25 | } 26 | 27 | [TestMethod] 28 | public async Task CustomHttpClient_WithoutBaseAddress() 29 | { 30 | // Initialize 31 | HttpClient httpClient = new(); 32 | WordPressClient wordPressClient = new(httpClient, uri: new Uri(ApiCredentials.WordPressUri)); 33 | await CustomHttpClientBase(httpClient, wordPressClient); 34 | } 35 | 36 | private static async Task CustomHttpClientBase(HttpClient httpClient, WordPressClient client) 37 | { 38 | httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0"); 39 | httpClient.DefaultRequestHeaders.Add("Referer", "https://github.com/wp-net/WordPressPCL"); 40 | 41 | List posts = await client.Posts.GetAllAsync(); 42 | Post post = await client.Posts.GetByIDAsync(posts.First().Id); 43 | Assert.IsTrue(posts.First().Id == post.Id); 44 | Assert.IsTrue(!string.IsNullOrEmpty(posts.First().Content.Rendered)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /dev/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | image: mysql:5.7 4 | container_name: db 5 | environment: 6 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} 7 | MYSQL_DATABASE: ${MYSQL_DATABASE} 8 | MYSQL_USER: ${MYSQL_USER} 9 | MYSQL_PASSWORD: ${MYSQL_PASSWORD} 10 | restart: unless-stopped 11 | 12 | wordpress: 13 | depends_on: 14 | - db 15 | build: 16 | context: . 17 | image: wordpress-custom 18 | volumes: 19 | - wp-volume:/var/www/html 20 | ports: 21 | - "8080:80" 22 | container_name: wordpress 23 | environment: 24 | WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST} 25 | WORDPRESS_DB_USER: ${WORDPRESS_DB_USER} 26 | WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} 27 | WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME} 28 | WORDPRESS_DEBUG: ${WORDPRESS_DEBUG} 29 | WORDPRESS_CONFIG_EXTRA: define('JWT_AUTH_SECRET_KEY', '.|)WaUppb-a>Z;(%p,4~^|5jjWom6Gw{(2_lqJv9r&]p 44 | /bin/sh -c ' 45 | sleep 10; 46 | wp core install --path="/var/www/html" --url="http://localhost:8080" --title="Test Instance" --admin_user=wordpress --admin_password=wordpress --admin_email=test@example.com; 47 | wp option update permalink_structure "/%postname%"; 48 | wp plugin install application-passwords-enable --activate; 49 | wp plugin install contact-form-7 --activate; 50 | wp plugin install jwt-auth --activate;' 51 | 52 | volumes: 53 | wp-volume: 54 | 55 | networks: 56 | default: 57 | name: net1 -------------------------------------------------------------------------------- /WordPressPCL/Models/Term.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// This is the base class for all terms, like categories and tags 7 | /// 8 | public class Term : Base 9 | { 10 | /// 11 | /// URL of the term. 12 | /// 13 | /// 14 | /// Read only 15 | /// Context: view, embed, edit 16 | /// 17 | [JsonProperty("link")] 18 | public string Link { get; set; } 19 | 20 | /// 21 | /// HTML title for the term. 22 | /// 23 | /// Context: view, embed, edit 24 | [JsonProperty("name")] 25 | public string Name { get; set; } 26 | 27 | /// 28 | /// An alphanumeric identifier for the term unique to its type. 29 | /// 30 | /// Context: view, embed, edit 31 | [JsonProperty("slug")] 32 | public string Slug { get; set; } 33 | 34 | /// 35 | /// Type attribution for the term. 36 | /// 37 | /// 38 | /// Read only 39 | /// Context: view, embed, edit 40 | /// One of: category, post_tag, nav_menu, link_category, post_format 41 | /// 42 | [JsonProperty("taxonomy")] 43 | public string Taxonomy { get; set; } 44 | 45 | /// 46 | /// Links to related resources 47 | /// 48 | [JsonProperty("_links", DefaultValueHandling = DefaultValueHandling.Ignore)] 49 | public Links Links { get; set; } 50 | 51 | /// 52 | /// parameterless constructor 53 | /// 54 | public Term() 55 | { 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/ListPosts_QueryBuilder_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WordPressPCL.Tests.Selfhosted.Utility; 3 | using System.Threading.Tasks; 4 | using WordPressPCL.Utility; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using WordPressPCL.Models; 8 | 9 | namespace WordPressPCL.Tests.Selfhosted; 10 | 11 | [TestClass] 12 | public class ListPosts_QueryBuilder_Tests 13 | { 14 | private static WordPressClient _client; 15 | 16 | [ClassInitialize] 17 | public static void Init(TestContext testContext) 18 | { 19 | _client = ClientHelper.GetWordPressClient(); 20 | } 21 | 22 | // TODO: initialize more test posts 23 | //[TestMethod] 24 | public async Task List_Posts_QueryBuilder_Test_Pagination() 25 | { 26 | // Posts 27 | List postsA = await _client.Posts.QueryAsync(new PostsQueryBuilder() 28 | { 29 | Page = 1, 30 | PerPage = 2 31 | }); 32 | List postsB = await _client.Posts.QueryAsync(new PostsQueryBuilder() 33 | { 34 | Page = 2, 35 | PerPage = 2 36 | }); 37 | Assert.IsNotNull(postsA); 38 | Assert.IsNotNull(postsB); 39 | Assert.AreNotEqual(postsA.Count, 0); 40 | Assert.AreNotEqual(postsB.Count, 0); 41 | CollectionAssert.AreNotEqual(postsA.Select(post => post.Id).ToList(), postsB.Select(post => post.Id).ToList()); 42 | } 43 | 44 | [TestMethod] 45 | [Description("Test that the ListPosts method with the QueryBuilder set to list posts published After a specific date works.")] 46 | public async Task List_Posts_QueryBuilder_After() 47 | { 48 | // Posts 49 | List posts = await _client.Posts.QueryAsync(new PostsQueryBuilder { After = System.DateTime.Parse("2017-05-22T13:41:09") }); 50 | Assert.IsNotNull(posts); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /docs/I version 2.x/entities/media.md: -------------------------------------------------------------------------------- 1 | # Media 2 | 3 | Here is a list of methods and examples of working with Media 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all media 9 | var media = await client.Media.GetAllAsync(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns media by ID 16 | var media = await client.Media.GetByIDAsync(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new MediaQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var media = await client.Pages.QueryAsync(queryBuilder); 28 | ``` 29 | 30 | ## Create new Media 31 | ### Create from Stream 32 | 33 | ```C# 34 | // returns created media 35 | // for create media item you must read them to Stream. Media items can be audio, video, image, pdf ot any othe type supported by wordpress 36 | Stream s = File.OpenRead("pathToMedia/media.jpg"); 37 | if (await client.IsValidJWTokenAsync()) 38 | { 39 | var createdMedia = await client.Media.CreateAsync(s,"media.jpg"); 40 | } 41 | ``` 42 | ### Create from file path 43 | 44 | ```C# 45 | // returns created media 46 | // for create media item you must read them to Stream. Media items can be audio, video, image, pdf ot any othe type supported by wordpress 47 | if (await client.IsValidJWToken()) 48 | { 49 | var createdMedia = await client.Media.CreateAsync(@"C:\pathToFile\media.jpg","media.jpg"); 50 | } 51 | ``` 52 | 53 | ## Update Media 54 | 55 | ```C# 56 | // returns updated media 57 | var media= client.Media.GetByID(123); 58 | media.Title.Raw = "New Title"; 59 | 60 | if (await client.IsValidJWTokenAsync()) 61 | { 62 | var updatedMedia = await client.Media.UpdateAsync(media); 63 | } 64 | ``` 65 | 66 | ## Delete Media 67 | 68 | ```C# 69 | // returns result of deletion 70 | if (await client.IsValidJWTokenAsync()) 71 | { 72 | var result = await client.Media.DeleteAsync(123); 73 | } 74 | ``` -------------------------------------------------------------------------------- /docs/II version 1.x/entities/comments.md: -------------------------------------------------------------------------------- 1 | # Comments 2 | 3 | Here is a list of methods and examples of working with Comments 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all comments 9 | var comments = await client.Comments.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns comment by ID 16 | var comment = await client.Comments.GetByID(123); 17 | ``` 18 | 19 | ## GetCommentsForPost 20 | 21 | ```C# 22 | // returns comments from post 23 | var comments = await client.Comments.GetCommentsForPost(123) 24 | ``` 25 | 26 | ## Query 27 | Create parametrized request 28 | ```C# 29 | // returns result of query 30 | var queryBuilder = new CommentsQueryBuilder(); 31 | queryBuilder.PerPage = 40; 32 | queryBuilder.Page = 2; 33 | queryBuilder.Before = DateTime.Now; 34 | var comments = await client.Comments.Query(queryBuilder); 35 | ``` 36 | 37 | ## Get threaded comments 38 | If your blog supports threaded comments (comments with direct answers) you can order and get the right depth for them with this handy extension method: 39 | 40 | ```c# 41 | var comments = await client.Comments.GetCommentsForPost(123) 42 | var commentsThreaded = comments.ToThreaded(); 43 | ``` 44 | 45 | ## Create new Comment 46 | 47 | ```C# 48 | // returns created comment 49 | var comment = new Comment() 50 | { 51 | Content = new Content("Comment"), 52 | PostId = 123, 53 | AuthorId = 1, 54 | AuthorEmail = "test@test.com" 55 | }; 56 | if (await client.IsValidJWToken()) 57 | { 58 | var createdComment = await client.Comments.Create(comment); 59 | } 60 | ``` 61 | 62 | ## Update Comment 63 | 64 | ```C# 65 | // returns updated comment 66 | var comment= client.Comments.GetByID(123); 67 | comment.Content.Raw = "New Content"; 68 | if (await client.IsValidJWToken()) 69 | { 70 | var updatedComment = await client.Comments.Update(comment); 71 | } 72 | ``` 73 | 74 | ## Delete Comment 75 | 76 | ```C# 77 | // returns result of deletion 78 | if (await client.IsValidJWToken()) 79 | { 80 | var result = await client.Comments.Delete(123); 81 | } 82 | ``` -------------------------------------------------------------------------------- /docs/I version 2.x/entities/comments.md: -------------------------------------------------------------------------------- 1 | Here is a list of methods and examples of working with Comments 2 | 3 | ## Get All 4 | 5 | ```C# 6 | // returns all comments 7 | var comments = await client.Comments.GetAllAsync(); 8 | ``` 9 | 10 | ## Get By ID 11 | 12 | ```C# 13 | // returns comment by ID 14 | var comment = await client.Comments.GetByIDAsync(123); 15 | ``` 16 | 17 | ## Get Comments by Post ID 18 | 19 | ```C# 20 | // returns comments from post 21 | var comments = await client.Comments.GetCommentsForPostAsync(123) 22 | ``` 23 | 24 | ## Query 25 | Create parametrized request 26 | ```C# 27 | // returns result of query 28 | var queryBuilder = new CommentsQueryBuilder(); 29 | queryBuilder.PerPage = 40; 30 | queryBuilder.Page = 2; 31 | queryBuilder.Before = DateTime.Now; 32 | var comments = await client.Comments.Query(queryBuilder); 33 | ``` 34 | 35 | ## Get threaded comments 36 | If your blog supports threaded comments (comments with direct answers) you can order and get the right depth for them with this handy extension method: 37 | 38 | ```c# 39 | var comments = await client.Comments.GetCommentsForPostAsync(123) 40 | var commentsThreaded = comments.ToThreaded(); 41 | ``` 42 | 43 | ## Create new Comment 44 | 45 | ```C# 46 | // returns created comment 47 | var comment = new Comment() 48 | { 49 | Content = new Content("Comment"), 50 | PostId = 123, 51 | AuthorId = 1, 52 | AuthorEmail = "test@test.com" 53 | }; 54 | if (await client.IsValidJWTokenAsync()) 55 | { 56 | var createdComment = await client.Comments.CreateAsync(comment); 57 | } 58 | ``` 59 | 60 | ## Update Comment 61 | 62 | ```C# 63 | // returns updated comment 64 | var comment= client.Comments.GetByIDAsync(123); 65 | comment.Content.Raw = "New Content"; 66 | if (await client.IsValidJWTokenAsync()) 67 | { 68 | var updatedComment = await client.Comments.UpdateAsync(comment); 69 | } 70 | ``` 71 | 72 | ## Delete Comment 73 | 74 | ```C# 75 | // returns result of deletion 76 | if (await client.IsValidJWToken()) 77 | { 78 | var result = await client.Comments.DeleteAsync(123); 79 | } 80 | ``` -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/ApplicationPasswords_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Tests.Selfhosted.Utility; 6 | 7 | namespace WordPressPCL.Tests.Selfhosted; 8 | 9 | [TestClass] 10 | public class ApplicationPasswords_Tests 11 | { 12 | private static WordPressClient _clientAuth; 13 | 14 | [ClassInitialize] 15 | public static async Task Init(TestContext testContext) 16 | { 17 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 18 | } 19 | 20 | [TestMethod] 21 | public async Task Application_Passwords_Create() 22 | { 23 | ApplicationPassword password = await _clientAuth.Users.CreateApplicationPasswordAsync(System.Guid.NewGuid().ToString()); 24 | Assert.IsNotNull(password.Password); 25 | } 26 | 27 | [TestMethod] 28 | public async Task Read() 29 | { 30 | await _clientAuth.Users.CreateApplicationPasswordAsync(System.Guid.NewGuid().ToString()); 31 | List passwords = await _clientAuth.Users.GetApplicationPasswords(); 32 | 33 | Assert.IsNotNull(passwords); 34 | Assert.AreNotEqual(0, passwords.Count); 35 | } 36 | 37 | [TestMethod] 38 | public async Task Application_Password_Auth() 39 | { 40 | ApplicationPassword appPassword = await _clientAuth.Users.CreateApplicationPasswordAsync(System.Guid.NewGuid().ToString()); 41 | WordPressClient appPasswordClient = new(ApiCredentials.WordPressUri); 42 | appPasswordClient.Auth.UseBasicAuth(ApiCredentials.Username, appPassword.Password); 43 | 44 | Post post = new() 45 | { 46 | Title = new Title("Title 1"), 47 | Content = new Content("Content PostCreate") 48 | }; 49 | Post postCreated = await appPasswordClient.Posts.CreateAsync(post); 50 | Assert.IsNotNull(postCreated); 51 | Assert.AreEqual("Title 1", postCreated.Title.Raw); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Exceptions/WPException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WordPressPCL.Models.Exceptions 4 | { 5 | /// 6 | /// WordPress request exceptions 7 | /// 8 | public class WPException : Exception 9 | { 10 | /// 11 | /// Constructor 12 | /// 13 | public WPException() 14 | { 15 | } 16 | 17 | /// 18 | /// Constructor 19 | /// 20 | /// 21 | public WPException(string message) : base(message) 22 | { 23 | } 24 | 25 | /// 26 | /// Constructor 27 | /// 28 | /// 29 | /// 30 | public WPException(string message, Exception innerException) : base(message, innerException) 31 | { 32 | } 33 | 34 | /// 35 | /// Constructor 36 | /// 37 | /// additional request info 38 | public WPException(BadRequest request) : base() { RequestData = request; } 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | /// additional message 44 | /// additional request info 45 | public WPException(string message, BadRequest request) : base(message) { RequestData = request; } 46 | 47 | /// 48 | /// Constructor 49 | /// 50 | /// additional message 51 | /// inner exception 52 | /// additional request info 53 | public WPException(string message, Exception inner, BadRequest request) : base(message, inner) { RequestData = request; } 54 | 55 | /// 56 | /// Bad request data info 57 | /// 58 | public BadRequest RequestData { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '27 3 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'csharp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v4 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | - name: Setup .NET SDK 54 | uses: actions/setup-dotnet@v4 55 | with: 56 | dotnet-version: 8.x 57 | - name: Build Solution 58 | run: dotnet build ${{ env.solution }} -c Release 59 | 60 | - name: Perform CodeQL Analysis 61 | uses: github/codeql-action/analyze@v1 62 | -------------------------------------------------------------------------------- /docs/II version 1.x/entities/media.md: -------------------------------------------------------------------------------- 1 | # Media 2 | 3 | Here is a list of methods and examples of working with Media 4 | 5 | ## GetAll() 6 | 7 | ```C# 8 | // returns all media 9 | var media = await client.Media.GetAll(); 10 | ``` 11 | 12 | ## GetByID 13 | 14 | ```C# 15 | // returns media by ID 16 | var media = await client.Media.GetByID(123); 17 | ``` 18 | 19 | ## Query 20 | Create parametrized request 21 | ```C# 22 | // returns result of query 23 | var queryBuilder = new MediaQueryBuilder(); 24 | queryBuilder.PerPage = 40; 25 | queryBuilder.Page = 2; 26 | queryBuilder.Before = DateTime.Now; 27 | var media = await client.Pages.Query(queryBuilder); 28 | ``` 29 | 30 | ## Create new Media 31 | ### Create for .Net Standard 1.1+ 32 | .Net Standard 1.1-1.3 doesn`t support file manipulation (read or write). So only way to send file content is to create Stream with file content manually. 33 | Applicable for .netcore 1.0 apps 34 | ```C# 35 | // returns created media 36 | // for create media item you must read them to Stream. Media items can be audio, video, image, pdf ot any othe type supported by wordpress 37 | Stream s = File.OpenRead("pathToMedia/media.jpg"); 38 | if (await client.IsValidJWToken()) 39 | { 40 | var createdMedia = await client.Media.Create(s,"media.jpg"); 41 | } 42 | ``` 43 | ### Create for .Net Standard 2.0+ 44 | .Net Standard 2.0 supports files manipulation. You can send media files by passing its full path to file. 45 | Applicable for .netcore 2.0 apps 46 | ```C# 47 | // returns created media 48 | // for create media item you must read them to Stream. Media items can be audio, video, image, pdf ot any othe type supported by wordpress 49 | if (await client.IsValidJWToken()) 50 | { 51 | var createdMedia = await client.Media.Create(@"C:\pathToFile\media.jpg","media.jpg"); 52 | } 53 | ``` 54 | 55 | ## Update Media 56 | 57 | ```C# 58 | // returns updated media 59 | var media= client.Media.GetByID(123); 60 | media.Title.Raw = "New Title"; 61 | 62 | if (await client.IsValidJWToken()) 63 | { 64 | var updatedMedia = await client.Media.Update(media); 65 | } 66 | ``` 67 | 68 | ## Delete Media 69 | 70 | ```C# 71 | // returns result of deletion 72 | if (await client.IsValidJWToken()) 73 | { 74 | var result = await client.Media.Delete(123); 75 | } 76 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Utility/UrlHelper.cs: -------------------------------------------------------------------------------- 1 | namespace WordPressPCL.Utility 2 | { 3 | /// 4 | /// Helper clas for URL manipulation 5 | /// 6 | public static class UrlHelper 7 | { 8 | /// 9 | /// Creates a new string with queryParam & queryValue appended as query parameters 10 | /// Ensures values are appended correctly with & or ? 11 | /// 12 | /// Absolute or relative URL 13 | /// Query Parameter Name 14 | /// Query Parameter Value 15 | /// New URL as string 16 | public static string SetQueryParam(this string url, string paramName, string paramValue) 17 | { 18 | if(url == null) 19 | { 20 | return null; 21 | } 22 | 23 | if (url.Contains("?")) 24 | { 25 | url += '&'; 26 | } 27 | else 28 | { 29 | url += '?'; 30 | } 31 | 32 | url += CombineEnsureSingleSeparator(paramName, paramValue, '='); 33 | return url; 34 | } 35 | 36 | /// 37 | /// Creates a new string with queryParam & queryValue appended as query parameters 38 | /// Ensures values are appended correctly with & or ? 39 | /// 40 | /// Absolute or relative URL 41 | /// Query Parameter Name 42 | /// Query Parameter Value 43 | /// New URL as string 44 | public static string SetQueryParam(this string url, string paramName, int paramValue) 45 | { 46 | return SetQueryParam(url, paramName, $"{paramValue}"); 47 | } 48 | 49 | private static string CombineEnsureSingleSeparator(string a, string b, char separator) 50 | { 51 | if (string.IsNullOrEmpty(a)) return b; 52 | if (string.IsNullOrEmpty(b)) return a; 53 | return a.TrimEnd(separator) + separator + b.TrimStart(separator); 54 | } 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /docs/I version 2.x/entities/posts.md: -------------------------------------------------------------------------------- 1 | # Posts 2 | 3 | Here is a list of methods and examples of working with Posts 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // returns all posts 9 | var posts = await client.Posts.GetAllAsync();``` 10 | 11 | ## Get By ID 12 | 13 | ```C# 14 | // returns post by ID 15 | var post = await client.Posts.GetByIDAsync(123); 16 | ``` 17 | 18 | ## Query 19 | Create parametrized request 20 | ```C# 21 | // returns result of query 22 | var queryBuilder = new PostsQueryBuilder(); 23 | queryBuilder.PerPage = 40; 24 | queryBuilder.Page = 2; 25 | queryBuilder.Categories = new int[]{1,2,3}; 26 | var posts = await client.Posts.QueryAsync(queryBuilder); 27 | ``` 28 | 29 | ## Create new Post 30 | 31 | ```C# 32 | // returns created post 33 | var post = new Post() 34 | { 35 | Title = new Title("Title 1"), 36 | Content = new Content("Content PostCreate") 37 | }; 38 | if (await client.IsValidJWTokenAsync()) 39 | { 40 | var createdPost = await client.Posts.CreateAsync(post); 41 | } 42 | ``` 43 | 44 | ## Update Post 45 | 46 | ```C# 47 | // returns updated post 48 | var post = client.Posts.GetByIDAsync(123); 49 | post.Content.Raw = "New Content"; 50 | if (await client.IsValidJWTokenAsync()) 51 | { 52 | var updatedPost = await client.Posts.UpdateAsync(post); 53 | } 54 | ``` 55 | 56 | ## Update Custom Fields 57 | 58 | ```C# 59 | var post = new Post 60 | { 61 | Id = 123, 62 | Meta = new Dictionary 63 | { 64 | ["my-custom-key"] = "some value" 65 | }, 66 | }; 67 | 68 | await client.Posts.UpdateAsync(post); 69 | ``` 70 | 71 | Please note that all meta keys need to be registered using [`register_post_meta()`](https://developer.wordpress.org/reference/functions/register_post_meta/) before you can use them, e.g. by using the following PHP snippet: 72 | 73 | ```php 74 | register_post_meta('post', 'my-custom-key', [ 75 | 'type' => 'string', 76 | 'single' => true, 77 | 'show_in_rest' => true, 78 | ]); 79 | ``` 80 | 81 | ## Delete Post 82 | 83 | ```C# 84 | // returns result of deletion 85 | if (await client.IsValidJWTokenAsync()) 86 | { 87 | var result = await client.Posts.DeleteAsync(123); 88 | } 89 | ``` 90 | 91 | ## Get Post Revisions 92 | 93 | ```C# 94 | // returns revisions of post 95 | var revisions = await client.Posts.RevisionsAsync(123); 96 | ``` -------------------------------------------------------------------------------- /WordPressPCL/Models/Exceptions/WPUnexpectedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | 4 | namespace WordPressPCL.Models.Exceptions 5 | { 6 | /// 7 | /// An exception resulting from a malformed WP response from a 8 | /// WP REST call, where the response is still a valid (but not 9 | /// successful) HTTP response. For example, the response returned 10 | /// from an endpoint that is not a WP REST endpoint 11 | /// 12 | public class WPUnexpectedException : Exception 13 | { 14 | /// 15 | /// Constructor 16 | /// 17 | public WPUnexpectedException() 18 | { 19 | } 20 | 21 | /// 22 | /// Constructor 23 | /// 24 | /// 25 | public WPUnexpectedException(string message) : base(message) 26 | { 27 | } 28 | 29 | /// 30 | /// Constructor 31 | /// 32 | /// 33 | /// 34 | public WPUnexpectedException(string message, Exception innerException) : base(message, innerException) 35 | { 36 | } 37 | 38 | /// 39 | /// Construct a WPUnexpectedException from a response 40 | /// 41 | /// The raw response 42 | /// The response body, if any 43 | public WPUnexpectedException(HttpResponseMessage response, string resonseBody) 44 | : base(FormatExceptionMessage(response)) 45 | { 46 | Response = response; 47 | ResponseBody = resonseBody; 48 | } 49 | 50 | /// 51 | /// The response that triggered the error 52 | /// 53 | public HttpResponseMessage Response { get; set; } 54 | 55 | /// 56 | /// The response body (if any) that was returned with the error status 57 | /// 58 | public string ResponseBody { get; set; } 59 | 60 | private static string FormatExceptionMessage(HttpResponseMessage response) 61 | { 62 | return $"Server returned HTTP status {response.StatusCode}"; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /WordPressPCL/Models/ImageMeta.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Meta info (EXIF) of image media 8 | /// 9 | /// 10 | public class ImageMeta 11 | { 12 | /// 13 | /// Aperture 14 | /// 15 | [JsonProperty("aperture")] 16 | public string Aperture { get; set; } 17 | /// 18 | /// Credit 19 | /// 20 | [JsonProperty("credit")] 21 | public string Credit { get; set; } 22 | /// 23 | /// Camera Model 24 | /// 25 | [JsonProperty("camera")] 26 | public string Camera { get; set; } 27 | /// 28 | /// Image Caption 29 | /// 30 | [JsonProperty("caption")] 31 | public string Caption { get; set; } 32 | /// 33 | /// Created Date 34 | /// 35 | [JsonProperty("created_timestamp")] 36 | public string CreatedTimestamp { get; set; } 37 | /// 38 | /// Copyright 39 | /// 40 | [JsonProperty("copyright")] 41 | public string Copyright { get; set; } 42 | /// 43 | /// Focal Length 44 | /// 45 | [JsonProperty("focal_length")] 46 | public string FocalLength { get; set; } 47 | /// 48 | /// ISO 49 | /// 50 | [JsonProperty("iso")] 51 | public string Iso { get; set; } 52 | /// 53 | /// Shutter Speed 54 | /// 55 | [JsonProperty("shutter_speed")] 56 | public string ShutterSpeed { get; set; } 57 | /// 58 | /// Image Title 59 | /// 60 | [JsonProperty("title")] 61 | public string Title { get; set; } 62 | /// 63 | /// Orientation 64 | /// 65 | [JsonProperty("orientation")] 66 | public string Orientation { get; set; } 67 | /// 68 | /// Image keywords 69 | /// 70 | [JsonProperty("keywords")] 71 | public IList Keywords { get; set; } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/PostRevisions_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using WordPressPCL.Models; 6 | using WordPressPCL.Tests.Selfhosted.Utility; 7 | 8 | namespace WordPressPCL.Tests.Selfhosted; 9 | 10 | [TestClass] 11 | public class PostRevisions_Tests 12 | { 13 | private static WordPressClient _clientAuth; 14 | 15 | [ClassInitialize] 16 | public static async Task Init(TestContext testContext) 17 | { 18 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 19 | } 20 | 21 | [TestMethod] 22 | public async Task PostRevisions_Read() 23 | { 24 | int id = await CreatePostWithRevision(); 25 | Client.PostRevisions revisionsclient = _clientAuth.Posts.Revisions(id); 26 | List revisions = await revisionsclient.GetAllAsync(); 27 | Assert.AreNotEqual(revisions.Count, 0); 28 | } 29 | 30 | [TestMethod] 31 | public async Task PostRevisions_Get() 32 | { 33 | int id = await CreatePostWithRevision(); 34 | Client.PostRevisions revisionsclient = _clientAuth.Posts.Revisions(id); 35 | List revisions = await revisionsclient.GetAsync(); 36 | Assert.AreNotEqual(revisions.Count, 0); 37 | } 38 | 39 | // TODO: check why revision can't be deleted 40 | //[TestMethod] 41 | public async Task PostRevisions_Delete() 42 | { 43 | int id = await CreatePostWithRevision(); 44 | 45 | Client.PostRevisions revisionsclient = _clientAuth.Posts.Revisions(id); 46 | List revisions = await revisionsclient.GetAllAsync(); 47 | Assert.AreNotEqual(revisions.Count, 0); 48 | bool res = await revisionsclient.DeleteAsync(revisions.First().Id); 49 | Assert.IsTrue(res); 50 | } 51 | 52 | private async Task CreatePostWithRevision() 53 | { 54 | Post post = new() 55 | { 56 | Title = new Title("Title 1"), 57 | Content = new Content("Content PostCreate"), 58 | }; 59 | Post createdPost = await _clientAuth.Posts.CreateAsync(post); 60 | createdPost.Content.Raw = "Updated Content"; 61 | Post updatedPost = await _clientAuth.Posts.UpdateAsync(createdPost); 62 | return updatedPost.Id; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Links.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Links helper class 8 | /// 9 | public class Links 10 | { 11 | /// 12 | /// Self link 13 | /// 14 | [JsonProperty("self")] 15 | public List Self { get; set; } 16 | 17 | /// 18 | /// Collection of links 19 | /// 20 | /// 21 | [JsonProperty("collection")] 22 | public List Collection { get; set; } 23 | 24 | /// 25 | /// About info 26 | /// 27 | /// 28 | [JsonProperty("about")] 29 | public List About { get; set; } 30 | 31 | /// 32 | /// WordPress post Type 33 | /// 34 | [JsonProperty("wp:post_type")] 35 | public List WpPostType { get; set; } 36 | 37 | /// 38 | /// Curries 39 | /// 40 | [JsonProperty("curies")] 41 | public List Curies { get; set; } 42 | 43 | /// 44 | /// Author 45 | /// 46 | [JsonProperty("author")] 47 | public List Author { get; set; } 48 | 49 | /// 50 | /// Replies 51 | /// 52 | [JsonProperty("replies")] 53 | public List Replies { get; set; } 54 | 55 | /// 56 | /// Versions 57 | /// 58 | [JsonProperty("version-history")] 59 | public List Versions { get; set; } 60 | 61 | /// 62 | /// Attachment 63 | /// 64 | [JsonProperty("wp:attachment")] 65 | public List Attachment { get; set; } 66 | 67 | /// 68 | /// Featured media 69 | /// 70 | [JsonProperty("wp:featuredmedia")] 71 | public List FeaturedMedia { get; set; } 72 | 73 | /// 74 | /// Featured media 75 | /// 76 | [JsonProperty("wp:term")] 77 | public List Term { get; set; } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Exception_Unexpected_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using WordPressPCL.Models; 6 | using WordPressPCL.Models.Exceptions; 7 | 8 | namespace WordPressPCL.Tests.Selfhosted; 9 | 10 | [TestClass] 11 | public class Exception_Unexpected_Tests 12 | { 13 | private WordPressClient _badConnectionClient; 14 | 15 | [TestInitialize] 16 | public void Initialize() 17 | { 18 | _badConnectionClient = new WordPressClient("https://microsoft.com"); 19 | } 20 | 21 | [TestMethod] 22 | public async Task Tags_Get_UnexpectedException() 23 | { 24 | await CheckForUnexpectedException(async () => await _badConnectionClient.Tags.GetAsync()); 25 | } 26 | 27 | [TestMethod] 28 | public async Task Tags_Post_UnexpectedException() 29 | { 30 | await CheckForUnexpectedException(async () => await _badConnectionClient.Tags.UpdateAsync(new Tag())); 31 | } 32 | 33 | [TestMethod] 34 | public async Task Tags_Delete_UnexpectedException() 35 | { 36 | await CheckForUnexpectedException(async () => await _badConnectionClient.Tags.DeleteAsync(1), HttpStatusCode.NotImplemented); 37 | } 38 | 39 | private async Task CheckForUnexpectedException(Func task, HttpStatusCode expectedStatus = HttpStatusCode.NotFound) 40 | { 41 | bool exceptionCaught = false; 42 | 43 | try 44 | { 45 | await task(); 46 | } 47 | catch (Exception ex) 48 | { 49 | exceptionCaught = true; 50 | 51 | if (!(ex is WPUnexpectedException typedException)) 52 | { 53 | Assert.Fail($"Expected an exception of type WPUnexpectedException, but saw exception of type {ex.GetType()}"); 54 | } 55 | else 56 | { 57 | Assert.AreEqual($"Server returned HTTP status {expectedStatus}", typedException.Message); 58 | Assert.IsNotNull(typedException.Response, "No HTTP response has been returned"); 59 | Assert.AreEqual(expectedStatus, typedException.Response.StatusCode); 60 | Assert.IsFalse(string.IsNullOrEmpty(typedException.ResponseBody), "Expected a response body but didn't see one"); 61 | } 62 | } 63 | 64 | Assert.IsTrue(exceptionCaught, "Exception was expected but none was seen"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /WordPressPCL/Models/PostStatus.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// Type represents Post Status Entity of WP REST API 7 | /// 8 | public class PostStatus 9 | { 10 | /// 11 | /// The title for the taxonomy. 12 | /// 13 | /// Context: view, edit, embed 14 | [JsonProperty("name")] 15 | public string Name { get; set; } 16 | 17 | /// 18 | /// Whether posts with this resource should be private. 19 | /// 20 | /// Context: edit 21 | [JsonProperty("private")] 22 | public bool Private { get; set; } 23 | 24 | /// 25 | /// Whether posts with this resource should be protected. 26 | /// 27 | /// Context: edit 28 | [JsonProperty("protected")] 29 | public bool Protected { get; set; } 30 | 31 | /// 32 | /// Whether posts with this resource should be public. 33 | /// 34 | /// Context: edit 35 | [JsonProperty("public")] 36 | public bool Public { get; set; } 37 | 38 | /// 39 | /// Whether posts with this resource should be publicly-queryable. 40 | /// 41 | /// Context: edit 42 | [JsonProperty("queryable")] 43 | public bool Queryable { get; set; } 44 | 45 | /// 46 | /// Whether to include posts in the edit listing for their post type. 47 | /// 48 | /// Context: edit 49 | [JsonProperty("show_in_list")] 50 | public bool ShowInList { get; set; } 51 | 52 | /// 53 | /// An alphanumeric identifier for the taxonomy. 54 | /// 55 | /// Context: view, edit, embed 56 | [JsonProperty("slug")] 57 | public string Slug { get; set; } 58 | 59 | /// 60 | /// Links to related resources 61 | /// 62 | [JsonProperty("_links", DefaultValueHandling = DefaultValueHandling.Ignore)] 63 | public Links Links { get; set; } 64 | 65 | /// 66 | /// Default constructor 67 | /// 68 | public PostStatus() 69 | { 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /docs/I version 2.x/entities/users.md: -------------------------------------------------------------------------------- 1 | # Users 2 | 3 | Here is a list of methods and examples of working with Users 4 | 5 | ## Get All 6 | 7 | ```C# 8 | // execute request users without credentials - returns only you 9 | var users = await client.Users.GetAllAsync(); 10 | 11 | // send credentials - list of all users 12 | var users = await client.Users.GetAllAsync(useAuth:true); 13 | ``` 14 | 15 | ## Get By ID 16 | 17 | ```C# 18 | // returns user by ID 19 | var user = await client.Users.GetByIDAsync(123); 20 | ``` 21 | 22 | ## Get Current User 23 | 24 | ```C# 25 | // returns current user 26 | var user = await client.Users.GetCurrentUserAsync(); 27 | ``` 28 | 29 | ## Query 30 | Create parametrized request 31 | ```C# 32 | // returns result of query 33 | var queryBuilder = new UsersQueryBuilder(); 34 | queryBuilder.PerPage = 40; 35 | queryBuilder.Page = 2; 36 | queryBuilder.Before = DateTime.Now; 37 | var users = await client.Users.QueryAsync(queryBuilder); 38 | ``` 39 | 40 | ## Query with Roles 41 | To retreive users with roles, you'll need to set the `Context` property of the `UsersQueryBuilder` to `Context.Edit`. 42 | ```C# 43 | UsersQueryBuilder queryBuilder = new() 44 | { 45 | // required for roles to be loaded 46 | Context = Context.Edit 47 | }; 48 | 49 | List users = await _clientAuth.Users.QueryAsync(queryBuilder, true); 50 | ``` 51 | 52 | ## Create new User 53 | 54 | ```C# 55 | // returns created user 56 | var user = new User("username","email","password") 57 | { 58 | NickName= "nickname" 59 | }; 60 | if (await client.IsValidJWTokenAsync()) 61 | { 62 | var user = await client.Users.CreateAsync(user); 63 | } 64 | ``` 65 | 66 | ## Update User 67 | 68 | ```C# 69 | // returns updated user 70 | var user = client.Users.GetByIDAsync(123); 71 | user.Name = "New Name"; 72 | if (await client.IsValidJWTokenAsync()) 73 | { 74 | var updatedUser = await client.Users.UpdateAsync(user); 75 | } 76 | ``` 77 | 78 | ## Delete User 79 | 80 | ```C# 81 | // returns result of deletion 82 | if (await client.IsValidJWTokenAsync()) 83 | { 84 | //second param - user to reassign all content 85 | var result = await client.Users.DeleteAsync(123,321); 86 | } 87 | ``` 88 | 89 | ## Create Application Password 90 | 91 | ```C# 92 | // Create an application password for the current user 93 | var password = await client.Users.CreateApplicationPasswordAsync("application-name"); 94 | 95 | // Create an application password for a specific user 96 | var password = await client.Users.CreateApplicationPasswordAsync("application-name", userId: "3"); 97 | ``` 98 | -------------------------------------------------------------------------------- /WordPressPCL/Utility/TagsQueryBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using WordPressPCL.Models; 3 | 4 | namespace WordPressPCL.Utility 5 | { 6 | /// 7 | /// Tag Query Builder class to construct queries with valid parameters 8 | /// 9 | public class TagsQueryBuilder : QueryBuilder 10 | { 11 | /// 12 | /// Current page of the collection. 13 | /// 14 | /// Default: 1 15 | [QueryText("page")] 16 | public int Page { get; set; } 17 | 18 | /// 19 | /// Maximum number of items to be returned in result set (1-100). 20 | /// 21 | /// Default: 10 22 | [QueryText("per_page")] 23 | public int PerPage { get; set; } 24 | 25 | /// 26 | /// Limit results to those matching a string. 27 | /// 28 | [QueryText("search")] 29 | public string Search { get; set; } 30 | 31 | /// 32 | /// Ensure result set excludes specific IDs. 33 | /// 34 | [QueryText("exclude")] 35 | public List Exclude { get; set; } 36 | 37 | /// 38 | /// Limit result set to specific IDs. 39 | /// 40 | [QueryText("include")] 41 | public List Include { get; set; } 42 | 43 | /// 44 | /// Offset the result set by a specific number of items. 45 | /// 46 | [QueryText("offset")] 47 | public int Offset { get; set; } 48 | 49 | /// 50 | /// Sort collection by object attribute. 51 | /// 52 | /// Default: name 53 | /// One of: id, include, name, slug, term_group, description, count 54 | [QueryText("orderby")] 55 | public TermsOrderBy OrderBy { get; set; } 56 | 57 | /// 58 | /// Limit result set to users with a specific slug. 59 | /// 60 | [QueryText("slug")] 61 | public List Slugs { get; set; } 62 | 63 | /// 64 | /// Whether to hide terms not assigned to any posts. 65 | /// 66 | [QueryText("hide_empty")] 67 | public bool HideEmpty { get; set; } 68 | 69 | /// 70 | /// Limit result set to terms assigned to a specific post. 71 | /// 72 | [QueryText("post")] 73 | public int Post { get; set; } 74 | } 75 | } -------------------------------------------------------------------------------- /WordPressPCL/Utility/UsersQueryBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using WordPressPCL.Models; 3 | 4 | namespace WordPressPCL.Utility 5 | { 6 | /// 7 | /// Users Query Builder class to construct queries with valid parameters 8 | /// 9 | public class UsersQueryBuilder : QueryBuilder 10 | { 11 | /// 12 | /// Current page of the collection. 13 | /// 14 | /// Default: 1 15 | [QueryText("page")] 16 | public int Page { get; set; } 17 | 18 | /// 19 | /// Maximum number of items to be returned in result set (1-100). 20 | /// 21 | /// Default: 10 22 | [QueryText("per_page")] 23 | public int PerPage { get; set; } 24 | 25 | /// 26 | /// Limit results to those matching a string. 27 | /// 28 | [QueryText("search")] 29 | public string Search { get; set; } 30 | 31 | /// 32 | /// Ensure result set excludes specific IDs. 33 | /// 34 | [QueryText("exclude")] 35 | public List Exclude { get; set; } 36 | 37 | /// 38 | /// Limit result set to specific IDs. 39 | /// 40 | [QueryText("include")] 41 | public List Include { get; set; } 42 | 43 | /// 44 | /// Offset the result set by a specific number of items. 45 | /// 46 | [QueryText("offset")] 47 | public int Offset { get; set; } 48 | 49 | /// 50 | /// Sort collection by object attribute. 51 | /// 52 | /// Default: name 53 | /// One of: id, include, name, registered_date, slug, email, url 54 | [QueryText("orderby")] 55 | public UsersOrderBy OrderBy { get; set; } 56 | 57 | /// 58 | /// Limit result set to users with a specific slug. 59 | /// 60 | [QueryText("slug")] 61 | public List Slugs { get; set; } 62 | 63 | // 64 | // Summary: 65 | // Limit result set to posts assigned one or more statuses. 66 | // 67 | // Remarks: 68 | // Default: publish 69 | [QueryText("status")] 70 | public List Statuses { get; set; } 71 | 72 | /// 73 | /// Limit result set to users matching at least one specific role provided. Accepts csv list or single role. 74 | /// 75 | [QueryText("roles")] 76 | public List Roles { get; set; } 77 | } 78 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /WordPressPCL/Utility/CategoriesQueryBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using WordPressPCL.Models; 3 | 4 | namespace WordPressPCL.Utility 5 | { 6 | /// 7 | /// Category Query Builder class to construct queries with valid parameters 8 | /// 9 | public class CategoriesQueryBuilder : QueryBuilder 10 | { 11 | /// 12 | /// Current page of the collection. 13 | /// 14 | /// Default: 1 15 | [QueryText("page")] 16 | public int Page { get; set; } 17 | 18 | /// 19 | /// Maximum number of items to be returned in result set (1-100). 20 | /// 21 | /// Default: 10 22 | [QueryText("per_page")] 23 | public int PerPage { get; set; } 24 | 25 | /// 26 | /// Limit results to those matching a string. 27 | /// 28 | [QueryText("search")] 29 | public string Search { get; set; } 30 | 31 | /// 32 | /// Ensure result set excludes specific IDs. 33 | /// 34 | [QueryText("exclude")] 35 | public List Exclude { get; set; } 36 | 37 | /// 38 | /// Limit result set to specific IDs. 39 | /// 40 | [QueryText("include")] 41 | public List Include { get; set; } 42 | 43 | /// 44 | /// Offset the result set by a specific number of items. 45 | /// 46 | [QueryText("offset")] 47 | public int Offset { get; set; } 48 | 49 | /// 50 | /// Sort collection by object attribute. 51 | /// 52 | /// Default: name 53 | /// One of: id, include, name, slug, term_group, description, count 54 | [QueryText("orderby")] 55 | public TermsOrderBy OrderBy { get; set; } 56 | 57 | /// 58 | /// Limit result set to users with a specific slug. 59 | /// 60 | [QueryText("slug")] 61 | public List Slugs { get; set; } 62 | 63 | /// 64 | /// Whether to hide terms not assigned to any posts. 65 | /// 66 | [QueryText("hide_empty")] 67 | public bool HideEmpty { get; set; } 68 | 69 | /// 70 | /// Limit result set to terms assigned to a specific post. 71 | /// 72 | [QueryText("post")] 73 | public int Post { get; set; } 74 | 75 | /// 76 | /// Limit result set to terms assigned to a specific parent. 77 | /// 78 | [QueryText("parent")] 79 | public int Parent { get; set; } 80 | } 81 | } -------------------------------------------------------------------------------- /WordPressPCL/Client/Pages.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Threading.Tasks; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Client 8 | { 9 | /// 10 | /// Client class for interaction with Pages endpoint WP REST API 11 | /// 12 | public class Pages : CRUDOperation 13 | { 14 | #region Init 15 | 16 | private const string _methodPath = "pages"; 17 | 18 | /// 19 | /// Constructor 20 | /// 21 | /// reference to HttpHelper class for interaction with HTTP 22 | public Pages(HttpHelper HttpHelper) : base(HttpHelper, _methodPath) 23 | { 24 | } 25 | 26 | #endregion Init 27 | 28 | #region Custom 29 | 30 | /// 31 | /// Get pages by its author 32 | /// 33 | /// Author id 34 | /// include embed info 35 | /// Send request with authentication header 36 | /// List of pages 37 | public Task> GetPagesByAuthorAsync(int authorId, bool embed = false, bool useAuth = false) 38 | { 39 | // default values 40 | // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date 41 | return HttpHelper.GetRequestAsync>($"{_methodPath}?author={authorId}", embed, useAuth); 42 | } 43 | 44 | /// 45 | /// Get pages by search term 46 | /// 47 | /// Search term 48 | /// include embed info 49 | /// Send request with authentication header 50 | /// List of pages 51 | public Task> GetPagesBySearchAsync(string searchTerm, bool embed = false, bool useAuth = false) 52 | { 53 | // default values 54 | // int page = 1, int per_page = 10, int offset = 0, Post.OrderBy orderby = Post.OrderBy.date 55 | return HttpHelper.GetRequestAsync>($"{_methodPath}?search={searchTerm}", embed, useAuth); 56 | } 57 | 58 | /// 59 | /// Delete page with force deletion 60 | /// 61 | /// Page id 62 | /// force deletion 63 | /// Result of operation 64 | public Task Delete(int ID, bool force = false) 65 | { 66 | return HttpHelper.DeleteRequestAsync($"{_methodPath}/{ID}?force={force.ToString().ToLower(CultureInfo.InvariantCulture)}"); 67 | } 68 | 69 | #endregion Custom 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /WordPressPCL/Client/PostRevisions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Interfaces; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Client 8 | { 9 | /// 10 | /// Client class for interaction with Post revisions endpoint WP REST API 11 | /// 12 | public class PostRevisions : IReadOperation, IDeleteOperation 13 | { 14 | private readonly HttpHelper _httpHelper; 15 | private const string _methodPath = "revisions"; 16 | private readonly int _postId; 17 | 18 | /// 19 | /// Constructor 20 | /// 21 | /// reference to HttpHelper class for interaction with HTTP 22 | /// ID of post 23 | public PostRevisions(ref HttpHelper httpHelper, int postId) 24 | { 25 | _httpHelper = httpHelper; 26 | _postId = postId; 27 | } 28 | /// 29 | /// Delete Entity 30 | /// 31 | /// Entity Id 32 | /// Result of operation 33 | public Task DeleteAsync(int ID) 34 | { 35 | return _httpHelper.DeleteRequestAsync($"posts/{_postId}/{_methodPath}/{ID}?force=true"); 36 | } 37 | 38 | /// 39 | /// Get latest 40 | /// 41 | /// include embed info 42 | /// Send request with authentication header 43 | /// Latest PostRevisions 44 | public Task> GetAsync(bool embed = false, bool useAuth = true) 45 | { 46 | return _httpHelper.GetRequestAsync>($"posts/{_postId}/{_methodPath}", embed, useAuth); 47 | } 48 | 49 | /// 50 | /// Get All 51 | /// 52 | /// Include embed info 53 | /// Send request with authentication header 54 | /// List of all result 55 | public Task> GetAllAsync(bool embed = false, bool useAuth = true) 56 | { 57 | return _httpHelper.GetRequestAsync>($"posts/{_postId}/{_methodPath}", embed, useAuth); 58 | } 59 | 60 | /// 61 | /// Get Entity by Id 62 | /// 63 | /// ID 64 | /// include embed info 65 | /// Send request with authentication header 66 | /// Entity by Id 67 | public Task GetByIDAsync(object ID, bool embed = false, bool useAuth = true) 68 | { 69 | return _httpHelper.GetRequestAsync($"posts/{_postId}/{_methodPath}/{ID}", embed, useAuth); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /WordPressPCL/Models/PostType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Type represents Post Type Entity of WP REST API 8 | /// 9 | public class PostType 10 | { 11 | /// 12 | /// All capabilities used by the taxonomy. 13 | /// 14 | /// Context: edit 15 | [JsonProperty("capabilities", DefaultValueHandling = DefaultValueHandling.Ignore)] 16 | public IDictionary Capabilities { get; set; } 17 | 18 | /// 19 | /// A human-readable description of the taxonomy. 20 | /// 21 | /// Context: view, edit 22 | [JsonProperty("description")] 23 | public string Description { get; set; } 24 | 25 | /// 26 | /// Whether or not the taxonomy should have children. 27 | /// 28 | /// Context: view, edit 29 | [JsonProperty("hierarchical")] 30 | public bool Hierarchical { get; set; } 31 | 32 | /// 33 | /// Human-readable labels for the taxonomy for various contexts. 34 | /// 35 | /// Context: edit 36 | [JsonProperty("labels")] 37 | public List Labels { get; set; } 38 | 39 | /// 40 | /// The title for the taxonomy. 41 | /// 42 | /// Context: view, edit, embed 43 | [JsonProperty("name")] 44 | public string Name { get; set; } 45 | 46 | /// 47 | /// An alphanumeric identifier for the taxonomy. 48 | /// 49 | /// Context: view, edit, embed 50 | [JsonProperty("slug")] 51 | public string Slug { get; set; } 52 | 53 | /// 54 | /// Whether or not the term cloud should be displayed. 55 | /// 56 | /// Context: edit 57 | [JsonProperty("show_cloud")] 58 | public bool ShowCloud { get; set; } 59 | 60 | /// 61 | /// List of taxonomies 62 | /// 63 | /// Context: edit 64 | [JsonProperty("taxonomies")] 65 | public List Taxonomies { get; set; } 66 | 67 | /// 68 | /// REST base route for the taxonomy. 69 | /// 70 | /// Context: view, edit, embed 71 | [JsonProperty("rest_base")] 72 | public string RestBase { get; set; } 73 | 74 | /// 75 | /// Links to related resources 76 | /// 77 | [JsonProperty("_links", DefaultValueHandling = DefaultValueHandling.Ignore)] 78 | public Links Links { get; set; } 79 | 80 | /// 81 | /// Default constructor 82 | /// 83 | public PostType() 84 | { 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Taxonomy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// Type represents Taxonomy Entity of WP REST API 8 | /// 9 | public class Taxonomy 10 | { 11 | /// 12 | /// All capabilities used by the taxonomy. 13 | /// 14 | /// Context: edit 15 | [JsonProperty("capabilities", DefaultValueHandling = DefaultValueHandling.Ignore)] 16 | public IDictionary Capabilities { get; set; } 17 | 18 | /// 19 | /// A human-readable description of the taxonomy. 20 | /// 21 | /// Context: view, edit 22 | [JsonProperty("description")] 23 | public string Description { get; set; } 24 | 25 | /// 26 | /// Whether or not the taxonomy should have children. 27 | /// 28 | /// Context: view, edit 29 | [JsonProperty("hierarchical")] 30 | public bool Hierarchical { get; set; } 31 | 32 | /// 33 | /// Human-readable labels for the taxonomy for various contexts. 34 | /// 35 | /// Context: edit 36 | [JsonProperty("labels")] 37 | public List Labels { get; set; } 38 | 39 | /// 40 | /// The title for the taxonomy. 41 | /// 42 | /// Context: view, edit, embed 43 | [JsonProperty("name")] 44 | public string Name { get; set; } 45 | 46 | /// 47 | /// An alphanumeric identifier for the taxonomy. 48 | /// 49 | /// Context: view, edit, embed 50 | [JsonProperty("slug")] 51 | public string Slug { get; set; } 52 | 53 | /// 54 | /// Whether or not the term cloud should be displayed. 55 | /// 56 | /// Context: edit 57 | [JsonProperty("show_cloud")] 58 | public bool ShowCloud { get; set; } 59 | 60 | /// 61 | /// Types associated with the taxonomy. 62 | /// 63 | /// Context: view, edit 64 | [JsonProperty("types")] 65 | public List Types { get; set; } 66 | 67 | /// 68 | /// REST base route for the taxonomy. 69 | /// 70 | /// Context: view, edit, embed 71 | [JsonProperty("rest_base")] 72 | public string RestBase { get; set; } 73 | 74 | /// 75 | /// Links to related resources 76 | /// 77 | [JsonProperty("_links", DefaultValueHandling = DefaultValueHandling.Ignore)] 78 | public Links Links { get; set; } 79 | 80 | /// 81 | /// Default constructor 82 | /// 83 | public Taxonomy() 84 | { 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Plugins_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using WordPressPCL.Models; 6 | using WordPressPCL.Tests.Selfhosted.Utility; 7 | using WordPressPCL.Utility; 8 | 9 | namespace WordPressPCL.Tests.Selfhosted; 10 | 11 | [TestClass] 12 | public class Plugins_Tests 13 | { 14 | private static WordPressClient _clientAuth; 15 | private static string PluginId= "wordpress-seo"; 16 | 17 | [ClassInitialize] 18 | public static async Task Init(TestContext testContext) 19 | { 20 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 21 | } 22 | 23 | [TestMethod] 24 | public async Task Plugins_Install_Activate_Deactivate_Delete() 25 | { 26 | Plugin plugin = await _clientAuth.Plugins.InstallAsync(PluginId); 27 | Assert.IsNotNull(plugin); 28 | Assert.AreEqual(PluginId, plugin.Id); 29 | 30 | 31 | //Activate plugin 32 | Plugin activePlugin = await _clientAuth.Plugins.ActivateAsync(plugin); 33 | Assert.AreEqual(activePlugin.Status, ActivationStatus.Active); 34 | Assert.AreEqual(activePlugin.Id, plugin.Id); 35 | 36 | 37 | //Deactivate plugin 38 | Plugin deactivatedPlugin = await _clientAuth.Plugins.DeactivateAsync(plugin); 39 | Assert.AreEqual(deactivatedPlugin.Status, ActivationStatus.Inactive); 40 | Assert.AreEqual(deactivatedPlugin.Id, plugin.Id); 41 | 42 | //Delete plugin 43 | bool response = await _clientAuth.Plugins.DeleteAsync(plugin); 44 | Assert.IsTrue(response); 45 | List plugins = await _clientAuth.Plugins.GetAllAsync(useAuth: true); 46 | List c = plugins.Where(x => x.Id == plugin.Id).ToList(); 47 | Assert.AreEqual(c.Count, 0); 48 | } 49 | 50 | [TestMethod] 51 | public async Task Plugins_GetActive() 52 | { 53 | //Active plugin 54 | List plugins = await _clientAuth.Plugins.QueryAsync(new PluginsQueryBuilder { Status = ActivationStatus.Active }, useAuth:true); 55 | Assert.IsNotNull(plugins); 56 | Assert.AreNotEqual(plugins.Count, 0); 57 | 58 | } 59 | [TestMethod] 60 | public async Task Plugins_Search () 61 | { 62 | //Active plugin 63 | List plugins = await _clientAuth.Plugins.QueryAsync(new PluginsQueryBuilder { Search="jwt" }, useAuth:true); 64 | Assert.IsNotNull(plugins); 65 | Assert.AreNotEqual(plugins.Count, 0); 66 | 67 | } 68 | 69 | [TestMethod] 70 | public async Task Plugins_Get() 71 | { 72 | List plugins = await _clientAuth.Plugins.GetAsync (useAuth: true); 73 | Assert.IsNotNull(plugins); 74 | Assert.AreNotEqual(plugins.Count, 0); 75 | CollectionAssert.AllItemsAreUnique(plugins.Select(tag => tag.Id).ToList()); 76 | } 77 | 78 | [TestMethod] 79 | public async Task Plugins_GetByID() 80 | { 81 | Plugin plugin = await _clientAuth.Plugins.GetByIDAsync("jwt-auth/jwt-auth", useAuth: true); 82 | Assert.IsNotNull(plugin); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /WordPressPCL/Client/PostTypes.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Interfaces; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Client 8 | { 9 | /// 10 | /// Client class for interaction with Post Types endpoint WP REST API 11 | /// 12 | public class PostTypes : IReadOperation 13 | { 14 | private HttpHelper _httpHelper; 15 | private const string _methodPath = "types"; 16 | 17 | /// 18 | /// Constructor 19 | /// 20 | /// reference to HttpHelper class for interaction with HTTP 21 | public PostTypes(HttpHelper httpHelper) 22 | { 23 | _httpHelper = httpHelper; 24 | } 25 | 26 | /// 27 | /// Get latest 28 | /// 29 | /// Include embed info 30 | /// Send request with authentication header 31 | /// List of latest PostTypes 32 | public async Task> GetAsync(bool embed = false, bool useAuth = false) 33 | { 34 | List entities = new(); 35 | Dictionary entities_page = (await _httpHelper.GetRequestAsync>($"{_methodPath}", embed, useAuth).ConfigureAwait(false)); 36 | foreach (KeyValuePair ent in entities_page) 37 | { 38 | entities.Add(ent.Value); 39 | } 40 | return entities; 41 | } 42 | 43 | /// 44 | /// Get All 45 | /// 46 | /// Include embed info 47 | /// Send request with authentication header 48 | /// List of all PostTypes 49 | public async Task> GetAllAsync(bool embed = false, bool useAuth = false) 50 | { 51 | //100 - Max posts per page in WordPress REST API, so this is hack with multiple requests 52 | List entities = new(); 53 | Dictionary entities_page = (await _httpHelper.GetRequestAsync>($"{_methodPath}", embed, useAuth).ConfigureAwait(false)); 54 | foreach (KeyValuePair ent in entities_page) 55 | { 56 | entities.Add(ent.Value); 57 | } 58 | return entities; 59 | } 60 | 61 | /// 62 | /// Get Entity by Id 63 | /// 64 | /// ID 65 | /// include embed info 66 | /// Send request with authentication header 67 | /// Entity by Id 68 | public Task GetByIDAsync(object ID, bool embed = false, bool useAuth = false) 69 | { 70 | return _httpHelper.GetRequestAsync($"{_methodPath}/{ID}", embed, useAuth); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /WordPressPCL/Client/PostStatuses.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Interfaces; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Client 8 | { 9 | /// 10 | /// Client class for interaction with Post Types endpoint WP REST API 11 | /// 12 | public class PostStatuses : IReadOperation 13 | { 14 | private readonly HttpHelper _httpHelper; 15 | private const string _methodPath = "statuses"; 16 | 17 | /// 18 | /// Constructor 19 | /// 20 | /// reference to HttpHelper class for interaction with HTTP 21 | public PostStatuses(HttpHelper httpHelper) 22 | { 23 | _httpHelper = httpHelper; 24 | } 25 | 26 | /// 27 | /// Get latest Post Statuses 28 | /// 29 | /// include embed info 30 | /// Send request with authentication header 31 | /// Entity by Id 32 | public async Task> GetAsync(bool embed = false, bool useAuth = false) 33 | { 34 | List entities = new(); 35 | Dictionary entities_page = await _httpHelper.GetRequestAsync>($"{_methodPath}", embed, useAuth).ConfigureAwait(false); 36 | foreach (KeyValuePair ent in entities_page) 37 | { 38 | entities.Add(ent.Value); 39 | } 40 | return entities; 41 | } 42 | 43 | /// 44 | /// Get All 45 | /// 46 | /// Include embed info 47 | /// Send request with authentication header 48 | /// List of all result 49 | public async Task> GetAllAsync(bool embed = false, bool useAuth = false) 50 | { 51 | //100 - Max posts per page in WordPress REST API, so this is hack with multiple requests 52 | List entities = new(); 53 | Dictionary entities_page = await _httpHelper.GetRequestAsync>($"{_methodPath}", embed, useAuth).ConfigureAwait(false); 54 | foreach (KeyValuePair ent in entities_page) 55 | { 56 | entities.Add(ent.Value); 57 | } 58 | return entities; 59 | } 60 | 61 | /// 62 | /// Get Entity by Id 63 | /// 64 | /// ID 65 | /// include embed info 66 | /// Send request with authentication header 67 | /// Entity by Id 68 | public Task GetByIDAsync(object ID, bool embed = false, bool useAuth = false) 69 | { 70 | return _httpHelper.GetRequestAsync($"{_methodPath}/{ID}", embed, useAuth); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /WordPressPCL/Models/Theme.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace WordPressPCL.Models 5 | { 6 | /// 7 | /// WordPress Themes 8 | /// Date: 8 June 2023 9 | /// Creator: Gregory Liénard 10 | /// 11 | public class Theme 12 | { 13 | /// 14 | /// The theme's stylesheet. This uniquely identifies the theme. 15 | /// 16 | [JsonProperty("stylesheet")] 17 | public string Stylesheet { get; set; } 18 | 19 | /// 20 | /// The theme's template. If this is a child theme, this refers to the parent theme. 21 | /// 22 | [JsonProperty("template")] 23 | public string Template { get; set; } 24 | 25 | /// 26 | /// The theme author. 27 | /// 28 | [JsonProperty("author")] 29 | public Rendered Author { get; set; } 30 | 31 | /// 32 | /// The website of the theme author. 33 | /// 34 | [JsonProperty("author_uri")] 35 | public Rendered AuthorUri { get; set; } 36 | 37 | /// 38 | /// A description of the theme. 39 | /// 40 | [JsonProperty("description")] 41 | public Description Description { get; set; } 42 | 43 | /// 44 | /// The name of the theme. 45 | /// 46 | [JsonProperty("name")] 47 | public Rendered Name { get; set; } 48 | 49 | /// 50 | /// The minimum PHP version required for the theme to work. 51 | /// 52 | [JsonProperty("requires_php")] 53 | public string RequiresPhp { get; set; } 54 | 55 | /// 56 | /// The minimum WordPress version required for the theme to work. 57 | /// 58 | [JsonProperty("requires_wp")] 59 | public string RequiresWp { get; set; } 60 | 61 | /// 62 | /// The theme's screenshot URL. 63 | /// 64 | [JsonProperty("screenshot")] 65 | public Uri Screenshot { get; set; } 66 | 67 | /// 68 | /// Tags indicating styles and features of the theme. 69 | /// 70 | [JsonProperty("tags")] 71 | public Tags Tags { get; set; } 72 | 73 | /// 74 | /// The theme's text domain. 75 | /// 76 | [JsonProperty("textdomain")] 77 | public string Textdomain { get; set; } 78 | 79 | 80 | /// 81 | /// The URI of the theme's webpage. 82 | /// 83 | [JsonProperty("theme_uri")] 84 | public Rendered ThemeUri { get; set; } 85 | 86 | /// 87 | /// The theme's current version. 88 | /// 89 | [JsonProperty("version")] 90 | public Version Version { get; set; } 91 | 92 | /// 93 | /// A named status for the theme. 94 | /// 95 | [JsonProperty("status")] 96 | public ActivationStatus Status { get; set; } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/ExceptionTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Models; 4 | using WordPressPCL.Models.Exceptions; 5 | using WordPressPCL.Tests.Selfhosted.Utility; 6 | 7 | namespace WordPressPCL.Tests.Selfhosted; 8 | 9 | [TestClass] 10 | public class ExceptionTests 11 | { 12 | private static WordPressClient _client; 13 | private static WordPressClient _clientAuth; 14 | 15 | [ClassInitialize] 16 | public static async Task Init(TestContext testContext) 17 | { 18 | _client = ClientHelper.GetWordPressClient(); 19 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 20 | } 21 | 22 | [TestMethod] 23 | public async Task Exception_JWTAuthExceptionTest() 24 | { 25 | // Initialize 26 | Assert.IsNotNull(_client); 27 | // Get settings without auth 28 | 29 | await Assert.ThrowsExceptionAsync(async () => 30 | { 31 | Settings settings = await _client.Settings.GetSettingsAsync(); 32 | }); 33 | } 34 | 35 | [TestMethod] 36 | public async Task Exception_RequestJWTokenForBasicAuth() 37 | { 38 | //Initialize 39 | Assert.IsNotNull(_client); 40 | const string dummyUser = "dummy"; 41 | const string dummyPassword = "dummy"; 42 | 43 | _client.Auth.UseBasicAuth(dummyUser, dummyPassword); 44 | 45 | await Assert.ThrowsExceptionAsync(async () => 46 | { 47 | await _client.Auth.RequestJWTokenAsync(dummyUser, dummyPassword); 48 | }); 49 | } 50 | 51 | [TestMethod] 52 | public async Task Exception_IsValidJWTokenForBasicAuth() 53 | { 54 | //Initialize 55 | Assert.IsNotNull(_client); 56 | const string dummyUser = "dummy"; 57 | const string dummyPassword = "dummy"; 58 | 59 | _client.Auth.UseBasicAuth(dummyUser, dummyPassword); 60 | 61 | await Assert.ThrowsExceptionAsync(async () => { 62 | await _client.Auth.IsValidJWTokenAsync(); 63 | }); 64 | } 65 | 66 | [TestMethod] 67 | public async Task Exception_PostCreateExceptionTest() 68 | { 69 | // Initialize 70 | Assert.IsNotNull(_clientAuth); 71 | // Create empty post 72 | try 73 | { 74 | Post post = await _clientAuth.Posts.CreateAsync(new Post()); 75 | } 76 | catch (WPException wpex) 77 | { 78 | Assert.IsNotNull(wpex.RequestData); 79 | Assert.AreEqual(wpex.RequestData.Name, "empty_content"); 80 | } 81 | } 82 | 83 | [TestMethod] 84 | public async Task Exception_DeleteExceptionTest() 85 | { 86 | // Initialize 87 | Assert.IsNotNull(_clientAuth); 88 | // Delete nonexisted post 89 | try 90 | { 91 | bool result = await _clientAuth.Posts.DeleteAsync(int.MaxValue); 92 | } 93 | catch (WPException wpex) 94 | { 95 | Assert.IsNotNull(wpex.RequestData); 96 | Assert.AreEqual(wpex.RequestData.Name, "rest_post_invalid_id"); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Utility/HttpHelper_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using WordPressPCL.Models; 7 | 8 | namespace WordPressPCL.Tests.Selfhosted.Utility; 9 | 10 | [TestClass] 11 | public class HttpHelper_Tests 12 | { 13 | private static WordPressClient _clientAuth; 14 | 15 | [ClassInitialize] 16 | public static async Task Init(TestContext testContext) 17 | { 18 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 19 | } 20 | 21 | 22 | [TestMethod] 23 | public async Task HttpHelper_InvalidPreProcessing() 24 | { 25 | // Create a random tag , must works: 26 | string tagname = $"Test {System.Guid.NewGuid()}"; 27 | Tag tag = await _clientAuth.Tags.CreateAsync(new Tag() 28 | { 29 | Name = tagname, 30 | Description = "Test Description" 31 | }); 32 | Assert.IsTrue(tag.Id > 0); 33 | Assert.IsNotNull(tag); 34 | Assert.AreEqual(tagname, tag.Name); 35 | Assert.AreEqual("Test Description", tag.Description); 36 | 37 | // We call Get tag list without pre processing 38 | List tags = await _clientAuth.Tags.GetAllAsync(); 39 | Assert.IsNotNull(tags); 40 | Assert.AreNotEqual(tags.Count, 0); 41 | CollectionAssert.AllItemsAreUnique(tags.Select(e => e.Id).ToList()); 42 | 43 | // Now we add a PreProcessing task 44 | _clientAuth.HttpResponsePreProcessing = (response) => 45 | { 46 | throw new InvalidOperationException("PreProcessing must fail"); 47 | }; 48 | await Assert.ThrowsExceptionAsync(async () => 49 | { 50 | await _clientAuth.Tags.GetAllAsync(); 51 | }); 52 | _clientAuth.HttpResponsePreProcessing = null; 53 | } 54 | 55 | [TestMethod] 56 | public async Task HttpHelper_ValidPreProcessing() 57 | { 58 | _clientAuth.HttpResponsePreProcessing = null; 59 | 60 | // Create a random tag 61 | Random random = new(); 62 | string tagname = $"Test {System.Guid.NewGuid()}"; 63 | Tag tag = await _clientAuth.Tags.CreateAsync(new Tag() 64 | { 65 | Name = tagname, 66 | Description = "Test Description" 67 | }); 68 | Assert.IsTrue(tag.Id > 0); 69 | Assert.IsNotNull(tag); 70 | Assert.AreEqual(tagname, tag.Name); 71 | Assert.AreEqual("Test Description", tag.Description); 72 | 73 | // We call Get tag list without pre processing 74 | List tags = await _clientAuth.Tags.GetAllAsync(); 75 | Assert.IsNotNull(tags); 76 | Assert.AreNotEqual(tags.Count, 0); 77 | CollectionAssert.AllItemsAreUnique(tags.Select(e => e.Id).ToList()); 78 | 79 | // Now we add a PreProcessing task 80 | _clientAuth.HttpResponsePreProcessing = (response) => 81 | { 82 | return response; 83 | }; 84 | 85 | tags = await _clientAuth.Tags.GetAllAsync(); 86 | Assert.IsNotNull(tags); 87 | Assert.AreNotEqual(tags.Count, 0); 88 | CollectionAssert.AllItemsAreUnique(tags.Select(e => e.Id).ToList()); 89 | _clientAuth.HttpResponsePreProcessing = null; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Settings.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// WordPress main settings 7 | /// 8 | public class Settings 9 | { 10 | /// 11 | /// Site title. 12 | /// 13 | [JsonProperty("title")] 14 | public string Title { get; set; } 15 | 16 | /// 17 | /// Site description. 18 | /// 19 | [JsonProperty("description")] 20 | public string Description { get; set; } 21 | 22 | /// 23 | /// Site URL. 24 | /// 25 | [JsonProperty("url")] 26 | public string Url { get; set; } 27 | 28 | /// 29 | /// This address is used for admin purposes. If you change this we will send you an email at your new address to confirm it. The new address will not become active until confirmed. 30 | /// 31 | [JsonProperty("email")] 32 | public string Email { get; set; } 33 | 34 | /// 35 | /// A city in the same timezone as you. 36 | /// 37 | [JsonProperty("timezone")] 38 | public string Timezone { get; set; } 39 | 40 | /// 41 | /// A date format for all date strings. 42 | /// 43 | [JsonProperty("date_format")] 44 | public string DateFormat { get; set; } 45 | 46 | /// 47 | /// A time format for all time strings. 48 | /// 49 | [JsonProperty("time_format")] 50 | public string TimeFormat { get; set; } 51 | 52 | /// 53 | /// A day number of the week that the week should start on. 54 | /// 55 | [JsonProperty("start_of_week")] 56 | public int StartOfWeek { get; set; } 57 | 58 | /// 59 | /// WordPress locale code. 60 | /// 61 | [JsonProperty("language")] 62 | public string Language { get; set; } 63 | 64 | /// 65 | /// Convert emoticons like :-) and :-P to graphics on display. 66 | /// 67 | [JsonProperty("use_smilies")] 68 | public bool UseSmilies { get; set; } 69 | 70 | /// 71 | /// Default category. 72 | /// 73 | [JsonProperty("default_category")] 74 | public int DefaultCategory { get; set; } 75 | 76 | /// 77 | /// Default post format. 78 | /// 79 | [JsonProperty("default_post_format")] 80 | public string DefaultPostFormat { get; set; } 81 | 82 | /// 83 | /// Blog pages show at most. 84 | /// 85 | [JsonProperty("posts_per_page")] 86 | public int PostsPerPage { get; set; } 87 | 88 | /// 89 | /// Default Ping Status 90 | /// 91 | [JsonProperty("default_ping_status")] 92 | public OpenStatus? DefaultPingStatus { get; set; } 93 | 94 | /// 95 | /// Default Comment Status 96 | /// 97 | [JsonProperty("default_comment_status")] 98 | public OpenStatus? DefaultCommentStatus { get; set; } 99 | } 100 | } -------------------------------------------------------------------------------- /WordPressPCL.Tests.Hosted/Utility/HttpHelper_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using WordPressPCL.Models; 6 | 7 | namespace WordPressPCL.Tests.Hosted.Utility; 8 | 9 | [TestClass] 10 | public class HttpHelper_Tests 11 | { 12 | 13 | [ClassInitialize] 14 | public static void Init(TestContext testContext) 15 | { 16 | } 17 | 18 | [TestMethod] 19 | public async Task Hosted_HttpHelper_InvalidPreProcessing() 20 | { 21 | // AUTHENTICATION DOES NOT YET WORK FOR HOSTED SITES 22 | var client = new WordPressClient(ApiCredentials.WordPressUri); 23 | client.Auth.UseBearerAuth(JWTPlugin.JWTAuthByEnriqueChavez); 24 | await client.Auth.RequestJWTokenAsync(ApiCredentials.Username, ApiCredentials.Password); 25 | 26 | // Create a random tag , must works: 27 | var tagname = $"Test {System.Guid.NewGuid()}"; 28 | var tag = await client.Tags.CreateAsync(new Tag() 29 | { 30 | Name = tagname, 31 | Description = "Test Description" 32 | }); 33 | Assert.IsTrue(tag.Id > 0); 34 | Assert.IsNotNull(tag); 35 | Assert.AreEqual(tagname, tag.Name); 36 | Assert.AreEqual("Test Description", tag.Description); 37 | 38 | // We call Get tag list without pre processing 39 | var tags = await client.Tags.GetAllAsync(); 40 | Assert.IsNotNull(tags); 41 | Assert.AreNotEqual(tags.Count, 0); 42 | CollectionAssert.AllItemsAreUnique(tags.Select(e => e.Id).ToList()); 43 | 44 | // Now we add a PreProcessing task 45 | client.HttpResponsePreProcessing = (response) => 46 | { 47 | throw new InvalidOperationException("PreProcessing must fail"); 48 | }; 49 | await Assert.ThrowsExceptionAsync(async () => 50 | { 51 | await client.Tags.GetAllAsync(); 52 | }); 53 | } 54 | 55 | [TestMethod] 56 | public async Task Hosted_HttpHelper_ValidPreProcessing() { 57 | var client = new WordPressClient(ApiCredentials.WordPressUri); 58 | client.Auth.UseBearerAuth(JWTPlugin.JWTAuthByEnriqueChavez); 59 | await client.Auth.RequestJWTokenAsync(ApiCredentials.Username, ApiCredentials.Password); 60 | 61 | // Create a random tag 62 | var tagname = $"Test {System.Guid.NewGuid()}"; 63 | var tag = await client.Tags.CreateAsync(new Tag() 64 | { 65 | Name = tagname, 66 | Description = "Test Description" 67 | }); 68 | Assert.IsTrue(tag.Id > 0); 69 | Assert.IsNotNull(tag); 70 | Assert.AreEqual(tagname, tag.Name); 71 | Assert.AreEqual("Test Description", tag.Description); 72 | 73 | // We call Get tag list without pre processing 74 | var tags = await client.Tags.GetAllAsync(); 75 | Assert.IsNotNull(tags); 76 | Assert.AreNotEqual(tags.Count, 0); 77 | CollectionAssert.AllItemsAreUnique(tags.Select(e => e.Id).ToList()); 78 | 79 | // Now we add a PreProcessing task 80 | client.HttpResponsePreProcessing = (response) => 81 | { 82 | return response; 83 | }; 84 | 85 | tags = await client.Tags.GetAllAsync(); 86 | Assert.IsNotNull(tags); 87 | Assert.AreNotEqual(tags.Count, 0); 88 | CollectionAssert.AllItemsAreUnique(tags.Select(e => e.Id).ToList()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /WordPressPCL/Client/Comments.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using WordPressPCL.Models; 6 | using WordPressPCL.Utility; 7 | 8 | namespace WordPressPCL.Client 9 | { 10 | /// 11 | /// Client class for interaction with Comments endpoint WP REST API 12 | /// 13 | 14 | public class Comments : CRUDOperation 15 | { 16 | #region Init 17 | 18 | private const string _methodPath = "comments"; 19 | 20 | /// 21 | /// Constructor 22 | /// 23 | /// reference to HttpHelper class for interaction with HTTP 24 | public Comments(HttpHelper HttpHelper) : base(HttpHelper, _methodPath) 25 | { 26 | } 27 | 28 | #endregion Init 29 | 30 | #region Custom 31 | 32 | /// 33 | /// Get comments for Post 34 | /// 35 | /// Post id 36 | /// include embed info 37 | /// Send request with authentication header 38 | /// List of comments for post 39 | public Task> GetCommentsForPostAsync(int PostID, bool embed = false, bool useAuth = false) 40 | { 41 | return HttpHelper.GetRequestAsync>($"{_methodPath}?post={PostID}", embed, useAuth); 42 | } 43 | 44 | /// 45 | /// Get all comments for Post 46 | /// 47 | /// Post id 48 | /// include embed info 49 | /// Send request with authentication header 50 | /// List of comments for post 51 | public async Task> GetAllCommentsForPostAsync(int PostID, bool embed = false, bool useAuth = false) 52 | { 53 | //100 - Max comments per page in WordPress REST API, so this is hack with multiple requests 54 | List comments = await HttpHelper.GetRequestAsync>($"{_methodPath}?post={PostID}&per_page=100&page=1", embed, useAuth).ConfigureAwait(false); 55 | if (HttpHelper.LastResponseHeaders.Contains("X-WP-TotalPages") && 56 | int.TryParse(HttpHelper.LastResponseHeaders.GetValues("X-WP-TotalPages").FirstOrDefault(), out int totalPages) && 57 | totalPages > 1) 58 | { 59 | for (int page = 2; page <= totalPages; page++) 60 | { 61 | comments.AddRange(await HttpHelper.GetRequestAsync>($"{_methodPath}?post={PostID}&per_page=100&page={page}", embed, useAuth).ConfigureAwait(false)); 62 | } 63 | } 64 | return comments; 65 | } 66 | 67 | /// 68 | /// Force deletion of comments 69 | /// 70 | /// Comment Id 71 | /// force deletion 72 | /// Result of operation 73 | public Task DeleteAsync(int ID, bool force = false) 74 | { 75 | return HttpHelper.DeleteRequestAsync($"{_methodPath}/{ID}?force={force.ToString().ToLower(CultureInfo.InvariantCulture)}"); 76 | } 77 | 78 | #endregion Custom 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/CustomRequests_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Newtonsoft.Json; 7 | using WordPressPCL.Tests.Selfhosted.Utility; 8 | 9 | namespace WordPressPCL.Tests.Selfhosted; 10 | 11 | [TestClass] 12 | public class CustomRequests_Tests 13 | { 14 | //Requires contact-form-7 plugin 15 | private class ContactFormItem 16 | { 17 | [JsonProperty("id")] 18 | public int? Id { get; set; } 19 | [JsonProperty("title")] 20 | public string Title { get; set; } 21 | [JsonProperty("slug")] 22 | public string Slug { get; set; } 23 | [JsonProperty("locale")] 24 | public string Locale { get; set; } 25 | } 26 | 27 | private static WordPressClient _clientAuth; 28 | 29 | [ClassInitialize] 30 | public static async Task Init(TestContext testContext) 31 | { 32 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 33 | } 34 | 35 | [TestMethod] 36 | public async Task CustomRequests_Read() 37 | { 38 | List forms = await _clientAuth.CustomRequest.GetAsync>("contact-form-7/v1/contact-forms", false, true); 39 | Assert.IsNotNull(forms); 40 | Assert.AreNotEqual(forms.Count, 0); 41 | } 42 | 43 | // TODO: check why this isn't returning the form 44 | //[TestMethod] 45 | public async Task CustomRequests_Create() 46 | { 47 | string title = $"Test Form {Guid.NewGuid()}"; 48 | ContactFormItem form = await _clientAuth.CustomRequest.CreateAsync("contact-form-7/v1/contact-forms", new ContactFormItem() { Title = title, Locale = "en-US" }); 49 | Assert.IsNotNull(form); 50 | Assert.IsNotNull(form.Id); 51 | Assert.AreEqual(form.Title, title); 52 | } 53 | 54 | // TODO: make test not depend on other tests 55 | //[TestMethod] 56 | public async Task CustomRequests_Update() 57 | { 58 | string title = $"Test Form {Guid.NewGuid()}"; 59 | ContactFormItem form = await _clientAuth.CustomRequest.CreateAsync("contact-form-7/v1/contact-forms", new ContactFormItem() { Title = title, Locale = "en-US" }); 60 | 61 | List forms = await _clientAuth.CustomRequest.GetAsync>("contact-form-7/v1/contact-forms", false, true); 62 | Assert.IsNotNull(forms); 63 | Assert.AreNotEqual(forms.Count, 0); 64 | ContactFormItem editform = forms.First(); 65 | editform.Title += "test"; 66 | ContactFormItem form2 = await _clientAuth.CustomRequest.UpdateAsync($"contact-form-7/v1/contact-forms/{editform.Id.Value}", editform); 67 | Assert.IsNotNull(form2); 68 | Assert.AreEqual(form.Title, editform.Title); 69 | } 70 | 71 | // TODO: make test not depend on other tests 72 | //[TestMethod] 73 | public async Task CustomRequests_Delete() 74 | { 75 | List forms = await _clientAuth.CustomRequest.GetAsync>("contact-form-7/v1/contact-forms", false, true); 76 | Assert.IsNotNull(forms); 77 | Assert.AreNotEqual(forms.Count, 0); 78 | ContactFormItem deleteform = forms.First(); 79 | bool result = await _clientAuth.CustomRequest.DeleteAsync($"contact-form-7/v1/contact-forms/{deleteform.Id.Value}"); 80 | Assert.IsTrue(result); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /WordPressPCL/Models/Plugin.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace WordPressPCL.Models 4 | { 5 | /// 6 | /// WordPress Plugins 7 | /// Date: 26 May 2023 8 | /// Creator: Gregory Liénard 9 | /// 10 | public class Plugin 11 | { 12 | /// 13 | /// The plugin file. unique identifier. 14 | /// 15 | [JsonProperty("plugin")] 16 | public string PluginFile 17 | { 18 | get 19 | { 20 | return _PluginFile; 21 | } 22 | set 23 | { 24 | _PluginFile = value; 25 | if (value.Contains("/")) 26 | Id = value.Substring(0, value.IndexOf("/")); 27 | else 28 | Id = value; 29 | } 30 | } 31 | private string _PluginFile; 32 | 33 | /// 34 | /// First part of the pluginfile: wordpress-seo/wp-seo => wordpress-seo 35 | /// Id is needed to install, PluginFile is needed to activate, deactivate, delete 36 | /// 37 | public string Id { get ; set; } 38 | 39 | /// 40 | /// The plugin activation status. 41 | /// 42 | [JsonProperty("status")] 43 | public ActivationStatus Status { get; set; } 44 | 45 | /// 46 | /// The plugin name. 47 | /// 48 | [JsonProperty("name")] 49 | public string Name { get; set; } 50 | 51 | /// 52 | /// The plugin's website address. 53 | /// 54 | [JsonProperty("plugin_uri")] 55 | public string PluginUri { get; set; } 56 | 57 | /// 58 | /// The plugin author. 59 | /// 60 | [JsonProperty("author")] 61 | public string Author { get; set; } 62 | 63 | /// 64 | /// Plugin author's website address. 65 | /// 66 | [JsonProperty("author_uri")] 67 | public string AuthorUri { get; set; } 68 | 69 | /// 70 | /// The plugin description. 71 | /// 72 | [JsonProperty("description")] 73 | public Description Description { get; set; } 74 | 75 | /// 76 | /// The plugin version number. 77 | /// 78 | [JsonProperty("version")] 79 | public string Version { get; set; } 80 | 81 | /// 82 | /// Whether the plugin can only be activated network-wide. 83 | /// 84 | [JsonProperty("network_only")] 85 | public bool NetworkOnly { get; set; } 86 | 87 | /// 88 | /// Minimum required version of WordPress. 89 | /// 90 | [JsonProperty("requires_wp")] 91 | public string RequiresWordPress { get; set; } 92 | 93 | /// 94 | /// Minimum required version of PHP. 95 | /// 96 | [JsonProperty("requires_php")] 97 | public string RequiresPHP { get; set; } 98 | 99 | /// 100 | /// The plugin's text domain. 101 | /// 102 | [JsonProperty("textdomain")] 103 | public string TextDomain { get; set; } 104 | 105 | /// 106 | /// The plugin's links. 107 | /// 108 | [JsonProperty("_links")] 109 | public Links Links { get; set; } 110 | } 111 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at pentenrieder@outlook.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Pages_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Models; 4 | using WordPressPCL.Tests.Selfhosted.Utility; 5 | using System.Linq; 6 | using WordPressPCL.Utility; 7 | using WordPressPCL.Models.Exceptions; 8 | using System.Collections.Generic; 9 | 10 | namespace WordPressPCL.Tests.Selfhosted; 11 | 12 | [TestClass] 13 | public class Pages_Tests 14 | { 15 | private static WordPressClient _client; 16 | private static WordPressClient _clientAuth; 17 | 18 | [ClassInitialize] 19 | public static async Task Init(TestContext testContext) 20 | { 21 | _client = ClientHelper.GetWordPressClient(); 22 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 23 | } 24 | 25 | [TestMethod] 26 | public async Task Pages_Create() 27 | { 28 | Page page = new() 29 | { 30 | Title = new Title("Title 1"), 31 | Content = new Content("Content PostCreate") 32 | }; 33 | Page createdPage = await _clientAuth.Pages.CreateAsync(page); 34 | 35 | Assert.AreEqual(page.Content.Raw, createdPage.Content.Raw); 36 | Assert.IsTrue(createdPage.Content.Rendered.Contains(page.Content.Rendered)); 37 | } 38 | 39 | [TestMethod] 40 | public async Task Pages_Read() 41 | { 42 | List pages = await _client.Pages.QueryAsync(new PagesQueryBuilder()); 43 | Assert.IsNotNull(pages); 44 | Assert.AreNotEqual(pages.Count, 0); 45 | } 46 | 47 | [TestMethod] 48 | public async Task Pages_Get() 49 | { 50 | List pages = await _client.Pages.GetAsync(); 51 | Assert.IsNotNull(pages); 52 | Assert.AreNotEqual(pages.Count, 0); 53 | } 54 | 55 | [TestMethod] 56 | public async Task Pages_Update() 57 | { 58 | string testContent = $"Test {System.Guid.NewGuid()}"; 59 | List pages = await _client.Pages.GetAllAsync(); 60 | Assert.IsTrue(pages.Count > 0); 61 | 62 | Page page = pages.FirstOrDefault(); 63 | page.Content.Raw = testContent; 64 | Page updatedPage = await _clientAuth.Pages.UpdateAsync(page); 65 | Assert.AreEqual(testContent, updatedPage.Content.Raw); 66 | } 67 | 68 | 69 | [TestMethod] 70 | public async Task Pages_Delete() 71 | { 72 | Page page = new() 73 | { 74 | Title = new Title("Title 1"), 75 | Content = new Content("Content PostCreate") 76 | }; 77 | Page createdPage = await _clientAuth.Pages.CreateAsync(page); 78 | Assert.IsNotNull(createdPage); 79 | 80 | bool response = await _clientAuth.Pages.Delete(createdPage.Id); 81 | Assert.IsTrue(response); 82 | await Assert.ThrowsExceptionAsync(async () => 83 | { 84 | Page pageById = await _client.Pages.GetByIDAsync(createdPage.Id); 85 | }); 86 | } 87 | 88 | [TestMethod] 89 | public async Task Pages_Query() 90 | { 91 | PagesQueryBuilder queryBuilder = new() 92 | { 93 | Page = 1, 94 | PerPage = 15, 95 | OrderBy = PagesOrderBy.Title, 96 | Order = Order.ASC, 97 | Statuses = new List { Status.Publish }, 98 | Embed = true 99 | }; 100 | List queryresult = await _client.Pages.QueryAsync(queryBuilder); 101 | Assert.AreEqual("?page=1&per_page=15&orderby=title&status=publish&order=asc&_embed=true&context=view", queryBuilder.BuildQuery()); 102 | Assert.IsNotNull(queryresult); 103 | Assert.AreNotSame(queryresult.Count, 0); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Hosted/Basic_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using WordPressPCL.Tests.Hosted.Utility; 4 | using System.Threading.Tasks; 5 | using WordPressPCL.Models; 6 | using System.Linq; 7 | 8 | namespace WordPressPCL.Hosted; 9 | 10 | [TestClass] 11 | public class Basic_Tests 12 | { 13 | private static WordPressClient _client; 14 | 15 | [ClassInitialize] 16 | public static void Init(TestContext testContext) 17 | { 18 | _client = ClientHelper.GetWordPressClient(); 19 | } 20 | 21 | [TestMethod] 22 | public async Task Hosted_BasicSetupTest() 23 | { 24 | // Initialize 25 | Assert.IsNotNull(_client); 26 | // Posts 27 | var posts = await _client.Posts.GetAllAsync(); 28 | Assert.AreNotEqual(posts.Count, 0); 29 | Assert.IsNotNull(posts); 30 | } 31 | 32 | [TestMethod] 33 | public async Task Hosted_GetFirstPostTest() 34 | { 35 | // Initialize 36 | var posts = await _client.Posts.GetAllAsync(); 37 | var post = await _client.Posts.GetByIDAsync(posts.First().Id); 38 | Assert.IsTrue(posts.First().Id == post.Id); 39 | Assert.IsTrue(!String.IsNullOrEmpty(posts.First().Content.Rendered)); 40 | } 41 | 42 | [TestMethod] 43 | public async Task Hosted_GetStickyPosts() 44 | { 45 | // Initialize 46 | var posts = await _client.Posts.GetStickyPostsAsync(); 47 | 48 | foreach (Post post in posts) 49 | { 50 | Assert.IsTrue(post.Sticky); 51 | } 52 | } 53 | 54 | [TestMethod] 55 | public async Task Hosted_GetPostsByCategory() 56 | { 57 | // This CategoryID MUST exists at ApiCredentials.WordPressUri 58 | int category = 1; 59 | // Initialize 60 | var posts = await _client.Posts.GetPostsByCategoryAsync(category); 61 | 62 | foreach (Post post in posts) 63 | { 64 | Assert.IsTrue(post.Categories.ToList().Contains(category)); 65 | } 66 | } 67 | 68 | [TestMethod] 69 | public async Task Hosted_GetPostsByTag() 70 | { 71 | var tags = await _client.Tags.GetAsync(); 72 | int tagId = tags.FirstOrDefault().Id; 73 | 74 | // Initialize 75 | var posts = await _client.Posts.GetPostsByTagAsync(tagId); 76 | Assert.AreNotEqual(0, posts.Count); 77 | foreach (Post post in posts) 78 | { 79 | Assert.IsTrue(post.Tags.ToList().Contains(tagId)); 80 | } 81 | } 82 | 83 | [TestMethod] 84 | public async Task Hosted_GetPostsByAuthor() 85 | { 86 | // This AuthorID MUST exists at ApiCredentials.WordPressUri 87 | int author = 3722200; 88 | // Initialize 89 | var posts = await _client.Posts.GetPostsByAuthorAsync(author); 90 | Assert.AreNotEqual(0, posts.Count); 91 | foreach (Post post in posts) 92 | { 93 | Assert.IsTrue(post.Author == author); 94 | } 95 | } 96 | 97 | [TestMethod] 98 | public async Task Hosted_GetPostsBySearch() 99 | { 100 | // This search term MUST be used at least once 101 | string search = "hello"; 102 | // Initialize 103 | var posts = await _client.Posts.GetPostsBySearchAsync(search); 104 | Assert.AreNotEqual(0, posts.Count); 105 | foreach (Post post in posts) 106 | { 107 | bool containsOnContentOrTitle = false; 108 | 109 | if (post.Content.Rendered.ToUpper().Contains(search.ToUpper()) || post.Title.Rendered.ToUpper().Contains(search.ToUpper())) 110 | { 111 | containsOnContentOrTitle = true; 112 | } 113 | 114 | Assert.IsTrue(containsOnContentOrTitle); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /WordPressPCL/Client/CustomRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using Newtonsoft.Json; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Client 8 | { 9 | /// 10 | /// Class to create custom requests 11 | /// 12 | public class CustomRequest 13 | { 14 | private readonly HttpHelper _httpHelper; 15 | 16 | /// 17 | /// Constructor 18 | /// 19 | /// HttpHelper class to operate with Http methods 20 | public CustomRequest(HttpHelper httpHelper) 21 | { 22 | _httpHelper = httpHelper; 23 | } 24 | 25 | /// 26 | /// Create object 27 | /// 28 | /// type of input object 29 | /// type of result object 30 | /// path to exec request 31 | /// object for creation 32 | /// request should prepend default path to route, defaults to true 33 | /// Created object 34 | public async Task CreateAsync(string route, TInput Entity, bool ignoreDefaultPath = true) where TOutput : class 35 | { 36 | string entity = _httpHelper.JsonSerializerSettings == null ? JsonConvert.SerializeObject(Entity) : JsonConvert.SerializeObject(Entity, _httpHelper.JsonSerializerSettings); 37 | using StringContent sc = new(entity, Encoding.UTF8, "application/json"); 38 | return (await _httpHelper.PostRequestAsync(route, sc, true, ignoreDefaultPath).ConfigureAwait(false)).Item1; 39 | } 40 | 41 | /// 42 | /// Delete object 43 | /// 44 | /// path to exec delete request 45 | /// request should prepend default path to route, defaults to true 46 | /// Result of deletion 47 | public Task DeleteAsync(string route, bool ignoreDefaultPath = true) 48 | { 49 | return _httpHelper.DeleteRequestAsync(route, true, ignoreDefaultPath); 50 | } 51 | 52 | /// 53 | /// Get object/s 54 | /// 55 | /// type of object 56 | /// path to exec request 57 | /// is get embed params 58 | /// i use auth 59 | /// request should prepend default path to route, defaults to true 60 | /// List of objects 61 | public Task GetAsync(string route, bool embed = false, bool useAuth = false, bool ignoreDefaultPath = true) where TClass : class 62 | { 63 | return _httpHelper.GetRequestAsync(route, embed, useAuth, ignoreDefaultPath); 64 | } 65 | 66 | /// 67 | /// Update object 68 | /// 69 | /// type of input object 70 | /// type of result object 71 | /// path to exec request 72 | /// object for update 73 | /// request should prepend default path to route, defaults to true 74 | /// Updated object 75 | public Task UpdateAsync(string route, TInput Entity, bool ignoreDefaultPath = true) where TOutput : class 76 | { 77 | return CreateAsync(route, Entity, ignoreDefaultPath); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Tag_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WordPressPCL.Tests.Selfhosted.Utility; 3 | using System.Threading.Tasks; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Utility; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | 9 | namespace WordPressPCL.Tests.Selfhosted; 10 | 11 | [TestClass] 12 | public class Tag_Tests 13 | { 14 | private static WordPressClient _client; 15 | private static WordPressClient _clientAuth; 16 | 17 | [ClassInitialize] 18 | public static async Task Init(TestContext testContext) 19 | { 20 | _client = ClientHelper.GetWordPressClient(); 21 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 22 | 23 | string tagname = $"Test {System.Guid.NewGuid()}"; 24 | await _clientAuth.Tags.CreateAsync(new Tag() 25 | { 26 | Name = tagname, 27 | Description = "Test Description" 28 | }); 29 | } 30 | 31 | [TestMethod] 32 | public async Task Tags_Create() 33 | { 34 | string tagname = $"Test {System.Guid.NewGuid()}"; 35 | Tag tag = await _clientAuth.Tags.CreateAsync(new Tag() 36 | { 37 | Name = tagname, 38 | Description = "Test Description" 39 | }); 40 | Assert.IsNotNull(tag); 41 | Assert.AreEqual(tagname, tag.Name); 42 | Assert.AreEqual("Test Description", tag.Description); 43 | } 44 | 45 | [TestMethod] 46 | public async Task Tags_Read() 47 | { 48 | List tags = await _clientAuth.Tags.GetAllAsync(); 49 | Assert.IsNotNull(tags); 50 | Assert.AreNotEqual(tags.Count, 0); 51 | CollectionAssert.AllItemsAreUnique(tags.Select(tag => tag.Id).ToList()); 52 | } 53 | 54 | [TestMethod] 55 | public async Task Tags_Get() 56 | { 57 | List tags = await _client.Tags.GetAsync(); 58 | Assert.IsNotNull(tags); 59 | Assert.AreNotEqual(tags.Count, 0); 60 | CollectionAssert.AllItemsAreUnique(tags.Select(tag => tag.Id).ToList()); 61 | } 62 | 63 | [TestMethod] 64 | public async Task Tags_Update() 65 | { 66 | List tags = await _clientAuth.Tags.GetAllAsync(); 67 | Tag tag = tags.FirstOrDefault(); 68 | if(tag == null) 69 | { 70 | Assert.Inconclusive(); 71 | } 72 | string tagname = $"Testname {System.Guid.NewGuid()}"; 73 | string tagdesc = "Test Description"; 74 | tag.Name = tagname; 75 | tag.Description = tagdesc; 76 | Tag tagUpdated = await _clientAuth.Tags.UpdateAsync(tag); 77 | Assert.AreEqual(tagname, tagUpdated.Name); 78 | Assert.AreEqual(tagdesc, tagUpdated.Description); 79 | } 80 | 81 | [TestMethod] 82 | public async Task Tags_Delete() 83 | { 84 | List tags = await _clientAuth.Tags.GetAllAsync(); 85 | Tag tag = tags.FirstOrDefault(); 86 | if (tag == null) 87 | { 88 | Assert.Inconclusive(); 89 | } 90 | int tagId = tag.Id; 91 | bool response = await _clientAuth.Tags.DeleteAsync(tagId); 92 | Assert.IsTrue(response); 93 | tags = await _clientAuth.Tags.GetAllAsync(); 94 | List tagsWithId = tags.Where(x => x.Id == tagId).ToList(); 95 | Assert.AreEqual(tagsWithId.Count, 0); 96 | } 97 | [TestMethod] 98 | public async Task Tags_Query() 99 | { 100 | TagsQueryBuilder queryBuilder = new() 101 | { 102 | Page = 1, 103 | PerPage = 15, 104 | OrderBy = TermsOrderBy.Id, 105 | Order = Order.DESC, 106 | }; 107 | List queryresult = await _clientAuth.Tags.QueryAsync(queryBuilder); 108 | Assert.AreEqual("?page=1&per_page=15&orderby=id&order=desc&context=view", queryBuilder.BuildQuery()); 109 | Assert.IsNotNull(queryresult); 110 | Assert.AreNotSame(queryresult.Count, 0); 111 | } 112 | 113 | } 114 | 115 | -------------------------------------------------------------------------------- /WordPressPCL.Tests.Selfhosted/Categories_Tests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using WordPressPCL.Models; 7 | using WordPressPCL.Tests.Selfhosted.Utility; 8 | using WordPressPCL.Utility; 9 | 10 | namespace WordPressPCL.Tests.Selfhosted; 11 | 12 | [TestClass] 13 | public class Categories_Tests 14 | { 15 | private static WordPressClient _client; 16 | private static WordPressClient _clientAuth; 17 | 18 | [ClassInitialize] 19 | public static async Task Init(TestContext testContext) 20 | { 21 | _client = ClientHelper.GetWordPressClient(); 22 | _clientAuth = await ClientHelper.GetAuthenticatedWordPressClient(testContext); 23 | } 24 | 25 | [TestMethod] 26 | public async Task Categories_Create() 27 | { 28 | Random random = new(); 29 | string name = $"TestCategory {random.Next(0, 10000)}"; 30 | Category category = await _clientAuth.Categories.CreateAsync(new Category() 31 | { 32 | Name = name, 33 | Description = "Test" 34 | }); 35 | Assert.IsNotNull(category); 36 | Assert.AreEqual(name, category.Name); 37 | Assert.AreEqual("Test", category.Description); 38 | } 39 | 40 | [TestMethod] 41 | public async Task Categories_Read() 42 | { 43 | List categories = await _client.Categories.GetAllAsync(); 44 | Assert.IsNotNull(categories); 45 | Assert.AreNotEqual(categories.Count, 0); 46 | CollectionAssert.AllItemsAreUnique(categories.Select(tag => tag.Id).ToList()); 47 | } 48 | 49 | [TestMethod] 50 | public async Task Categories_Get() 51 | { 52 | List categories = await _client.Categories.GetAsync(); 53 | Assert.IsNotNull(categories); 54 | Assert.AreNotEqual(categories.Count, 0); 55 | CollectionAssert.AllItemsAreUnique(categories.Select(tag => tag.Id).ToList()); 56 | } 57 | 58 | [TestMethod] 59 | public async Task Categories_Update() 60 | { 61 | List categories = await _clientAuth.Categories.GetAllAsync(); 62 | Category category = categories.First(); 63 | Random random = new(); 64 | string name = $"UpdatedCategory {random.Next(0, 10000)}"; 65 | category.Name = name; 66 | Category updatedCategory = await _clientAuth.Categories.UpdateAsync(category); 67 | Assert.AreEqual(updatedCategory.Name, name); 68 | Assert.AreEqual(updatedCategory.Id, category.Id); 69 | } 70 | 71 | [TestMethod] 72 | public async Task Categories_Delete() 73 | { 74 | Random random = new(); 75 | string name = $"TestCategory {random.Next(0, 10000)}"; 76 | Category category = await _clientAuth.Categories.CreateAsync(new Category() 77 | { 78 | Name = name, 79 | Description = "Test" 80 | }); 81 | 82 | if (category == null) 83 | { 84 | Assert.Inconclusive(); 85 | } 86 | bool response = await _clientAuth.Categories.DeleteAsync(category.Id); 87 | Assert.IsTrue(response); 88 | List categories = await _clientAuth.Categories.GetAllAsync(); 89 | List c = categories.Where(x => x.Id == category.Id).ToList(); 90 | Assert.AreEqual(c.Count, 0); 91 | } 92 | 93 | [TestMethod] 94 | public async Task Categories_Query() 95 | { 96 | CategoriesQueryBuilder queryBuilder = new() 97 | { 98 | Page = 1, 99 | PerPage = 15, 100 | OrderBy = TermsOrderBy.Id, 101 | Order = Order.DESC, 102 | }; 103 | List queryresult = await _clientAuth.Categories.QueryAsync(queryBuilder); 104 | Assert.AreEqual("?page=1&per_page=15&orderby=id&order=desc&context=view", queryBuilder.BuildQuery()); 105 | Assert.IsNotNull(queryresult); 106 | Assert.AreNotSame(queryresult.Count, 0); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /WordPressPCL/Client/Taxonomies.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using WordPressPCL.Interfaces; 4 | using WordPressPCL.Models; 5 | using WordPressPCL.Utility; 6 | 7 | namespace WordPressPCL.Client 8 | { 9 | /// 10 | /// Client class for interaction with Taxonomies endpoint WP REST API 11 | /// 12 | public class Taxonomies : IReadOperation, IQueryOperation 13 | { 14 | private readonly HttpHelper _httpHelper; 15 | private const string _methodPath = "taxonomies"; 16 | 17 | /// 18 | /// Constructor 19 | /// 20 | /// reference to HttpHelper class for interaction with HTTP 21 | public Taxonomies(HttpHelper httpHelper) 22 | { 23 | _httpHelper = httpHelper; 24 | } 25 | /// 26 | /// Get latest 27 | /// 28 | /// include embed info 29 | /// Send request with authentication header 30 | /// Get latest taxonomies 31 | public async Task> GetAsync(bool embed = false, bool useAuth = false) 32 | { 33 | List entities = new(); 34 | Dictionary entities_page = await _httpHelper.GetRequestAsync>($"{_methodPath}", embed, useAuth).ConfigureAwait(false); 35 | foreach (KeyValuePair ent in entities_page) 36 | { 37 | entities.Add(ent.Value); 38 | } 39 | return entities; 40 | } 41 | 42 | /// 43 | /// Get All 44 | /// 45 | /// Include embed info 46 | /// Send request with authentication header 47 | /// List of all result 48 | public async Task> GetAllAsync(bool embed = false, bool useAuth = false) 49 | { 50 | //100 - Max posts per page in WordPress REST API, so this is hack with multiple requests 51 | List entities = new(); 52 | Dictionary entities_page = (await _httpHelper.GetRequestAsync>($"{_methodPath}", embed, useAuth).ConfigureAwait(false)); 53 | foreach (KeyValuePair ent in entities_page) 54 | { 55 | entities.Add(ent.Value); 56 | } 57 | return entities; 58 | } 59 | 60 | /// 61 | /// Get Entity by Id 62 | /// 63 | /// ID 64 | /// include embed info 65 | /// Send request with authentication header 66 | /// Entity by Id 67 | public Task GetByIDAsync(object ID, bool embed = false, bool useAuth = false) 68 | { 69 | return _httpHelper.GetRequestAsync($"{_methodPath}/{ID}", embed, useAuth); 70 | } 71 | 72 | /// 73 | /// Create a parametrized query and get a result 74 | /// 75 | /// Query builder with specific parameters 76 | /// Send request with authentication header 77 | /// List of filtered result 78 | public async Task> QueryAsync(TaxonomiesQueryBuilder queryBuilder, bool useAuth = false) 79 | { 80 | List entities = new(); 81 | Dictionary entities_dict = await _httpHelper.GetRequestAsync>($"{_methodPath}{queryBuilder.BuildQuery()}", false, useAuth).ConfigureAwait(false); 82 | foreach (KeyValuePair ent in entities_dict) 83 | { 84 | entities.Add(ent.Value); 85 | } 86 | return entities; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Home 2 | 3 | This is a portable library for consuming the WordPress REST-API in (almost) any C# application. 4 | If you find bugs or have any suggestions, feel free to create an issue. 5 | 6 | ## License 7 | WordPressPCL is published under the [MIT License](https://github.com/wp-net/WordPressPCL/blob/master/LICENSE) 8 | 9 | # Quickstart 10 | 11 | ## WordPress Requirements 12 | Since WordPress 4.7 the REST API has been integrated into the core so there's no need for any plugins to get basic functionality. If you want to access protected endpoints, this library supports authentication through JSON Web Tokens (JWT) (plugin required). 13 | 14 | * [WordPress 4.7 or newer](https://wordpress.org/) 15 | * [JWT Authentication for WP REST API](https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/) 16 | 17 | ## Including WordPressPCL 18 | The WordPressPCL API Wrapper is avaiable through [NuGet](https://www.nuget.org/packages/WordPressPCL/): 19 | 20 | ``` 21 | > Install-Package WordPressPCL 22 | ``` 23 | 24 | ## Supported Plattforms 25 | WordPressPCL is built on top of the new [.NET Standard](https://github.com/dotnet/standard) targeting netstandard versions 1.1 and 2.0 - therefore it should work on the following plaforms: 26 | * .NET Framework 4.5 and newer 27 | * .NET Core 28 | * Universal Windows Platform (uap) 29 | * Windows 8.0 and newer 30 | * Windows Phone (WinRT, not Silverlight) 31 | * Mono / Xamarin 32 | 33 | ## Quickstart: Using the API Wrapper 34 | 35 | ```c# 36 | // Initialize 37 | var client = new WordPressClient("http://demo.wp-api.org/wp-json/"); 38 | 39 | // Posts 40 | var posts = await client.Posts.GetAll(); 41 | var postbyid = await client.Posts.GetById(id); 42 | 43 | // Comments 44 | var comments = await client.Comments.GetAll(); 45 | var commentbyid = await client.Comments.GetById(id); 46 | var commentsbypost = await client.Comments.GetCommentsForPost(postid, true, false); 47 | 48 | // Users 49 | // JWT authentication 50 | var client = new WordPressClient(ApiCredentials.WordPressUri); 51 | client.AuthMethod = AuthMethod.JWT; 52 | await client.RequestJWToken(ApiCredentials.Username,ApiCredentials.Password); 53 | 54 | // check if authentication has been successful 55 | var isValidToken = await client.IsValidJWToken(); 56 | 57 | // now you can send requests that require authentication 58 | var response = client.Posts.Delete(postid); 59 | ``` 60 | 61 | ## Supported REST Methods 62 | 63 | | | Create | Read | Update | Delete | 64 | |--------------------|---------|---------|---------|---------| 65 | | **Posts** | yes | yes | yes | yes | 66 | | **Pages** | yes | yes | yes | yes | 67 | | **Comments** | yes | yes | yes | yes | 68 | | **Categories** | yes | yes | yes | yes | 69 | | **Tags** | yes | yes | yes | yes | 70 | | **Users** | yes | yes | yes | yes | 71 | | **Media** | yes | yes | yes | yes | 72 | | **Post Revisions** | --- | yes | --- | yes | 73 | | **Taxonomies** | --- | yes | --- | --- | 74 | | **Post Types** | --- | yes | --- | --- | 75 | | **Post Statuses** | --- | yes | --- | --- | 76 | | **Settings** | --- | yes | yes | --- | 77 | 78 | ## Additional Features 79 | 80 | - Authentication using [JSON Web Tokens (JWT)](https://jwt.io/) 81 | - [HttpResponsePreProcessing](https://github.com/wp-net/WordPressPCL/wiki/HttpResponsePreProcessing): manipulate the API response before deserializing it 82 | 83 | ## Contribution Guidelines 84 | We're very happy to get input from the community on this project! To keep the code clean we ask you to follow a few simple contribution guidelines. 85 | 86 | First, create an issue describing what feature you want to add or what problem you're trying to solve, just to make sure no one is already working on that. That also gives us a chance to debate whether a feature is within the scope of this project. 87 | 88 | Second, please try to stick to the official C# coding guidelines. https://msdn.microsoft.com/en-us/library/ms229002(v=vs.110).aspx 89 | 90 | Also, make sure to write some tests covering your new or modified code. 91 | --------------------------------------------------------------------------------