Category Archives: EPiServer

Workaround that ImageVault.EPiServer.UI 5.5.36 does not support non-standard ports

We have upgraded the NuGet package ImageVault.EPiServer.UI to 5.5.36.

This package is used to install ImageVault properties and editor UI into Episerver.

However I noticed that I was unable to add images to the ImageReference properties in Episerver. The “Insert” button was replaced with “Publish”.

iv insert.png

Looking into the javascript errors I found the following:

imagevault-insert-media.min.js:1 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://my-imagevault-core') does not match the recipient window's origin ('https://my-imagevault-core:9901').

As we’re hosting our ImageVault Core on a non-standard port 9901 it looked like something was lost.

Browsing the zip packaged containing the ImageVault UI resources I found the javascript imagevault-insert-media.min.js that it does not include the port when defining the origin.

Patching it up

It was easy to patch the .js file but since I did not want to fool around with the .zip package, I needed to find another way to override the file.

Creating a HttpHandler that listened to the path for the original file, and returning my modified file instead it’s easy for me to implement my fix without the risk of it being overriden. It’s also easy to remove when the fix is patched!

If you need help

If you also experienced this issue, you can find my patch on https://github.com/alfnilsson/ImageVault/tree/master/ImageVault.EPiServer.UI%205.5.36

As it would only be a temporary patch, I don’t think it’s worth creating a nuget package for it.

This is reported to Meriworks, but I hope that this helps you if you encountered the same problems.

Advertisements

Automatic visual tests with Episerver: Test after a Deploy

A common reason I have heard that Episerver developers don’t write automatic tests are that they’re “only doing content websites”.

First of all I suggest that you have an “API first” approach, even if you’re only going to make content based functionality such as listing and filtering News Entries.

This falls well into the classic three tier layer where you separate data, logic and presentation.

3tier.png

But that’s not what I want to share today. I’m here to talk about Visual Tests!

Visual tests

With visual tests I mean that you’re comparing what your website looks like before and after a release. This is normally done just by performing a test routine – following steps such as clicking links or buttons, entering form fields and so on.

This is something that can take far too much time and energy and within a DevOps mindset you want to automate these tasks as much as possible.

automate.png

Selenium

The first tool I use for automatic visual tests is Selenium which is a tool where you can script a web browser to do basically anything.

You can use various techniques to create your automated scripts, either coding them with many different languages (C#, Python, Ruby or even Perl, JavaScript and PHP).

You can also use your scripts with different web drivers which are available for most modern browsers on the most popular Operating Systems out there. This is very good when you want to make sure that the same function works and looks the same on Microsoft Edge, Firefox, Chrome and so on.

A simple piece of code to browse to a website using the Chrome browser and take a screenshot.

using (IWebDriver driver = new ChromeDriver())
{
    driver.Manage().Window.Size = new Size(1280, 980);
    driver.Url = "http://www.alfnilsson.se";
    ((ITakesScreenshot)driver).GetScreenshot().SaveAsFile(@"c:\temp\www.alfnilsson.se.png", ScreenshotImageFormat.Png);
}

Simple as that. Next problem is that it would take too much time to compare screenshots and there is a risk of the human error. Missing that the font might be wrong or that the margin is a few pixels too large or small.

Simplify comparison with Applitools Eyes

For one of our customers we’re using Applitools Eyes to help us compare the screenshots.

It is a tool where you can create test cases and compare screenshots between different test rounds and get a warning if there is something different between each round.

If you get a warning you can say yes or no whether the change was OK, and if you want to use this screenshot as base for an upcoming comparison.

So what we do in our code is instead of creating a screenshot with the WebDriver, we wrap our WebDriver with the Eyes API.

So with this code we’re doing the same test as previously, but instead of saving the screenshot to disk, we’re starting a test in Eyes and sends the screenshot of our start page.

using (IWebDriver driver = new ChromeDriver())
{
    var eyes = new Eyes
    {
        ApiKey = "--APIKEY--"
    };

    eyes.Open(driver, "My Application", "My Test");

    driver.Manage().Window.Size = new Size(1280, 980);
    driver.Url = "http://www.alfnilsson.se";
    eyes.CheckWindow("Start Page");
    eyes.Close();
}

If there’s any differences, the API will throw an Exception with information on where to verify the screenshots. We can also adjust the code to not throw Exceptions in case we want to.

Automatic Visual Tests after a Release

At the latest Episerver Partner Close Up earlier this year, Eric Herlitz and I had a presentation about DevOps where we showed how you can use your automatic tests as an automated step while deploying.

We implemented these visual tests with the simple tests. Note that some of the screenshots and code are not best practice in a release and test setup but this was enough for demo cases.

1: Create a normal Alloy Website solution

Just use the Episerver Visual Studio Templates to create a standard Alloy Website.

 

2: Switch the WebDriver to use Phantom JS

As the visual tests will be performed on a TFS agent outside a user context, it is better to use the Headless browser “Phantom JS”. It’s a browser that does not have a UI, but can render and take screenshots of websites as if it was a normal browser.

Simply install the NuGet packages required for Phantom JS and use the OpenQA.Selenium.PhantomJS.PhantomJSDriver instead.

3: Put the code inside a Test Project

Create a Test Project in your solution and start scripting.

In my tests I am performing the following steps

  1. Browse to the start page
  2. Send a screenshot to Eyes
  3. Browe to the contact page which contains a contact form built with Episerver Forms
  4. Send a screenshot to Eyes
  5. Submit the form, expecting that some validation messages should show for the required fields
  6. Send a screenshot to Eyes
  7. Fill in the field for name and enter an invalid e-mail address into the e-mail field.
  8. Submit the form, expecting that some validation messages should show for the invalid e-mail
  9. Send a screenshot to Eyes
  10. Finish the e-mail address, check some checkboxes and a comment.
  11. Send a screenshot to Eyes
  12. Submit the form, expecting that the form should be submitted and a confirmation message to be displayed
  13. Send a screenshot to Eyes

I have uploaded our tests scripts to my GitHub. In these steps I have made sure that the tests does not throw Exceptions if Eyes notices any differences.

4: Push the code to VSTS and setup VSTS Build to Publish the Artifacts for the test project

Just push your code to a repository in your VSTS, or to a repository that is accessible by VSTS (such as GitHub).

Setup VSTS to build your solution.

vsts build.png

Looking closer at the task for publishing the test project you can see that the artifacts are called “VisualTests”. We will use this later.vsts publush tests.png

5: Deploy to Azure using VSTS

Create the Azure Resources required for running an Episerver website. this blog post from Erik Henningson contains some Powershell scripts that makes it very easy to get everything up and running.

Me and Eric are more used to using Octopus Deploy for Release Management but we decided to use the VSTS Release Management for our demo.

The steps are quite simple:

vsts release 1.png

  1. Get the code from the Artifacts
  2. Put the code to the Azure website that represents my Dev environment (including some configuration transformation)

In a real project I would also add environments for Test, Pre-prod and Production (or simply the Integration environment if I was running DXC Services).

6: Add the Release Step to run your Tests from the artifact “VisualTests”

I added the step to also run the tests from the Artifact “VisualTests”. It will simply run the tests which in turn will start pushing screenshots to AppliTools Eyes.vsts release 2.png

After the deploy, I let my Release Managers approve the release before it can continue on in the Release Life Cycle.

vsts release 3.png

This is so that the manager can verify that everything works as intended without manually performing the test procedures.

Summary

These are the basics of setting up Automatic Visual Tests using Selenium, AppliTools Eyes, VSTS and Azure. As I mentioned everything also doesn’t follow best practices but I imagine that there are experts out in the open on these specific tools, Feel free if you have any hints to share.

Keep in mind that even if Visual Tests is a simple step if you don’t have any automatic tests, but they do NOT replace a good suit of Unit Tests.

Unit Tests can still give you a better best coverage when you have a more complex application and/or have integrations against other systems.

What’s next?

Our next issue is that the tests often fails if someone plays around with content on the dev server for other tests. One approach for this could be to spin more environments for each test purpose, or reset the database.

I have another idea that might help with this and can help with other challenges we’ve experiences. Stay tune for next blog post about Automatic Visual Tests.

Simple pattern for custom forms

Through the years I can’t really keep count on how many custom forms I have created. I have found a simple pattern so that I can easily collect, validate and save the submitted information using a Model, Validator and Saver (MVS?).

Dos and don’ts

I want to show you some practices I consider as Don’t and what you can Do to avoid them. These are some ways I’ve seen that people have handled form submits in a bad way. Some of them are really obvious but worth repeating.

The use case throughout this post is a form that contains a name field. The name field is required and the name must start with “Alf”

I also want to make it easy to add several fields into the form without making the code too complex.

In the code examples I won’t go into details on how I pass validation messages to the view but in the end I will give some examples of that as well.

Let’s start:

1: Don’t repeat yourself:

A common way to collect the form input after submit is to go either to TextBox.Text (WebForms) or form[“name”] on the FormCollection (MVC).

Nothing wrong with that but don’t run to the FormCollection every time you need your value.

 

1-dont1.png

Specially if you want to add fallback functionality or extend the form somehow.

1 dont fallback

 

Do save it:

So save it to a single variable and reuse it.

Personally I prefer a local variable that is passed throughout the life cycle instead of a private Field or Property.
That makes it easier to make sure that nothing is fiddling with the value.

1-do-variable1.png

 

And if you would like to add fallback, you only need to take care of that at one place.

1-do-variable-fallback1.png

 

2: Don’t have a never ending list of parameters

Having just the name is a piece of cake. But when we need to add more fields it can get quite messy.

2-dont-parameters3.png

 

It makes all the methods hard to read, hard to extend and easy to use wrong. This also takes me to an ugly part (in my opinion) in C# – the out parameter.
For example if you move the gathering of the form input into a separate method:

2-dont-parameters2.png

 

I think this is very hard to work with and makes the code quite ugly and comples.

Do use a Model

It’s much easier to transport lots of values using a Model. Also if you need to extend the form, you can easily add more properties to the class.

2 do model.png

 

Just create a new instance of it, populate it and pass it around:

2-do-formcollection-controller1.png

 

Bonus: With MVC you can use the Model already in the view and get it populated in the Controller!

Quite simple, since I don’t need to have the FormModel in my ViewModel (normally I don’t) but I want to use the FormModel, just do it like this View.

2 do formmodel controller.png

 

And let it be sent straight to the controller. Just keep in mind that the variable in the Lambda expression must match the variable of the FormModel parameter in the Controller.

2 do formmodel variable.png

 

Though you’ll need to implement separate logic and fallback functionality yourself unless you start doing some more fancy MVC magic.

2-formmodel-controller-fallback1.png

 

Validation

With MVC populating your Model, you could add validating attributes such as [Required] to the Model Properties, however I think it’s quite alot of work to get that working the way I want to so I’m doing the frontend and backend validation myself.
Of course using native browser functionality as much as possible for example with the required attribute.

3: Separate functionality

With this I’m using patterns and principles such as “single responsibility”, “separations of concerns” and striving for testability and so on.

It’s good to separate your code so that you can isolate the functionality based on what they do. It is also easier to create abstractions for these to change behavior in the future or be used with Feature Toggles.

What parts can we separate?

Looking at the Controller I can identify two parts that concerns the form: Validating and saving the form.
Therefore we can create separate classes for this.

Summarize.png

Validator:

3 validator.png

 

A Saver:

3 saver.png

 

And use them in the Controller:

3-controller1.png

 

If I don’t let MVC doing the population of the Model, I sometimes have a specific class for that as well.

3 populator.png

 

Result

Note that each class have a Result class. This is to not have magic code such as “result message == null” means “everything wen’t fine”.

 

Some people would have the validation to return a bool and have an out parameter with the message. But what did I just say about outing?

So they return a ResultModel that contains whether everything wen’t fine and possible messages telling what is not fine.

As you can see, the Validator and Saver have a Result class each. This could be the same class but sometimes I have ended up extending the class for one of the classes so therefore I keep them separated.

Tadaa!

So with this done, we have

  • Extracted validating and saving the form into separate classes.
  • To keep it simple and extendable, we have created a FormModel that contains all values
  • A ResultModel to return the result (hence the name).

You can see the code examples on my GitHub as well as a more finished concept that can be added to an Alloy website.

Do you have any tips on how to work with forms? Leave a comment!

Presenting: Find My Content

My main project is a multi website solution where 16 different websites is sharing the same source code.

Sometimes when we need to change specific Content Type and find a strategy on how to migrate the changes it’s good to know how much that Content Type is used, and in what way.

How do you do that?

A way to to find how much it is used is to perform a simple SQL Query:

SELECT c.pkID FROM tblContent c
INNER JOIN tblContentType ct ON c.fkContentTypeID = ct.pkID
WHERE ... = '...' -- various ways to identify your Content Type using pkID, ContentTypeGUID or Name

And go to Episerver Edit Mode to see how the editor has worked with each content.

But to be able to run the SQL query, you will need to have access to the production database which makes things a little more annoying. Specially if you need to use VPN connections or similar.

Therefore I have created an Admin Tool called “Find My Content” where you easily can find all content based on their Content Types.

Listing all Content Types

findmycontent.PNG

Here you see how many items of this Content Type is created by clicking the Name.

Details of a Content Type

findmycontent standardpage.PNG

In the details you can see all Content of this Content Type and which language they are created in. There is also a shortcut that will take you to Edit mode for that specific Content.

How do I get it?

I’m waiting for a NuGet package to be published on Episerver’s NuGet feed. Otherwise you can find the code on my GitHub.

Update: It is now available in the feed: https://nuget.episerver.com/en/OtherPages/Package/?packageId=Toders.FindMyContent

What’s next?

This was just something I threw together since I got tired connecting to my client’s environments using VPN and Remote Desktop.

I’ve got some ideas to add references between the different content, filtering on values for specific properties and so on.

Do you have any other ideas that would be nice to have in this tool? Just give me a shout in the comments below.

Episerver Event Helper v3.0

Years pass and hopefully you learn something and become better!

One thing that have been poking on my conscience for a while is the old EPiEventHelper that I blogged about a couple of years ago.

What’s wrong with it?

There are two things I don’t like with it.

1) Unclear usage

I have seen that it has been some confusion with how to best access the content – using the eventArgs.Content or using this.

public class StandardPage : PageData, IPublishingContent
{
    public void PublishingContent(object sender, ContentEventArgs e)
    {
        // this is how to access the already cast instance of the content.
        StandardPage standardPage = this;

        //This is the content from the argument, needs to be casted to your Content Type. But to me it feels more correct.
        IContent contentFromArgument = e.Content;
    }

}

A common question is if the e.Content and this are two different instances (they are the same) and which one should be used (honestly I don’t have a recommendation).

2) Single Responsibility Principle

The second thing is that this implementation violates the Single Responsibility Principle. The class representing your Content Type should been seen as a View Model or Data Model and therefore it should not contain any behavior or business logic.

I have seen some examples where the logic found in the event is really large, making the Content Type class very messy and hard to understand.

Presenting – EPiEventHelper 3.0

What? Why 3.0?

Pavel Nezhencev already created a NuGet package with EPiEventHelper and later upgraded it to use Episerver 10, calling it version 2.0. If I want to publish my version with new features to NuGet.org I need to bump the version even further.

What’s new?

First of all I have stopped checking if the current Content implements the event interface. Instead I’m using to use the IoC container in Episerver where you as a developer can add your own things.

This means that you will need to decorate your implementations of the interfaces with the ServiceConfiguration attribute.

[ServiceConfiguration(typeof(IPublishingContent))]
public class PublishingContentEvent : IPublishingContent
{
    public void PublishingContent(object sender, ContentEventArgs e)
    {
        IContent content = e.Content;
        var standardPage = content as StandardPage;
        if (standardPage == null)
            return;

        // Do something with your standard page
    }
}

Breaking change: This also means that if you are using the previous version of EPiEventHelper, you can no longer refer to the content using this!

public void PublishingContent(object
    sender, ContentEventArgs e)
{
    this.Name = "Hello World!";
}
public void PublishingContent(object
    sender, ContentEventArgs e)
{
        e.Content.Name = "Hello World!";
}

Breaking change: The interface does not know anything about the class representing your Content Type and therefore you will need to cast e.Content yourself!

public void PublishingContent(object
    sender, ContentEventArgs e)
{
    this.Title = "Hello World!";
}
public void PublishingContent(object sender,
    ContentEventArgs e)
{
        var content = e.Content as StandardPage;
        if (content == null)
            return;

        content.Title = "Hello World!";
}

Unless…

I have created a workaround for this where you can have the same pattern to bind the Episerver events, but only apply if to specific Content Types AND the Content will also be typed!

New functionality: Each interface have an abstract base class where you specify which Content Type class you want to use. As usual you use the ServiceConfiguration attribute to bind your implementation to the event.

[ServiceConfiguration(typeof(IPublishingContent))]
public class PublishingStandardPage : PublishingContentBase<StandardPage>
{
    protected override void PublishingContent(object sender, TypedContentEventArgs e)
    {
        // Here you can access the standard page
        StandardPage standardPage = e.Content;
    }
}

You can also specify if the Content that the event applies to needs to be the exact Content Type or that the class would inherit from it by overriding the boolean property AllowInheritance.

protected override bool AllowInheritance { get { return true; } }

Tests

I’m still learning how to best create tests with Episerver, so I have added a Test Project where I want to test if the events are bound and unbound properly.

I have also added some tests to make sure that I have not missed any interfaces or classes. The project is found in the same GitHub Repo.

You’re welcome!

I would really recommend that you take your time to extract the code for your events from the Content Types.
Just putting the ServiceConfiguration attribute on your Content Types would instantiate the class in a way that doesn’t always play well with Episerver.

I’m planning to update the NuGet feed with my new version! Keep your eyes open for updates in Visual Studio if you have used the previous version.

Removing all translations – Part I (understanding the Episerver Database)

Me and some colleagues had a project where we needed to remove all translations from an Episerver website except one.

For that we needed to know what the database structure looks like in Episerver and I will share this to all of you!

Now I’m trying out something new – a video post! I know it’s not the best quality but it’s a start.

In this video I will describe some of the most important tables in the strucuture and how they are related to each other.

I hope you like it! Please add some comments for questions or feedback.

Planning your Episerver project

You’re going to start working on a brand new Episerver project! Your solution is a brand new slate where you can add your best code ever and you just received the design specs and requirements!

xmBjXcm.gif

Now you need to sit down and get an idea how to build your solution. But where should you start?

In this blog post I’m trying to give some hints of how I use to plan building an Episerver website. Since the sales, concept and requirement phases can be very different from company to company (and sometimes even project to project) I will try to keep to a very fundamental level.

First of all I’m asking myself the following questions:

What do I want do to?

Get an idea of what the solution should solve and which functions that is needed.

giphy-1.gif
The answer to this could be “a website” – but what should it contain? News articles where the user can list and filter based on categories and date? Product presentation where you can rate each product? Logged on functionality with profile presentation?

Sometimes the requirements from earlier phases in the project already have this but I’ve seen lots of requirements which are still on a concept level rather than a functional spec.

Try to summarize a map of functions from an abstract perspective and leave out the details for now.

My example throughout this post have

  • Customer profiles.
  • Product presentation.
  • Authenticate to edit their profile.
  • A customer can also rate news. In the future maybe even products so let’s keep that function isolated.

scope.png

Using the SOLID principles, this could maybe become the start of your architectural design of functionality in the project as you want to separate the functions.

What areas are Content logic vs Business logic?

When I say Content Logic I refer to things you can do within your CMS’ API such as listing, filtering and reading content based on what is created by the editor while Business Logic would be connected to other data and functionality from systems or databases than your CMS.

Looking at the defined functions from the image above, I define what I can do in Episerver in terms of data structure, and what comes from other systems.

Example how to define: Your page displays a list of links but you don’t know where they are stored:

  • Should the editor just add links using the Link Item Collection, Content Area or the XHTML property in Episerver?
  • Should the list automatically be fetched from the content in Episerver (for example children to a specific page)?
  • Should the list be fetched from an external service such as Delicious, Pocket or similar?

The first options would be Content Logic while the second one is Business Logic.

Based on the functions in the image above and what I know so far I can start marking which sections are Content or Business.

business vs content.png

Also remember to start focus on the areas that are critical for the project, the other things can usually be decided later on (especially if you’re working agile).

From this I would start with the content logic to see how to structure these.

Content – because it’s a website!

What is the content structure?

When I have decided which areas comes from the CMS, I would try to define what parts I would need to create in Episerver based on the design specs and requirements.

Example how to define: The “normal page” and “news article page” are visually identical, except that the “news article page” also have images.

  • Should I create two separate page types?
  • Should I only create only one page type, but be able to add the image list as a block?
  • Should I only create one page type, as the normal page might also be able to have an image list?

 

How the editor would work with the content has a large role in my decisions as well as how it can be generalized and reused to minimize the headache for the developers in the future.

I try to make pages as generic as possible. To have many very similar content types will in the end become hard to maintain since they could have the same functionality.

Having a modular approach is quite nice for that, although it can get quite messy for the editor if there are hundreds of block types to choose from.

Another trick is to find a structure where you restrict enough for the editor, while still letting the editor be able to be creative and have freedom with their content. This is a very hard thing to find!

My suggestion is to have a simple data structure and look at how to simplify the editor experience using EditorDescriptors and maybe even Dojo. Sometimes you can even use the Edit mode to make a better Editor experience than the Properties mode.

So based on the “News” section in the image above I would structure the implementation like this.

content logic.png

Where the normal “Data Layer” in a three tier is the Episerver API. In the Content Logic I build functionality to filter and sort the content and the presentation is the usual Views and frontend resources.

Content – How should everything be rendered?

To start with, try to keep the rendering of your website as a normal “.net website” as much possible using Episerver as the data container.

I would recommend that you use the EPiServer:Property web control (WebForms) or PropertyFor HTML extension method (MVC) when rendering properties and content.
You’ll get tons of nice features with RenderSettings, Tags, Display Options, Display Channels and just to name a few.

When it comes to tweaking your rendering, the Alloy project has quite a lot of examples, however they are no documentation on why, how or exactly to use these and which code is related to which functionality. Don’t refer too much to Alloy templates as it would be overkill for a normal content website.

Business Logic – because I have lots of complex things to build!

Here is a nice part about building an Episerver website – basically it’s like building a normal .NET website where you’ll get a great API for your content (and an interface for your editors to work with that content).

For the other logic you can add tools like Entity Framework, IoC containers, external web services or other stuff you like to play with! Mostly I recommend using a 3 tier approach where you separate data from logic.

Once I have figured out the functionality that does not directly concern content, I look at how I can fit the presentation in Episerver.

There are three common approaches to present external data:

  • Import all data as content using a scheduled job or similar.
  • Create a content type for your data, but use keys in the context (QueryStrings or Routing) to identify which data item to display.
  • Use a Content Repository which is a built in tool in Episerver that handles content structure, routing, access rights and so on for programmatically.

Normally I would go for importing the data or using a Content Repository.

Based on the “Products” section in the image above, I would structure the implementation something like this.

business.png

Where I read and save from my own database or external sources in the Data Layer, creating business logic that handles the Data Layer in “Business Logic” and glue this together with Episerver in the Content Logic.

On top of that I would have the presentation layer as usual with Views and frontend resources.

There is one thing you should keep in mind though!

Don’t make your solution too dependent on external systems – have a fallback plan

closed.png

When your website loose connection to an external service that provides information make sure that it does not go down.

For example store the information locally so that it can be used when the connection is lost, or display a sign that tells the visitor that all areas of the website are not working at the moment.

Unfortunately I don’t have a silver bullet solution on how to present your data, but as long as you don’t start throwing lots of 500 (specially not on the start page) and that reading the external data does not make the website slow.

Logic finished – now what?

Normally I work in agile projects (based on how we can work with the client), at least we split most of our project into iterations or sprints.

i.chzbgr.gif

Most of the time, the customer wants to be up and running with the content work as soon as possible, so in the first iterations I try to plan the most common content types that they will work with.

Creating an MVP

In the example above I would have Henric Kniberg’s example of creating a MVP (minimum viable product) and start working with the basics that are less automated.

  • News
    The editor can create news articles but the listing would either be a very basic one or even be created manually by the editor.
    Automatic listing with filters would be created in a later sprint.
  • Products
    Say that I decided to go for the “import all products as pages” approach.
    I would prioritize building the product pages so that the editors can create some examples that they give feedback to.
    I would create the product import in a later sprint.
  • Profiles
    I will create the content types necessary, but after the first sprint the customers needs to contact support to change these.
    Authentication would come later, and then I can look at letting the customer update their profiles.

Ok I admit that this is a very simple priority! Pushing more complex sections such as product import and authentication to later sprints means there is a risk that they won’t make it to launch.

But this is a rough plan based on that the only priority for the customer is to start working with content.

However I want you to keep in mind that take small steps instead of building the entire project at the same time.

Summary

So! Thank you for following me through this article. These are some major thoughts I have on how to plan an Episerver project.

So a short recap

  • An Episerver website is simply a .NET solution where you get an API to fetch content (and an interface for the editor to work with the content).
  • Build your system integrations separately and integrate them using the Episerver API.
    • Make sure that your site does not crash because your integrations can’t integrate.
  • Start of simple and add the more complex parts later.

If you don’t agree of want to add to my list, please add a comment below.

bye.gif