Skip to main content

Using Netlify Identity with NetlifyCMS and Wyam

· One min read
Mark Burton
Software Engineer & Technical Writer
build_script:  - ..\Wyam\wyam --output ..\output -s LinkRoot="BigDoorWyamBlog" # https:/noknokmls.github.ioBigDoorWyamBlogposts2018-02-12-where-i-live-john-naughton-krakow-rzaska.html
``` # Setup your Netlify site to use Now we have AppVeyor building the site and pushing the output back to GitHub we can configure the deploy settings in Netlify so that changes are automatically published to your blog.

Setting up NetlifyCMS with Wyam, Part 3

· 2 min read
Mark Burton
Software Engineer & Technical Writer

Use AppVeyor for CI

To get started with a simple NetlifyCMS setup without the editorial workflow you can use the AppVeyor for Continuous Integration Wyam document. # Enabling the editorial workflow ## Turn off webhook for PR to prevent extra build being triggered in AppVeyor For more details see Do not build virtual merge on Pull Requests #1636 and Preventing master CI run when a commit a made on a feature branch If this is not done AppVeyor will run the master branch config when a draft is saved in NetlifyCMS as a PR is created, it will fail with

git push
remote: Anonymous access to MarkZitherProjectName.git denied.
fatal: Authentication failed for 'https:/github.comMarkZitherProjectName.git'
Command exited with code 128
``` ## Advanced AppVeyor config to support Editorial_Workflow
When a blog post is saved as a draft it will create a branch starting with 'cms', use the for branches with a regular expression to control different aspects of the build on master and cms branches, here is [an example of this in action](https:/github.comNokNokMLSBigDoorWyamBlogblobmasterappveyor.yml)

for: # override settings for master branch

  • version: 1.0. branches: only: - master ####################### # removed for brevity # ####################### # override settings for cms* branches
  • branches: only: - /cms.*/ deploy:

git push remote: Anonymous access to MarkZitherProjectName.git denied. fatal: Authentication failed for 'https:/github.comMarkZitherProjectName.git' Command exited with code 128

This is caused by the PR firing a Webhook which triggers the build on the master branch.  ## Other things to write about
* show the correct loggedlogged out menu based on [Netlify Identity Widget](https:/github.comnetlifynetlify-identity-widget) events and user object.
* how to populate the author field from the Netlify Identity metadata.

Using Netlify Identity with NetlifyCMS and Wyam

· 2 min read
Mark Burton
Software Engineer & Technical Writer

The following was set up in part 1, but it is worth reviewing again. I think more external providers will be available.

Set registration preference and external providers

Registration settings and External providers are located in settings under Identity

Enable Git Gateway in Netlify

Your CMS users are likely to not have Github logins, so enable the Git Gateway to allow them to save and publish posts to GitHub without having to setup an account on Github.

It is explained further in the Netlify Docs on Git Gateway The Git Gateway option is in settings under Identity, further down than the Registration preferences and external providers settings. ## Make it easy for user to complete signup by adding Netlify Identity Widget to the site

The email inviting a user to use the CMS links to the homepage with a invite token in the URL, if you do not follow this step the user will be left looking at the homepage not understanding why they are there or what they should do next. Add the netlify-identity-widget to your homepage, you can just add the following to the body of your layout page

<script src="https:identity.netlify.comv1netlify-identity-widget.js" /><script />

Now when the user hits the homepage with the #invite_token= in the URL the complete signup modal will appear.

Invite some users

Might be best to come back to this step after setting the registration preferences

Setting up NetlifyCMS with Wyam, Part 1

· 8 min read
Mark Burton
Software Engineer & Technical Writer

Despite the length of this series of blog posts NetlifyCMS is actually really easy to setup. It just involves configuration across a few different tools to get it all working in an automated fashion, from publishing a post in the CMS, thereby the file being pushed to GitHub, to automatically building and publishing your site, in this instance to Netlify. I was not familiar with all these tools when i started so please leave a comment if there are improvements that can be made.

Lets start with what Wyam and NetlifyCMS are;

""Wyam is different. It's a static content toolkit and can be used to generate web sites, produce documentation, create ebooks, and much more. Since everything is configured by chaining together flexible modules (that you can even write yourself), the only limits to what it can create are your imagination.""-- wyam.io > ""Netlify CMS is an open source content management system for your Git workflow that enables you to provide editors with friendly UI and intuitive workflow. You can use it with any static site generator to create faster, more flexible web projects."" --<cite> NetlifyCMS ## Now onto the steps I went through to setup NetlifyCMS for my blog. This information is what worked for me and makes some assumptions

  • Your code is in a GitHub repository
  • Your site is hosted on Netlify
  • You are using AppVeyor for continuous deployment
  • You are the only editor of your blog (Part 2 will deal with adding users and Netlify Identify features to the blog)
  • You know that your CMS will be accessed at by manually adding admin to your site URL
  • You are happy pushing your changes directly to master (Part 3 will explain the editorial workflow, which saves drafts to feature branches, creates a PR and merges once the post is published) ## Changes in your Wyam project

Add an admin directory under your Wyam input directory

The general instructions to add NetlifyCMS to your site cover several static site generators but not Wyam (I have a PR in to add it to the table for App File Structure). Assuming you are using the default Wyam input directory name and structure the setup is; * add a folder called admin under your Wyam input directory * add 2 files to it, index.html and config.yml. In the index.html add the following, ```html


```yaml
backend:
name: git-gateway
branch: master # Branch to update (optional; defaults to master)

media_folder: "input/assets/Images" # Media files will be stored in the repo under input/assets/Images
public_folder: "/assets/Images" # public_folder indicates where they can be found in the published site

collections:
- name: "blog" # Used in routes, e.g., /admin/collections/blog
label: "Blog" # Used in the UI
folder: "input/posts" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
fields: # The fields for each document, usually in front matter
- {label: "Title", name: "Title", widget: "string"}
- {label: "Lead", name: "Lead", widget: "string", optional: true, required: false}
- {label: "Published", name: "Published", widget: "datetime"}
- {label: "Featured Image", name: "Image", widget: "image", optional: true, required: false, pattern: ['^\S*$', "Please rename your image remove any spaces from the filename"]}
- label: Tags
name: Tags
widget: list
- {label: "Body", name: "body", widget: "markdown"}

::: A couple of things to note here are; It is using git-gateway as the backend, if you don't intend on having additional editors for your blog you could connect directly to GitHub as described in authentication and backends and Using an Authentication Provider, but it looks to be more work for less functionality. The Tags field, in the CMS it will display as a simple textbox, but you add your tags as a comma separated list. I tried to get the list to add and item for each tag i wanted to add, but hit some JavaScript errors which prevented me typing more than 1 character per tag. The Featured Image (and media upload screen) will happily accept a filename with spaces in it, which markdownhtml will then fail to render as it doesn't get escaped to %20. The field can have validation added to it like pattern: ['^\S*$', "Please rename your image remove any spaces from the filename"] not very user friendly, but better than the image just not working. ::: ::: ::: :::::: ## Setup your blog on Netlify The Wyam website already has a guide on setting up a site on Netlify to get started you can just drag and drop your site as described, in Part 3 we will setup the continuous deployment from GitHub. ### Enable Netlify Identity As the config states is setup with backend: name:git-gateway we will be using Netlify Identity. ### Set registration preference and external providers Registration settings and External providers are in settings under Identity. At this stage I only want access myself, so I have made it invite only and enabled GitHub as the sole external provider. ### Enable Git Gateway in Netlify

::: Your CMS users are likely to not have GitHub logins. Enable the Git Gateway to allow them to save and publish posts to GitHub without having to setup an account on Github, this is also required as we set the backend to git-gateway. Without it you will get errors in the console when you try to save media or posts. It is explained further in the Netlify Docs on Git Gateway The Git Gateway option is in settings under Identity, further down than the Registration preferences and external providers settings. ::: ::: ::: :::::: ### NetlifyCMS in action

::: At this point you should have a working CMS, run wyam -p 5080 -w - here is more information on the wyam command line - then navigate to http:/localhost:5080admin, you should be greeted with the NetlifyCMS screen. ::: ::: ::: :::::: ::::::row ::: Click Login with Netlify and login with your GitHub account, you might be redirected here to your site on Netlify, just correct the first part of the URL to https:/localhost:5080admin and be sure to keep the #access_token=eyj...lkdAGI&expires_in=3600&refresh_token=zEM...BQSw&token_type=bearer in the URL, this should log you in to your local instance. ::: ::: ::: :::::: Finally we have NetlifyCMS in all its glory Create a new post and publish it ## NetlifyCMS successfully publishing to GitHub

::: you should see a commit in GitHub ::: ::: and the markdown as rendered in GitHub ::: :::::: This is all well and good, but you could have done that in VS Code or similar, in Parts 2 and 3 I will describe how I configured AppVeyor and Netlify to support continuous deployment, the editorial workflow and adding the Netlify Identity widget to my blog to make this a fully functioning CMS. ## Errors and issues I encountered while setting this up

Local Testing

::: If you are testing your site locally you can still use the Netlify Identity. You just need to specify the URL of your site on Netlify. This is explained in full in the netlify identity widget docs. It does however seem to have a side effect of redirecting to your hosted site after login if you use the external provider, logging in with a Netlify Identity account works and leaves you on the same page, alternatively just correct the first part of the URL to https:/localhost:5080admin/ and be sure to keep the #access_token=eyj...lkdAGI&expires_in=3600&refresh_token=zEM...BQSw&token_type=bearer in the URL, this should log you in to your local instance. Sometimes it needs a refresh to load the actual CMS. ::: ::: ::: :::::: ### Front Matter syntax Initially i found that none of my existing posts were displaying any details at all in the CMS, it just showed up as a blank line, you can click on the blank line and it will open a blank editor window . Any new posts created by the CMS did display correctly. The only difference i found between the existing posts and those created in NetlifyCMS was the leading --- to start the front matter section. Once i added that in all posts displayed correctly in the CMS.

How to fork a cloned repository in Visual Studio

· One min read
Mark Burton
Software Engineer & Technical Writer

based on these gists https:/gist.github.comjpiersonb6c0815e9dd7078f6b8cc3cb9076ddf4 https:/gist.github.comElectricRCAircraftGuy8ca9c04924ac11a50d48c2061d28b090 fork the repository in github go to team explorer repository settings ![Repository Settings](/img/GitHub fork%20Repository%20Settings.png) rename local origin to upstream (and update push address to the new repo, or you will always have items waiting to be pushed) ![Rename remote to upstream](/img/GitHub fork%20Repository%20Rename%20to%20Upstream.png) add new remote called origin ![Repository Settings](/img/GitHub fork%20Repository%20Settings.png) commit any changes fetch from new origin - won't work if there are conflicting changes, pull and merge worked update push remote to use origin rather than upstream, otherwise you will still be pushing to the original repo which you likely don't have permissions on and will see something like

Error encountered while pushing to the remote repository: Git failed with a fatal error.
unable to access 'https:/github.com...Demo.AspNetCore.PushNotifications.git': The requested URL returned error: 403
Pushing to https:/github.com...Demo.AspNetCore.PushNotifications.git

First Post

· 2 min read
Mark Burton
Software Engineer & Technical Writer
  • strongly typed configs (yay) * sqlite in ef core - Julie Lerman and Geoffrey Grosenback have a nice introduction to EF Core 1.0 on PluralSight[^2]
  • upgrading packages can have terrible side effects (IOptions from Microsoft.Framework.OptionsModelIOptions to Microsoft.Extensions.OptionsIOptions leaving a reference to Microsoft.Framework.OptionsModel)
  • metaweblog api for Open Live Writer[^3] integration now using markdown monster[^4]
  • XMLRPC thanks Shawn Wildermuth [^5] ### Setting up EF CORE with SQLite As easy as adding ```json ... "Microsoft.EntityFrameworkCore.SQLite": "1.0.1", ...
to project.json (just be sure put add it after it dependencies or use the sort to fix things)
Update startup.cs to use sqlite rather than sqlserver
```csharp This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) \\{ ... services.AddDbContext<ApplicationDbContext />(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); ... \}
``` ### Links
[MiniBlog](http:/github.commadskristensenminiblog) [An ASP.NET Core Middleware Component for Implementing MetaWeblog API ](https:/github.comshawnwildermuthMetaWeblog) [^1]: Source: [MiniBlog](http:/github.commadskristensenminiblog)
[^2]: Source: [EF Core 1.0](https:/www.pluralsight.comcoursesplay-by-play-ef-core-1-0-first-look-julie-lerman)
[^3]: Source: [Open Live Writer](http:/openlivewriter.org)
[^4]: Source: [Markdown Monster Web Site](http:/markdownmonster.west-wind.com)
[^5]: Source: [An ASP.NET Core Middleware Component for Implementing MetaWeblog API ](https:/github.comshawnwildermuthMetaWeblog)

Playing with Service Workers

· 2 min read
Mark Burton
Software Engineer & Technical Writer

This will not work for you as service workers are not supported ## Location of the serviceworker.js file and scope matters I started with the sw.js file in the assetsscripts folder with the rest of the script files, but calls to navigator.serviceWorker.getRegistration() were never returning, this stackoverflow thread explains the issue. Service Worker is never ready. Maybe this is the proper way ```javascript \{ navigator.serviceWorker.register('sw.js', { scope: '.' \}

<div id="dashboard-controls" /><div /><button disabled="disabled" id="subscribe" />Subscribe for Push Notifications<button />  <button disabled="disabled" id="unsubscribe" />Unsubscribe from Push Notifications<button />  <<br /><<hr />  <label for="topic" />Topic:<label /><<input type="text" id="topic" size="32" maxlength="32" />  <label for="urgency" />Urgency:<label />  <select id="urgency" />  <option value="VeryLow" />Very Low<option />  <option value="Low" />Low<option />  <option value="Normal" selected="selected" />Normal<option />  <option value="High" />High<option />  <select /><<br />  <<input id="notification" size="65" />  <button id="send" />Send Push Notification<button />
<div /> <<hr />
<div id="dashboard-console" /><div /><label />Log:<label /> <button id="clear" style="position: relative; top: 3px;" />Clear&lt;button>
<div /> ## Building Progressive Web Apps with Chris Love
Great explaination of Service Workers and further reading and [offer](https:/love2dev.comdnrpwa) for Chris' course on this episode of
[DotNetRocks](https:/dotnetrocks.com?show=1509). [PWA Builder](http:/www.pwabuilder.comgenerator) ## CORS
As the blog is a static site and the backend is hosted in azure it is neccessary setup CORS using one of the examples here[Enabling Cross-Origin Requests (CORS)](https:/docs.microsoft.comen-usaspnetcoresecuritycors) don't forget to do the CORS section in Azure if you are using that.<<button id="clear" style="position: relative; top: 3px;"&gt;Clear&lt;button> />

<script src="imgjspush-notifications.js" /><script />
<script /> window.addEventListener('load', function() \\{ // Registration was successful console.log('PAGE: Get Notification permission'); askPermission(); setTimeout(notify, 2000); \}); function notify()\\\{ var title = 'Welcome to the blog.'; var body = 'Enjoy this post about service workers and the push and notification APIs.'; var icon = 'https:/noknok.plimagesfavico.png'; var tag = 'simple-push-demo-notification-tag';
if (Notification.permission == 'granted') \{ navigator.serviceWorker.getRegistration().then(function(reg) { reg.showNotification(title, { body: body, icon: icon, tag: tag \\}) }); }
} function askPermission() \\\{ return new Promise(function(resolve, reject) \{ const permissionResult = Notification.requestPermission(function(result) { resolve(result); \\}); if (permissionResult) \\{ permissionResult.then(resolve, reject); \} }) .then(function(permissionResult) \\\{ if (permissionResult !== 'granted') \{ throw new Error('We weren\'t granted permission.'); \\} });
}
</script&gt;

Resolving System.NotSupportedException No data is available for encoding 850 in .net Core

· One min read
Mark Burton
Software Engineer & Technical Writer

https:/msdn.microsoft.comen-uslibrarysystem.text.encodingprovider(v=vs.110).aspx Message: System.NotSupportedException : No data is available for encoding 850. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. ```Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

Job Interview Technical Test Preparation

· 2 min read
Mark Burton
Software Engineer & Technical Writer

docs.microsoft.com Apply to a controller action by specifying it in the signature public IActionResult EncodedName([ModelBinder(typeof(Base64StringBinder))] string name). Apply to a model using [ModelBinder(BinderType = typeof(AuthorEntityBinder))] and register in startup.cs

public void ConfigureServices(IServiceCollection services)  \\\{  services.AddMvc(options =>  \{  Insert at the top so this gets used before default binder  options.ModelBinderProviders.Insert(0, new AuthorEntityBinderProvider());  \\});  }
``` using a [model binder provider](https:/github.comaspnetDocstreemasteraspnetcoremvcadvancedcustom-model-bindingsampleCustomModelBindingSample) ``` csharp
public class AuthorEntityBinderProvider : IModelBinderProvider \\\{ if (context.Metadata.ModelType == typeof(Author)) \{ return new BinderTypeModelBinder(typeof(AuthorEntityBinder)); \\} return null; }
``` ## Decode base64 string
``` csharp string decodedJson = Encoding.UTF8.GetString(Convert.FromBase64String(value));

Deserialise

json convert

10 //  "Name": "Apple",
11 // "ExpiryDate": "2008-12-28T00:00:00",
12 // "Price": 3.99,
13 // "Sizes": [
14 // "Small",
15 // "Medium",
16 // "Large"
17 // ]
18 /\}
19
20 Product deserializedProduct = JsonConvert.DeserializeObject<Product />(output);

without netwonsoft csharp [DataContract] public class Product \\\{ [DataMember] public string Name \{ get; set; \\} [DataMember] public DateTime ExpiryDate \\{ get; set; \} } public static string WriteFromObject() \\{ Product user = new Product("Bob", DateTime.Now); MemoryStream ms = new MemoryStream(); Serializer the User object to the stream. DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Product)); ser.WriteObject(ms, product); byte[] json = ms.ToArray(); ms.Close(); return Encoding.UTF8.GetString(json, 0, json.Length); \} public static User ReadToObject(string json) \\{ Product deserializedProduct = new Product(); MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); DataContractJsonSerializer ser = new DataContractJsonSerializer(deserializedUser.GetType()); deserializedUser = ser.ReadObject(ms) as Product; \} ##As a action filter decorate the controller action with [DecodingFilter]

```  #CQRS
[Martin Fowler - Command Query Responsibility Segregation](https:/www.martinfowler.comblikiCQRS.html) #Message queues ##AMQP
[Azure Service Bus .NET Standard client library ](https:/www.nuget.orgpackagesMicrosoft.Azure.ServiceBus)
[Using Service Bus from .NET with AMQP 1.0](https:/docs.microsoft.comen-usazureservice-bus-messagingservice-bus-amqp-dotnet) Void - Action<string /> prints = x => \\{ Debug.WriteLine(x); \}; Returns - Func&lt;int, int, int&gt; add = (x, y) => \\{ return x + y; \};

Error loading the CMS configuration

The config.yml file could not be loaded or failed to parse properly.

Error message: YAMLException: end of the stream or a document separator is expected at line 6, column 1520: ... -url-issue-banner" style="border: thick solid red; background-co ... ^

Check your console for details.