Blog archive
RSS

Blog

Managing nopCommerce plugins with git submodule
In this article I’m going to demonstrate the way we have found works best for us when managing the same nopCommerce plugin in several projects. When we started out doing nopCommerce and plugin development around 2012, we began by creating one plugin for one project. This plugin was then copied over to the next project and then the next project and so on. When each project also needed its own little tweak of the plugin functionality it quickly becomes very hard to keep all the plugins up to date with each other, and it’s a very time consuming daunting task. that takes away time from creating business value.
How to install and configure the nopCommerce Fortnox plugin

To purchase the Fortnox Plugin click here

Installation

  1. Download the Fortnox Plugin.
  2. Unzip the zip-file and browse to the appropirate version.
  3. Inside the version folder; copy the Misc.Fortnox folder and place it in [NopRoot]/Plugins.
  4. Log in to your nopCommerce administration and go to Configuration > Plugins, click Reload list of plugins.
  5. Locate Fortnox integration in the list of Local plugins and click Install.

The plugin is now installed and it’s time to configure it.

Connect the plugin with Fortnox

In order to communicate with Fortnox we need the generate an aceess token.

  1. Log in to you Fortnox account.
  2. Click the user dropdown in the top right menu, and click Administrera användare.
  3. Scroll down to the list of integrations: Integrationer and click + Lägg till Integration.
  4. In the prompt search after the plugin by typing Majako AB.
  5. Click nopCommerce plugin and Godkänn.
  6. Copy the generated API-code and click OK.
  7. nopCommerce plugin should now be listed and have status: Not enabled.
  8. nopCommerce and the configuration of the Fortnox plugin.
  9. Paste the copied API-code in the Authorization Code textbox and hit Generate access token.
  10. An Access Token should now have been generated and displayed on the Access Token section.
  11. Restar your nopCommerce application in order for the access token to be working.
  12. The integration should also have status Active in your Fortnox Account.

Configrue the plugin

You can configure the plugin to make sure it works for your needs. If you are missing a certain configuration we are happy to help support@majako.net.

  • Invoice due in days - How many due days a generated invoice should have by default.
  • Default Price List Code - An article in Fortnox needs to have a Price List Code. Check your Fortnox account which price list code the synced products should have.
  • Sale inside Sweden - Enter Fortnox sale account for sale inside Sweden (default: 3000).
  • Sale inside EU, VAT - Enter Fortnox sale account for sale outside Sweden but inside EU with VAT (default: 3106).
  • Sale inside EU, no VAT - Enter Fortnox sale account for sale outside Sweden but inside EU without VAT (default: 3108).
  • Sale outside EU - Enter Fortnox sale account for sale outside EU (default: 3105).
  • Tax Category - Fortnox Sale Account mapping - Map each nopCommerce tax category to a specific sale account in Fortnox.
  • Act on product low stock activity - Check if you want the plugin to act on a products low stock activity setting in nopCommerce (i.e. unpublish product when it’s out of stock).

When an order is placed in nopCommerce (or more particularly when nopCommerce publishes an OrderPlacedEvent the plugin will invoke a configurable workflow in the communication with Fortnox. The plugin can be configurable to take action on on three different order payment statused: pending, authorized and paid. On each of these payment statuses upon the order being placed in nopCommerce, the plugin can do one or more of following: create order, create invoice and bookkeep invoice.

Workflow on payment status: ‘Pending’

  • Create order - Check if an order should be created in Fortnox when a nopCommerce order is placed with payment status ‘Pending’.
  • Create invoice - Check if an invoice should be created in Fortnox when a nopCommerce order is placed with payment status ‘Pending’.
  • Bookkeep invoice - Check if the created invoice should be bookkept in Fortnox when a nopCommerce order is placed with payment status ‘Pending’.

Workflow on payment status: ‘Authorized’

  • Create order - Check if an order should be created in Fortnox when a nopCommerce order is placed with payment status ‘Authorized’.
  • Create invoice - Check if an invoice should be created in Fortnox when a nopCommerce order is placed with payment status ‘Authorized’.
  • Bookkeep invoice - Check if the created invoice should be bookkept in Fortnox when a nopCommerce order is placed with payment status ‘Authorized’.

Workflow on payment status: ‘Paid’

  • Create order - Check if an order should be created in Fortnox when a nopCommerce order is placed with payment status ‘Paid’.
  • Create invoice - Check if an invoice should be created in Fortnox when a nopCommerce order is placed with payment status ‘Paid’.
  • Bookkeep invoice - Check if the created invoice should be bookkept in Fortnox when a nopCommerce order is placed with payment status ‘Paid’.

To purchase the Fortnox Plugin click here

How to use Aurelia inside nopCommerce

In this article we are going to take a look how Aurelia together with TypeScript and Webpack can be used to build UI components inside of nopCommerce.

Complete source code available at GitHub.

What is Aurelia?

Aurelia is a modern JavaScript Framework to build web, mobile and desktop apps - similar to Angular 2+, React + Redux, VueJs etc. Where Aurelia embraces convention over configuration, is built around dependency injection, highly performant (1), enables two-way databinding and much more. I'm not going to go in much detail of Aurelia in this article, but a good starting point to learn more is by visiting Aurelia's website.

Why should I use Aurelia inside nopCommerce?

With nopCommerce I have never felt so productive building e-commerce solutions. With Aurelia I have never felt so productive building UI components for the web. For several projects I have been using these in combination by running Aurelia inside nopCommerce - it's been a great combination to deliver high quality e-commerce solutions.

nopCommerce comes with jQuery and although you technically can achive the very same tasks by using jQuery alone you will find that jQuery quickly becomes hard to manage as soon as the UI gets a bit more complex. With Aurelia you will quickly get extremely productive writing complex UI components.

What are we going to build?

We are going to build a simple Aurelia component inside of a nopCommerce v4.0 installation. We will use TypeScript as transpiler and let Webpack manage the build.

1-2-3 Go!

For this demo I have started with a fresh nopCommerce v4.0 installation. But any version of nopCommerce would be fine.

In order to get Aurelia up and running we need a few tools:

1. Install node modules

First off we need node and npm in order to install Aurelia, TypeScript and Webpack. If you haven't already installed node you can download it from here.

Add following package.json file in the root of your Nop.Web nopCommerce folder

If your project is under source control it's a good idea to configure the soruce control to ignore the node_modeles folder.

{
  "name": "nopcommerce-aurelia",
  "version": "1.0.0",
  "scripts": {
    "prebuild": "cross-env rimraf wwwroot/dist",
    "build": "webpack  --progress -d",
    "prebuild:prod": "cross-env rimraf wwwroot/dist",
    "build:prod": "webpack --progress -p --env.production"
  },
  "dependencies": {
    "aurelia-bootstrapper": "2.1.1",
    "aurelia-framework": "1.1.4",
    "aurelia-history-browser": "^1.1.0",
    "aurelia-loader-webpack": "2.1.0",
    "aurelia-logging-console": "1.0.0",
    "aurelia-pal-browser": "1.2.1",
    "aurelia-polyfills": "1.2.2",
    "aurelia-templating": "1.4.2",
    "aurelia-templating-binding": "1.3.0",
    "aurelia-templating-resources": "1.4.0",
    "aurelia-templating-router": "^1.2.0",
    "bluebird": "3.5.0",
    "isomorphic-fetch": "2.2.1"
  },
  "devDependencies": {
    "@types/node": "^7.0.12",
    "aurelia-webpack-plugin": "2.0.0-rc.2",
    "awesome-typescript-loader": "3.2.1",
    "cross-env": "^5.1.3",
    "expose-loader": "0.7.3",
    "html-loader": "^0.4.5",
    "rimraf": "^2.6.2",
    "setimmediate": "^1.0.5",
    "typescript": "2.4.1",
    "webpack": "3.3.0"
  }
}

Install all packages by following command inside of your Nop.Web folder

npm install  

If you are using Visual Studio 2017 the node modules will download automatically as soon as the package.json file is added.

2. Configure Webpack

We are using Webpack to transpile our TypeScript to JavaScript and to create two JavaScript bundles: one for the Aurelia framwework code and one for our own code.

Add following webpack.config.js to same Nop.Web folder

const path = require('path');  
const { AureliaPlugin } = require('aurelia-webpack-plugin');  
const { ProvidePlugin } = require('webpack');  
const { TsConfigPathsPlugin, CheckerPlugin } = require('awesome-typescript-loader');

const outDir = path.resolve(__dirname, 'wwwroot/dist');  
const srcDir = path.resolve(__dirname, 'wwwroot/aurelia');  
const nodeModulesDir = path.resolve(__dirname, 'node_modules');

/**
 * @return {webpack.Configuration}
 */
module.exports = ({production} = {}) => ({  
  resolve: {
    extensions: ['.ts', '.js'],
    modules: [srcDir, nodeModulesDir],
  },  
  devtool: production ? 'source-map' : 'cheap-module-eval-source-map',
  entry: {
    app: ['aurelia-bootstrapper'],
    vendor: ['bluebird'],
  },
  output: {
    path: outDir,
    filename: '[name].bundle.js',
    sourceMapFilename: '[name].bundle.map'
  },
  module: {
    rules: [
      { test: /\.html$/i, loader: 'html-loader' },
      { test: /\.ts$/i, loader: 'awesome-typescript-loader', exclude: nodeModulesDir },
      // use Bluebird as the global Promise implementation:
      { test: /[\/\\]node_modules[\/\\]bluebird[\/\\].+\.js$/, loader: 'expose-loader?Promise' }
    ]
  },
  plugins: [
    new AureliaPlugin({ aureliaApp: 'main' }),
    new ProvidePlugin({
      'Promise': 'bluebird'
    }),
    new TsConfigPathsPlugin(),
    new CheckerPlugin()
  ],
})
3. Configure TypeScript

Configure the TypeScript compiler by adding following tsconfig.json to Nop.Web

{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "es5",
    "sourceMap": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "skipDefaultLibCheck": true,
    "skipLibCheck": true,
    "lib": [ "es2015", "dom" ],
    "types": [ "node" ]
  },
  "exclude": [ "bin", "node_modules" ]
}

Configure, build and run Aurelia

With all that configuration in place we are ready to start builing our Aurelia component. Very much like ASP.NET Core has an entry point in Startup.cs, Aurelia follows similar concept:

Create a new folder: aurelia inside of Nop.Web/wwwroot and create a new TypeScript file: main.ts:

import { Aurelia, PLATFORM } from 'aurelia-framework';

export async function configure(aurelia: Aurelia) {  
    aurelia.use
        .defaultBindingLanguage()
        .defaultResources()
        .developmentLogging();

    await aurelia.start();
}

Inside of the configure function we do all the global Aurelia configuration such installing and configure plugins, setting up the dependency injection container etc. This is all we need to get Aurelia running. To read more about Aurelia configuration check out App configuration and startup of the Aurelia documentation.

Webpack will upon build produce two JavaScript bundles inside wwwroot/dist: vendor.bundle.js which contins our framework and libraries code and app.bundle.js containing our own application code. Add these to bundles in Nop.Web/Views/Shared/_Root.head.cshtml next to all other scripts:

Html.AppendScriptParts(ResourceLocation.Footer, "~/dist/vendor.bundle.js");  
Html.AppendScriptParts(ResourceLocation.Footer, "~/dist/app.bundle.js");  

All we need now is to tell Aurelia that main.ts is our main entry point. We do so by adding an aurelia-app html attribute on the page we want to load Aurelia. In this case I add it to the body tag inside Nop.Web/Views/Shared/_Root.Head.cshtml:

<body aurelia-app="main">  

Build the application by following command

npm run build  

Start a web browser, open up it's developer and start the nopCommerce site. If everything went well you should see INFO [aurelia] Aurelia Started get logged in the console, indication Aurelia successfully started.

Creating the welcome component

The Aurelia team has a GitHub repository Skeleton Navigation that is a great starting point for setting up an Aurelia SPA-application. From this repository we will copy the welcome component and include it in our application:

Inside wwwroot/aurelia create welcome.ts and paste following code:

//import {computedFrom} from 'aurelia-framework';

export class Welcome {  
  heading: string = 'Welcome to Aurelia';
  firstName: string = 'John';
  lastName: string = 'Doe';
  previousValue: string = this.fullName;

  //Getters can't be directly observed, so they must be dirty checked.
  //However, if you tell Aurelia the dependencies, it no longer needs to dirty check the property.
  //To optimize by declaring the properties that this getter is computed from, uncomment the line below
  //as well as the corresponding import above.
  //@computedFrom('firstName', 'lastName')
  get fullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }

  submit() {
    this.previousValue = this.fullName;
    alert(`Welcome, ${this.fullName}!`);
  }

  canDeactivate(): boolean | undefined {
    if (this.fullName !== this.previousValue) {
      return confirm('Are you sure you want to leave?');
    }
  }
}

export class UpperValueConverter {  
  toView(value: string): string {
    return value && value.toUpperCase();
  }
}

The component will have a corresponding view: in the same folder add welcome.html with following markup:

<template>  
  <section class="au-animate">
    <h2>${heading}</h2>
    <form role="form" submit.delegate="submit()">
      <div>
        <label for="fn">First Name</label>
        <input type="text" value.bind="firstName" id="fn" placeholder="first name">
      </div>
      <div>
        <label for="ln">Last Name</label>
        <input type="text" value.bind="lastName" id="ln" placeholder="last name">
      </div>
      <div>
        <label>Full Name</label>
        <p>${fullName | upper}</p>
      </div>
      <button type="submit">Submit</button>
    </form>
  </section>
</template>  

These two files (the view-model and the view) will automatically be bound together. Where Aurelia by default convention assumes a script file and html file with the same name is a complete component with view and view-model. This convention can be overridden, either on application level in main.ts or on component level.

What is going on here is that properties in welcome.ts are bound to the view, for example:

heading: string = 'Welcome to Aurelia';  

is bound to

<h2>${heading}</h2>  

and

firstName: string = 'John';  

is bound to

<input type="text" value.bind="firstName" id="fn" placeholder="first name">  

Pretty straight forward, right? No weird html-markup and just plain JavaScript (TypeScript). Actually, as you might have noticed, welcome.ts doesn't have any Aurelia API code at all, only plain old JavaScript - everything is handled by conventions, your application code is not mixed up by framework code like most other frameworks.

Another thing worth notice is the difference between the binding methods of heading and firstName - where heading is bound one-way. Where updates only flow from the view-model to the view, not the other way around. This is the default binding mode in Aurelia for everything but form elements which need the be two-way bound to update in both directions, as seen on the firstName property that is bount to an input element.

Enhence the component

In our case we won't have a full blown Aurelia application with client-side routing, instead we just want to let Aurelia handle certain components inside nopCommerce. We can easily do that by using enhence - a very nice feature for scenarios like this. We also want to make the component a global resource: meaning it will be available from anywhere in our Aurelia application.

Open up main.ts and update accordingly:

import { Aurelia, PLATFORM } from 'aurelia-framework';

export async function configure(aurelia: Aurelia) {  
    aurelia.use
        .defaultBindingLanguage()
        .defaultResources()
        .developmentLogging()
        .globalResources(PLATFORM.moduleName('welcome')); // <- Make the component a global resource
    await aurelia.start();
    await aurelia.enhance(document.querySelector('welcome')); // <- Enhence the component
}

This will tell Aurelia to handle any <welcome></welcome> html tags as an Aurelia component.

In the feature (hopefully pretty soon) Aurelia will have official support for server-side rendering and nopCommerce will be migrated to the .NET Core platform, then we will not need to use enhance but instead let the component be rendered on the server.

More on Aurelia's enhence can be read in this article.

For just the ease of this tutorial we will be adding the component to the home page of nopCommerce: open up Nop.Web/Views/Home/Index.cshtml (or the corresponding razor file if you are using a theme) and inside the page-body tag add <welcome></welcome>. Build the application with npm run build.

Browse to your nopCommerce application and the welcome component should be loaded and the functionality be working then using the form.

You homepage should look something like this:

Maybe not an extremely useful component to have in an e-commerce store, but from here you can build upon the same concept to create really awesome UI components taking advantage of the entire Aurelia framework.

Complete source code available at GitHub.

"

How to create a Custom Action Filter in a nopCommerce v4 plugin

With Action Filters in ASP.NET Core you can alter the behavior of an Action Method before or after the method is called. You can use it to alter the arguments passed to the method or the returned result, also if you want to take some action when an Action Method is called, for example logging something everytime someone adds a product to the cart.

Action Filters can come in very handy in nopCommerce development when there is a need to extend or manipulate nopCommerce but without touching the core. In a nopCommerce plugin you can easily create a custom Action Filter and add it to the ASP.NET filter pipeline.

Creating the Action Filter

In this simple example we are going to create a custom Action Filter that manipulates the product names before the user loads the product details page. Perhaps not the most useful Action Filter but you will get an understanding of how they are created.

To create the Action Filter we need to perform following steps:

  1. Create a class that derive from ActionFilterAttribute.
  2. Implement the code that manipulates the products name.
  3. Add the filter to the ASP.NET filter pipeline.

1. Create the class

Let's get started by creating a new class ProductDetailsActionFilter in a plugin (for convenience I usually put all filters in Filters folder in root of the plugin). Let the class derive from ActionFilterAttribute.

Filters/ProductDetailsActionFilter.cs

    public class ProductDetailsActionFilter : ActionFilterAttribute
    {        
    }

ActionFilterAttribute has 5 methods that can be overridden in order to hook into various stages of the call to an Action Method:

  1. OnActionExecuting is called before the action is executed.
  2. OnActionExecuted is called after the action is executed.
  3. OnResultExecuting is called before the action's result is executed.
  4. OnResultExecuted is called after the action's result is executed.
  5. OnActionExecutionAsync is called asynchronously before the action is executed, with this method you can execute code both before and after the action is executed.

We want to manipulate the product name before the result is executed and we will need to do this in the OnResultExecuting method

public override void OnResultExecuting(ResultExecutingContext context)  
{
    // TODO only proceed if we are on the ProductDetails Action Method in the ProductController

    var result = context.Result as ViewResult;
    if (result == null) return;
    var model = result.Model as ProductDetailsModel;
    if (model == null) return;
    model.Name += " - This is magic from a Custom Action Filter";
}

From the passed ResultExecutionContext we can access the action result. In this case the result will be of type ViewResult since that is what's returned from ProductDetails action method if everything went well. The null check is important since a method could return something else if the code does not take the expected path, in this case if a product is not found a RedirectToRoute result is returned which would casuse our casting to ViewResult to be null and we want to stop executing our filter. Next we need to get hold of the returned model and cast it to ProductDetailsModel. From the model we can manipulate the name property.

Our filter will be called everytime any Action Method in our application is called. We need make sure we only proceeed executing our filter only if the ProductDetails Action Method on the ProductController is called. We can do that by inspecting the ResultExecutingContext that gets passed to our OnResultExecuting method.

public override void OnResultExecuting(ResultExecutingContext context)  
{
    if (!(context.ActionDescriptor is ControllerActionDescriptor actionDescriptor)) return;

    if (actionDescriptor.ControllerTypeInfo != typeof(ProductController) ||
        actionDescriptor.ActionName != "ProductDetails" ||
        context.HttpContext.Request.Method != "GET")
    {
        return;
    }

    var result = context.Result as ViewResult;
    if (result == null) return;
    var model = result.Model as ProductDetailsModel;
    if (model == null) return;
    model.Name += " - This is magic from a Custom Action Filter";
}
if (!(context.ActionContext.ActionDescriptor is ControllerActionDescriptor actionDescriptor)) return;  

if this code look unfamiliar it is the pattern matching feature in C# 7. It is just a shorter way of typing

var actionDescriptor = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;  
if (actionDescriptor == null) return;  

From the action descriptor we can get the controller currently called by actionDescriptor.ControllerTypeInfo. If this is the ProductController we know we are in the right controller

 actionDescriptor.ControllerTypeInfo == typeof(ProductController)

Now we need the check if it is the ProductDetails method that is called by looking at the ActionName property

 actionDescriptor.ActionName == "ProductDetails"

Optionally (in this case) we can also check that the current request is of the desired HTTP method (GET). Since the ProductDetails method only exists for GET requests this check is not needed. But it's common that an Action Method exists for multiple HTTP methods, and this is how to distinguish them

 context.HttpContext.Request.Method == "GET"

3. Add the filter to the ASP.NET filter pipeline

We can easily add the filter to the ASP.NET filter pipeline by creating a class that inherits nopCommmerce INopStartup interfaace

public class NopStartup : INopStartup  
{
    public void ConfigureServices(IServiceCollection services, IConfigurationRoot configuration)
    {
        services.Configure<MvcOptions>(options =>
        {
            options.Filters.Add<ProductDetailsActionFilter>();
        });
    }

    public void Configure(IApplicationBuilder application)
    {

    }

    public int Order => 0;
}

That is it. If we run the application and navigates to a product, the name should have been manipulated like this:

As mentioned earlier this was just a very simple example of a custom action filter, from here you can do all kinds of things without having to touch the core of nopCommerce which will make future upgraded much easier.

Download complete code from this Gist: https://gist.github.com/martingust/d5cc3204ba505ae6472335ff32dbc8eb

"