Creating Umbraco Packages: Week 7

Creating Umbraco Packages: Week 7

Hi all!

Happy new year everyone! As you can see, I kinda ran out of ideas for the title, so I will just up the number each week now. We are currently on week 7 of creating an SEO package. I am very glad that you have once again joined me on my journey!

This overview will be a bit longer as I also didn't blog the last two weeks, so I'll also include my activities of those weeks in this blog.

Meta Fields

I continued on the meta fields logic. Last time I spoke about the ability to have an inheritance for meta fields so it'll be easier to do your setup of the meta fields. And that has been done!

You can now, on the document type screen, choose the document type that you want to inherit from. It'll then use the settings from that document type instead of the settings of your document type. You can however still choose to use your own settings for specific fields. Maybe the document type has another field that is not part of your composition, but you do want to use it for your meta fields.

Then there is also the ability to choose "Inherited Value" for the field selector. You can choose to first check your new fields before going further with the inherited settings. A bit confusing, but I assure you that this will really help you with setting up your settings!

SeoFieldsInheritance.gif

Then I also wrote some custom code for custom fields. Before this change, this dropdown:

image.png

Would only show the different fields that you have. But what if you wanted to have custom values here? Or maybe use the name of the page here? Well, I've added a way of adding new values here that then gets translated to an SEO value in the backend.

image.png

Just create your own class implementing IFieldProvider and implement the given classes and you are ready to go! In the above example, it'll add the "Page Name" value and then translate that to the name of the page. So, in this way you can easily add more values to this as well.

After that, I also took a look at the rendering part of the package. After all, you'll want to get those meta fields rendered on your page! I either wanted to use an extension method for it or a taghelper. In the end, I decided to use a taghelper because I am able to use Dependency Injection within it. You can now add the tag

<meta-fields></meta-fields>

to your master template and you are set to go.

The only "issue" that I am still looking at has to do with the imports. Whenever you want to use a taghelper, you'll have to register it in the _ViewImports.cshtml file. Ideally, I would like to do this with the install step or somewhere in the code, but I couldn't find a good way of doing that. If someone has any suggestions then please let me know!

And that was all for meta fields. There were some small things like the performance, but I won't bore you with that!

General Cleanup

This doesn't sound like such an important topic, but I think it might be for a package this size. In my case, each big functionality will have its own package. I also want each package to consist of two projects. One for all the code and one with just the views. There is a very good reason to have this. If I would have everything (code + views) in one project and also in one package, then every time you would include this in a project separate from the Web project it would also copy over all the views from the App_Plugins. That is why Umbraco also has many nuget packages separate from the web package.

Because of this, I have quite some projects in my solution. For now, I do want them all in the same solution, because it makes it a lot easier for me and everything is together on Github. But I can also get that it becomes a bit messy. So for now, I've moved everything in Solution Folders:

image.png

Which are quite handy to use. Maybe in the future, I'll have to move them to separate solutions, but I think this will do for now! And this is also important for the next topic:

Generic Dashboard

Okay, this doesn't really sound like a good reason but hear me out. Each of my packages has a reference to a global core package. This is just a small package with only a few things in it. Like handling the tree, some models/utils that are used in multiple packages etc. My idea was to show a small generic dashboard showing the people what functionalities are enabled and what ones are available. For this, I needed a way of "registering" functionalities so that I could show them. And that is exactly what the global core package is great for.

image.png

And just like that, I can say that functionality "scriptManager" has been enabled! And this then gets translated to a dashboard like this:

image.png

(Just imagine that "ScriptManager" has the enabled status for now.)

A very small, but useful feature in my opinion.

Sitemap

And unto the last topic of today. Sitemaps! Everyone working with websites knows that this is something that always has to be built. And is unfortunately something that I keep on having to rebuild for each project. Sitemaps are also important for SEO because they get used to seeing what has to be crawled.

In this package, I want to be able to also set up my own sitemaps. Sometimes you would want all your products to have a separate sitemap instead of everything in one. This is especially helpful for bigger sitemaps where the amount of pages is very large. While keeping this in the back of my mind for this package, it is not something that I'll have ready for the first version, because it is more of a niche functionality.

I started out by seeing how I could catch "/sitemap.xml" requests so that I could generate my own response. I want this done from within the package as I don't want the installer having to add a manual rewrite for it. I also took a look at the code of Callum's package "Umbraco Friendly Sitemap". This package was still from .Net Framework and used RouteTables. I think they are now replaced with 'IAreaRoutes' in .Net Core, but they also weren't working for me. First, I tried to add a rewrite to a Umbraco controller from within them, but I couldn't quite get that working. And the second reason was that I wasn't able to dynamically add new sitemaps in runtime. It only allows you to register routes at the start of the program. So if I were to have the ability to add new sitemap logic from within Umbraco, I would have to use something else.

Then I thought about using Middleware. Something new that is introduced in .Net Core and it sounded like it was a great fit for the job. I didn't have to register custom routes, but could just check if a sitemap lived at a specific URL and then return that as the response. Luckily, I also had some experience with them due to my other package "InboundLinkErrors". In there, I also created a middleware to catch all 404 responses, so I also knew where to inject the middleware.

With that all in mind, I was able to create the middleware, check if the request was for a sitemap.xml, and then return the sitemap that was constructed with the information of the website.

image.png

There was one interesting issue that I had to work around. So, because the sitemap.xml doesn't actually exist within Umbraco, the PublishedRequest information is NULL in the UmbracoContext. This also means that I don't know under what domain I am getting the request. Luckily I was able to find how Umbraco does it by digging through their source code on Github. They are using something called DomainUtilities.SelectDomain to find the domain with a given URL. So, I was able to find the domain, find the root node and generate the sitemap from there. Nice and easy. Thanks, Umbraco source code!

And that is all for this week. Thank you for reading this blog post. It always gives me a lot of motivation to write these and see that people enjoy them. Also, if you have any suggestions on these blog posts, please let me know!

Have a good week everyone!