How to update Advanced Custom Fields for a WordPress blogpost from C# with a JSON REST API call

 

When you want to edit the advanced custom fields for a WordPress blogpost, you must first install the ACF to REST API plugin.

 

 

For sending authenticated POST request I used the JWT Autentication for WP-API plugin.

(see https://www.roelvanlisdonk.nl/2018/06/18/how-to-create-wordpress-blog-posts-from-c-with-httpclient-and-the-wordpress-rest-api-on-docker-for-windows-10/)

 

Now I have created a blogpost in WordPress and filled the advanced custom field: “my-field-1” with value: “value 1”.

The blogpost Id = 15.

 

To update the value, I used the following code:

 

 

 

 

 

 

 

 

 

 

 

 

 

How to create WordPress blog posts from C# with HttpClient and the WordPress REST API on Docker for Windows 10

 
 

To create WordPress blog posts from C# I will be using docker container on my Windows 10 Pro installation.

Before installing docker on Windows 10 make sure the Windows 10 feature “Hyper-V” is enabled:

 
 


 
 

Install docker on Windows 10

I followed this blog post to install docker on Windows 10

https://docs.docker.com/docker-for-windows/install/#about-windows-containers

 
 

Create WordPress docker container

To create a WordPress docker container I followed this post:

https://docs.docker.com/compose/wordpress/

 
 

How to read blog post information from C#

When the WordPress docker container is running, you should create your first blog post manually on http://localhost:8000.

Now reading all blog post data, is easy, because we can use an GET request without authentication, by using the C# code below.

 

 

 

Installing JWT Authentication for WP-API plugin

Now when you want to create a blog post from C# you will need to use a POST request with authentication.

I used JWT (JSON Web Tokens).

When you want to use JWT in WordPress you should first install the JWT Authentication for WP-API plugin.

After installing the plugin we must first edit two files before activating the plugin.

 
 

 
 

Edit .htaccess file on Windows

 
 

Get WordPress container name on windows:

  • Open PowerShell and enter:
  • docker ps –all

Copy .htaccess file from docker container to local Windows host:

  • docker cp wordpress_wordpress_1:/var/www/html/.htaccess .htaccess

Edit the file with your preferred editor (I used Visual Studio Code), add the rows in red:

 
 

# BEGIN WordPress

<IfModule mod_rewrite.c>

RewriteEngine On

RewriteBase /

RewriteRule ^index\.php$ – [L]

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . /index.php [L]

RewriteCond %{HTTP:Authorization} ^(.*)

RewriteRule ^(.*) – [E=HTTP_AUTHORIZATION:%1]

</IfModule>

SetEnvIf Authorization “(.*)” HTTP_AUTHORIZATION=$1

# END WordPress

 
 

 
 

Copy .htaccess file from local Windows host to Docker container:

  • docker cp .htaccess wordpress_wordpress_1:/var/www/html/.htaccess

 
 

 
 

Edit wp-config.php on Windows

 
 

Get WordPress container name on windows:

  • Open PowerShell and enter:
  • docker ps –all

Copy wp-config.php file from docker container to local Windows host:

  • docker cp wordpress_wordpress_1:/var/www/html/wp-config.php wp-config.php

Edit the file with your preferred editor (I used Visual Studio Code), add the rows in blue:

 
 

<?php

define(‘DB_NAME’, ‘wordpress’);

define(‘DB_USER’, ‘wordpress’);

define(‘DB_PASSWORD’, ‘bla bla bla’);

define(‘DB_HOST’, ‘db:3306’);

define(‘DB_CHARSET’, ‘utf8’);

define(‘DB_COLLATE’, );

define(‘AUTH_KEY’, ‘bla bla bla’);

define(‘SECURE_AUTH_KEY’, ‘bla bla bla’);

define(‘LOGGED_IN_KEY’, ‘bla bla bla’);

define(‘NONCE_KEY’, ‘bla bla bla’);

define(‘AUTH_SALT’, ‘bla bla bla’);

define(‘SECURE_AUTH_SALT’, ‘bla bla bla’);

define(‘LOGGED_IN_SALT’, ‘bla bla bla’);

define(‘NONCE_SALT’, ‘bla bla bla’);

define(‘JWT_AUTH_SECRET_KEY’, ‘bla bla bla’);

define(‘JWT_AUTH_CORS_ENABLE’, true);

$table_prefix = ‘wp_’;

define(‘WP_DEBUG’, false);

// If we’re behind a proxy server and using HTTPS, we need to alert WordPress of that fact

// see also http://codex.wordpress.org/Administration_Over_SSL#Using_a_Reverse_Proxy

if (isset($_SERVER[‘HTTP_X_FORWARDED_PROTO’]) && $_SERVER[‘HTTP_X_FORWARDED_PROTO’] === ‘https’) {

    $_SERVER[‘HTTPS’] = ‘on’;

}

/* That’s all, stop editing! Happy blogging. */

/** Absolute path to the WordPress directory. */

if ( !defined(‘ABSPATH’) )

    define(‘ABSPATH’, dirname(__FILE__) . ‘/’);

/** Sets up WordPress vars and included files. */

require_once(ABSPATH . ‘wp-settings.php’);

 
 

Copy wp-config.php file from local Windows host to Docker container:

  • docker cp wp-config.php wordpress_wordpress_1:/var/www/html/wp-config.php

 
 

 
 

How to create blog posts from C#

 
 

using System;

using System.Collections.Generic;

using System.Net.Http;

using System.Net.Http.Headers;

using System.Text;

using Newtonsoft.Json;

 
 

public
class
TokenInfo

{

    public
string token { get; set; }

public
string user_email { get; set; }

public
string user_nicename { get; set; }

public
string user_display_name { get; set; }

}

 
 

     using (var client = new HttpClient())

{

client.BaseAddress = new Uri(“http://localhost:8000”);

 
 

string loginInfo = @”{“”username””: “”your-wordpress-user””, “”password””:””your-wordpress-password””}”;

 
 

 
 

var tokenResponse = await client.PostAsync(

“/wp-json/jwt-auth/v1/token”,

new StringContent(loginInfo, Encoding.UTF8, “application/json”));

 
 

if (tokenResponse.IsSuccessStatusCode)

{

string tokenAsText = await tokenResponse.Content.ReadAsStringAsync();

TokenInfo tokenInfo = JsonConvert.DeserializeObject<TokenInfo>(tokenAsText);

 
 

string blogPost = @”{“”title””: “”Blog post from C# – v2″”, “”content””: “”<div>Just some content</div>””}”;

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, tokenInfo.token);

 
 

var createPostResponse = await client.PostAsync(

“/wp-json/wp/v2/posts”,

new StringContent(blogPost, Encoding.UTF8, “application/json”));

 
 

if (createPostResponse.IsSuccessStatusCode)

{

Console.WriteLine(“Success!”);

}

}

}

 
 

Now this new post will be created, but it will not directly be visible, because it is created as draft.

To publish the post you will have to set the status to publish

 
 

How to publish a draft post from C#

 
 

using System;

using System.Collections.Generic;

using System.Net.Http;

using System.Net.Http.Headers;

using System.Text;

using Newtonsoft.Json;

 
 

public
class
TokenInfo

{

    public
string token { get; set; }

public
string user_email { get; set; }

public
string user_nicename { get; set; }

public
string user_display_name { get; set; }

}

 
 

using (var client = new HttpClient())

{

client.BaseAddress = new Uri(“http://localhost:8000”);

 
 

string loginInfo = @”{“”username””: “”your-wordpress-user””, “”password””:””your-wordpress-password””}”;

 
 

 
 

var tokenResponse = await client.PostAsync(

“/wp-json/jwt-auth/v1/token”,

new StringContent(loginInfo, Encoding.UTF8, “application/json”));

 
 

if (tokenResponse.IsSuccessStatusCode)

{

string tokenAsText = await tokenResponse.Content.ReadAsStringAsync();

TokenInfo tokenInfo = JsonConvert.DeserializeObject<TokenInfo>(tokenAsText);

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, tokenInfo.token);

 
 

string blogPost = @”{“”status””: “”publish””}”;

int blogPostId = 8; // This is the ID for the draft blogpost you want to publish.

string blogPostUpdateUrl = $”/wp-json/wp/v2/posts/{blogPostId};

var createPostResponse = await client.PostAsync(

blogPostUpdateUrl,

new StringContent(blogPost, Encoding.UTF8, “application/json”));

 
 

if (createPostResponse.IsSuccessStatusCode)

{

Console.WriteLine(“Success!”);

}

}

Assert.AreEqual(true, true);

}

 
 

 
 

 
 

How to get all draft posts from C#

To get all draft posts from C# you must be authenticated so use the following code:

 
 

using System;

using System.Collections.Generic;

using System.Net.Http;

using System.Net.Http.Headers;

using System.Text;

using Newtonsoft.Json;

 
 

public
class
TokenInfo

{

    public
string token { get; set; }

public
string user_email { get; set; }

public
string user_nicename { get; set; }

public
string user_display_name { get; set; }

}

 
 

 
 

using (var client = new HttpClient())

{

client.BaseAddress = new Uri(“http://localhost:8000”);

 
 

string loginInfo = @”{“”username””: “”your-wordpress-user””, “”password””:””your-wordpress-password””}”;

 
 

 
 

var tokenResponse = await client.PostAsync(

“/wp-json/jwt-auth/v1/token”,

new StringContent(loginInfo, Encoding.UTF8, “application/json”));

 
 

if (tokenResponse.IsSuccessStatusCode)

{

string tokenAsText = await tokenResponse.Content.ReadAsStringAsync();

TokenInfo tokenInfo = JsonConvert.DeserializeObject<TokenInfo>(tokenAsText);

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, tokenInfo.token);

 
 

var response = await client.GetAsync(“/wp-json/wp/v2/posts?status=draft”);

 
 

if (response.IsSuccessStatusCode)

{

string postsAsText = await response.Content.ReadAsStringAsync();

List<WpPost> draftPosts = JsonConvert.DeserializeObject<List<WpPost>>(postsAsText);

foreach (WpPost post in draftPosts)

{

Console.WriteLine($”tile [{post.title}]”);

}

}

 
 

}

Assert.AreEqual(true, true);

}

 
 

Note : I used http://json2csharp.com to convert JSON to C# classes to get a type safe response from the server.

The following code is generated by http://json2csharp.com

 public
class
WpGuid

{

public
string rendered { get; set; }

}

 

public
class
Title

{

public
string rendered { get; set; }

}

 

public
class
Content

{

public
string rendered { get; set; }

public
bool @protected { get; set; }

}

 

public
class
Excerpt

{

public
string rendered { get; set; }

public
bool @protected { get; set; }

}

 

public
class
Self

{

public
string href { get; set; }

}

 

public
class
Collection

{

public
string href { get; set; }

}

 

public
class
About

{

public
string href { get; set; }

}

 

public
class
Author

{

public
bool embeddable { get; set; }

public
string href { get; set; }

}

 

public
class
Reply

{

public
bool embeddable { get; set; }

public
string href { get; set; }

}

 

public
class
VersionHistory

{

public
string href { get; set; }

}

 

public
class
WpAttachment

{

public
string href { get; set; }

}

 

public
class
WpTerm

{

public
string taxonomy { get; set; }

public
bool embeddable { get; set; }

public
string href { get; set; }

}

 

public
class
Cury

{

public
string name { get; set; }

public
string href { get; set; }

public
bool templated { get; set; }

}

 

public
class
Links

{

public List<Self> self { get; set; }

public List<Collection> collection { get; set; }

public List<About> about { get; set; }

public List<Author> author { get; set; }

public List<Reply> replies { get; set; }

public List<Cury> curies { get; set; }

}

 

public
class
WpPost

{

public
int id { get; set; }

public DateTime date { get; set; }

public DateTime date_gmt { get; set; }

public WpGuid guid { get; set; }

public DateTime modified { get; set; }

public DateTime modified_gmt { get; set; }

public
string slug { get; set; }

public
string status { get; set; }

public
string type { get; set; }

public
string link { get; set; }

public Title title { get; set; }

public Content content { get; set; }

public Excerpt excerpt { get; set; }

public
int author { get; set; }

public
int featured_media { get; set; }

public
string comment_status { get; set; }

public
string ping_status { get; set; }

public
bool sticky { get; set; }

public
string template { get; set; }

public
string format { get; set; }

public List<object> meta { get; set; }

public List<int> categories { get; set; }

public List<object> tags { get; set; }

public Links _links { get; set; }

}

 
 

 
 

 
 

 
 

  

How to fix: Could not load file or assembly ‘System.EnterpriseServices’ or one of its dependencies

 

An old ASP .NET 32-bit web application would not start, when reinstalled on a new machine:

Could not load file or assembly ‘System.EnterpriseServices’ or one of its dependencies. An attempt was made to load a program with an incorrect format. 

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

 Exception Details: System.BadImageFormatException: Could not load file or assembly ‘System.EnterpriseServices’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

 

To fix this problem, I just had to enable 32-bits applications on the application pool (Advanced Settings):

 

Where to download google chrome for offline installation 32-bit and 64-bit Windows

 

 

First go to: https://www.google.com/intl/en/chrome/browser/desktop/index.html?standalone=1

Important to note the parameter “standalone=1”

 

Then click on “Other Platforms” at the bottom of the page.

 

 

 

Then select your platform, e.g. Windows 7 32-bit

 

Then click: “ACCEPT AND INSTALL”

 

 

Download of the +/- 50MB download, should now start at the bottom of the browser:

 

 

 

“Possibly unhandled rejection” when closing ngDialog

 
 

Found my solution at: https://github.com/angular/material/issues/10369

When using the openConfirm function use:

TypeScript:

ngDialog

.openConfirm(dialogOptions)

.catch(function dialogCloseErrorCallback(reason: any) {

        // ngDialog v1.4.0 throws an exception, when closing the dialog with reason “undefined”.

        // Prevent this error:

    if (reason !== undefined) {

        throw
new Error(`Error when closing the dialog: [${reason}].`);

    }

});

 

When using open, use:

 

const openResult = self.ngDialog.open(dialogOptions);

if (openResult.closePromise) {

    openResult.closePromise.catch(function dialogCloseErrorCallback(reason: any) {

            // ngDialog v1.4.0 throws an exception, when closing the dialog with reason “undefined”.

           // Prevent this error:

        if (reason !== undefined) {

            throw
new Error(`Error when closing the dialog: [${reason}].`);

        }

    });

}

Allow *.css, *.js and *.html from the MVC 5 Views map

 

If you want to allow static content to be served from the asp .net MVC 5 views folder, just added the web.config in this folder and change path=”*” to path=”*.cshtml”.

https://stackoverflow.com/questions/17949460/how-do-you-request-static-html-files-under-the-views-folder-in-asp-net-mvc

 

<system.webServer>

<handlers>

<remove
name=BlockViewHandler/>

<add
name=BlockViewHandler
path=*.cshtml
verb=*
preCondition=integratedMode
type=System.Web.HttpNotFoundHandler />

</handlers>

</system.webServer>

Fix: TypeScript error Module ‘…’ has no default export

 

When I compiled my TypeScript project I got the error: [ts] Module ‘”c:/dev/am/common/element/after-visible”‘ has not default export:

 

This was caused by the fact, that I forgot to surround “afterVisible” with “{ }”.

The contents of the after-visible.ts module was:

 

/**

* Check the DOM repeatedly to see if the given element is visible, when element is visible call given function.

*/

export
function afterVisible(selector: string, fn: (element?: HTMLElement) => void, interval: number = 10, retryCount: number = 30): void {


const element = <HTMLElement>document.querySelector(selector);


if (element) {


const elementIsVisible = element.offsetWidth > 0 && element.offsetHeight > 0;


if (elementIsVisible) {

fn(element);


return;

}

}

 

setTimeout(function afterVisibleSetTimeoutElapsed() {


if (retryCount === 0) {


if (console) {

console.log(`Element ‘${selector}‘ does not exist or was not visible.`);

}

} else {


// Try again

afterVisible(selector, fn, retryCount – 1, interval);

}

}, interval);

}

 

 

So when you want to use the function afterVisible in an other function you will have to import it like so:

 

import { afterVisible } from
“./after-visible”;

 

function focus(element: HTMLElement) {

element.focus();

}

 

/**

* Check the DOM repeatedly to see if the given element is visible, when element is visible focus it.

*/

export
function focusAfterVisible(selector: string, interval: number = 10, retryCount: number = 30): void {

afterVisible(selector, focus, interval, retryCount);

}