Tuesday, April 22, 2025
How to render scrollbars
Sunday, March 23, 2025
Window in X11
X11, also known as the X Window System, is a graphical display management system for Unix and Linux operating systems. It allows users to run graphical applications and manage windows on the screen. X11 provides basic functionality for displaying graphics, managing input devices such as the keyboard and mouse, and supporting multiple windows and multiple displays. This system is essential for the operation of many graphical environments on Linux, such as GNOME, KDE, and others.
The trick we're going to perform today involves rendering a ttf font
but without known development frameworks (Gnome, KDE).
So, we are faced with creating an application in the C programming language.
Prerequisites
We need development libraries:
sudo apt-get install build-essential
sudo apt-get install libx11-dev
Andheader lib
stb_truetype.h
Application
//main.c
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define STB_TRUETYPE_IMPLEMENTATION
#include "./stb_truetype.h"
Display *display;
Window window;
XEvent event;
int screen;
GC gc;
int main() {
// Open a connection to the X server
display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
screen = DefaultScreen(display);
// Create a window
window = XCreateSimpleWindow(display, RootWindow(display, screen),
10, 10, 400, 300, 1,
BlackPixel(display, screen),
WhitePixel(display, screen));
XStoreName(display, window, "My X11 Window");
gc = XCreateGC(display, window, 0, NULL);
XSelectInput(display, window, ExposureMask | KeyPressMask | StructureNotifyMask);
XMapWindow(display, window);
// Load the font
unsigned char font_data[1 << 20];
FILE *font_file = fopen("./fantasquesansmono-regular.otf", "rb"); //ovdje učitajte bilo koji .ttf font
if (font_file == NULL) {
fprintf(stderr, "Cannot open font file\n");
exit(1);
}
fread(font_data, 1, sizeof(font_data), font_file);
fclose(font_file);
stbtt_fontinfo font;
if (!stbtt_InitFont(&font, font_data, stbtt_GetFontOffsetForIndex(font_data, 0))) {
fprintf(stderr, "Cannot initialize font\n");
exit(1);
}
float scale = stbtt_ScaleForPixelHeight(&font, 24.0f);
// Event loop
while (1) {
XNextEvent(display, &event);
if (event.type == Expose) {
XClearWindow(display, window);
int x = 10;
int y = 40; // Adjust vertical position for better visibility
const char *text = "Hello, world!";
int text_len = strlen(text);
for (int i = 0; i < text_len; i++) {
int codepoint = text[i];
int w, h;
int x_offset = 0, y_offset = 0;
// Get bitmap box info for proper positioning
if (codepoint == ',' || codepoint == '.' || codepoint == ';' || codepoint == ':') {
// Special handling for punctuation
stbtt_GetCodepointBitmapBox(&font, codepoint, scale, scale,
&x_offset, &y_offset, NULL, NULL);
}
// Get the actual bitmap
unsigned char *bitmap = stbtt_GetCodepointBitmap(&font, 0, scale, codepoint, &w, &h, &x_offset, &y_offset);
if (bitmap != NULL) {
// Create an XImage
XImage *image = XCreateImage(display, DefaultVisual(display, screen),
DefaultDepth(display, screen), ZPixmap, 0, NULL, w, h, 32, 0);
image->data = (char *)malloc(image->bytes_per_line * h);
// Fill the image data
for (int j = 0; j < h; j++) {
for (int k = 0; k < w; k++) {
int index = j * w + k;
unsigned char alpha = bitmap[index];
// Calculate color (black text on white background)
unsigned char r = 255 - alpha;
unsigned char g = 255 - alpha;
unsigned char b = 255 - alpha;
// Pack the color into the image
XPutPixel(image, k, j, (r << 16) | (g << 8) | b);
}
}
// Adjust y position for punctuation
int render_y = y;
if (codepoint == ',' || codepoint == ';') {
// Lower commas and semicolons slightly
render_y += 4;
}
XPutImage(display, window, gc, image, 0, 0, x, render_y + y_offset, w, h);
XDestroyImage(image);
stbtt_FreeBitmap(bitmap, NULL);
}
// Advance to next character position
int advance, lsb;
stbtt_GetCodepointHMetrics(&font, codepoint, &advance, &lsb);
// Adjust kerning between characters
if (i < text_len - 1) {
int kern = stbtt_GetCodepointKernAdvance(&font, codepoint, text[i+1]);
x += (int)((advance + kern) * scale);
} else {
x += (int)(advance * scale);
}
}
}
if (event.type == ConfigureNotify) {
printf("Window resized to %dx%d\n", event.xconfigure.width, event.xconfigure.height);
}
if (event.type == KeyPress) {
// break;
}
}
XFreeGC(display, gc);
XDestroyWindow(display, window);
XCloseDisplay(display);
return 0;
}
Compiling
gcc -o main main.c -lX11 -lm
>
Wednesday, February 19, 2025
OPENGL intro
OpenGL (Open Graphics Library) is a cross-platform, low-level graphics API (Application Programming Interface) used for rendering 2D and 3D vector graphics. It provides a set of functions that allow developers to create graphics applications, such as video games, simulations, and visualizations, by interacting with the graphics hardware. We will use Ubuntu or LinuxMint for this tutorial.
Basically it's API that we use to work with 3D graphic cards.
GLEW
GLEW (OpenGL Extension Wrangler Library). GLEW is a cross-platform library that helps manage OpenGL extensions and core functionality. It simplifies the process of accessing OpenGL features, especially extensions, which are not part of the core OpenGL specification.
Key Features of GLEW
Feature |
Description |
Extension Loading |
GLEW provides a way to dynamically load OpenGL extensions at runtime. This is important because different graphics hardware and drivers may support different sets of OpenGL extensions. It allows you to check whether a specific extension or OpenGL version is supported by the current system.
|
Core OpenGL Functions |
GLEW also provides access to core OpenGL functions, making it easier to write portable OpenGL code.
|
Cross-Platform |
GLEW works on multiple platforms, including Windows, macOS, and Linux.
|
Automatic Initialization |
GLEW automatically initializes itself and queries the available OpenGL extensions when you call glewInit() .
|
GLFW
GLFW (Graphics Library Framework) is a cross-platform library designed to create windows, handle input (keyboard, mouse, joystick), and manage OpenGL contexts. It is commonly used in OpenGL applications to simplify the process of setting up a rendering environment.
Key Features of GLFW
Feature |
Description |
Window and Context Management |
GLFW creates windows and OpenGL contexts, making it easier to set up a rendering environment. It supports multiple monitors and full-screen mode.
|
Input Handling |
GLFW provides functions to handle keyboard, mouse, and joystick input. It supports callbacks for events like key presses, mouse movement, and window resizing.
|
Cross-Platform |
GLFW works on Windows, macOS, and Linux, making it a popular choice for cross-platform OpenGL applications.
|
Integration with OpenGL |
GLFW is designed to work seamlessly with OpenGL, allowing you to focus on rendering rather than platform-specific details.
|
Minimalistic and Lightweight |
GLFW is simple to use and does not impose a heavy framework on your application.
|
Preparing system
We need to prepare our Linux system for development.
Check that your system supports OpenGL and that the necessary drivers are installed:
🚀 glxinfo | grep "OpenGL version"
If yes then let's install following packages:
🚀 sudo apt-get update
🚀 sudo apt-get install build-essential automake autoconf cmake git checkinstall pkg-config g++ libtool
Test program
Now open your favorite editor (use Geany) and save this to "test.c" file:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
int main() {
// Initialize GLFW
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW\n");
return -1;
}
// Set OpenGL version to 3.3 (core profile)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create a windowed mode window and its OpenGL context
GLFWwindow* window = glfwCreateWindow(640, 480, "OpenGL Test", NULL, NULL);
if (!window) {
fprintf(stderr, "Failed to create GLFW window\n");
glfwTerminate();
return -1;
}
// Make the window's context current
glfwMakeContextCurrent(window);
// Initialize GLEW
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
return -1;
}
// Print OpenGL version
printf("OpenGL Version: %s\n", glGetString(GL_VERSION));
// Main loop
while (!glfwWindowShouldClose(window)) {
// Clear the screen to red
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Red background
glClear(GL_COLOR_BUFFER_BIT);
// Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
// Clean up and exit
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
To compile the code open terminal where your code is and type:
🚀 gcc test.c -o test -lglfw -lGLEW -lGL
If everything is compiled, run the binary:
🚀 ./test
Monday, January 6, 2025
How to migrate e-mails to new e-mail provider?
Are you considering a switch to a new email provider but worried about
losing your valuable emails? Or perhaps you're looking to archive your
messages to another account for better organization and accessibility?
Transitioning your email can feel daunting, but it doesn't have to be.
In this blog, we'll explore effective strategies for keeping your
emails intact during a provider change and how to seamlessly archive
your messages to ensure they remain accessible whenever you need them.
Say goodbye to the stress of email management and hello to a more
organized inbox!
First we want IMAP type of e-mail server.
IMAP, or Internet Message Access Protocol, is a standard protocol used
by email clients to retrieve and manage email messages from a mail
server. Unlike the older POP (Post Office Protocol), which downloads
emails to a local device and typically removes them from the server,
IMAP allows users to access their emails directly on the server. This
means that emails can be organized into folders, marked as read or
unread, and managed from multiple devices while keeping everything
synchronized.
Thunderbird
To begin the process, the first step is to add both your old and new
email accounts to Thunderbird. Once both accounts are set up, navigate
to the sidebar where you’ll find your new account listed. Here, you can
create a new folder within the "Received Mails" section and name it
"Old Received Emails."
Next, head over to your old account's "Received Mails" folder.
Right-click on the emails you wish to transfer, and select the "Copy
To" option. From the list that appears, choose the newly created "Old
Received Emails" folder in your new account. This simple action will
copy your old emails into your new mailbox, ensuring that all your
important messages are preserved and easily accessible in one place.
You can turn your gmail account into IMAP and move your archive there.
IMAPSYNC
Imapsync is a command-line tool used for migrating emails between two
IMAP servers. It allows users to synchronize email accounts by copying
messages from one IMAP account to another while preserving the folder
structure, message flags, and other metadata. This tool is particularly
useful for individuals or organizations looking to transfer emails when
changing email providers or consolidating accounts.
There is also GUI version: https://github.com/imap-migrator/imap-sync-gui
Terminal example:
./imapsync \
--host1 test1.lamiral.info \
--user1 test1 \
--password1 "secret1" \
--host2 test2.lamiral.info \
--user2 test2 \
--password2 "secret2"
Thursday, August 29, 2024
Symfony PHP - Intro
Symfony is a powerful and flexible PHP framework designed for building robust web applications. It follows the Model-View-Controller (MVC) architectural pattern, which helps separate the application logic from the user interface, promoting organized and maintainable code. One of Symfony's standout features is its modularity; it is built on a collection of reusable components that can be used independently or together, allowing developers to tailor their applications to specific needs. I will install Symfony 6.4 version and this is Linux 🐧 tutorial!
Why should you use Symfony?
1. Modularity
Symfony is built on a set of reusable components, allowing developers to use only the parts they need. This modularity promotes flexibility and efficiency in application development.
2. Best Practices
Symfony encourages the use of best practices and design patterns, such as MVC (Model-View-Controller), which helps maintain clean and organized code. This makes it easier to manage and scale applications over time.
3. Robust Ecosystem
Symfony has a rich ecosystem of bundles and libraries that extend its functionality. This allows developers to integrate various features, such as security, form handling, and templating, without reinventing the wheel.
4. Community and Support
Symfony has a large and active community, providing extensive documentation, tutorials, and forums for support. This makes it easier for developers to find help and resources.
5. Performance
Symfony is designed for performance, with features like caching and optimized routing. It can handle high-traffic applications efficiently.
6. Long-Term Support (LTS)
Symfony offers long-term support versions, ensuring that critical updates and security patches are provided for an extended period. This is crucial for enterprise applications that require stability.
7. Flexibility
Symfony can be used for a wide range of applications, from small projects to large enterprise solutions. Its flexibility allows developers to adapt it to their specific needs.
Install
🚀 composer create-project symfony/skeleton skeleton
Alright let's enter into "skeleton/public" directory and start PHP server there:
🚀 php -S localhost:9876
And now go to:
http://localhost:9876
Remember: use composer command inside skeleton directory!
Maker bundle
The Symfony Maker Bundle is a development tool that helps Symfony developers quickly generate code and boilerplate for common tasks. It streamlines the process of creating various components of a Symfony application, allowing developers to focus more on the business logic rather than repetitive coding tasks.
🚀 composer require --dev symfony/maker-bundle
For example to create
controller:
🚀 php bin/console make:controller MyController
And file is created: ./src/Controller/MyController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
class MyController extends AbstractController
{
#[Route('/my', name: 'app_my')]
public function index(): JsonResponse
{
return $this->json([
'message' => 'Welcome to your new controller!',
'path' => 'src/Controller/MyController.php',
]);
}
}
And we can use route
http://localhost/test to see controller page:
List of all maker commands:
Command |
Description |
🚀 php bin/console make:controller MyController |
Creates a new controller class and a corresponding template file. |
🚀 php bin/console make:entity MyEntity |
Generates a new Doctrine entity class. |
🚀 php bin/console make:form MyFormType |
Creates a new form type class. |
🚀 php bin/console make:crud MyEntity |
Generates a complete CRUD (Create, Read, Update, Delete) interface for an entity. |
🚀 php bin/console make:migration |
Creates a new migration file based on changes to your Doctrine entities. |
🚀 php bin/console make:seeder MySeeder |
Generates a new database seeder class. |
🚀 php bin/console make:command MyCommand |
Creates a new console command class. |
🚀 php bin/console make:subscriber MySubscriber |
Generates a new event subscriber class. |
🚀 php bin/console make:listener MyListener |
Creates a new event listener class. |
🚀 php bin/console make:twig-extension MyExtension |
Generates a new Twig extension class. |
To add another controller action named "hello":
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpFoundation\Response;
class MyController extends AbstractController
{
#[Route('/my', name: 'app_my')]
public function index(): JsonResponse
{
return $this->json([
'message' => 'Welcome to your new controller!',
'path' => 'src/Controller/MyController.php',
]);
}
#[Route('/my2', name: 'app_my_2')]
public function hello(): Response
{
return new Response("HELLO WORLD!");
}
}
We have added Response class and name our new route "/my2".
Routes
You have seen above that we can use attribute routes in controller methods
#[Route('/my', name: 'app_my')]
If we need complex routing should use
routes.yaml. Read about
YAML format before you edit these type of files.
./config/routes.yaml
Now just add this:
test:
path: /test
controller: App\Controller\MyController::index
methods: ['GET']
And now you can access our new controller from browser:
http://localhost/test
Service
In Symfony, a service is a reusable piece of code that performs a specific task or provides a particular functionality within an application. Services are typically defined as classes that encapsulate business logic, data processing, or any other operations that can be reused across different parts of the application. Sadly we can't use maker to create service.
Basically, here you can put code that doesn't belong in controller.
1. Create the Service Class
You can create a new PHP class for your service in the src/Service
directory. For example, create a file named TemplateService.php
:
// src/Service/TemplateService.php
namespace App\Service;
use InvalidArgumentException;
class TemplateService
{
public function parse($str, $assoc)
{
if (!is_array($assoc))
{
throw new InvalidArgumentException('"assoc" parametar must be associative array');
}
$t = $str;
foreach($assoc as $k => $v)
{
if (is_string($v))
{
$t = str_replace('{{'. $k .'}}', $v, $t);
}
}
$purgedTemplateData = preg_replace('/{{[\s\S]+?}}/', '', $t);
return $purgedTemplateData;
}
};
2. Register the Service
Symfony automatically registers services located in the src/
directory, so you typically don't need to do anything extra if you follow the default conventions. However, if you want to customize the service configuration, you can do so in the config/services.yaml
file.
# config/services.yaml
services:
App\Service\TemplateService: ~
Make sure that you convert all tabs into spaces with your editor when editing YAML file.
3. Use the Service
You can now use your service in controllers or other services by injecting it through the constructor. For example, in a controller:
// src/Controller/MyController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpFoundation\Response;
use App\Service\TemplateService;
class MyController extends AbstractController
{
#[Route('/template', name: 'app_template')]
public function template(TemplateService $templateService): Response
{
//load html template file into $output
// $templateString = file_get_contents(__DIR__ . '/my-template.html');
$output = $templateService->parse("<h1>{{test}}</h1>", ["test" => "Hellooo!"]);
return new Response($output);
}
}
With the TemplateService, you can load an HTML file and pass it as a string into the parse method. The method will search through the loaded string for tokens like {{test}} and replace them with the corresponding values from the associative array you provide.
TWIG
PHP Twig is a popular templating engine for PHP. It is designed to be simple, flexible, and powerful, making it easier to create and manage dynamic web pages and applications. Twig provides a syntax that is easy to read and write, and it allows developers to separate the presentation logic from the application logic. This separation of concerns makes the code more maintainable and easier to update.
Install:
🚀 composer require symfony/twig-bundle
Create dir
templates outside of
/src directory. This is where your twig html files will be. Create file
test.html.twig and add html inside:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{title}}</title>
</head>
<body>
<h1>{{body}}</h1>
</body>
</html>
Now this is controller method:
#[Route('/twig', name: 'app_twig')]
public function twig(Request $request): Response
{
$mainTemplate = $this->renderView('test.html.twig', [
'rootUrl' => $request->getSchemeAndHttpHost(),
'title' => "My title",
'body' => "Hello body!",
]);
return new Response($mainTemplate);
}
The rootUrl (obtained using $request->getSchemeAndHttpHost()) contains the base URL of the application, including the scheme (HTTP/HTTPS) and the host (domain name or IP address). This can be very useful in templates where you need to generate absolute URLs for assets, links, or API endpoints.
For instance, if you need to link to a static asset (like an image or a stylesheet) or create an absolute link in your HTML, you can prepend rootUrl to relative paths.
<img src="{{rootUrl}}/images/logo.png" alt="Logo">
<a href="{{rootUrl}}/some/page">Go to some page</a>
Database
What is an Entity?
In Symfony, an
Entity is a PHP class that maps to a database table. Each property in the entity class corresponds to a column in the table. Entities are the building blocks of your application's data model.
Symfony uses Doctrine ORM (Object-Relational Mapping) to manage entities. Doctrine allows you to define your data structure in PHP code, and it handles the underlying SQL database interactions.
So we will need to install
doctrine:
composer require doctrine/orm
😋 What's the point of ORM when you can just ask AI to create an SQL database for you and provide more secure PDO functions?
Creating an Entity
php bin/console make:entity
This command will guide you through creating a new entity class. You'll be asked to define the class name and its properties. Each property corresponds to a column in the database table.
Remember you are doing this so that you don't need to write SQL code. Entities are in
/src/Entity directory.
For this tutorial, create "title" string [255] and "content" string [5000] chars long.
Mapping Entities to the Database
After defining your entity, Symfony will use Doctrine's
annotations to map the class properties to the database columns. These annotations specify the data type, constraints, and relationships between entities.
Example of an entity class:
namespace App\Entity;
use App\Repository\PostRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PostRepository::class)]
class Post
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(length: 5000)]
private ?string $content = null;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): static
{
$this->content = $content;
return $this;
}
}
In Symfony, a Repository is a class that provides a way to interact with the database for a specific entity. It allows you to encapsulate database queries, offering methods to find, save, or retrieve data from the entity's table, making data access more organized and reusable in your application. PostRepository.php is generated in
/src/Repository:
namespace App\Repository;
use App\Entity\Post;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository
*/
class PostRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Post::class);
}
// /**
// * @return Post[] Returns an array of Post objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('p')
// ->andWhere('p.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('p.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Post
// {
// return $this->createQueryBuilder('p')
// ->andWhere('p.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}
ENV
The
.env file in Symfony is a key component used to manage environment-specific configurations in your Symfony application. It allows you to define environment variables that can be used throughout your application, helping to keep sensitive information, such as database credentials or API keys, out of your source code.
First let's create database in phpMyAdmin:
DROP DATABASE IF EXISTS symfony_test;
CREATE DATABASE symfony_test;
DROP USER IF EXISTS 'symfony_test'@'localhost';
CREATE USER 'symfony_test'@'localhost' IDENTIFIED BY 'symfony_test';
GRANT ALL ON symfony_test.* TO 'symfony_test'@'localhost';
USE symfony_test;
We want to add is mariadb (MYSQL) server to env.
🚀 mariadb --version
mariadb Ver 15.1 Distrib 10.6.18-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
Version number is important. So at bottom of
.env we add:
DATABASE_URL="mysql://symfony_test:symfony_test@127.0.0.1:3306/symfony_test?serverVersion=mariadb-10.6.18"
Make sure you have only one DATABASE_URL uncommented!
Database Migrations
Once your entity is defined, you need to create a corresponding database table. Symfony uses
migrations to handle this. A migration is a PHP file that contains SQL statements to update your database schema.
Install:
🚀 composer require doctrine/doctrine-migrations-bundle
You can generate a migration file using the following command:
🚀 php bin/console make:migration
To execute the migration and apply the changes to your database, run:
🚀 php bin/console doctrine:migrations:migrate
...and it's in our database:
How to add data to database?
Before we do anything, we could generate some fake data for our database. So I'll ask
AI to do it:
i have sql tables "title" 225 long and "content" 255 long, write me sql insert script with data about sea animals. 20 entries.
INSERT INTO post(title, content) VALUES
('Dolphin', 'Dolphins are highly intelligent marine mammals known for their playful behavior and social nature.'),
('Shark', 'Sharks are a group of elasmobranch fish characterized by a cartilaginous skeleton and a powerful sense of smell.'),
('Octopus', 'Octopuses are soft-bodied, eight-limbed mollusks known for their intelligence and ability to camouflage.'),
('Sea Turtle', 'Sea turtles are reptiles that spend most of their lives in the ocean and are known for their long migrations.'),
('Clownfish', 'Clownfish are small, brightly colored fish that live in anemones and are known for their symbiotic relationship with them.'),
('Blue Whale', 'The blue whale is the largest animal known to have ever existed, reaching lengths of up to 100 feet.'),
('Starfish', 'Starfish, or sea stars, are echinoderms that typically have five arms and can regenerate lost limbs.'),
('Jellyfish', 'Jellyfish are gelatinous creatures that drift through the ocean and have tentacles that can sting.'),
('Seahorse', 'Seahorses are unique fish known for their horse-like appearance and the male’s role in pregnancy.'),
('Manta Ray', 'Manta rays are large, gentle creatures that glide through the ocean and are known for their acrobatic jumps.'),
('Anglerfish', 'Anglerfish are deep-sea fish known for their bioluminescent lure used to attract prey in dark waters.'),
('Narwhal', 'Narwhals are medium-sized whales known for their long, spiral tusks, often referred to as the "unicorn of the sea."'),
('Sea Lion', 'Sea lions are marine mammals known for their playful behavior and are often seen basking on rocks.'),
('Coral', 'Coral is a marine invertebrate that forms large colonies and is vital for marine ecosystems as a habitat.'),
('Manatee', 'Manatees, also known as sea cows, are large, gentle herbivorous mammals that inhabit warm coastal waters.'),
('Barracuda', 'Barracudas are large, predatory fish known for their speed and sharp teeth, often found in tropical oceans.'),
('Pufferfish', 'Pufferfish are known for their ability to inflate themselves as a defense mechanism against predators.'),
('Sea Urchin', 'Sea urchins are spiny, globular animals that play a crucial role in marine ecosystems as grazers.'),
('Kraken', 'The kraken is a legendary sea monster said to dwell off the coasts of Norway and Greenland, often depicted as a giant octopus.'),
('Swordfish', 'Swordfish are large, predatory fish known for their elongated, flat bills and are popular in sport fishing.');
Open
phpMyAdmin and paste this code into SQL tab.
Back to our
MyController.php file:
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use App\Service\TemplateService;
// 👇 ADD THIS:
use Doctrine\Persistence\ManagerRegistry;
use App\Entity\Post;
use App\Repository\PostRepository;
class MyController extends AbstractController
{
#[Route('/my', name: 'app_my')]
public function index(): JsonResponse
{
return $this->json([
'message' => 'Welcome to your new controller!',
'path' => 'src/Controller/MyController.php',
]);
}
#[Route('/my2', name: 'app_my_2')]
public function hello(): Response
{
return new Response("HELLO WORLD!");
}
#[Route('/template', name: 'app_template')]
public function template(TemplateService $templateService): Response
{
$output = $templateService->parse("{{test}}
", ["test" => "Hellooo!"]);
return new Response($output);
}
#[Route('/twig', name: 'app_twig')]
public function twig(Request $request): Response
{
$mainTemplate = $this->renderView('test.html.twig', [
'rootUrl' => $request->getSchemeAndHttpHost(),
'title' => "My title",
'body' => "Hello body!",
]);
return new Response($mainTemplate);
}
// 👇 NEW METHOD IS HERE:
#[Route('/add-data', name: 'app_addData')]
public function addData(ManagerRegistry $doctrine): Response
{
$entityManager = $doctrine->getManager();
$post = new Post();
$post->setTitle("Hello");
$post->setContent("Void!");
$entityManager->persist($post);
$entityManager->flush();
return new Response('ok');
}
}
This will add data to database if we go to
/add-data route:
Get data
Get data by id:
#[Route('/get-data/{id}', name: 'app_getDataById')]
public function getDataById(ManagerRegistry $doctrine, int $id): Response
{
$entityManager = $doctrine->getManager();
$postRepository = $entityManager->getRepository(Post::class);
// Find the post by its ID
$post = $postRepository->find($id);
if (!$post) {
return new Response('Post not found', 404);
}
// Prepare the response
$responseContent = 'Title: ' . $post->getTitle() . ', Content: ' . $post->getContent();
return new Response($responseContent);
}
Get data with pagination:
http://localhost:9876/get-all-data?page=1&per_page=10
We will use "page" and "per_page" queryParams to get pagination data.
#[Route('/get-all-data', name: 'app_getAllData')]
public function getAllData(ManagerRegistry $doctrine, Request $request): JsonResponse
{
$entityManager = $doctrine->getManager();
$postRepository = $entityManager->getRepository(Post::class);
// Get query parameters
$page = max(1, $request->query->getInt('page', 1)); // default to 1
$perPage = max(1, $request->query->getInt('per_page', 10)); // default to 10
// Calculate offset
$offset = ($page - 1) * $perPage;
// Get the total count of posts
$totalPosts = $postRepository->count([]);
// Fetch paginated results
$posts = $postRepository->findBy([], null, $perPage, $offset);
// Prepare the data
$data = [];
foreach ($posts as $post) {
$data[] = [
'id' => $post->getId(),
'title' => $post->getTitle(),
'content' => $post->getContent(),
];
}
// Prepare the response with pagination metadata
$response = [
'data' => $data,
'meta' => [
'current_page' => $page,
'per_page' => $perPage,
'total' => $totalPosts,
'total_pages' => ceil($totalPosts / $perPage),
],
];
return new JsonResponse($response);
}
Search in database:
#[Route('/search', name: 'app_search')]
public function search(ManagerRegistry $doctrine, Request $request): JsonResponse
{
$entityManager = $doctrine->getManager();
$postRepository = $entityManager->getRepository(Post::class);
// Get the search query parameter
$query = $request->query->get('q', '');
if (empty($query)) {
return new JsonResponse(['error' => 'Query parameter "q" is required'], 400);
}
// Create a QueryBuilder instance
$qb = $postRepository->createQueryBuilder('p');
// Add conditions to search in both title and content columns
$qb->where(
$qb->expr()->orX(
$qb->expr()->like('p.title', ':query'),
$qb->expr()->like('p.content', ':query')
)
)
->setParameter('query', '%' . $query . '%');
// Execute the query
$posts = $qb->getQuery()->getResult();
// Prepare the data
$data = [];
foreach ($posts as $post) {
$data[] = [
'id' => $post->getId(),
'title' => $post->getTitle(),
'content' => $post->getContent(),
];
}
return new JsonResponse(['data' => $data]);
}
We will again use queryparams to search database:
http://localhost:9876/search?q=whale
User Authentication
To add login and logout functionality we will do following:
Install Security Bundle
Install Symfony Security bundle using Composer:
🚀 composer require symfony/security-bundle
Okay, this is where things get a little messy in Symfony, especially when handling registration and plain passwords. There's nothing in the documentation about registration, and not even AI was able to help me with that.
So I have done what I could with the documentation and with a little help from Mr. AI.
We will create "Dashboard" page that will be not accessible unless you have logged on!
🚀 php bin/console make:controller Dashboard
Create "User" entity
/src/Entity/User.php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255, nullable: true, unique: true)]
private ?string $nickname = null;
#[ORM\Column(length: 320, unique: true)]
private ?string $email = null;
#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $modifiedAt = null;
#[ORM\Column(length: 320)]
private ?string $password = null;
#[ORM\Column]
private ?bool $active = null;
#[ORM\Column(type: 'json')]
private array $roles = [];
public function getId(): ?int
{
return $this->id;
}
public function getNickname(): ?string
{
return $this->nickname;
}
public function setNickname(?string $nickname): static
{
$this->nickname = $nickname;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): static
{
$this->createdAt = $createdAt;
return $this;
}
public function getModifiedAt(): ?\DateTimeImmutable
{
return $this->modifiedAt;
}
public function setModifiedAt(?\DateTimeImmutable $modifiedAt): static
{
$this->modifiedAt = $modifiedAt;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): static
{
$this->password = $password;
return $this;
}
public function isActive(): ?bool
{
return $this->active;
}
public function setActive(bool $active): static
{
$this->active = $active;
return $this;
}
public function getRoles(): array
{
$roles = $this->roles;
// Ensure every user has at least ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
/**
* @see UserInterface
*/
public function getUserIdentifier(): string
{
// Return the email or username as the unique identifier
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function eraseCredentials(): void
{
// If you had any temporary sensitive data, clear it here
// For example, $this->plainPassword = null;
}
}
And
/usr/Repository/UserRepository.php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<User>
*
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
// /**
// * @return User[] Returns an array of User objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('a')
// ->andWhere('a.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('a.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?User
// {
// return $this->createQueryBuilder('a')
// ->andWhere('a.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}
UserController
/src/Controller/UserController.php
namespace App\Controller;
use App\Entity\User;
use App\Form\RegistrationFormType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Bundle\SecurityBundle\Security;
class UserController extends AbstractController
{
#[Route('/user/login', name: 'user_login_get', methods: ['GET'])]
public function loginGet(): Response
{
return $this->render('/user/login.html.twig', [
'controller_name' => 'DashboardController',
]);
}
#[Route('/user/login', name: 'user_login_post', methods: ['POST'])]
public function loginPost(Request $request, Security $security, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher): Response
{
$email = $request->request->get('email');
$password = $request->request->get('password');
$userRepository = $entityManager->getRepository(User::class);
$user = $userRepository->findOneBy(['email' => $email]);
if (!$user) {
return $this->json(['message' => 'User not found!'], Response::HTTP_NOT_FOUND);
}
if (!$passwordHasher->isPasswordValid($user, $password)) {
return $this->json(['message' => 'Password is invalid!'], Response::HTTP_NOT_FOUND);
}
$security->login($user);
return $this->json(['message' => 'User logged on.']);
}
#[Route('/user/register', name: 'user_register_get', methods: ['GET'])]
public function registerGet(): Response
{
return $this->render('/user/register.html.twig', [
'controller_name' => 'DashboardController',
]);
}
#[Route('/user/register', name: 'user_register_post', methods: ['POST'])]
public function registerPost(Request $request, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher): Response
{
$email = $request->request->get('email');
$password = $request->request->get('password');
$userRepository = $entityManager->getRepository(User::class);
$user = $userRepository->findOneBy(['email' => $email]);
if ($user) {
return $this->json(['message' => 'User with this email already exists!'], Response::HTTP_NOT_FOUND);
}
$hashedPassword = $passwordHasher->hashPassword(new User(), $password);
$user = new User();
$user->setEmail($email)
->setPassword($hashedPassword)
->setRoles(['ROLE_USER'])
->setCreatedAt(new \DateTimeImmutable())
->setModifiedAt(new \DateTimeImmutable())
->setActive(1);
$entityManager->persist($user);
$entityManager->flush();
return $this->json(['message' => 'User registrated!']);
}
// The logout is automatically handled by Symfony at route: /user/logout
};
And finally
/config/packages/security.yaml. It's yaml, be careful with spacing and tabs!
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
users_in_memory: { memory: null }
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: users_in_memory
json_login:
check_path: user_login_post
username_path: email
password_path: password
logout:
path: /user/logout
invalidate_session: true
target: /
access_control:
- { path: ^/dashboard, roles: IS_AUTHENTICATED_FULLY }
Run
reset.sh to setup database with new structure.
I will stop here. You have the rest of the code here:
Download Project
Unpack and run:
🚀 composer install
Tuesday, August 27, 2024
How to install Konica Minolta printer on Linux Mint
Go to
Website
Type you printer model and download PPD file.
Copy all ppd files into:
/usr/share/cups/model
In Printers app add printer as ipp://192.168.1.71 and select generic (ppd).
Change printer ppd name, it's easier to remember.
Please note that you should know your printers LAN IP address,
Saturday, August 24, 2024
Install nodejs on LinuxMint (2024)

In this guide, we'll walk you through the process of installing Node.js on an Ubuntu system. Node.js is a powerful JavaScript runtime built on Chrome's V8 JavaScript engine. It's widely used for building server-side applications.
Step 1: Update the Package List
Before installing any new software, it's always a good idea to update the package list on your system. This ensures that you have the latest versions of the software available to install.
sudo apt-get update
In this command:
sudo
: Runs the command with superuser privileges, which is necessary for system-wide changes.
apt-get
: The package handling utility in Ubuntu, used to install, update, and remove software packages.
update
: Updates the list of available packages and their versions, but doesn't install or upgrade any packages yet.
Step 2: Add the NodeSource Repository
Next, we need to add the NodeSource repository to our system. This repository provides the latest versions of Node.js, which might not be available in Ubuntu's default repositories.
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
Here's what this command does:
curl -fsSL
: Downloads the setup script from NodeSource securely.
https://deb.nodesource.com/setup_20.x
: The URL of the setup script that adds the NodeSource repository for Node.js version 20.x.
|
: Pipes the output of the curl command into the next command.
sudo -E bash -
: Executes the setup script with superuser privileges.
Step 3: Install Node.js
Finally, with the NodeSource repository added, we can install Node.js using the package manager.
sudo apt-get install -y nodejs
Breaking down this command:
sudo
: Runs the command with superuser privileges.
apt-get install
: Installs the specified package.
-y
: Automatically answers "yes" to prompts during installation, allowing the process to complete without manual intervention.
nodejs
: The package name for Node.js, which will be installed on your system.
After running this command, Node.js and npm (Node Package Manager) will be installed on your system, and you can start using them to develop and run JavaScript applications.
If you encounter any issues during installation, check the official Node.js documentation or consider using a version manager like nvm (Node Version Manager) to manage multiple Node.js versions on your system.
