Annice 2022-11-20 Photo
A few new photos are now available on the page Digital photography. Most of the pictures are taken during my vacation this summer with the exception of the first one taken this fall. Moreover, the pictures are taken with my cell phone - Huawei P30 Pro.
The first picture below is taken when I was at Halloween at Liseberg in Gothenburg this October. The roller coaster that you can see in the picture is Helix that was described in previous post which was one of the carousels I rode there, and which was quite an experience to say the least. ヾ(☉□☉)ノ

The next image was taken in Stockholm Old Town when I was visiting a friend this summer during my vacation:

During my vacation this summer, I also went on a trip to the Greek island "Kalymnos". And the following picture was taken one day during a walk in the area around the hotel I stayed at:

It was very nice on Kalymnos, but terribly many mosquitoes! In retrospect, I figure why the trip was so cheap. ;-P

However, the island offered many beautiful views. The view of this mountain, for instance, graced my view from my hotel balcony for a week (the above picture wasn't taken from the hotel balcony, though, but from a walking occasion).
Furhermore, the picture below was taken from the "Massouri Beach" - about 20 meters from the hotel:

Last but not least, I immortilized this little lady bug that was keeping me company one day from the hotel pool:

Annice 2022-10-30 Other, Entertainment
Yesterday I was at Halloween at Liseberg. The first thing we rode was Helix (see video).
Did I mention that I'm almost 40? And that I've not ridden roller coasters for about 15 years?
Well, at least the body and vocal cords are starting to recover now...
Annice 2022-06-05 Photo
I have uploaded a few new photos on the page Digital photography. Or they're not really new; I took them quite a long time ago but I haven't uploaded them here until now.
Anyhow, all pictures are taken with my mobile phone, Huawei P30 Pro, which I think has a great camera to bring on the go.
The below picture is taken this spring at Stenpiren, Gothenburg (2022):

Moreover, the following picture is also taken this spring from Stenpiren (2022):

Huawei P30 Pro is really grateful to take evening pictures with, and the following evening picture is taken last summer while I was visiting Norrköping during my vacation (2021):

However, I also took a picture during daytime of the current in Norrköping while I was visiting a youth friend and we were chit-chatting over some lunch (2021):

During my vacation last summer (2021), I was also visiting my cousing who lives in the country in Ullared. And while we were having a barbeque on their backyard, this fog formed which I felt compelled to perpetuate:

Furthermore, I took the opportunity to upload a picture I took while I was walking along Klippan in Gothenburg (2021) noticing this little swan family:

Annice 2022-04-17 Music
Yesterday I finally got to experience them LIVE at Trädgår'n here in Gothenburg, and a youthful dream came true.
I may be an omnivore within music, but there are few sound loops that have conveyed such euphoric feelings like theirs'. They have really ment so much for me through the years since I first heard them back in my early 20s. So thank you for existing and for all the wonderful things you've done, Infected Mushroom. <3
Two music geniuses accompanying:
Tracklist from above video:
2:19 - More of Just the Same
8:52 - Only Solutions
13:11 - Ani Mevushal
19:26 - Symphonatic Remix
25:23 - No Line In Midi
31:05 - Splicon
35:02 - Freedom Bill
40:23 - Infected Megamix
47:25 - Electro Panic Remix
51:03 - Wider Remix
55:00 - Astrix - Coolio (Infected Mushroom Remix)
1:02:22 - Bust A Move (Bliss Remix)
1:07:50 - Muse Breaks Reborn Remix
1:12:54 - Suliman Remix
1:19:23 - Dracul Reborn Remix
1:25:40 - Head Of Nasa
1:32:21 - Gravity Waves
1:38:58 - Return Of The Shadows Reborn Remix
Annice 2021-10-19 Commands, Linux
In my current profession working as a system integration developer I mainly work with PHP as a server language.
Moreover, we work on Mac computers with Linux based file systems through terminals on a daily basis.
In this post, I have just listed a few handy Linux commands.
| Command: | Description: |
| chmod +rwx |
Add permissions to read, write and execute a file. |
| chmod -rwx |
Remove permissions to read, write and execute a file. |
| clear | Clear the console. |
| cp |
Copy multiple files to a directory. |
| diff -y -W 70 |
Compare differences between two files. The "y" flag displays the differences side by side, while the "w" flag lets you choose the row width to avoid nestled rows. |
|
find . -type f -name "*.php" |
Find all files with the file extension "php" in the current directory. |
|
find . -name file.txt |
Find all files named "file.txt" in the current directory. |
|
grep |
Search for a specific word in all files with a certain file extension (in this case "txt" files) and then list matching files. |
|
grep -rl "text here" |
Search for all files including "text here" in a given directory. |
|
gzip -k |
Zipp a file, but keep the original file using the "k" flag. |
|
kill |
Terminate a specific system process by its PID. |
|
kill -9 |
Force termination of a system process/PID. |
|
less |
Read a file's content without edit mode, including pagination etc. |
|
ls -a |
List all content such as files and directories in the current directory. |
|
lsof -i : |
List all processes using a specific port. |
|
mkdir |
Create a new folder in the current directory. |
|
mv |
Move a given file to a given destination specified as a path. |
|
mv |
Change the name of a given file by "moving" it to its new name. |
|
mv *.png ~/ |
Move all files with the file extension "png" to a given directory. |
|
php -a |
Aktivera en php-editor direkt i terminalen. |
|
ping |
Pinga a host and check its status, e.g. "ping google.com". |
|
ps |
List all running system processes. |
|
ps -a |
List all system processes, including the ones not running. |
|
ps -ax | grep |
Find a system process by its name or PID. |
| pwd | Output the full path of the current directory. |
| rm -R |
Remove a given directory/folder and its content in the current directory. |
| rm -f |
Force removal of two given files, including all files with a certain file extension (in this example "png"). |
| scp |
Remotely copy a file, e.g. "scp fil.txt" |
| ssh | Remote connect to a server, e.g. "ssh |
| su -c |
Run a command as another system user. |
| sudo |
Execute a command as a super user. This is useful for commands requiring admin privileges. |
| tail |
Output the last paragraph from a file, e.g. "tail log.txt". This command is useful while troubleshooting given that errors are logged in some sort of file. |
| touch |
Create a file in the current directory. |
| uuidgen | awk '{print tolower($0)}' | Create and output a lower cased UUID string (version 4). |
| whoami | Output the currently logged in system user. |
| !! | Run the latest command. |
Annice 2021-04-10 PHP, Symfony, MySQL, Doctrine
When building web pages with PHP based on the Symfony framework, you can easily integrate with database tables using the EntityManagerInterface with Doctrine ORM (Object Relational Mapper) along with generated entity classes. Depending on the used table structures, as well as which SQL query that is to be executed, you may need to implement a workaround for this though.
Let's say, for simplicity, that you want to insert a new row in a table in which the structure is based on the following code, where a new primary key is auto incremented with every new insert:
CREATE TABLE Person (
Id INT AUTO_INCREMENT PRIMARY KEY,
Email VARCHAR(100) UNIQUE,
Firstname VARCHAR(30),
Lastname VARCHAR(30)
);
In a Symfony application with Doctrine ORM installed - along with a generated entity class with getters and setters corresponding the above database table and columns - you can easily add a new row to the table according to the following example code.
namespace App\Repository;
use App\Entity\Person;
use Doctrine\ORM\EntityManagerInterface;
class PersonRepository
{
/**
* Inject dependency to EntityManagerInterface.
*/
public function createPerson(EntityManagerInterface $em)
{
$person = new Person();
$person->setEmail("jane.doe@mail.com");
$person->setFirstname("Jane");
$person->setLastname("Doe");
$em->persist($person);
$em->flush();
}
}
So far so good! But let's say that you want to insert a customized primary key for every new row, e.g. based on the following table structure:
CREATE TABLE Person (
Email VARCHAR(100) PRIMARY KEY,
Firstname VARCHAR(30),
Lastname VARCHAR(30)
);
Then you have to find another way of handling this as the persist() and flush() methods tend to oppose you with these kind of inserts. And one way to cope with this while still using the EntityManagerInterface is to also use the Doctrine DBAL (DataBase Abstraction Layer):
namespace App\Repository;
use Doctrine\ORM\EntityManagerInterface;
class PersonRepository
{
public function createPerson(EntityManagerInterface $em)
{
$email = 'jane.doe@mail.com';
$firstName = 'Jane';
$lastName = 'Doe';
// Prevent SQL injections by using placeholders ("?") in the query:
$sql = 'INSERT INTO Person (Email, Firstname, Lastname) VALUES (?, ?, ?)';
$em->getConnection()
->prepare($sql)
->execute(
[
$email,
$firstName,
$lastName
]
);
}
}
The above example deviates the ORM principle. However, when using INSERT queries in specific, it can be easiest to just use the above persistence instead when you want to add customized primary keys.
Annice 2021-03-19 C#, ASP.NET Core, Security
Cookies are commonly used in web development contexts, e.g. to remember a specific user login by having a cookie created based on a logged in user ID. However, cookie values saved in browsers means that anybody can view their actual values by inspecting the web browsers' elements.
For security reasons, you usually don't want to expose actual user IDs fetched from databases for unauthorized users, and one way to hide user IDs used in cookies can be to encrypt these cookie values.
Within C# ASP.NET Core, this can be done by using the DataProtection extension. In an MVC ASP.NET Core solution, this is done by initially registering this extension in the file "Startup.cs" as illustrated below:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// Register support for DataProtection extension:
services.AddDataProtection();
}
// ...
Furthermore, you can create a separate service class that implements this DataProtection extension, e.g. in the class and code illustrated below which can be put in a separate service layer (folder) in the application:
using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
namespace TestApp.Services
{
public class DataProtector
{
private IDataProtector CookieProtector()
{
var dataProtectionProvider = DataProtectionProvider.Create("TestApp");
var protector = dataProtectionProvider.CreateProtector("Cookie.Encryption");
return protector;
}
/// Create a user cookie and encrypt it to prevent the real value
/// from being exposed on the client side (via the browser).
public void EncryptUserId(HttpContext context, int userId)
{
string protectedUserId = CookieProtector().Protect(userId.ToString());
context.Response.Cookies.Append("User", protectedUserId);
}
/// Receive the encrypted user cookie and decrypt it to be able to handle its
/// real value on the server side.
public int? ReadUserId(HttpContext context)
{
int userId;
if (!string.IsNullOrEmpty(context.Request.Cookies["User"]))
{
string id = CookieProtector().Unprotect(context.Request.Cookies["User"]);
userId = Convert.ToInt32(id);
return userId;
}
return (int?)null;
}
}
}
Moreover, to be able to use this service during the rendering (preparation) of web pages via a controller, it can look like the following example where cookie decryption is applied before the page "Start" is presented to the end user:
using Microsoft.AspNetCore.Mvc;
using TestApp.Services; // Include the service layer to reach our data protector service.
namespace TestApp.Controllers
{
public class HomeController : Controller
{
// Autowire our DataProtector service via dependency injection:
public IActionResult Start(DataProtector protector)
{
// E.g. userId fetched from DB, but hard coded in this example for simplicity:
int userId = 1;
protector.EncryptUserId(HttpContext, userId);
// ...
return View();
}
}
}
When the above user ID has been assigned to a cookie and encrypted via the service mentioned above, the cookie value "1" will only be exposed as an encrypted value in the browser as you can se in the following screenshot:
Finally, when you want to decrypt the cookie value to be able to handle its actual value on the server side again, this can be done by calling the implemented service class like in the example below:
/// A method used in a suitable place on the server-side where we want to call our DataProtection service
/// via a dependency injection to decrypt our cookie back to its actual value.
public void HandleDecryptedCookieValue(DataProtector protector)
{
// This will assign a userId variable with the decrypted cookie value:
var userId = protector.ReadUserId(HttpContext);
// Now do something with the decrypted userId...
}
Annice 2021-03-08 Updates, Photo
Added a couple of new photos to the page digital photography.
Annice 2021-02-15 Docker, Commands
Docker is a powerful tool to virtualize an operating system and different techniques by separated, independent services and containers, which in turn are based on one or many Docker images.
In other words, a Docker image can be created with e.g. Ubuntu as a virtual OS base, which in turn can install Apache web server, PHP, and MySQL to run a web application based on these techniques. Moreover, this image can execute these techniques by starting different containers: one running the Apache server with PHP, another one running MySQL etc.
Creating apps based on micro services like this enables an application to run on any machine with Docker installed. A container based infrastructure is also more resource efficient as you can stop, change and start new containers and services without taking into account the host computer/server's OS, in which they are completely isolated from.
Anyhow, in this entry I just wanted to list some Docker commands I find useful when managing images and containers via a system shell:
| Command: | Description: |
| docker build . | Build a Docker image in the same folder where your docker file (Dockerfile) is. |
| docker build -t |
Build a Docker image with a given image name and label (version) based on a Dockerfile. |
| docker-compose up | Given that you are in the same folder as the Dockerfile: Build, (re)create, and attach to containers for a service. |
| docker-compose start | Given that you are in the same folder as the Dockerfile: Start all containers that have been created by an image. |
| docker-compose stop | Given that you are in the same folder as the Dockerfile: Stop all running containers of an image. |
| docker images | List all Docker images. |
| docker run |
Create and start a container based on a given image. The number of executions of this command will create and start the same number of containers based on the same image. |
| docker ps |
List all running containers. |
| docker ps -a |
List all containers - including the exited ones. |
| docker start |
Start a container. |
| docker stop |
Stop a container. |
| docker rm |
Remove a container (given that the container has been stopped). |
| docker rmi |
Remove an image (given that all attached containers have been stopped). |
| docker exec -i -t |
SSH (connect to/enter) a container in Docker. |
Annice 2020-12-27 PHP, Symfony
Applications based on the MVC pattern (Model-View-Controller) use controller classes as an interaction layer between a view and a model layer. Moreover, the purpose with entity/object classes and their properties in the model layer is to reflect the database tables and their columns, while the controller classes and their action methods reflect the different application pages in the view layer.
In other words, this means that the controller methods receive the data inputs from end users via the view layer to pass it to the database via the model/entity layer. Also, these methods render the different web pages to be presented to the end users. When you navigate to a web page based on MVC, a controller method is first called on the server side to prepare the requested page with all necessary data, which is then fetched from one or many database tables via one or many entities.
When I have the opportunity to choose, I usually prefer to use the MVC pattern as I think it is a very structured way of building and managing the application code. Within C# ASP.NET, these layers are simply called model, view, and controller, but within the PHP Symfony framework they are called entity (model), templates (view), and controller.
In larger MVC applications, it is common to use several controllers which in turn consist of several different action methods. Many of these methods might also have the task to render different admin pages. To prevent unauthorized users from accessing these pages, you need to some how control this before the pages are presented. But instead of having to control this with the same code in every concerned action method, which would violate the DRY principle (Don't Repeat Yourself), this can rather be handled via e.g. a middleware to control this before controllers are called (which I have also described in another entry).
However, a similar way of handling this, which I have discovered within PHP Symfony, is to use event subscribers. The principle with these subscribers is to implement an event subscriber class, which in turn implements an interface named "EventSubscriberInterface". Furthermore, the EventSubscriberInterface is available via an EventDispatcher component which can be installed as a bundle via the Symfony framework using the package manager composer. Moreover, this enables you to let an event subscriber control e.g. a valid user login.
Furthermore, you can ensure that this subscriber is called before wanted controllers by letting these controllers implement an empty interface, in which the subscriber checks if the controller is an instance of. I have tried to illustrate this context flow in the sequence diagram below:

An example of an implementation of this can look like below, where I first chose to create a separate layer (folder) named "Service". Furthermore, I created an empty interface called "AuthInterface" put in a sub folder named "Abstraction". The following code is also based on PHP 7.4 with Symfony 5.1.8:
namespace App\Service\Abstraction;
interface AuthInterface
{
// No need for method declarations in this interface.
}
Additionally, I created another folder named "EventSubscriber" in which I created an AuthSubscriber class. With the EventDispatcher component installed, I was further able to let this AuthSubscriber class implement the EventSubscriberInterface as below:
namespace App\EventSubscriber;
use App\Service\Abstraction\AuthInterface;
use App\Service\AuthManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* The AuthSubscriber is called before the controllers to check valid admin login
* for password protected controllers.
*/
class AuthSubscriber implements EventSubscriberInterface
{
private $authManager;
private $router;
public function __construct(AuthManager $auth, UrlGeneratorInterface $router)
{
$this->authManager = $auth;
$this->router = $router;
}
public function onKernelController(ControllerEvent $event)
{
$controller = $event->getController();
// When a controller class defines multiple action methods, the controller
// is returned as [$controllerInstance, 'methodName']:
if (is_array($controller)) {
$controller = $controller[0];
}
// Check if the controller implements our AuthInterface:
if ($controller instanceof AuthInterface) {
// Call a separately implemented authManager class in which we check if an admin session is set.
// If the session is not set, just redirect the user to a public start page.
if (!$this->authManager->isLoggedIn()) {
$redirectUrl = $this->router->generate('start');
$event->setController(function () use ($redirectUrl) {
return new RedirectResponse($redirectUrl);
});
}
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::CONTROLLER => 'onKernelController',
];
}
} // End class.
Finally, I can choose which controllers I want to be checked by this AuthSubscriber by letting these controllers implement the AuthInterface as the following code:
namespace App\Controller;
use App\Service\Abstraction\AuthInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
/**
* Controller class to handle the admin pages.
*/
class AdminController extends AbstractController implements AuthInterface
{
/**
* @Route("/admin", name="admin_page")
* @Method({"GET"})
*/
public function adminPageAction()
{
// Prepare data etc here for the admin page...
return $this->render("admin/admin-page.html.twig");
}
} // End class.
Annice 2020-12-06 PHP, Symfony, MySQL, Doctrine
When I started re-building Annice.se based on PHP with Symfony, I was hoping that PHP would have some equivalent to Lambda expressions with LINQ (Language-Integrated Query). Lambda expressions with LINQ was namely something I used quite frequently within C# ASP.NET Core concerning filtering of lists and arrays, which in turn was oftently based on database queries.
A kind of equivalent to the above within PHP is Doctrine. Moreover, something I have used a bit so far in conjunction with the re-building of Annice.se is Doctrine ORM (Object-Relational Mapper) with Symfony.
As the ORM acronyme implies, the purpose with this query language is to ease the PHP objects' (entities') interaction with one or many relational databases, in which the database tables reflect the entities used in the PHP code, as well as where the table attributes (columns) are mapped to the different properties of the PHP entities.
Furthermore, what appealed to me the most with Doctrine ORM was that it also allows you to use raw SQL code, i.e. like the actual queries used in the end when they are executed to the database. In cases where you use sub-queries and/or different joins to reach different data, I have to admit that I prefer using pure SQL code.
Given that you have PHP Symfony installed with a configured database connection, further prerequisits to run Doctrine ORM within Symfony are to have the package manager Composer installed. In turn, that enables you to easily install support for the Doctrine package. Within C# ASP.NET Core you talk about NuGet packages, why I am also using terms like "packages" in this case. But it should be mentioned that bundles is the more accurate term to use for this within the Symfony framework. However, a more detailed guide for this can be found on Symfony's documention page: Databases and the Doctrine ORM.
Anyhow, the purpose with this post was really to list some Doctrine ORM queries I have collected, which I find useful to go back to as starting points regarding different database quiries. And to finally enable the function of Doctrine, you need to call/import an EntityManagerInterface where you want to use Doctrine ORM, e.g. in a controller.
The following code illustrates a holistic context of where the EntityManagerInterface has been autowired via a controller constructor, which is useful when Doctrine quiries are used in many action methods in the same controller class. Moreover, the constructor injected Entity Manager service can then be applied on a Doctrine query to get posts from a database table in action methods like the example below. Also, the following code snippets are based on Symfony 5.1.8 with Doctrine 2, which in turn requires at least PHP 7.1:
namespace App\Controller;
use App\Entity\Entry;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Doctrine\ORM\EntityManagerInterface;
class EntryController extends AbstractController
{
private $entityManager;
/**
* Inject dependencies.
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* @Route("/entry/entry_list", name="entry_list")
* @Method({"GET"})
*/
public function entryListAction()
{
// Call the entity manager service:
$em = $this->entityManager;
// Get all entries from DB via Doctrine entity mapping and return the result in descending order by entry date:
$entries = $em->getRepository(Entry::class)->findBy([], ['date' => 'DESC']);
// Finally, pass the entry list to be reached via an "entries" variable from a twig template/view:
return $this->render('entries/entry_list.html.twig', [
"entries" => $entries
]);
}
}
If you would like to formulate the same query without having to autowire the EntityManagerInterface via a constructor, i.e. only use it directly from some single method. Then you can exclude the dependency injection into the constructor and instead call the service as below:
public function entryListAction()
{
// Call the entity manager service:
$em = $this->getDoctrine()->getManager();
// Get all entries from DB via Doctrine entity mapping and return the result in descending order by entry date:
$entries = $em->getRepository(Entry::class)->findBy([], ['date' => 'DESC']);
// ...
If you would want to get a specific Entry object based on a specific condition in Doctrine ORM, this could look like the code snippet below (given that the Entity Manager service has been imported and instantiated):
$entry = $em->getRepository(Entry::class)->createQueryBuilder("alias")
->select("alias")
->where("alias.email = :email")
->setParameter("email", "user@email.com")
->getQuery()
->getOneOrNullResult();
By choosing to return the above result with the method getOneOrNullResult(), it will return null if there are not hits, meaning that this could be handled directly in a PHP statement without risking the application to crash. Otherwise, a corresponding method for this would be to use the getSingleResult() method. However, this would throw an exception in case of no hits, or in case when more than one hit occurs.
In addition, if you would like to fetch objects based on several conditions and return the result into an array in descending order by date, it can look like this:
$entries = $em->getRepository(Entry::class)->createQueryBuilder("alias")
->select("alias")
->where("alias.date = :date")
->andWhere("alias.categoryid = :categoryid")
->setParameters(["date" => "2020-12-06", "categoryid" => 1])
->orderBy("alias.date", "DESC")
->getQuery()
->getResult();
Furthermore, you can use the following code snippet if you would like to get the same result as above, but formulated in raw MySQL code with Doctrine:
$entries = $em->createQuery(
"SELECT alias FROM App\Entity\Entry alias
WHERE alias.date = :date AND alias.categoryid = :categoryid
ORDER BY alias.date DESC"
)->setParameters(["date" => "2020-12-06", "categoryid" => 1])
->getResult();
Within relational databases where many-to-many relations occur between tables, you sometimes need to fetch values via a linking table. In turn, the linking table shall contain at least reference/foreign keys pointing to all the primary keys for each table where the many-to-many relation takes place.
If a business rule says that an entry can have many categories while every category can be linked to many entries, we have a many-to-many relation between the entry table and the category table. Moreover, the linking table connecting these two tables must consist of at least one reference key pointing to the primary key in the entry table, and one reference key pointing to the primary key in the category table.
Let us say that you would like to use raw MySQL with Doctrine to fetch all categories related to an entry, which in turn shall be returned as a category list sorted in alphabetic order by category name. Then the query for this can look like the following code snippet:
$entryCategories = $em->createQuery(
"SELECT c FROM App\Entity\Category c
WHERE c.id IN (
SELECT IDENTITY(ec.categoryId) FROM App\Entity\EntryCategories ec
WHERE ec.entryId = :entryid
)
ORDER BY c.name ASC"
)->setParameter("entryid", $entryIdInput)
->getResult();
In order for the above query to work it requires - except that the EntityManagerInterface has been imported and instantiated - also that the concerning entities "Category" and "EntryCategories" are available in the entity layer of the PHP project. Also, these entities need to be imported in the PHP class in which the query is used. Furthermore, the query above presupposes that the entity "Category" has properties named "id" and "name", as well as the linking entity "EntryCategories" has properties named "categoryId" and "entryId".
Annice 2020-11-28 Updates, C#, ASP.NET Core
I have now upgraded the C# script "Blog System 2.0" to version 2.2. The new script "Blog System 2.2" is also availble to download from the script page.
From now on, the script ensures that anonymous users cannot navigate to an entry ID having the status set to "draft", i.e. an unpublished blog entry.
The authentication in this script is mainly handled via a middleware that senses specific names of controllers through every HTTP request for different admin pages. In other words, the auth server ensures via the middleware that controllers rendering admin pages will not display these pages for unauthorized users. Moreover, the code snippet below that handles this control can be found in the script file "AuthServer.cs" in the folder "Middleware":
public async Task InvokeAsync(HttpContext context, [FromServices] IAuthHandler handler)
{
string[] paths = { "/Entry", "/Comment", "/Category", "/User" };
foreach(string path in paths)
{
if (!handler.IsLoggedIn() && context.Request.Path.ToString().StartsWith(path))
{
context.Response.StatusCode = 401; // Unauthorized.
context.Response.Redirect(context.Request.Scheme + "://" + context.Request.Host + "/Home/AccessDenied");
return;
}
}
await _next.Invoke(context);
}
Simultaneously, an auth handler service is called to ensure these controllers are always accessible by a logged in admin. In turn, this auth handler checks whether a session variable is set that has temporarily saved the admin's user ID after a valid login. The control method that is being called by the auth server above and that controls whether this session value exists or not can also be found in the file "AuthHandler.cs" in the script folder "Services":
public bool IsLoggedIn()
{
var scope = _scope.CreateScope();
var context = scope.ServiceProvider.GetService ();
// If user session is set, it means we have a valid login:
if (context.HttpContext.Session.GetString("UserID") != null)
return true;
else
return false;
}
Moreover, the controller handling the public pages in this blog system is called "HomeController". This is also the controller handling every entry detail page, i.e. where you can see an entry in its entirety as well as where visitors can leave comments.
Also, as this controller handles these detail pages regardless if they are drafts or published - while the controller name "Home" is not sensed by the auth server - a check has now been added to this controller to ensure only admins can see draft entries. This was done by adding a condition in the action method "EntryComments", which renders detail pages according to the code snippet below:
[HttpGet]
public IActionResult EntryComments(int? id)
{
// Catch posted feedback:
ViewBag.Success = TempData["Success"];
// Check valid admin login if draft entry:
if (HttpContext.Session.GetString("UserID") == null &&
_db.BsEntries.Where(e =>e.Id == id && e.IsPublished == false).Any())
{
return RedirectToAction(nameof(Index));
}
// ...
The condition in the if statement above is now checking if the admin session is unkown at the same time as the requested entry ID is checked to be a draft. In that case, the visitor will be interpreted as an anonymous user and therefore just redirected to the public start page, i.e. never access the requested entry. Moreover, the added code illustrated above can be found in its full context in the script file "HomeController.cs" in the folder "Controllers".
Annice 2020-11-22 Updates, PHP, Symfony, MySQL
Annice.se is now completely rebuilt, meaning that at the time of writing it is based on the web application language PHP 7.4 with Symfony 5.1.8. This means that all server logic have been moved from my former OutSystems' cloud server to my current web hotel at One.com, which instead of C# runs PHP as a server language. This work has also led to a total database migration from the former SQL Server based DBMS to the currently used MySQL based one with MariaDB.
The reason for me rebuilding the webpage was partly to keep as much of the server logic as centralized as possible, but also to gain greater detail control of all the server code. Since OutSystems is based on Low-Code development - along with the fact that I only had a free instance to use on their server - it also meant limited development possibilities. However, I also have to admit that I simply enjoy developing more when it is done in pure code. :)
Why I chose PHP as the application code instead of, for example, C# has nothing to do with me thinking that C# would be less good in any way. The reason is rather that the web hotel I have been hosting my site at does not support C#. Furthermore, I have been hosting parts or the entire website at the web hotel One.com for nearly 15 years, and I have always been happy with their services, customer service, as well as prices. Thus, I simply do not see a reason for a change, so cred to One.com!
Just like C#, I have always had a good impression of PHP as a development language. In fact, PHP was also the first programming language I got in contact with when I first became interested in web development for real in my early 20s. Moreover, it has also been a language I have been developing in intermittently ever since.
Furthermore, when I started studying information systems and informatics at Linköping University in 2013, Java was the first programming course we studied there. Moreover, that was also when I first got in contact with object-oriented programming, which was a kind of code structure I got a very good impression of, structured as I am. :-] When I later discovered the object-oriented language C# - which in my opinion is a more clean version of Java - it sort of became my new favorite language.
PHP which, however, has never originally been an object-oriented language has still remained strong among pure web development languages over the years. Over time, PHP has also evolved to having a fantastic support for object-orientation as well. And I guess that is where I also get into my choice of Symfony as a framework.
When I started researching a bit regarding a potentially new code approach for Annice.se, the choice was finally between PHP Symfony and PHP Laravel. However, it seems that many people's opinions regarding the two frameworks in comparison are that Symfony still provides the best detail control in the end. Furthermore, I noticed that the basic principle with the Symfony approach seemed to be strikingly similar to the way of working with C# ASP.NET Core. Particularily when it comes to object-orientation with design patterns such as MVC, along with the management of classes, components, dependency injections of different services and such like. I could compare those two frameworks more thoroughly in another blog post, but the conclusion was simply that Symfony turned out to appeal to me best.
All in all, the choice fell on PHP with Symfony. And with the introduction I have given myself so far of the framework in question in conjunction with the rebuilding of Annice.se, I would say it has only aroused further interest!
For the latest version of Annice.se, I have also built this new blog. Moreover, the purpose with the blog is mainly to write about stuff that interest myself, such as web development and digital creation. I have always tended to write down tutorials, code snippets and other good-to-have information in general in my OneNote tool to keep track of things anyway. And instead of just keeping these notes in my personal OneNote, I thought I might as well create my own kind of web encyclopedia in the form of a blog instead. :)
Annice 2020-10-20 Updates, PHP, REST API
An upgraded version of the PHP script "REST API JWT Auth" is now available on the script page as version 1.2.
The script is a small system integration script built in HTML5, CSS3, JavaScript, PHP and MySQL. Moreover, the system integration is based on REST APIs with JSON web tokens for update request authentication.
Other features included are a login ability for an admin to update the admin credentials, as well as a responsive design with form validation.
For version 1.2 a new endpoint has been added to clear the registered user email on the "server 2" app, which is triggered by a logout request sent from the "server 1" app.
Annice 2020-10-18 Updates, C#, ASP.NET Core, Entity Framework Core, T-SQL
I have now upgraded and uploaded a cross-platform version of the former C# script "Blogsystem 1.0", which is now available to download completely free as "BlogSystem 2.0" on the script page.
The script is a database driven blog system based on C# ASP.NET Core and Entity Framework Core. Some features provided are full CRUD functionality for an admin to create and edit blog posts, categories and comments.
Other supported features are a responsive design via Bootstrap, sort and filter function, pagination, client and server side validation, password recovery function via a token link, WYSIWYG HTML entry editor, etc. More information with illlustrations etc. are available via the related README link to this script on the script page.
Annice 2020-09-22 Updates, C#, ASP.NET Core, Entity Framework Core
A new C# script is now available on the script page called "Visitor Statistics 1.0".
This script can be used to register visit data of an application to view information such as visitors' IP addresses, hosts, agents, visit times, referer URLs, etc. Moreover, the script is built in C# ASP.NET Core MVC with Entity Framework Core - database first - using T-SQL with SQL Server as a DBMS.
Other languages used are HTML5, CSS and JavaScript with jQuery. The script also uses Ajax for enhanced performance on form post requests. For more information such as setup guide and different illustrations, see the related README link for this script on the script page.
Annice 2020-08-14 Updates
I have now fixed a bug that made the contact form unreliable since emails were not always sent due to a malfunctioning SMTP server. However, I have now replaced the SMTP server to ensure emails reach my inbox from now on!
Annice 2020-06-23 Updates, C#, ASP.NET Core, Entity Framework Core
A new multi-user role login system based on C# ASP.NET Core Identity and Entity Framework Core is now available on the script page named "IdentityLoginSystem 1.0".
Annice 2020-04-25 Updates, C#, ASP.NET Core, REST API
A new C# ASP.NET Core integration script called "REST API JWT Auth 1.0" is now available on the script page.
Annice 2019-08-27 Updates, Photo
I have now uploaded a couple of photos which are available on the page: Digital photography
Annice 2019-08-25 Updates, C#
A new C# script named "MvcNews 1.0" is now available on the script page.