Our customer receives only spam from a certain Top Level Domain, in our case it was .icu. The customer assumes they will never receive legitimate E-Mails from this top level domain. They want all emails from this top level domain to be blacklisted.

According to the MailWatch FAQ (https://docs.mailwatch.org/using/faq.html), question “Can I use wildcards when using the Blacklist/Whitelist (SQLBlackWhiteList)?”, matching top level domains is not currently supported.

So we changed our file /usr/lib/MailScanner/MailScanner/CustomFunctions/SQLBlackWhiteList.pm

In sub LookupList, add $fromtld to the local variables, and use a regular expression to get the fromtld from the message->fromdomain.

Then we added some lines like $BlackWhite->{$to}{$fromtld}; and we are done.

# Based on the address it is going to, choose the right spam white/blacklist.
# Return 1 if the "from" address is white/blacklisted, 0 if not.
sub LookupList {
  my($message, $BlackWhite) = @_;
  return 0 unless $message; # Sanity check the input
  # Find the "from" address and the first "to" address
  my($from, $fromdomain, @todomain, $todomain, @to, $to, $ip, $fromtld);
  $from       = $message->{from};
  $fromdomain = $message->{fromdomain};
  @todomain   = @{$message->{todomain}};
  $todomain   = $todomain[0];
  @to         = @{$message->{to}};
  $to         = $to[0];
  $ip         = $message->{clientip};
  $fromtld    = $message->{fromdomain};
  ($fromtld)  = $fromtld =~ /(\.\w+)$/;
  # It is in the list if either the exact address is listed,
  # or the domain is listed
  return 1 if $BlackWhite->{$to}{$from};
  return 1 if $BlackWhite->{$to}{$fromdomain};
  return 1 if $BlackWhite->{$to}{$fromtld};
  return 1 if $BlackWhite->{$to}{$ip};
  return 1 if $BlackWhite->{$to}{'default'};
  return 1 if $BlackWhite->{$todomain}{$from};
  return 1 if $BlackWhite->{$todomain}{$fromdomain};
  return 1 if $BlackWhite->{$todomain}{$fromtld};
  return 1 if $BlackWhite->{$todomain}{$ip};
  return 1 if $BlackWhite->{$todomain}{'default'};
  return 1 if $BlackWhite->{'default'}{$from};
  return 1 if $BlackWhite->{'default'}{$fromdomain};
  return 1 if $BlackWhite->{'default'}{$fromtld};
  return 1 if $BlackWhite->{'default'}{$ip};
  # It is not in the list
  return 0;
Now you can define .icu on your Blacklist in MailWatch. Beware of the leading dot, don’t forget that!
Tags: , ,
Posted in Hosting, Software Development | Comments Closed

Mono 5.18 on copr   January 2nd, 2019

The status of Mono in Fedora is sad. We cannot upgrade to the latest Mono versions because we require binary reference assemblies for the build, as described in https://bugzilla.redhat.com/show_bug.cgi?id=1436896#c91

My hope is that when .NET Core and msbuild become part of official Fedora, we can create the various packages for the binary reference assemblies.

For the moment, I create a copr for each Mono version, where we build Mono “the Fedora way”, but with Bootstrap mode always enabled.

So here is the current release Mono for Fedora 29 and Epel 7:

The sources for the spec file and the patches are here:

Tags: , , , ,
Posted in Hosting, Software Development | Comments Closed

In 2014, I migrated the OpenPetra forum from phpBB to Discourse. The reason was that Discourse had a fresh look on the way how forums can be done, it had a nice design, and easy and modern user interface. Here is the post about the details: https://www.pokorra.de/2014/09/migrate-phpbb-forum-to-discourse-using-the-ubuntu-packages-from-packager-io/

Unfortunately, the requirements of Discourse for RAM is quite high, and you cannot just install it inside a shared hosting environment, because it is recommended to be run inside a docker container. It uses technology that I just don’t know very much, with Ruby on Rails and Redis etc.

Then I discovered Flarum, which is written in PHP, but looks as nice and feels as modern as Discourse. It does not have exotic requirements, it just works. It is helpful to have an SSH account, to install the dependencies with composer, but that is available at Hostsharing for my shared hosting anyway.
Flarum is still work in progress, and officially in Beta testing. But you can run your forums in production with the latest Beta already, if you know what you are doing.

So now the issue is, how to migrate your forum from Discourse to Flarum.

At TBits.net, we worked on a migration tool, based on the scripts https://github.com/robrotheram/phpbb_to_flarumhttps://github.com/viruxe/phpbb_to_flarum, and https://github.com/Reflic/phpbb_to_flarum.

We did not cover all aspects, but only those issues we needed for our OpenPetra forum.

Things we covered were:

  • Format text properly, code, lists, http links, etc.
  • convert categories to tags
  • keep the users email addresses

We did not cover:

  • migration of uploaded content
  • we did not convert the user passwords (each user must use the resend password functionality of Flarum)
  • and probably more…

So this blog post has the goal to make our script public, and to encourage you to use it and improve it! Pull Requests are welcome, or just fork it and use it as you like!

We started to host the script first at https://github.com/Crusader99/discourse_to_flarum, then at https://github.com/The-CJ/discourse-flarum, and now the official home for the script is:


Of course, in your real life scenario, you will have a database already. But for contributing to this script, we might need a test database, for reproducing specific situations. See my other post for details how to create a test database with dummy data for Discourse!

Setting up Flarum is quite easy if you can access your webspace with SSH and you can run Composer. See the german instructions how to install Flarum inside your Hostsharing account.

If you are working with a virtual machine and CentOS7, this Ansible script might be useful:

You can run the playbook like this (assuming your test server or container has the IP, and you can access it as root via SSH and via HTTP):

ansible-playbook flarum.yml -u root -e working_host=

For running the script discourse_to_flarum.php, you also need a PostgreSQL database inside that container. This Ansible playbook will setup PostgreSQL, and will load the Discourse Backup into the PostgreSQL database. It will also install pgadmin and phpMyAdmin, which helps with debugging of the conversion routines.
You can run the playbook like this:

ansible-playbook migration.yml -u root -e working_host=

After this, you can access phpMyAdmin on the url, the user is flarum and the password is flarum. You can access phpPgAdmin on the url, the user is discourse, and the password is discourse.
And on, your Flarum forum is running.

Now you can actually run the conversion script:

cd /root/flarummigration
composer update
cp migrate-example.yaml migrate.yaml
# perhaps you need to modify the database credentials in migrate.yaml if you have your own setup
# if your prefix is not fl_ for the flarum tables
sed -i "s/fl_//g" migrate.yaml
php discourse_to_flarum.php

to reset the flarum database for a rerun:

zcat ../flarum.sql.gz | mysql -u flarum flarum -p
# vi discourse_flarum.php, or vi migrate.yaml
php discourse_to_flarum.php

Here are two screenshots of the sample posts in Discourse and in Flarum after the migration (mind you, some of the BBCodes also don’t work in Discourse…)




So there is probably still quite some work to do. Pull requests are welcome!

Tags: , , , , ,
Posted in Hosting, Software Development | Comments Closed

This post is part of the work on a project to migrate a forum from Discourse to Flarum. See the related post here: Migrating a forum from Discourse to Flarum

Of course, in your real life scenario, you will have a database already. But for contributing to the script discourse_to_flarum.php, we need a test database, for reproducing specific situations.

It took a while to figure out how to create a test database…

Initial Setup of a test installation

First, follow the instructions at https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md to setup Discourse with a docker container.

On Fedora, I did:

wget -qO- https://get.docker.com/ | sh
mkdir /var/discourse
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse

I changed the hostname from discourse.example.com to localhost, and used admin@example.com as the E-mail address for the admin account. For the SMTP settings, you must enter values different than the defaults, but I use dummy values and create the admin account as described below.

My values for discourse-setup for a local test installation of Discourse:

Hostname for your Discourse? [discourse.example.com]: localhost
Email address for admin account(s)? [me@example.com,you@example.com]: admin@example.com
SMTP server address? [smtp.example.com]: my.smtp.example.com
SMTP port? [587]: 
SMTP user name? [user@example.com]: my.user@example.com
SMTP password? [pa$$word]: mypassword
Optional email address for setting up Let's Encrypt? (ENTER to skip) [me@example.com]:

To activate the admin user, type these commands:

cd /var/discourse
./launcher enter app
rake admin:create

I use the Email address for admin account specified above, eg. admin@example.com, and I am using the password Demo1234Demo1234! for this test database.

root@localhost-app:/var/www/discourse# rake admin:create
Email:  admin@example.com
Repeat password:  
Ensuring account is active!
Account created successfully with username example
Do you want to grant Admin privileges to this account? (Y/n)  y
Your account now has Admin privileges!

Now the Discourse instance is running at http://localhost on my Fedora machine, and I can login with user admin@example.com and the password Demo1234Demo1234! (including the exclamation mark).

Just a note, how to start a fresh database again:

launcher stop app
launcher destroy app
rm -Rf shared/standalone/*

Then start again with ./discourse-setup as described above!

Creating sample data

To create some sample data, you can use the script https://github.com/discourse/discourse/blob/master/script/profile_db_generator.rb

I have modified that script a little bit, you can see my changes at https://gist.github.com/tpokorra/b2e34238dea0243572e822f649a35bab/revisions.

To execute this script:

./launcher enter app
cd /var/www/discourse
vi script/profile_db_generator.rb
# make some modifications as described in the gist linked above:
#     unless Rails.env == "profile"  becomes: unless Rails.env == "production"
#      if User.count > 2 becomes if User.count > 3
#      users = 100.times.map    becomes: users = 10.times.map
#      topic_ids = 100.times.map do becomes:  topic_ids = 50.times.map do
#   and for replies: 2000.times do becomes:   200.times do
RAILS_ENV=production sudo -H -E -u discourse bundle exec ruby script/profile_db_generator.rb

This is the output:

root@localhost-app:/var/www/discourse# RAILS_ENV=production sudo -H -E -u discourse bundle exec ruby script/profile_db_generator.rb
installing gabbler gem
Successfully installed gabbler-0.1.0
1 gem installed
Creating 100 users
Creating 10 categories
Creating 100 topics
creating 200 replies

Creating a backup

To create a backup of the Postgresql Database:

./launcher enter app
sudo -u discourse pg_dump discourse | gzip > /shared/postgres_backup/discourse_pg`date '+%Y-%m-%d_%H-%M-%S'`.sql.gz

You find the result on your host machine, in directory /var/discourse/shared/standalone/postgres_backup.

I have uploaded this backup of the PostgreSQL database into the discourse_to_flarum repository.

Tags: ,
Posted in Hosting | Comments Closed

Seit Juli 2017 unterstützt Hostsharing die automatische Verwaltung von Let’s Encrypt Zertifikaten für einzelne Domains und auch Subdomains.

In diesem Post soll es um die Wildcard Zertifikate gehen, die Let’s Encrypt seit März 2018 ausstellt. In meinem Fall brauche ich ein Wildcard Zertifikat für das Massenhosting von OpenPetra. Die Domain wird bei Hostsharing verwaltet, aber die Server laufen bei TBits.net, also muss ich die Zertifikate nicht bei Hostsharing einrichten. Wildcard Zertifikate werden nicht über HTTP authentifiziert, sondern über DNS. Daher muss ich also bei Hostsharing die Einträge im DNS vornehmen.

Ich habe nach einem leichtgewichtigen ACME Client gesucht, der Wildcard Domains unterstützt. Dabei bin ich auf acme.sh gestoßen, das als Shell Script (bash, dash, sh kompatibel) geschrieben ist, und auf Github gehostet ist.

Installiert wird es mit diesem Befehl:

curl https://get.acme.sh | sh

Es wird dadurch ein Verzeichnis ~/.acme.sh angelegt und mit entsprechenden Dateien gefüllt, weniger als ein MB.

Hier ist eine Anleitung für manuelle Konfiguration über DNS: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode

Der erste Aufruf sieht so aus, für die Domain openpetra.com:

~/.acme.sh/acme.sh  --issue  -d '*.openpetra.com'  --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please
Die Ausgabe sieht dann in etwa so aus:
[Tue Sep 25 23:08:42 CEST 2018] Creating domain key
[Tue Sep 25 23:08:42 CEST 2018] The domain key is here: /home/pacs/tim00/users/.../.acme.sh/*.openpetra.com/*.openpetra.com.key
[Tue Sep 25 23:08:42 CEST 2018] Single domain='*.openpetra.com'
[Tue Sep 25 23:08:42 CEST 2018] Getting domain auth token for each domain
[Tue Sep 25 23:08:43 CEST 2018] Getting webroot for domain='*.openpetra.com'
[Tue Sep 25 23:08:43 CEST 2018] Add the following TXT record:
[Tue Sep 25 23:08:43 CEST 2018] Domain: '_acme-challenge.openpetra.com'
[Tue Sep 25 23:08:43 CEST 2018] TXT value: 'nrsGiN7Yb4R8eYxx7RNRAP8x6MA2zTrgbCLemD1t7aB'
[Tue Sep 25 23:08:43 CEST 2018] Please be aware that you prepend _acme-challenge. before your domain
[Tue Sep 25 23:08:43 CEST 2018] so the resulting subdomain will be: _acme-challenge.openpetra.com
[Tue Sep 25 23:08:43 CEST 2018] Please add the TXT records to the domains, and re-run with --renew.
[Tue Sep 25 23:08:43 CEST 2018] Please add '--debug' or '--log' to check more details.
[Tue Sep 25 23:08:43 CEST 2018] See: https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh

Nun muss ich diesen TXT record für die Domain openpetra.com einrichten.

Ich muss zuerst eine Datei im Verzeichnis ~/doms/openpetra.com/etc einrichten, mit dem Namen pri.openpetra.com. Der Inhalt sieht in etwa so aus:

$TTL 60M
_acme-challenge.openpetra.com.  3600  IN  TXT  "nrsGiN7Yb4R8eYxx7RNRAP8x6MA2zTrgbCLemD1t7aB" 
www       IN  A {DOM_IPNUMBER}
op012345  IN  A

Nach etwa 3 Minuten sieht man in der Datei /var/log/named/named.log, dass die eigene Domain aktualisiert wurde.

Dann kann der zweite Schritt mit acme.sh ausgeführt werden:

~/.acme.sh/acme.sh  --issue  -d '*.openpetra.com'  --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --renew

Die Ausgabe sieht wie folgt aus:

[Tue Sep 25 23:09:54 CEST 2018] Renew: '*.openpetra.com'
[Tue Sep 25 23:09:55 CEST 2018] Single domain='*.openpetra.com'
[Tue Sep 25 23:09:55 CEST 2018] Getting domain auth token for each domain
[Tue Sep 25 23:09:55 CEST 2018] Verifying:*.openpetra.com
[Tue Sep 25 23:09:58 CEST 2018] Success
[Tue Sep 25 23:09:58 CEST 2018] Verify finished, start to sign.
[Tue Sep 25 23:10:00 CEST 2018] Cert success.
[Tue Sep 25 23:10:00 CEST 2018] Your cert is in  /home/pacs/tim00/users/..../.acme.sh/*.openpetra.com/*.openpetra.com.cer 
[Tue Sep 25 23:10:00 CEST 2018] Your cert key is in  /home/pacs/tim00/users/..../.acme.sh/*.openpetra.com/*.openpetra.com.key 
[Tue Sep 25 23:10:00 CEST 2018] The intermediate CA cert is in  /home/pacs/tim00/users/..../.acme.sh/*.openpetra.com/ca.cer 
[Tue Sep 25 23:10:00 CEST 2018] And the full chain certs is there:  /home/pacs/tim00/users/..../.acme.sh/*.openpetra.com/fullchain.cer 
[Tue Sep 25 23:10:01 CEST 2018] It seems that you are using dns manual mode. please take care: The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead.
[Tue Sep 25 23:10:01 CEST 2018] Call hook error.

Ich kopiere nun die Dateien *.openpetra.com.key nach wildcard.openpetra.com.key und fullchain.cer nach wildcard.openpetra.com.crt auf den Produktionsserver, und binde sie im Nginx ein:

    ssl_certificate /var/lib/certs/wildcard.openpetra.com.crt;  
    ssl_certificate_key /var/lib/certs/wildcard.openpetra.com.key;

Da das Zertifikat nur für 3 Monate gültig ist, habe ich eine automatische Erinnerung
in meinem Kalender eingerichtet, die mich alle 2 Monate daran erinnert, das Wildcard Zertifikat zu erneuern.

Tags: , , , ,
Posted in Hosting | Comments Closed

Migrating from KnowledgeTree to SeedDMS   September 24th, 2018

For many years, I had an installation of KnowledgeTree for our small charity “Nazareth Kinderheim Kenia e.V.”.

Unfortunately, it has not been maintained anymore for many years, and the replacement of PHP5 with PHP7 forced me to do something.

I finally settled for SeedDMS as a replacement, and I really like it!

See instructions how to install SeedDMS in a shared hosting environment, at Hostsharing: https://wiki.hostsharing.net/index.php?title=SeedDMS_installieren (in German).

To export the files from KnowledgeTree, I made the decision, that I would ignore deleted files, and would not need the history of files, so only the latest version of each file. That made the migration much easier.

I found a Python script by Ben E. C. Boyter at https://boyter.org/2015/09/exporting-documents-knowledgetree-3-7-0-2/ which was a good start. I found only two issues with it: it would restore even deleted or archived files, and it would restore a random version of a file, not the latest version. So I fixed the Python script, and you can see it here as a gist: https://gist.github.com/tpokorra/71526bc60ced2649dba2c528d5cd2259

There is an import function in SeedDMS, where you can import from a directory on the server, which minimizes the used bandwidth. Alternatively, you can use WebDAV to import a lot of files at the same time.


Tags: , , ,
Posted in Hosting | Comments Closed

During our project with Dynamics NAV 2018, we needed to consume a web service from CleverReach (see documentation of their REST API: https://rest.cleverreach.com/howto/), so that we can synchronize the recipients of the newsletters.

Here are now procedures that can be universally useful for using web services from within Dynamics NAV:

See the code at https://github.com/TBits/Al-examples/blob/master/HTTPRequests/cod50131.HttpRequest.al

How to do a simple GET http request:

procedure LoadWebpage(url: Text) result: Text;

How to do a simple POST http request:

procedure PostRequest(url: Text; post_names: List of [Text]; post_values: List of [Text]) result: Text;

And then how to do a POST http request, that is behind Basic Authentication (eg. done with htaccess/htpasswd):

procedure PostRequestWithBasicAuth(
        url: Text;
        post_names: List of [Text];
        post_values: List of [Text];
        username: Text;
        password: Text) result: Text;

I have prepared some web services for testing, see the code in https://github.com/TBits/Al-examples/blob/master/HTTPRequests/pag50130.HttpRequest.al for how to call them.

By the way, this example also shows how to use lists in AL, which is a feature that can hardly be found in the documentation:

    post_names: List of [Text];
    i: Integer;
    for i := 1 to post_names.Count do
Tags: ,
Posted in Software Development | Comments Closed

Today, we had an issue that a web service returned a timestamp in unixtime format, ie. all seconds that have passed since January 1st 1970.

We would like to convert that timestamp to a DateTime variable in AL Code.

We finally came up with these functions: UnixTimeToDateTime (see https://github.com/TBits/Al-examples/blob/master/ALBasics/cod50101.AlBasics.al#L8) and DateTimeToUnixTime (see https://github.com/TBits/Al-examples/blob/master/ALBasics/cod50101.AlBasics.al#L36).

See also the example calls at https://github.com/TBits/Al-examples/blob/master/ALBasics/pag50101.ALBasics.al

procedure TestUnixTime();
	myCodeUnit: Codeunit myLibrary;
	nowUnixTime: BigInteger;
	FormatString: Text;
	nowDateTime: DateTime;
	nowUnixTime := myCodeUnit.DateTimeToUnixTime(CurrentDateTime);
	Message('to unixtime: ' + Format(nowUnixTime));
	nowDateTime := myCodeUnit.UnixTimeToDateTime(nowUnixTime);
	// formatting the date time to string
	FormatString := '<Day,2>.<Month,2>.<Year4> <Hours24,2>:<Minutes,2>:<Seconds,2>.<Thousands,3>';
	Message('from unixtime: ' + Format(nowDateTime, 0, FormatString));

Also note how you can specify the formatting of a date with the keywords Day, Month, Year4, Hours24, Minutes.

Tags: ,
Posted in Software Development | Comments Closed

In the previous post, Setting up Dynamics NAV 2018 for Testing and Development in Docker we have covered how to setup a development and testing environment, and had our first steps with Visual Studio Code to publish our first hello world application.

Now I want to show you some AL language thing that might be useful. You can see them in action in the example app ALBasics: https://github.com/TBits/Al-examples/tree/master/ALBasics

There are test procedures illustrating how to:

  • Working with basic constructs like “if”
  • Format various types of variables to string (known as Text), using Format(), or StrSubstNo().
  • Parse Integer to String with Evaluate()
  • Working with Date and DateTime, getting the current Now with CurrentDateTime or Today with Today. Comparing dates, even by a given difference in for example days, with CalcDate(). Converting between DateTime and Date, with DT2Time(), and setting a date with DMY2DATE().
  • How to work with options: calling a procedure with an option parameter, and the issues you can get if you have different definitions of the options.
  • How to call procedures with reference parameters (keyword var), and how to return a result either by a named variable or with exit(<value>)
  • How to call a procedure that is defined inside a code unit.
Tags: ,
Posted in Software Development | Comments Closed

In the past months, I have learned a lot about Dynamics NAV 2018. There are some examples available on the net, but not enough. So I will provide some examples on my blog.

Let’s start with setting up the free Docker container on Windows of Dynamics NAV for testing and development purposes.

My first obstacle was the requirements to the CPU.

First, you need a CPU with SLAT for running Hyper-V on Windows 10. See also https://social.technet.microsoft.com/wiki/contents/articles/1401.hyper-v-list-of-slat-capable-cpus-for-hosts.aspx which states: “Every Core i3, i5 and i7 supports SLAT. You can look up the supported AMD versions at KB article on AMD“.
So I bought a refurbished HP ProBook 6560b with a Intel Core i5-2520M CPU, but that did not work. It does not support docker images with more than 2 GB of RAM. But you need 4 GB to run Dynamics NAV 2018 in a container. See this bug for more details: https://github.com/docker/for-win/issues/1094.
With the coreinfo tool (download here for free: https://docs.microsoft.com/en-us/sysinternals/downloads/coreinfo), you can test if your CPU is supported:

Coreinfo.exe -f

If you see a star, then you should be fine.

PAGE1GB * Supports 1 GB large pages

If you see a hyphen, then you cannot use that computer:

PAGE1GB - Supports 1 GB large pages

You can install Docker for Windows from https://www.docker.com/docker-windows. You need to register in the Docker Hub for free. You install it, and it will ask you to restart the computer, the latest version enables Hyper-V, and it asks if you want to use Linux or Windows containers. Let the installer prepare your computer for Windows containers.

When you start Docker, and right click on the symbol, you see Kitematic. That is a useful tool to manage your containers graphically. Click on it, and it will download a zip file. Extract the contents to C:\Program Files\Docker\Kitematic.

The docker images from Microsoft are listed here: https://hub.docker.com/r/microsoft/dynamics-nav/

As administrator you open a PowerShell, and run these commands:

# this should pull the latest dynamics-nav, 2018 at the moment
docker pull microsoft/dynamics-nav
# now start a container
docker run -e accept_eula=Y -e ClickOnce=Y -e UseSSL=N -m 4G microsoft/dynamics-nav

I chose the setting ClickOnce=Y because I wanted to test not only the web client, but also the Windows Client.

I chose the UseSSL=N setting because I had issues with Visual Studio Code to download the symbols or to upload my extension if it Dynamics NAV was installed with a self-signed certificate. I got the message:

Die zugrunde liegende Verbindung wurde geschlossen: Für den geschützten SSL/TLS-Kanal konnte keine Vertrauensstellung hergestellt werden..
Das Remotezertifikat ist laut Validierungsverfahren ungültig.

You probably could download the certificate from http://YOURCONTAINERIP:8080/certificate.cer and install it into your “Trusted Root Certification Authorities” but I did not want to do that just for testing purposes.

This is the output of the docker run command:

PS C:\WINDOWS\system32> docker run -e accept_eula=Y -e ClickOnce=Y -e UseSSL=N -m 4G microsoft/dynamics-nav
Starting Container
Hostname is c63405985172
PublicDnsName is c63405985172
Using NavUserPassword Authentication
Starting Local SQL Server
Starting Internet Information Server
Creating Self Signed Certificate
Self Signed Certificate Thumbprint 5D0E6813F13AA90A59328F196F9594CCEF3F1DF9
Modifying Service Tier Config File with Instance Specific Settings
Starting NAV Service Tier
Creating DotNetCore Web Server Instance
Creating http download site
Creating Windows user admin
Setting SA Password and enabling SA
Creating admin as SQL User and add to sysadmin
Creating SUPER user
Creating ClickOnce Manifest
Container IP Address:
Container Hostname  : c63405985172
Container Dns Name  : c63405985172
Web Client          : http://c63405985172/NAV/
NAV Admin Username  : admin
NAV Admin Password  : Qake5494
Dev. Server         : http://c63405985172
Dev. ServerInstance : NAV
ClickOnce Manifest  : http://c63405985172:8080/NAV


Initialization took 114 seconds
Ready for connections!

Now how to test your Dynamics NAV instance:

    • Web Client: you see the URL in the output above. It will be different in your installation…
    • Windows Client: it does not work in Firefox, to download the ClickOnce manifest from the URL mentioned above, installing it gives the error: System.Deployment.Application.InvalidDeploymentException (Zone)
      – Die Bereitstellung und die Anwendung haben keine übereinstimmenden Sicherheitszonen.
      But it works in Edge or Internet Explorer just fine. You use the NAV Admin Username and Password as specified in the docker output.
    • With Visual Studio Code: Install the AL Language extension from http://c63405985172:8080/, the file is called like al-0.12.25327.vsix. In Visual Studio Code, go to Extension, select the hamburger menu at the top, and choose “Install from VSIX file”. Installing the AL Language extension from the Marketplace would install a newer version of the extension, but it probably causes issues with your server because it is not compatible.

First steps with Visual Studio Code: Create a sample application: Ctrl-Shift-P, search for AL:Go!, and run it. This will ask you where you want the project stored, and then create a hello world example.

You need to modify the file launch.json, and enter for server the URL which was in the docker output labelled as “Dev. Server”, in my case “http://c63405985172”

Then you can press Ctrl-Shift-P, search for AL: Download Symbols, and run that.

After the symbols have been downloaded, run AL: Publish, which should first ask your for your username and password (use admin and the password from the docker output), then it will upload your extension to the NAV server, and VS Code will start the Web client with the page Contacts, because that page is the Object ID 22, which is defined in startupObjectId in launch.json. You will see the message box.

I have submitted this basic extension on Github: https://github.com/TBits/Al-examples/tree/master/HelloWorld

In the next post in this series, we will discover the AL language in more detail: Getting used to the AL language for Dynamics NAV 2018