diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..f568e03
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,46 @@
+# Git files
+.git
+.gitignore
+.github
+
+# Documentation
+README.md
+CHANGELOG.md
+AUTHORS
+LICENSE
+SECURITY.md
+*.md
+
+# Development files
+vagrant/
+wsl/
+INSTALL/
+.idea
+.vscode
+.DS_Store
+
+# Dependencies (will be installed in container)
+vendor/
+node_modules/
+
+# Data directories (will be mounted as volumes)
+data/
+
+# Build artifacts
+public/css/
+public/js/
+public/img/
+public/flags/
+public/views/
+
+# Logs
+*.log
+npm-debug.log
+
+# Cache
+*.cache
+.docker-initialized
+
+# Environment files (should be configured separately)
+.env
+.env.dev
diff --git a/.env.dev b/.env.dev
new file mode 100644
index 0000000..1981b09
--- /dev/null
+++ b/.env.dev
@@ -0,0 +1,39 @@
+# MariaDB Configuration
+DBHOST=fodb
+DBPORT_HOST=3307
+DBPASSWORD_ADMIN=root
+DBNAME_COMMON=monarc_common
+DBNAME_CLI=monarc_cli
+DBUSER_MONARC=sqlmonarcuser
+DBPASSWORD_MONARC=sqlmonarcuser
+# Set USE_BO_COMMON=1 and DBHOST to the BackOffice DB host to reuse monarc_common.
+USE_BO_COMMON=1
+
+# Shared Docker network name (must exist when using external network)
+MONARC_NETWORK_NAME=monarc-network
+
+# Node.js major version for frontend build tooling
+NODE_MAJOR=16
+
+# Optional: set to 0/false/no to disable Xdebug in the image build
+XDEBUG_ENABLED=1
+XDEBUG_MODE=debug
+XDEBUG_START_WITH_REQUEST=trigger
+# On Linux, set XDEBUG_CLIENT_HOST to your Docker bridge (often 172.17.0.1).
+XDEBUG_CLIENT_HOST=host.docker.internal
+XDEBUG_CLIENT_PORT=9003
+XDEBUG_IDEKEY=IDEKEY
+XDEBUG_DISCOVER_CLIENT_HOST=0
+
+# Stats Service Configuration
+STATS_HOST=0.0.0.0
+STATS_PORT=5005
+STATS_DB_NAME=statsservice
+STATS_DB_USER=sqlmonarcuser
+STATS_DB_PASSWORD=sqlmonarcuser
+# Generate a random secret key for production use:
+# openssl rand -hex 32
+STATS_SECRET_KEY=changeme_generate_random_secret_key_for_production
+# Stats API Key - Leave empty on first run, it will be generated by the stats service
+# After first run, get it from the stats service logs or container
+STATS_API_KEY=
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index 78723fa..4be55b1 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -45,7 +45,7 @@ jobs:
run: composer validate
- name: Install PHP dependencies
- run: composer install --prefer-dist --no-progress --no-suggest --ignore-platform-req=php
+ run: composer install --prefer-dist --no-progress --no-suggest
- name: Create synlinks for MONARC PHP modules
run: |
diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml
index 8f53443..8fe5c4e 100644
--- a/.github/workflows/releases.yml
+++ b/.github/workflows/releases.yml
@@ -34,7 +34,7 @@ jobs:
run: composer validate
- name: Install PHP dependencies
- run: composer install --prefer-dist --no-progress --no-suggest --no-dev --ignore-platform-req=php
+ run: composer install --prefer-dist --no-progress --no-suggest --no-dev
- name: Symlink Monarc modules
run: |
diff --git a/.gitignore b/.gitignore
index 9fb0019..dae9d99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,7 @@ bin/
data/*
!data/fonts
.docker/mariaDb/data/*
+.env
+.docker-initialized
+docker/db_data
+.vscode
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..27e423b
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,115 @@
+FROM ubuntu:22.04
+
+ARG XDEBUG_ENABLED=1
+ARG XDEBUG_MODE=debug
+ARG XDEBUG_START_WITH_REQUEST=trigger
+ARG XDEBUG_CLIENT_HOST=host.docker.internal
+ARG XDEBUG_CLIENT_PORT=9003
+ARG XDEBUG_IDEKEY=IDEKEY
+ARG XDEBUG_DISCOVER_CLIENT_HOST=0
+ARG NODE_MAJOR=16
+
+# Prevent interactive prompts during package installation
+ENV DEBIAN_FRONTEND=noninteractive
+ENV LANGUAGE=en_US.UTF-8
+ENV LANG=en_US.UTF-8
+ENV LC_ALL=en_US.UTF-8
+
+# Install system dependencies
+RUN apt-get update && apt-get upgrade -y && \
+ packages="vim zip unzip git gettext curl gsfonts mariadb-client apache2 php8.1 php8.1-cli \
+ php8.1-common php8.1-mysql php8.1-zip php8.1-gd php8.1-mbstring php8.1-curl php8.1-xml \
+ php8.1-bcmath php8.1-intl php8.1-imagick locales wget ca-certificates gnupg \
+ build-essential python3 pkg-config libcairo2-dev libpango1.0-dev libjpeg-dev \
+ libgif-dev librsvg2-dev libpixman-1-dev"; \
+ if [ "$XDEBUG_ENABLED" = "1" ] || [ "$XDEBUG_ENABLED" = "true" ] || [ "$XDEBUG_ENABLED" = "yes" ]; then \
+ packages="$packages php8.1-xdebug"; \
+ fi; \
+ apt-get install -y $packages && \
+ locale-gen en_US.UTF-8 && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Configure PHP
+RUN sed -i 's/upload_max_filesize = .*/upload_max_filesize = 200M/' /etc/php/8.1/apache2/php.ini && \
+ sed -i 's/post_max_size = .*/post_max_size = 50M/' /etc/php/8.1/apache2/php.ini && \
+ sed -i 's/max_execution_time = .*/max_execution_time = 100/' /etc/php/8.1/apache2/php.ini && \
+ sed -i 's/max_input_time = .*/max_input_time = 223/' /etc/php/8.1/apache2/php.ini && \
+ sed -i 's/memory_limit = .*/memory_limit = 512M/' /etc/php/8.1/apache2/php.ini && \
+ sed -i 's/session\.gc_maxlifetime = .*/session.gc_maxlifetime = 604800/' /etc/php/8.1/apache2/php.ini && \
+ sed -i 's/session\.gc_probability = .*/session.gc_probability = 1/' /etc/php/8.1/apache2/php.ini && \
+ sed -i 's/session\.gc_divisor = .*/session.gc_divisor = 1000/' /etc/php/8.1/apache2/php.ini
+
+# Configure Xdebug for development
+RUN if [ "$XDEBUG_ENABLED" = "1" ] || [ "$XDEBUG_ENABLED" = "true" ] || [ "$XDEBUG_ENABLED" = "yes" ]; then \
+ echo "zend_extension=xdebug.so" > /etc/php/8.1/apache2/conf.d/20-xdebug.ini && \
+ echo "xdebug.mode=${XDEBUG_MODE}" >> /etc/php/8.1/apache2/conf.d/20-xdebug.ini && \
+ echo "xdebug.start_with_request=${XDEBUG_START_WITH_REQUEST}" >> /etc/php/8.1/apache2/conf.d/20-xdebug.ini && \
+ echo "xdebug.client_host=${XDEBUG_CLIENT_HOST}" >> /etc/php/8.1/apache2/conf.d/20-xdebug.ini && \
+ echo "xdebug.client_port=${XDEBUG_CLIENT_PORT}" >> /etc/php/8.1/apache2/conf.d/20-xdebug.ini && \
+ echo "xdebug.discover_client_host=${XDEBUG_DISCOVER_CLIENT_HOST}" >> /etc/php/8.1/apache2/conf.d/20-xdebug.ini && \
+ echo "xdebug.idekey=${XDEBUG_IDEKEY}" >> /etc/php/8.1/apache2/conf.d/20-xdebug.ini; \
+ fi
+
+# Enable Apache modules
+RUN a2enmod rewrite ssl headers
+
+# Set global ServerName to avoid AH00558 warning
+RUN echo "ServerName localhost" > /etc/apache2/conf-available/servername.conf \
+ && a2enconf servername
+
+# Install Composer
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+
+# Install Node.js and npm
+RUN mkdir -p /etc/apt/keyrings && \
+ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
+ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" > /etc/apt/sources.list.d/nodesource.list && \
+ apt-get update && \
+ apt-get install -y nodejs && \
+ npm install -g grunt-cli && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Set working directory
+WORKDIR /var/www/html/monarc
+
+# Configure Apache
+RUN echo '\n\
+ ServerName localhost\n\
+ DocumentRoot /var/www/html/monarc/public\n\
+\n\
+ \n\
+ DirectoryIndex index.php\n\
+ AllowOverride All\n\
+ Require all granted\n\
+ \n\
+\n\
+ \n\
+ Header always set X-Content-Type-Options nosniff\n\
+ Header always set X-XSS-Protection "1; mode=block"\n\
+ Header always set X-Robots-Tag none\n\
+ Header always set X-Frame-Options SAMEORIGIN\n\
+ \n\
+\n\
+ SetEnv APP_ENV development\n\
+ SetEnv APP_DIR /var/www/html/monarc\n\
+' > /etc/apache2/sites-available/000-default.conf
+
+# Allow Apache override to all
+RUN sed -i 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf
+
+# Create necessary directories
+RUN mkdir -p /var/www/html/monarc/data/cache \
+ /var/www/html/monarc/data/LazyServices/Proxy \
+ /var/www/html/monarc/data/DoctrineORMModule/Proxy \
+ /var/www/html/monarc/data/import/files
+
+# Copy entrypoint script
+COPY docker-entrypoint.sh /usr/local/bin/
+RUN chmod +x /usr/local/bin/docker-entrypoint.sh
+
+EXPOSE 80
+
+ENTRYPOINT ["docker-entrypoint.sh"]
+CMD ["apachectl", "-D", "FOREGROUND"]
diff --git a/Dockerfile.stats b/Dockerfile.stats
new file mode 100644
index 0000000..3191564
--- /dev/null
+++ b/Dockerfile.stats
@@ -0,0 +1,38 @@
+FROM ubuntu:22.04
+
+ENV DEBIAN_FRONTEND=noninteractive
+ENV LANGUAGE=en_US.UTF-8
+ENV LANG=en_US.UTF-8
+ENV LC_ALL=en_US.UTF-8
+ENV FLASK_APP=app.py
+ENV STATS_CONFIG=production.py
+
+# Install dependencies
+RUN apt-get update && \
+ apt-get install -y \
+ git \
+ curl \
+ python3 \
+ python3-pip \
+ python3-venv \
+ nodejs \
+ npm \
+ postgresql-client \
+ locales && \
+ locale-gen en_US.UTF-8 && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Install Poetry
+RUN curl -sSL https://install.python-poetry.org | python3 - && \
+ ln -s /root/.local/bin/poetry /usr/local/bin/poetry
+
+WORKDIR /var/www/stats-service
+
+# Copy entrypoint script
+COPY docker-stats-entrypoint.sh /usr/local/bin/
+RUN chmod +x /usr/local/bin/docker-stats-entrypoint.sh
+
+EXPOSE 5005
+
+ENTRYPOINT ["docker-stats-entrypoint.sh"]
diff --git a/INSTALL/INSTALL.docker.md b/INSTALL/INSTALL.docker.md
new file mode 100644
index 0000000..365bcd1
--- /dev/null
+++ b/INSTALL/INSTALL.docker.md
@@ -0,0 +1,188 @@
+# Docker Installation Guide for MONARC FrontOffice
+
+This document provides installation instructions for setting up MONARC FrontOffice using Docker for development purposes.
+
+## Quick Installation
+
+```bash
+# Clone the repository
+git clone https://github.com/monarc-project/MonarcAppFO
+cd MonarcAppFO
+
+# Start with the Makefile (recommended)
+make start
+
+# Or use docker compose directly
+cp .env.dev .env
+docker compose -f docker-compose.dev.yml up -d --build
+```
+
+## What Gets Installed
+
+The Docker setup includes:
+
+1. **MONARC FrontOffice Application**
+ - Ubuntu 22.04 base
+ - PHP 8.1 with all required extensions
+ - Apache web server
+ - Composer for PHP dependencies
+ - Node.js for frontend
+ - All MONARC modules and dependencies
+
+2. **MariaDB 10.11**
+ - Database for MONARC application data
+ - Pre-configured with proper character sets
+
+3. **PostgreSQL 15**
+ - Database for the statistics service
+
+4. **Stats Service**
+ - Python/Flask application
+ - Poetry for dependency management
+ - Integrated with MONARC FrontOffice
+
+5. **MailCatcher**
+ - Email testing interface
+ - SMTP server for development
+
+## First Time Setup
+
+When you start the environment for the first time:
+
+1. Docker images will be built (5-10 minutes)
+2. Dependencies will be installed automatically
+3. Databases will be created and initialized
+4. Frontend repositories will be cloned and built
+5. Initial admin user will be created
+
+## Access URLs
+
+After startup, access the application at:
+
+- **MONARC FrontOffice**: http://localhost:5001
+- **Stats Service**: http://localhost:5005
+- **MailCatcher**: http://localhost:1080
+
+## Default Credentials
+
+- **Username**: admin@admin.localhost
+- **Password**: admin
+
+⚠️ **Security Warning**: Change these credentials before deploying to production!
+
+## System Requirements
+
+- Docker Engine 20.10 or later
+- Docker Compose V2
+- At least 8GB RAM available for Docker
+- At least 20GB free disk space
+- Linux, macOS, or Windows with WSL2
+
+## Configuration
+
+Environment variables are defined in the `.env` file (copied from `.env.dev`):
+
+- Database credentials
+- Service ports
+- Secret keys
+
+You can customize these before starting the environment.
+
+## Troubleshooting
+
+### Port Conflicts
+
+If ports 5001, 5005, 3307, 5432, or 1080 are already in use:
+
+1. Edit `docker-compose.dev.yml`
+2. Change the host port (left side of the port mapping)
+3. Example: Change `"5001:80"` to `"8001:80"`
+
+You can also change the MariaDB host port by setting `DBPORT_HOST` in `.env`.
+
+### Permission Issues
+
+If you encounter permission errors:
+
+```bash
+docker exec -it monarc-fo-app bash
+chown -R www-data:www-data /var/www/html/monarc/data
+chmod -R 775 /var/www/html/monarc/data
+```
+
+### Service Not Starting
+
+Check the logs:
+
+```bash
+make logs
+# or
+docker compose -f docker-compose.dev.yml logs
+```
+
+### Reset Everything
+
+To start completely fresh:
+
+```bash
+make reset
+# or
+docker compose -f docker-compose.dev.yml down -v
+```
+
+## Common Tasks
+
+### View Logs
+```bash
+make logs
+```
+
+### Access Container Shell
+```bash
+make shell
+```
+
+### Access Database
+```bash
+make db
+```
+
+### Stop Services
+```bash
+make stop
+```
+
+### Restart Services
+```bash
+make restart
+```
+
+## Comparison with Other Installation Methods
+
+| Method | Complexity | Time | Best For |
+|--------|-----------|------|----------|
+| Docker | Low | Fast (2-5 min startup) | Development |
+| Vagrant | Medium | Slow (10-15 min startup) | Development |
+| Manual | High | Slow (30+ min) | Production |
+| VM | Low | Medium | Testing |
+
+## Next Steps
+
+After installation:
+
+1. Read the [full Docker documentation](README.docker.md)
+2. Review the [MONARC documentation](https://www.monarc.lu/documentation)
+3. Configure the stats API key (optional)
+4. Start developing!
+
+## Getting Help
+
+- **Documentation**: [README.docker.md](README.docker.md)
+- **MONARC Website**: https://www.monarc.lu
+- **GitHub Issues**: https://github.com/monarc-project/MonarcAppFO/issues
+- **Community**: https://www.monarc.lu/community
+
+## License
+
+This project is licensed under the GNU Affero General Public License version 3.
+See [LICENSE](LICENSE) for details.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..551882a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,104 @@
+.DEFAULT_GOAL := help
+
+SHELL := /bin/bash
+
+ENV ?= dev
+COMPOSE_FILE ?= docker-compose.$(ENV).yml
+COMPOSE := docker compose -f $(COMPOSE_FILE)
+
+GREEN := \033[0;32m
+YELLOW := \033[1;33m
+RED := \033[0;31m
+NC := \033[0m
+
+.PHONY: help check-env start stop restart logs logs-app logs-stats shell shell-stats db reset status stats-key
+
+help:
+ @printf "%b\n" "$(GREEN)MONARC FrontOffice Docker Development Environment Manager$(NC)"
+ @printf "\n%b\n" "Usage: make "
+ @printf "%b\n" "Environment: ENV= (default: dev, uses docker-compose..yml)"
+ @printf "\n%b\n" "Commands:"
+ @printf " %-12s %s\n" "start" "Start all services (builds on first run)"
+ @printf " %-12s %s\n" "stop" "Stop all services"
+ @printf " %-12s %s\n" "restart" "Restart all services"
+ @printf " %-12s %s\n" "logs" "View logs from all services"
+ @printf " %-12s %s\n" "logs-app" "View logs from MONARC application"
+ @printf " %-12s %s\n" "logs-stats" "View logs from stats service"
+ @printf " %-12s %s\n" "shell" "Open a shell in the MONARC container"
+ @printf " %-12s %s\n" "shell-stats" "Open a shell in the stats service container"
+ @printf " %-12s %s\n" "db" "Open MySQL client in the database"
+ @printf " %-12s %s\n" "stats-key" "Show the stats API key"
+ @printf " %-12s %s\n" "reset" "Reset everything (removes all data)"
+ @printf " %-12s %s\n" "status" "Show status of all services"
+
+check-env:
+ @if [ ! -f .env ]; then \
+ printf "%b\n" "$(YELLOW)No .env file found. Creating from .env.dev...$(NC)"; \
+ cp .env.dev .env; \
+ printf "%b\n" "$(GREEN).env file created. You can edit it to customize configuration.$(NC)"; \
+ fi
+
+start: check-env
+ @printf "%b\n" "$(GREEN)Starting MONARC FrontOffice development environment...$(NC)"
+ @$(COMPOSE) up -d --build
+ @printf "%b\n" "$(GREEN)Services started!$(NC)"
+ @printf "%b\n" "MONARC FrontOffice: http://localhost:5001"
+ @printf "%b\n" "Stats Service: http://localhost:5005"
+ @printf "%b\n" "MailCatcher: http://localhost:1080"
+ @printf "\n%b\n" "$(YELLOW)To view logs: make logs ENV=$(ENV)$(NC)"
+
+stop:
+ @printf "%b\n" "$(YELLOW)Stopping all services...$(NC)"
+ @$(COMPOSE) stop
+ @printf "%b\n" "$(GREEN)Services stopped.$(NC)"
+
+restart:
+ @printf "%b\n" "$(YELLOW)Restarting all services...$(NC)"
+ @$(COMPOSE) restart
+ @printf "%b\n" "$(GREEN)Services restarted.$(NC)"
+
+logs:
+ @$(COMPOSE) logs -f
+
+logs-app:
+ @$(COMPOSE) logs -f monarc
+
+logs-stats:
+ @$(COMPOSE) logs -f stats-service
+
+shell:
+ @printf "%b\n" "$(GREEN)Opening shell in MONARC container...$(NC)"
+ @docker exec -it monarc-fo-app bash
+
+shell-stats:
+ @printf "%b\n" "$(GREEN)Opening shell in stats service container...$(NC)"
+ @docker exec -it monarc-fo-stats bash
+
+db:
+ @printf "%b\n" "$(GREEN)Opening MySQL client...$(NC)"
+ @if [ -f .env ]; then \
+ export $$(grep -v '^#' .env | xargs); \
+ fi; \
+ export MYSQL_PWD="$${DBPASSWORD_MONARC:-sqlmonarcuser}"; \
+ docker exec -it monarc-fo-db mysql -u"$${DBUSER_MONARC:-sqlmonarcuser}" "$${DBNAME_COMMON:-monarc_common}"
+
+stats-key:
+ @printf "%b\n" "$(GREEN)Retrieving stats API key...$(NC)"
+ @$(COMPOSE) logs stats-service | grep "Token:" | tail -1
+ @printf "\n%b\n" "$(YELLOW)Note: Update this key in your .env file and restart MONARC service:$(NC)"
+ @printf "%b\n" " 1. Edit .env and set STATS_API_KEY="
+ @printf "%b\n" " 2. Run: docker compose -f \"$(COMPOSE_FILE)\" restart monarc"
+
+reset:
+ @printf "%b\n" "$(RED)WARNING: This will remove all data!$(NC)"; \
+ read -p "Are you sure? (yes/no): " confirm; \
+ if [ "$$confirm" = "yes" ]; then \
+ printf "%b\n" "$(YELLOW)Stopping and removing all containers, volumes, and data...$(NC)"; \
+ $(COMPOSE) down -v; \
+ printf "%b\n" "$(GREEN)Reset complete. Run 'make start' to start fresh.$(NC)"; \
+ else \
+ printf "%b\n" "$(GREEN)Reset cancelled.$(NC)"; \
+ fi
+
+status:
+ @$(COMPOSE) ps
diff --git a/README.docker.md b/README.docker.md
new file mode 100644
index 0000000..aa121e3
--- /dev/null
+++ b/README.docker.md
@@ -0,0 +1,464 @@
+# Docker Development Environment for MONARC FrontOffice
+
+This guide explains how to set up a local development environment for MONARC FrontOffice using Docker.
+
+## Prerequisites
+
+- Docker Engine 20.10 or later
+- Docker Compose V2 (comes with Docker Desktop)
+- At least 8GB of RAM available for Docker
+- At least 20GB of free disk space
+
+## Quick Start
+
+### Option 1: Using the Makefile (Recommended)
+
+1. **Clone the repository** (if you haven't already):
+ ```bash
+ git clone https://github.com/monarc-project/MonarcAppFO
+ cd MonarcAppFO
+ ```
+
+2. **Start the development environment**:
+ ```bash
+ make start
+ ```
+
+ This will automatically:
+ - Create `.env` file from `.env.dev` if it doesn't exist
+ - Build and start all services
+ - Display access URLs
+
+ The first run will take several minutes as it:
+ - Builds the Docker images
+ - Installs all dependencies (PHP, Node.js, Python)
+ - Clones frontend repositories
+ - Initializes databases
+ - Sets up the stats service
+ - Creates the initial admin user
+
+3. **Access the application**:
+ - MONARC FrontOffice: http://localhost:5001
+ - Stats Service: http://localhost:5005
+ - MailCatcher (email testing): http://localhost:1080
+
+4. **Login credentials**:
+ - Username: `admin@admin.localhost`
+ - Password: `admin`
+
+### Option 2: Using Docker Compose Directly
+
+1. **Clone the repository** (if you haven't already):
+ ```bash
+ git clone https://github.com/monarc-project/MonarcAppFO
+ cd MonarcAppFO
+ ```
+
+2. **Copy the environment file**:
+ ```bash
+ cp .env.dev .env
+ ```
+
+3. **Customize environment variables** (optional):
+ Edit `.env` file to change default passwords and configuration:
+ ```bash
+ # Generate a secure secret key for stats service
+ openssl rand -hex 32
+ # Update STATS_SECRET_KEY in .env with the generated value
+ ```
+
+4. **Start the development environment**:
+ ```bash
+ docker compose -f docker-compose.dev.yml up --build
+ ```
+
+5. **Access the application**:
+ - MONARC FrontOffice: http://localhost:5001
+ - Stats Service: http://localhost:5005
+ - MailCatcher (email testing): http://localhost:1080
+
+6. **(Optional) Configure Stats API Key**:
+ The stats service generates an API key on first run. To retrieve it:
+ ```bash
+ # Using Makefile
+ make stats-key
+
+ # Or manually check logs
+ docker compose -f docker-compose.dev.yml logs stats-service | grep "Token:"
+
+ # Or create a new client and get the key
+ docker exec -it monarc-fo-stats poetry run flask client_create --name monarc
+
+ # Update the .env file with the API key and restart the monarc service
+ docker compose -f docker-compose.dev.yml restart monarc
+ ```
+
+7. **Login credentials**:
+ - Username: `admin@admin.localhost`
+ - Password: `admin`
+
+## Services
+
+The development environment includes the following services:
+
+| Service | Description | Port | Container Name |
+|---------|-------------|------|----------------|
+| monarc | Main FrontOffice application (PHP/Apache) | 5001 | monarc-fo-app |
+| db | MariaDB database | 3307 | monarc-fo-db |
+| postgres | PostgreSQL database (for stats) | 5432 | monarc-fo-postgres |
+| stats-service | MONARC Statistics Service | 5005 | monarc-fo-stats |
+| mailcatcher | Email testing tool | 1080 (web), 1025 (SMTP) | monarc-fo-mailcatcher |
+
+## Development Workflow
+
+### Makefile Commands
+
+The Makefile provides convenient commands for managing the development environment.
+Use `ENV=` to select `docker compose..yml` (default: `dev`).
+
+```bash
+make start # Start all services
+make stop # Stop all services
+make restart # Restart all services
+make logs # View logs from all services
+make logs-app # View logs from MONARC application
+make logs-stats # View logs from stats service
+make shell # Open a shell in the MONARC container
+make shell-stats # Open a shell in the stats service container
+make db # Open MySQL client
+make status # Show status of all services
+make stats-key # Show the stats API key
+make reset # Reset everything (removes all data)
+```
+
+### Live Code Editing
+
+The application source code is mounted as a volume, so changes you make on your host machine will be immediately reflected in the container. After making changes:
+
+1. **PHP/Backend changes**: Apache automatically reloads modified files
+2. **Frontend changes**: You may need to rebuild the frontend:
+ ```bash
+ docker exec -it monarc-fo-app bash
+ cd /var/www/html/monarc
+ ./scripts/update-all.sh -d
+ ```
+
+### Accessing the Container
+
+Using Makefile:
+```bash
+make shell # MONARC application
+make shell-stats # Stats service
+```
+
+Or directly with docker:
+```bash
+docker exec -it monarc-fo-app bash # MONARC application
+docker exec -it monarc-fo-stats bash # Stats service
+```
+
+### Database Access
+
+Using Makefile:
+```bash
+make db # Connect to MariaDB
+```
+
+Or directly with docker:
+```bash
+# Connect to MariaDB
+docker exec -it monarc-fo-db mysql -usqlmonarcuser -psqlmonarcuser monarc_common
+
+# Connect to PostgreSQL
+docker exec -it monarc-fo-postgres psql -U sqlmonarcuser -d statsservice
+```
+
+### Viewing Logs
+
+Using Makefile:
+```bash
+make logs # All services
+make logs-app # MONARC application only
+make logs-stats # Stats service only
+```
+
+Or directly with docker compose:
+```bash
+# View logs for all services
+docker compose -f docker-compose.dev.yml logs -f
+
+# View logs for a specific service
+docker compose -f docker-compose.dev.yml logs -f monarc
+docker compose -f docker-compose.dev.yml logs -f stats-service
+```
+
+### Restarting Services
+
+Using Makefile:
+```bash
+make restart # Restart all services
+```
+
+Or directly with docker compose:
+```bash
+# Restart all services
+docker compose -f docker-compose.dev.yml restart
+
+# Restart a specific service
+docker compose -f docker-compose.dev.yml restart monarc
+```
+
+### Stopping the Environment
+
+Using Makefile:
+```bash
+make stop # Stop all services (keeps data)
+make reset # Stop and remove everything including data
+```
+
+Or directly with docker compose:
+```bash
+# Stop all services (keeps data)
+docker compose -f docker-compose.dev.yml stop
+
+# Stop and remove containers (keeps volumes/data)
+docker compose -f docker-compose.dev.yml down
+
+# Stop and remove everything including data
+docker compose -f docker-compose.dev.yml down -v
+```
+
+## Common Tasks
+
+### Using BackOffice monarc_common
+
+To reuse the BackOffice `monarc_common` database:
+
+1. Set `USE_BO_COMMON=1` in `.env`.
+2. Point `DBHOST` to the BackOffice MariaDB host (for example, `monarc-bo-db` on a shared Docker network).
+3. Ensure the BackOffice database already has `monarc_common` and grants for `DBUSER_MONARC`.
+
+### Shared Docker Network
+
+To connect FrontOffice and BackOffice containers, use a shared external network:
+
+1. Create the network once: `docker network create monarc-network`
+2. Set `MONARC_NETWORK_NAME=monarc-network` in both projects' `.env` files.
+3. Ensure the BackOffice compose file also uses the same external network name.
+
+### Resetting the Database
+
+To completely reset the databases:
+```bash
+docker compose -f docker-compose.dev.yml down -v
+docker compose -f docker-compose.dev.yml up --build
+```
+
+### Installing New PHP Dependencies
+
+```bash
+docker exec -it monarc-fo-app bash
+composer require package/name
+```
+
+### Installing New Node Dependencies
+
+```bash
+docker exec -it monarc-fo-app bash
+cd node_modules/ng_client # or ng_anr
+npm install package-name
+```
+
+### Running Database Migrations
+
+```bash
+docker exec -it monarc-fo-app bash
+php ./vendor/robmorgan/phinx/bin/phinx migrate -c ./module/Monarc/FrontOffice/migrations/phinx.php
+```
+
+### Creating Database Seeds
+
+```bash
+docker exec -it monarc-fo-app bash
+php ./vendor/robmorgan/phinx/bin/phinx seed:run -c ./module/Monarc/FrontOffice/migrations/phinx.php
+```
+
+### Rebuilding Frontend
+
+```bash
+docker exec -it monarc-fo-app bash
+cd /var/www/html/monarc
+./scripts/update-all.sh -d
+```
+
+## Debugging
+
+### Xdebug Configuration
+
+Xdebug is enabled by default in the development image. To disable it, set
+`XDEBUG_ENABLED=0` in `.env` and rebuild the image (for example, `make start`).
+You can also tune the connection behavior via `.env`:
+`XDEBUG_START_WITH_REQUEST`, `XDEBUG_CLIENT_HOST`, and `XDEBUG_CLIENT_PORT`.
+
+When enabled, Xdebug is pre-configured. To use it:
+
+1. Configure your IDE to listen on port 9003
+2. Set the IDE key to `IDEKEY`
+3. Start debugging in your IDE
+4. Trigger a request to the application
+
+For PhpStorm:
+- Go to Settings → PHP → Debug
+- Set Xdebug port to 9003
+- Enable "Can accept external connections"
+- Set the path mappings: `/var/www/html/monarc` → your local project path
+
+### Checking Service Health
+
+```bash
+# Check if all services are running
+docker compose -f docker-compose.dev.yml ps
+
+# Check specific service health
+docker compose -f docker-compose.dev.yml ps monarc
+```
+
+## Troubleshooting
+
+### Port Conflicts
+
+If you get port conflicts, you can change the ports in the `docker-compose.dev.yml` file:
+```yaml
+ports:
+ - "5001:80" # Change 5001 to another available port
+```
+
+To change the MariaDB host port without editing the compose file, set
+`DBPORT_HOST` in `.env` (default: `3307`).
+
+### Permission Issues
+
+If you encounter permission issues with mounted volumes:
+```bash
+docker exec -it monarc-fo-app bash
+chown -R www-data:www-data /var/www/html/monarc/data
+chmod -R 775 /var/www/html/monarc/data
+```
+
+### Database Connection Issues
+
+Check if the database is healthy:
+```bash
+docker compose -f docker-compose.dev.yml ps db
+```
+
+If needed, restart the database:
+```bash
+docker compose -f docker-compose.dev.yml restart db
+```
+
+### Stats Service Issues
+
+Check stats service logs:
+```bash
+docker compose -f docker-compose.dev.yml logs stats-service
+```
+
+Restart the stats service:
+```bash
+docker compose -f docker-compose.dev.yml restart stats-service
+```
+
+### Rebuilding from Scratch
+
+If something goes wrong and you want to start fresh:
+```bash
+# Stop everything
+docker compose -f docker-compose.dev.yml down -v
+
+# Remove all related containers, images, and volumes
+docker system prune -a
+
+# Rebuild and start
+docker compose -f docker-compose.dev.yml up --build
+```
+
+## Performance Optimization
+
+For better performance on macOS and Windows:
+
+1. **Use Docker volume mounts for dependencies**: The compose file already uses named volumes for `vendor` and `node_modules` to improve performance.
+
+2. **Allocate more resources**: In Docker Desktop settings, increase:
+ - CPUs: 4 or more
+ - Memory: 8GB or more
+ - Swap: 2GB or more
+
+3. **Enable caching**: The Dockerfile uses apt cache and composer optimizations.
+
+## Comparison with Vagrant
+
+| Feature | Docker | Vagrant |
+|---------|--------|---------|
+| Startup time | Fast (~2-3 min) | Slow (~10-15 min) |
+| Resource usage | Lower | Higher |
+| Isolation | Container-level | VM-level |
+| Portability | Excellent | Good |
+| Live code reload | Yes | Yes |
+| Learning curve | Moderate | Low |
+
+## Environment Variables Reference
+
+All environment variables are defined in the `.env` file:
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `DBHOST` | MariaDB host for MONARC | `db` |
+| `DBPORT_HOST` | MariaDB host port (published) | `3307` |
+| `DBPASSWORD_ADMIN` | MariaDB root password | `root` |
+| `DBNAME_COMMON` | Common database name | `monarc_common` |
+| `DBNAME_CLI` | CLI database name | `monarc_cli` |
+| `DBUSER_MONARC` | Database user | `sqlmonarcuser` |
+| `DBPASSWORD_MONARC` | Database password | `sqlmonarcuser` |
+| `USE_BO_COMMON` | Use BackOffice `monarc_common` (`1/0`, `true/false`, `yes/no`) | `0` |
+| `MONARC_NETWORK_NAME` | Shared Docker network name | `monarc-network` |
+| `NODE_MAJOR` | Node.js major version for frontend tools | `16` |
+| `STATS_HOST` | Stats service host | `0.0.0.0` |
+| `STATS_PORT` | Stats service port | `5005` |
+| `STATS_DB_NAME` | Stats database name | `statsservice` |
+| `STATS_DB_USER` | Stats database user | `sqlmonarcuser` |
+| `STATS_DB_PASSWORD` | Stats database password | `sqlmonarcuser` |
+| `STATS_SECRET_KEY` | Stats service secret key | `changeme_generate_random_secret_key_for_production` |
+| `XDEBUG_ENABLED` | Enable Xdebug in the build (`1/0`, `true/false`, `yes/no`) | `1` |
+| `XDEBUG_MODE` | Xdebug modes (`debug`, `develop`, etc.) | `debug` |
+| `XDEBUG_START_WITH_REQUEST` | Start mode (`trigger` or `yes`) | `trigger` |
+| `XDEBUG_CLIENT_HOST` | Host IDE address | `host.docker.internal` |
+| `XDEBUG_CLIENT_PORT` | IDE port | `9003` |
+| `XDEBUG_IDEKEY` | IDE key | `IDEKEY` |
+| `XDEBUG_DISCOVER_CLIENT_HOST` | Auto-detect client host (`1/0`) | `0` |
+
+## Security Notes
+
+⚠️ **Important**: The default credentials provided are for development only. Never use these in production!
+
+For production deployments:
+1. Change all default passwords
+2. Generate a secure `STATS_SECRET_KEY` using `openssl rand -hex 32`
+3. Use proper SSL/TLS certificates
+4. Follow security best practices
+
+## Additional Resources
+
+- [MONARC Website](https://www.monarc.lu)
+- [MONARC Documentation](https://www.monarc.lu/documentation)
+- [GitHub Repository](https://github.com/monarc-project/MonarcAppFO)
+- [MonarcAppBO (BackOffice)](https://github.com/monarc-project/MonarcAppBO)
+
+## Getting Help
+
+If you encounter issues:
+
+1. Check the [troubleshooting section](#troubleshooting)
+2. Review the logs: `docker compose -f docker-compose.dev.yml logs`
+3. Open an issue on [GitHub](https://github.com/monarc-project/MonarcAppFO/issues)
+4. Join the community discussions on [GitHub](https://github.com/monarc-project/MonarcAppFO/discussions)
diff --git a/README.md b/README.md
index 21a8a85..4e35f9e 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,22 @@ For installation instructions see
You can also use the provided [Virtual Machine](https://vm.monarc.lu).
+Development Environment
+-----------------------
+
+For local development, you can use either:
+
+- **Docker** (recommended): See [README.docker.md](README.docker.md) for instructions
+ ```bash
+ make start
+ ```
+
+- **Vagrant**: See [vagrant/README.rst](vagrant/README.rst) for instructions
+ ```bash
+ cd vagrant && vagrant up
+ ```
+
+
Contributing
------------
diff --git a/composer.json b/composer.json
index ee05ad4..658b756 100644
--- a/composer.json
+++ b/composer.json
@@ -72,6 +72,9 @@
},
"config": {
"bin-dir": "bin/",
+ "platform": {
+ "php": "8.1.0"
+ },
"allow-plugins": {
"laminas/laminas-dependency-plugin": true
}
diff --git a/composer.lock b/composer.lock
index 4409834..c988834 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1b3a6cbf1c4dfa360fc41fdcae59b1fb",
+ "content-hash": "1470ca72272464dff4ea4ab7cd06c8d7",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -62,25 +62,25 @@
},
{
"name": "brick/math",
- "version": "0.14.1",
+ "version": "0.13.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
- "reference": "f05858549e5f9d7bb45875a75583240a38a281d0"
+ "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0",
- "reference": "f05858549e5f9d7bb45875a75583240a38a281d0",
+ "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
+ "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
"shasum": ""
},
"require": {
- "php": "^8.2"
+ "php": "^8.1"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
- "phpstan/phpstan": "2.1.22",
- "phpunit/phpunit": "^11.5"
+ "phpunit/phpunit": "^10.1",
+ "vimeo/psalm": "6.8.8"
},
"type": "library",
"autoload": {
@@ -110,7 +110,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
- "source": "https://github.com/brick/math/tree/0.14.1"
+ "source": "https://github.com/brick/math/tree/0.13.1"
},
"funding": [
{
@@ -118,7 +118,7 @@
"type": "github"
}
],
- "time": "2025-11-24T14:40:29+00:00"
+ "time": "2025-03-29T13:50:30+00:00"
},
{
"name": "brick/varexporter",
@@ -1935,6 +1935,88 @@
},
"time": "2020-11-24T22:02:12+00:00"
},
+ {
+ "name": "friendsofphp/proxy-manager-lts",
+ "version": "v1.0.19",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FriendsOfPHP/proxy-manager-lts.git",
+ "reference": "c20299aa9f48a622052964a75c5a4cef017398b2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FriendsOfPHP/proxy-manager-lts/zipball/c20299aa9f48a622052964a75c5a4cef017398b2",
+ "reference": "c20299aa9f48a622052964a75c5a4cef017398b2",
+ "shasum": ""
+ },
+ "require": {
+ "laminas/laminas-code": "~3.4.1|^4.0",
+ "php": ">=7.1",
+ "symfony/filesystem": "^4.4.17|^5.0|^6.0|^7.0|^8.0"
+ },
+ "conflict": {
+ "laminas/laminas-stdlib": "<3.2.1",
+ "zendframework/zend-stdlib": "<3.2.1"
+ },
+ "replace": {
+ "ocramius/proxy-manager": "^2.1"
+ },
+ "require-dev": {
+ "ext-phar": "*",
+ "symfony/phpunit-bridge": "^5.4|^6.0|^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/Ocramius/ProxyManager",
+ "name": "ocramius/proxy-manager"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "ProxyManager\\": "src/ProxyManager"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ }
+ ],
+ "description": "Adding support for a wider range of PHP versions to ocramius/proxy-manager",
+ "homepage": "https://github.com/FriendsOfPHP/proxy-manager-lts",
+ "keywords": [
+ "aop",
+ "lazy loading",
+ "proxy",
+ "proxy pattern",
+ "service proxies"
+ ],
+ "support": {
+ "issues": "https://github.com/FriendsOfPHP/proxy-manager-lts/issues",
+ "source": "https://github.com/FriendsOfPHP/proxy-manager-lts/tree/v1.0.19"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Ocramius",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-10-28T10:28:17+00:00"
+ },
{
"name": "guzzlehttp/guzzle",
"version": "6.5.8",
@@ -2236,37 +2318,38 @@
},
{
"name": "laminas/laminas-authentication",
- "version": "2.19.0",
+ "version": "2.18.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-authentication.git",
- "reference": "717b7a26e55f65a7c97325e49fa0b6fa2ea4c79a"
+ "reference": "c1da3ec75bd4d6e3c63cf3a89f0f1a59a81a82bd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-authentication/zipball/717b7a26e55f65a7c97325e49fa0b6fa2ea4c79a",
- "reference": "717b7a26e55f65a7c97325e49fa0b6fa2ea4c79a",
+ "url": "https://api.github.com/repos/laminas/laminas-authentication/zipball/c1da3ec75bd4d6e3c63cf3a89f0f1a59a81a82bd",
+ "reference": "c1da3ec75bd4d6e3c63cf3a89f0f1a59a81a82bd",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"laminas/laminas-stdlib": "^3.19.0",
- "php": "~8.2.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-authentication": "*"
},
"require-dev": {
- "laminas/laminas-coding-standard": "~3.1.0",
+ "laminas/laminas-coding-standard": "~2.5.0",
"laminas/laminas-db": "^2.20.0",
"laminas/laminas-http": "^2.19.0",
"laminas/laminas-ldap": "^2.18.1",
"laminas/laminas-session": "^2.21.0",
"laminas/laminas-uri": "^2.12.0",
"laminas/laminas-validator": "^2.64.1",
- "phpunit/phpunit": "^11.5.42",
- "psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.13.1"
+ "phpunit/phpunit": "^9.6.20",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "squizlabs/php_codesniffer": "^3.10.2",
+ "vimeo/psalm": "^5.26.0"
},
"suggest": {
"laminas/laminas-db": "Laminas\\Db component",
@@ -2306,7 +2389,7 @@
"type": "community_bridge"
}
],
- "time": "2025-10-17T15:08:49+00:00"
+ "time": "2024-10-21T10:45:35+00:00"
},
{
"name": "laminas/laminas-cache",
@@ -2411,25 +2494,25 @@
},
{
"name": "laminas/laminas-captcha",
- "version": "2.19.0",
+ "version": "2.18.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-captcha.git",
- "reference": "768d2152f9687c27b5dfdb2322bf6001d48cda89"
+ "reference": "4c0965b31ec310ec95c72acd76a016b5030182fe"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/768d2152f9687c27b5dfdb2322bf6001d48cda89",
- "reference": "768d2152f9687c27b5dfdb2322bf6001d48cda89",
+ "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/4c0965b31ec310ec95c72acd76a016b5030182fe",
+ "reference": "4c0965b31ec310ec95c72acd76a016b5030182fe",
"shasum": ""
},
"require": {
"laminas/laminas-recaptcha": "^3.4.0",
- "laminas/laminas-session": "^2.25",
+ "laminas/laminas-session": "^2.12",
"laminas/laminas-stdlib": "^3.10.1",
- "laminas/laminas-text": "^2.12.1",
+ "laminas/laminas-text": "^2.9.0",
"laminas/laminas-validator": "^2.19.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-captcha": "*"
@@ -2439,7 +2522,7 @@
"laminas/laminas-coding-standard": "~2.5.0",
"phpunit/phpunit": "^9.5.26",
"psalm/plugin-phpunit": "^0.19.0",
- "vimeo/psalm": "^6.13"
+ "vimeo/psalm": "^5.1"
},
"suggest": {
"laminas/laminas-i18n-resources": "Translations of captcha messages"
@@ -2474,31 +2557,31 @@
"type": "community_bridge"
}
],
- "time": "2025-10-29T17:20:39+00:00"
+ "time": "2025-01-06T20:04:41+00:00"
},
{
"name": "laminas/laminas-code",
- "version": "4.17.0",
+ "version": "4.16.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-code.git",
- "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd"
+ "reference": "1793e78dad4108b594084d05d1fb818b85b110af"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-code/zipball/40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd",
- "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd",
+ "url": "https://api.github.com/repos/laminas/laminas-code/zipball/1793e78dad4108b594084d05d1fb818b85b110af",
+ "reference": "1793e78dad4108b594084d05d1fb818b85b110af",
"shasum": ""
},
"require": {
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"doctrine/annotations": "^2.0.1",
"ext-phar": "*",
"laminas/laminas-coding-standard": "^3.0.0",
"laminas/laminas-stdlib": "^3.18.0",
- "phpunit/phpunit": "^10.5.58",
+ "phpunit/phpunit": "^10.5.37",
"psalm/plugin-phpunit": "^0.19.0",
"vimeo/psalm": "^5.15.0"
},
@@ -2537,7 +2620,7 @@
"type": "community_bridge"
}
],
- "time": "2025-11-01T09:38:14+00:00"
+ "time": "2024-11-20T13:15:13+00:00"
},
{
"name": "laminas/laminas-config",
@@ -2741,20 +2824,20 @@
},
{
"name": "laminas/laminas-diactoros",
- "version": "3.8.0",
+ "version": "3.7.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-diactoros.git",
- "reference": "60c182916b2749480895601649563970f3f12ec4"
+ "reference": "b6a3b5bebb1a124f6e4ae22f3571ac83dee4b07d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/60c182916b2749480895601649563970f3f12ec4",
- "reference": "60c182916b2749480895601649563970f3f12ec4",
+ "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/b6a3b5bebb1a124f6e4ae22f3571ac83dee4b07d",
+ "reference": "b6a3b5bebb1a124f6e4ae22f3571ac83dee4b07d",
"shasum": ""
},
"require": {
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"psr/http-factory": "^1.1",
"psr/http-message": "^1.1 || ^2.0"
},
@@ -2825,36 +2908,36 @@
"type": "community_bridge"
}
],
- "time": "2025-10-12T15:31:36+00:00"
+ "time": "2025-10-11T18:30:50+00:00"
},
{
"name": "laminas/laminas-escaper",
- "version": "2.18.0",
+ "version": "2.17.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-escaper.git",
- "reference": "06f211dfffff18d91844c1f55250d5d13c007e18"
+ "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/06f211dfffff18d91844c1f55250d5d13c007e18",
- "reference": "06f211dfffff18d91844c1f55250d5d13c007e18",
+ "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba",
+ "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-mbstring": "*",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-escaper": "*"
},
"require-dev": {
- "infection/infection": "^0.31.0",
- "laminas/laminas-coding-standard": "~3.1.0",
- "phpunit/phpunit": "^11.5.42",
- "psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.13.1"
+ "infection/infection": "^0.29.8",
+ "laminas/laminas-coding-standard": "~3.0.1",
+ "phpunit/phpunit": "^10.5.45",
+ "psalm/plugin-phpunit": "^0.19.2",
+ "vimeo/psalm": "^6.6.2"
},
"type": "library",
"autoload": {
@@ -2886,40 +2969,37 @@
"type": "community_bridge"
}
],
- "time": "2025-10-14T18:31:13+00:00"
+ "time": "2025-05-06T19:29:36+00:00"
},
{
"name": "laminas/laminas-eventmanager",
- "version": "3.15.0",
+ "version": "3.14.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-eventmanager.git",
- "reference": "90b4bd33264629af8e39caf5aa83473ac03aa04c"
+ "reference": "1837cafaaaee74437f6d8ec9ff7da03e6f81d809"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/90b4bd33264629af8e39caf5aa83473ac03aa04c",
- "reference": "90b4bd33264629af8e39caf5aa83473ac03aa04c",
+ "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/1837cafaaaee74437f6d8ec9ff7da03e6f81d809",
+ "reference": "1837cafaaaee74437f6d8ec9ff7da03e6f81d809",
"shasum": ""
},
"require": {
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"container-interop/container-interop": "<1.2",
"zendframework/zend-eventmanager": "*"
},
"require-dev": {
- "amphp/dns": "^2.2",
- "amphp/socket": "^2.3.1",
- "laminas/laminas-coding-standard": "~3.1.0",
+ "laminas/laminas-coding-standard": "~3.0.0",
"laminas/laminas-stdlib": "^3.20",
"phpbench/phpbench": "^1.3.1",
- "phpunit/phpunit": "^10.5.58",
+ "phpunit/phpunit": "^10.5.38",
"psalm/plugin-phpunit": "^0.19.0",
"psr/container": "^1.1.2 || ^2.0.2",
- "sebastian/recursion-context": "^5.0.1",
- "vimeo/psalm": "^6.13"
+ "vimeo/psalm": "^5.26.1"
},
"suggest": {
"laminas/laminas-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature",
@@ -2957,39 +3037,39 @@
"type": "community_bridge"
}
],
- "time": "2025-10-31T10:29:01+00:00"
+ "time": "2024-11-21T11:31:22+00:00"
},
{
"name": "laminas/laminas-filter",
- "version": "2.42.0",
+ "version": "2.41.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-filter.git",
- "reference": "985d27bd42daf51b415ce1ee889e0978cc1e59ed"
+ "reference": "eaa00111231bf6669826ae84d3abe85b94477585"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/985d27bd42daf51b415ce1ee889e0978cc1e59ed",
- "reference": "985d27bd42daf51b415ce1ee889e0978cc1e59ed",
+ "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/eaa00111231bf6669826ae84d3abe85b94477585",
+ "reference": "eaa00111231bf6669826ae84d3abe85b94477585",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"laminas/laminas-servicemanager": "^3.21.0",
"laminas/laminas-stdlib": "^3.19.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"laminas/laminas-validator": "<2.10.1",
"zendframework/zend-filter": "*"
},
"require-dev": {
- "laminas/laminas-coding-standard": "^3.1",
+ "laminas/laminas-coding-standard": "~3.0",
"laminas/laminas-crypt": "^3.12",
- "laminas/laminas-i18n": "^2.30.0",
- "laminas/laminas-uri": "^2.13",
- "pear/archive_tar": "^1.6.0",
- "phpunit/phpunit": "^10.5.58",
+ "laminas/laminas-i18n": "^2.28.1",
+ "laminas/laminas-uri": "^2.12",
+ "pear/archive_tar": "^1.5.0",
+ "phpunit/phpunit": "^10.5.36",
"psalm/plugin-phpunit": "^0.19.0",
"psr/http-factory": "^1.1.0",
"vimeo/psalm": "^5.26.1"
@@ -3036,33 +3116,27 @@
"type": "community_bridge"
}
],
- "time": "2025-10-13T15:44:52+00:00"
+ "time": "2025-05-05T02:02:31+00:00"
},
{
"name": "laminas/laminas-form",
- "version": "3.24.0",
+ "version": "3.21.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-form.git",
- "reference": "f8d04284db03a0cc6cc712e565c2b012179ec196"
+ "reference": "653c869d10c361027ae6c660c991ec3e3f38ed65"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-form/zipball/f8d04284db03a0cc6cc712e565c2b012179ec196",
- "reference": "f8d04284db03a0cc6cc712e565c2b012179ec196",
+ "url": "https://api.github.com/repos/laminas/laminas-form/zipball/653c869d10c361027ae6c660c991ec3e3f38ed65",
+ "reference": "653c869d10c361027ae6c660c991ec3e3f38ed65",
"shasum": ""
},
"require": {
- "ext-mbstring": "*",
- "laminas/laminas-escaper": "^2",
- "laminas/laminas-filter": "^2",
- "laminas/laminas-hydrator": "^4.16.0",
- "laminas/laminas-inputfilter": "^2.32.0",
- "laminas/laminas-servicemanager": "^3.22.1",
- "laminas/laminas-stdlib": "^3.20.0",
- "laminas/laminas-validator": "^2",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
- "psr/container": "^1.1.2"
+ "laminas/laminas-hydrator": "^4.13.0",
+ "laminas/laminas-inputfilter": "^2.24.0",
+ "laminas/laminas-stdlib": "^3.16.1",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"doctrine/annotations": "<1.14.0",
@@ -3074,26 +3148,30 @@
"laminas/laminas-view": "<2.27.0"
},
"require-dev": {
- "doctrine/annotations": "^1.14.3 || ^2.0.2",
- "ext-gd": "*",
+ "doctrine/annotations": "^1.14.3 || ^2.0.1",
"ext-intl": "*",
- "laminas/laminas-captcha": "^2.19",
- "laminas/laminas-coding-standard": "^3.1.0",
- "laminas/laminas-eventmanager": "^3.15.0",
- "laminas/laminas-i18n": "^2.31.0",
- "laminas/laminas-modulemanager": "^2.19.0",
- "laminas/laminas-recaptcha": "^3.8",
- "laminas/laminas-session": "^2.25.1",
- "laminas/laminas-text": "^2.12.1",
- "laminas/laminas-view": "^2.43",
- "phpunit/phpunit": "^11.5.43",
- "psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.13.1"
+ "laminas/laminas-captcha": "^2.17",
+ "laminas/laminas-coding-standard": "^2.5",
+ "laminas/laminas-db": "^2.20",
+ "laminas/laminas-escaper": "^2.13",
+ "laminas/laminas-eventmanager": "^3.13.1",
+ "laminas/laminas-filter": "^2.36",
+ "laminas/laminas-i18n": "^2.28.0",
+ "laminas/laminas-modulemanager": "^2.16.0",
+ "laminas/laminas-recaptcha": "^3.7",
+ "laminas/laminas-servicemanager": "^3.22.1",
+ "laminas/laminas-session": "^2.21",
+ "laminas/laminas-text": "^2.11.0",
+ "laminas/laminas-validator": "^2.64.1",
+ "laminas/laminas-view": "^2.35",
+ "phpunit/phpunit": "^10.5.29",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "vimeo/psalm": "^5.25"
},
"suggest": {
"doctrine/annotations": "^1.14, required to use laminas-form annotations support",
"laminas/laminas-captcha": "^2.16, required for using CAPTCHA form elements",
- "laminas/laminas-eventmanager": "^3.10, required for laminas-form annotations support",
+ "laminas/laminas-eventmanager": "^3.10, reuired for laminas-form annotations support",
"laminas/laminas-i18n": "^2.21, required when using laminas-form view helpers",
"laminas/laminas-recaptcha": "^3.6, in order to use the ReCaptcha form element",
"laminas/laminas-servicemanager": "^3.20, required to use the form factories or provide services",
@@ -3135,28 +3213,28 @@
"type": "community_bridge"
}
],
- "time": "2025-12-15T13:11:28+00:00"
+ "time": "2024-10-09T08:28:30+00:00"
},
{
"name": "laminas/laminas-http",
- "version": "2.23.0",
+ "version": "2.22.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-http.git",
- "reference": "9462fc84330d25b23383823831380abb33907fdd"
+ "reference": "5052177fb8176e00b0d4b89108648f557be072b7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-http/zipball/9462fc84330d25b23383823831380abb33907fdd",
- "reference": "9462fc84330d25b23383823831380abb33907fdd",
+ "url": "https://api.github.com/repos/laminas/laminas-http/zipball/5052177fb8176e00b0d4b89108648f557be072b7",
+ "reference": "5052177fb8176e00b0d4b89108648f557be072b7",
"shasum": ""
},
"require": {
"laminas/laminas-loader": "^2.10",
"laminas/laminas-stdlib": "^3.6",
- "laminas/laminas-uri": "^2.14",
+ "laminas/laminas-uri": "^2.11",
"laminas/laminas-validator": "^2.15 || ^3.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-http": "*"
@@ -3200,25 +3278,25 @@
"type": "community_bridge"
}
],
- "time": "2025-12-05T11:02:08+00:00"
+ "time": "2025-05-06T08:24:40+00:00"
},
{
"name": "laminas/laminas-hydrator",
- "version": "4.17.0",
+ "version": "4.16.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-hydrator.git",
- "reference": "626c5e446fdfa27865dfe3cd29123108408c2555"
+ "reference": "a162bd571924968d67ef1f43aed044b8f9c108ef"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/626c5e446fdfa27865dfe3cd29123108408c2555",
- "reference": "626c5e446fdfa27865dfe3cd29123108408c2555",
+ "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/a162bd571924968d67ef1f43aed044b8f9c108ef",
+ "reference": "a162bd571924968d67ef1f43aed044b8f9c108ef",
"shasum": ""
},
"require": {
"laminas/laminas-stdlib": "^3.20",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"webmozart/assert": "^1.11"
},
"conflict": {
@@ -3230,11 +3308,11 @@
"laminas/laminas-eventmanager": "^3.13.1",
"laminas/laminas-modulemanager": "^2.16.0",
"laminas/laminas-serializer": "^2.17.0",
- "laminas/laminas-servicemanager": "^3.24.0",
+ "laminas/laminas-servicemanager": "^3.23.0",
"phpbench/phpbench": "^1.3.1",
- "phpunit/phpunit": "^11.5.42",
+ "phpunit/phpunit": "^10.5.38",
"psalm/plugin-phpunit": "^0.19.0",
- "vimeo/psalm": "^6.13.1"
+ "vimeo/psalm": "^5.26.1"
},
"suggest": {
"laminas/laminas-eventmanager": "^3.13, to support aggregate hydrator usage",
@@ -3277,31 +3355,28 @@
"type": "community_bridge"
}
],
- "time": "2025-11-06T11:05:29+00:00"
+ "time": "2024-11-13T14:04:02+00:00"
},
{
"name": "laminas/laminas-i18n",
- "version": "2.32.1",
+ "version": "2.30.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-i18n.git",
- "reference": "8e71b40318f0df6253329e837188e1d77cf83aea"
+ "reference": "397907ee061e147939364df9d6c485ac1e0fed87"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/8e71b40318f0df6253329e837188e1d77cf83aea",
- "reference": "8e71b40318f0df6253329e837188e1d77cf83aea",
+ "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/397907ee061e147939364df9d6c485ac1e0fed87",
+ "reference": "397907ee061e147939364df9d6c485ac1e0fed87",
"shasum": ""
},
"require": {
- "ext-ctype": "*",
"ext-intl": "*",
- "laminas/laminas-escaper": "^2.0",
"laminas/laminas-servicemanager": "^3.21.0",
"laminas/laminas-stdlib": "^3.0",
"laminas/laminas-translator": "^1.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
- "psr/container": "^1.0.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"laminas/laminas-view": "<2.20.0",
@@ -3311,15 +3386,15 @@
"laminas/laminas-cache": "^3.13.0",
"laminas/laminas-cache-storage-adapter-memory": "^2.4.0",
"laminas/laminas-cache-storage-deprecated-factory": "^1.3",
- "laminas/laminas-coding-standard": "^3.1",
+ "laminas/laminas-coding-standard": "~2.5.0",
"laminas/laminas-config": "^3.10.1",
- "laminas/laminas-eventmanager": "^3.15.0",
- "laminas/laminas-filter": "^2.42",
- "laminas/laminas-validator": "^2.65.0",
- "laminas/laminas-view": "^2.44",
- "phpunit/phpunit": "^11.5.46",
+ "laminas/laminas-eventmanager": "^3.14.0",
+ "laminas/laminas-filter": "^2.40",
+ "laminas/laminas-validator": "^2.64.2",
+ "laminas/laminas-view": "^2.36",
+ "phpunit/phpunit": "^10.5.45",
"psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.14.2"
+ "vimeo/psalm": "^6.10.0"
},
"suggest": {
"laminas/laminas-cache": "You should install this package to cache the translations",
@@ -3366,33 +3441,33 @@
"type": "community_bridge"
}
],
- "time": "2025-12-15T14:23:40+00:00"
+ "time": "2025-04-15T09:07:02+00:00"
},
{
"name": "laminas/laminas-i18n-resources",
- "version": "2.13.0",
+ "version": "2.12.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-i18n-resources.git",
- "reference": "6709bdccd7d884f9c68fd2d193ad428f1d1ff0f0"
+ "reference": "4c6148b97fea3e5f6857053fb07b8794f6ff9245"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-i18n-resources/zipball/6709bdccd7d884f9c68fd2d193ad428f1d1ff0f0",
- "reference": "6709bdccd7d884f9c68fd2d193ad428f1d1ff0f0",
+ "url": "https://api.github.com/repos/laminas/laminas-i18n-resources/zipball/4c6148b97fea3e5f6857053fb07b8794f6ff9245",
+ "reference": "4c6148b97fea3e5f6857053fb07b8794f6ff9245",
"shasum": ""
},
"require": {
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-i18n-resources": "*"
},
"require-dev": {
"laminas/laminas-coding-standard": "~3.1.0",
- "phpunit/phpunit": "^11.5.42",
- "psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.13.1"
+ "phpunit/phpunit": "^10.5.38",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "vimeo/psalm": "^5.26.1"
},
"type": "library",
"autoload": {
@@ -3425,20 +3500,20 @@
"type": "community_bridge"
}
],
- "time": "2025-10-15T07:40:55+00:00"
+ "time": "2025-08-27T12:54:13+00:00"
},
{
"name": "laminas/laminas-inputfilter",
- "version": "2.34.0",
+ "version": "2.33.1",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-inputfilter.git",
- "reference": "5589fcfa366de04f34a5634b43c6c66e69bd2fe8"
+ "reference": "f53f4db544a22bb608d11c213bec0740266b34e9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/5589fcfa366de04f34a5634b43c6c66e69bd2fe8",
- "reference": "5589fcfa366de04f34a5634b43c6c66e69bd2fe8",
+ "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/f53f4db544a22bb608d11c213bec0740266b34e9",
+ "reference": "f53f4db544a22bb608d11c213bec0740266b34e9",
"shasum": ""
},
"require": {
@@ -3446,7 +3521,7 @@
"laminas/laminas-servicemanager": "^3.21.0",
"laminas/laminas-stdlib": "^3.19",
"laminas/laminas-validator": "^2.60.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"psr/container": "^1.1 || ^2.0"
},
"conflict": {
@@ -3455,10 +3530,10 @@
"require-dev": {
"ext-json": "*",
"laminas/laminas-coding-standard": "^3.1.0",
- "phpunit/phpunit": "^11.5.42",
+ "phpunit/phpunit": "^10.5.46",
"psalm/plugin-phpunit": "^0.19.5",
"psr/http-message": "^2.0",
- "vimeo/psalm": "^6.13.1",
+ "vimeo/psalm": "^6.11.0",
"webmozart/assert": "^1.11"
},
"suggest": {
@@ -3500,7 +3575,7 @@
"type": "community_bridge"
}
],
- "time": "2025-10-14T19:52:47+00:00"
+ "time": "2025-10-14T19:29:44+00:00"
},
{
"name": "laminas/laminas-json",
@@ -3566,20 +3641,20 @@
},
{
"name": "laminas/laminas-loader",
- "version": "2.11.1",
+ "version": "2.12.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-loader.git",
- "reference": "c507d5eccb969f7208434e3980680a1f6c0b1d8d"
+ "reference": "ec8cee33fb254ee4d9c8e8908c870e5c797e1272"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/c507d5eccb969f7208434e3980680a1f6c0b1d8d",
- "reference": "c507d5eccb969f7208434e3980680a1f6c0b1d8d",
+ "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/ec8cee33fb254ee4d9c8e8908c870e5c797e1272",
+ "reference": "ec8cee33fb254ee4d9c8e8908c870e5c797e1272",
"shasum": ""
},
"require": {
- "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
+ "php": "^8.0.0"
},
"conflict": {
"zendframework/zend-loader": "*"
@@ -3619,7 +3694,7 @@
}
],
"abandoned": true,
- "time": "2024-12-05T14:43:32+00:00"
+ "time": "2025-12-30T11:30:39+00:00"
},
{
"name": "laminas/laminas-log",
@@ -3850,25 +3925,25 @@
},
{
"name": "laminas/laminas-modulemanager",
- "version": "2.19.0",
+ "version": "2.18.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-modulemanager.git",
- "reference": "ca2438b20ffc0ec3352bd0a583a31f24403320ad"
+ "reference": "4b14617df01be6c83ac62fdb689427f8ed56d9c1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/ca2438b20ffc0ec3352bd0a583a31f24403320ad",
- "reference": "ca2438b20ffc0ec3352bd0a583a31f24403320ad",
+ "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/4b14617df01be6c83ac62fdb689427f8ed56d9c1",
+ "reference": "4b14617df01be6c83ac62fdb689427f8ed56d9c1",
"shasum": ""
},
"require": {
- "brick/varexporter": "^0.6",
- "laminas/laminas-config": "^3.10.1",
- "laminas/laminas-eventmanager": "^3.14.0",
- "laminas/laminas-stdlib": "^3.21.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
- "webimpress/safe-writer": "^2.2.0"
+ "brick/varexporter": "^0.3.2 || ^0.4 || ^0.5 || ^0.6",
+ "laminas/laminas-config": "^3.7",
+ "laminas/laminas-eventmanager": "^3.4",
+ "laminas/laminas-stdlib": "^3.6",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
+ "webimpress/safe-writer": "^1.0.2 || ^2.1"
},
"conflict": {
"amphp/amp": "<2.6.4",
@@ -3879,9 +3954,9 @@
"amphp/socket": "^1.2.1 || ^2.3.1",
"laminas/laminas-coding-standard": "~3.1.0",
"laminas/laminas-loader": "^2.11",
- "laminas/laminas-mvc": "^3.8.0",
+ "laminas/laminas-mvc": "^3.7.0",
"laminas/laminas-servicemanager": "^3.23.0",
- "phpunit/phpunit": "^11.5.42",
+ "phpunit/phpunit": "^10.5.38",
"psalm/plugin-phpunit": "^0.19.5",
"vimeo/psalm": "^6.13.1"
},
@@ -3921,7 +3996,7 @@
"type": "community_bridge"
}
],
- "time": "2025-10-17T22:17:10+00:00"
+ "time": "2025-10-17T18:22:00+00:00"
},
{
"name": "laminas/laminas-mvc",
@@ -4156,22 +4231,22 @@
},
{
"name": "laminas/laminas-paginator",
- "version": "2.22.0",
+ "version": "2.20.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-paginator.git",
- "reference": "46e62444f467b000a9a0e3959551a29b3223d7e8"
+ "reference": "235b57db1f7939513465ec49f05b12228fe5391f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-paginator/zipball/46e62444f467b000a9a0e3959551a29b3223d7e8",
- "reference": "46e62444f467b000a9a0e3959551a29b3223d7e8",
+ "url": "https://api.github.com/repos/laminas/laminas-paginator/zipball/235b57db1f7939513465ec49f05b12228fe5391f",
+ "reference": "235b57db1f7939513465ec49f05b12228fe5391f",
"shasum": ""
},
"require": {
"ext-json": "*",
"laminas/laminas-stdlib": "^3.10.1",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-paginator": "*"
@@ -4179,12 +4254,12 @@
"require-dev": {
"laminas/laminas-cache": "^3.13.0",
"laminas/laminas-cache-storage-adapter-memory": "^2.4.0",
- "laminas/laminas-coding-standard": "^3.1.0",
+ "laminas/laminas-coding-standard": "^2.5.0",
"laminas/laminas-config": "^3.10.1",
- "laminas/laminas-filter": "^2.42",
- "laminas/laminas-servicemanager": "^3.24.0",
+ "laminas/laminas-filter": "^2.41",
+ "laminas/laminas-servicemanager": "^3.23.1",
"laminas/laminas-view": "^2.43",
- "phpunit/phpunit": "^11.5.42",
+ "phpunit/phpunit": "^10.5.58",
"psalm/plugin-phpunit": "^0.19.5",
"vimeo/psalm": "^6.13.1"
},
@@ -4231,36 +4306,33 @@
"type": "community_bridge"
}
],
- "time": "2025-11-06T17:36:56+00:00"
+ "time": "2025-10-15T18:30:48+00:00"
},
{
"name": "laminas/laminas-permissions-rbac",
- "version": "3.8.0",
+ "version": "3.7.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-permissions-rbac.git",
- "reference": "1a1c80b5d351f46b2391632bfc87606d8c194bed"
+ "reference": "0fbde38f0cc2b462856e8a5edeaa26eb97dd23be"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-permissions-rbac/zipball/1a1c80b5d351f46b2391632bfc87606d8c194bed",
- "reference": "1a1c80b5d351f46b2391632bfc87606d8c194bed",
+ "url": "https://api.github.com/repos/laminas/laminas-permissions-rbac/zipball/0fbde38f0cc2b462856e8a5edeaa26eb97dd23be",
+ "reference": "0fbde38f0cc2b462856e8a5edeaa26eb97dd23be",
"shasum": ""
},
"require": {
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-permissions-rbac": "*"
},
"require-dev": {
- "amphp/dns": "^2.4.0",
- "amphp/socket": "^2.3.1",
- "laminas/laminas-coding-standard": "~3.1.0",
- "phpunit/phpunit": "^11.5.42",
+ "laminas/laminas-coding-standard": "~3.0.0",
+ "phpunit/phpunit": "^10.5.38",
"psalm/plugin-phpunit": "^0.19.0",
- "sebastian/recursion-context": "^6.0.3",
- "vimeo/psalm": "^6.13.1"
+ "vimeo/psalm": "^5.26.1"
},
"type": "library",
"autoload": {
@@ -4294,7 +4366,7 @@
"type": "community_bridge"
}
],
- "time": "2025-11-10T10:14:08+00:00"
+ "time": "2024-11-21T21:46:24+00:00"
},
{
"name": "laminas/laminas-psr7bridge",
@@ -4358,23 +4430,23 @@
},
{
"name": "laminas/laminas-recaptcha",
- "version": "3.9.0",
+ "version": "3.8.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-recaptcha.git",
- "reference": "d8c710d6ed90f245db5ee85bd024279b35476f3f"
+ "reference": "ab4efc2768b1d9e90df9a49301158ec288cd48dd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-recaptcha/zipball/d8c710d6ed90f245db5ee85bd024279b35476f3f",
- "reference": "d8c710d6ed90f245db5ee85bd024279b35476f3f",
+ "url": "https://api.github.com/repos/laminas/laminas-recaptcha/zipball/ab4efc2768b1d9e90df9a49301158ec288cd48dd",
+ "reference": "ab4efc2768b1d9e90df9a49301158ec288cd48dd",
"shasum": ""
},
"require": {
"ext-json": "*",
"laminas/laminas-http": "^2.15",
"laminas/laminas-stdlib": "^3.10.1",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zendservice-recaptcha": "*"
@@ -4383,9 +4455,9 @@
"laminas/laminas-coding-standard": "~2.5.0",
"laminas/laminas-config": "^3.9",
"laminas/laminas-validator": "^2.30.1",
- "phpunit/phpunit": "^11.5",
+ "phpunit/phpunit": "^9.6.15",
"psalm/plugin-phpunit": "^0.19.0",
- "vimeo/psalm": "^6.13"
+ "vimeo/psalm": "^5.19"
},
"suggest": {
"laminas/laminas-validator": "~2.0, if using ReCaptcha's Mailhide API"
@@ -4420,26 +4492,26 @@
"type": "community_bridge"
}
],
- "time": "2025-12-05T10:07:10+00:00"
+ "time": "2024-10-24T08:57:20+00:00"
},
{
"name": "laminas/laminas-router",
- "version": "3.16.0",
+ "version": "3.15.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-router.git",
- "reference": "ec4b33834199bcf04c1b90b98c10f53c9e77e046"
+ "reference": "bce9a5b2b0d7ecdaddbd191ab06b61d73c5e02a5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-router/zipball/ec4b33834199bcf04c1b90b98c10f53c9e77e046",
- "reference": "ec4b33834199bcf04c1b90b98c10f53c9e77e046",
+ "url": "https://api.github.com/repos/laminas/laminas-router/zipball/bce9a5b2b0d7ecdaddbd191ab06b61d73c5e02a5",
+ "reference": "bce9a5b2b0d7ecdaddbd191ab06b61d73c5e02a5",
"shasum": ""
},
"require": {
"laminas/laminas-servicemanager": "^3.14.0",
"laminas/laminas-stdlib": "^3.10.1",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-router": "*"
@@ -4447,10 +4519,10 @@
"require-dev": {
"laminas/laminas-coding-standard": "~3.1.0",
"laminas/laminas-http": "^2.22",
- "laminas/laminas-i18n": "^2.31.0",
- "phpunit/phpunit": "^11.5.43",
- "psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.13.1"
+ "laminas/laminas-i18n": "^2.30.0",
+ "phpunit/phpunit": "^10.5.58",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "vimeo/psalm": "^5.26.1"
},
"suggest": {
"laminas/laminas-i18n": "^2.15.0 if defining translatable HTTP path segments"
@@ -4491,25 +4563,25 @@
"type": "community_bridge"
}
],
- "time": "2025-11-09T16:54:18+00:00"
+ "time": "2025-11-09T15:44:07+00:00"
},
{
"name": "laminas/laminas-servicemanager",
- "version": "3.24.0",
+ "version": "3.23.1",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-servicemanager.git",
- "reference": "b172a0df568bf37ebdfb3658263156eefe3c1e8c"
+ "reference": "06594db3a644447521eace9b5efdb12dc8446a33"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/b172a0df568bf37ebdfb3658263156eefe3c1e8c",
- "reference": "b172a0df568bf37ebdfb3658263156eefe3c1e8c",
+ "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/06594db3a644447521eace9b5efdb12dc8446a33",
+ "reference": "06594db3a644447521eace9b5efdb12dc8446a33",
"shasum": ""
},
"require": {
"laminas/laminas-stdlib": "^3.19",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"psr/container": "^1.0"
},
"conflict": {
@@ -4532,7 +4604,7 @@
"laminas/laminas-container-config-test": "^0.8",
"mikey179/vfsstream": "^1.6.12",
"phpbench/phpbench": "^1.4.1",
- "phpunit/phpunit": "^10.5.58",
+ "phpunit/phpunit": "^10.5.51",
"psalm/plugin-phpunit": "^0.18.4",
"vimeo/psalm": "^5.26.1"
},
@@ -4581,46 +4653,44 @@
"type": "community_bridge"
}
],
- "time": "2025-10-14T09:03:51+00:00"
+ "time": "2025-08-12T08:44:02+00:00"
},
{
"name": "laminas/laminas-session",
- "version": "2.26.0",
+ "version": "2.25.1",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-session.git",
- "reference": "1c4f1a80ccc6403fff85bb13fb928da79dd1636e"
+ "reference": "17b5fd6aa9889dc8362c26d4c7500c93ff4347cb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-session/zipball/1c4f1a80ccc6403fff85bb13fb928da79dd1636e",
- "reference": "1c4f1a80ccc6403fff85bb13fb928da79dd1636e",
+ "url": "https://api.github.com/repos/laminas/laminas-session/zipball/17b5fd6aa9889dc8362c26d4c7500c93ff4347cb",
+ "reference": "17b5fd6aa9889dc8362c26d4c7500c93ff4347cb",
"shasum": ""
},
"require": {
- "laminas/laminas-eventmanager": "^3.14.0",
- "laminas/laminas-servicemanager": "^3.23.1",
- "laminas/laminas-stdlib": "^3.20.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "laminas/laminas-eventmanager": "^3.12",
+ "laminas/laminas-servicemanager": "^3.22",
+ "laminas/laminas-stdlib": "^3.18",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"amphp/amp": "<2.6.4",
"zendframework/zend-session": "*"
},
"require-dev": {
- "amphp/dns": "^2.4.0",
- "amphp/socket": "^2.3.1",
"ext-xdebug": "*",
- "laminas/laminas-cache": "^3.13.0",
- "laminas/laminas-cache-storage-adapter-memory": "^2.4",
+ "laminas/laminas-cache": "^3.12.2",
+ "laminas/laminas-cache-storage-adapter-memory": "^2.3",
"laminas/laminas-coding-standard": "~3.1.0",
"laminas/laminas-db": "^2.20.0",
- "laminas/laminas-http": "^2.22",
- "laminas/laminas-validator": "^2.64.4",
- "mongodb/mongodb": "~2.1.2",
- "phpunit/phpunit": "^11.5.42",
- "psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.13.1"
+ "laminas/laminas-http": "^2.20",
+ "laminas/laminas-validator": "^2.64.1",
+ "mongodb/mongodb": "~2.1.0",
+ "phpunit/phpunit": "^10.5.38",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "vimeo/psalm": "^5.26.1"
},
"suggest": {
"laminas/laminas-cache": "Laminas\\Cache component",
@@ -4666,34 +4736,34 @@
"type": "community_bridge"
}
],
- "time": "2025-11-14T12:52:38+00:00"
+ "time": "2025-10-11T09:26:06+00:00"
},
{
"name": "laminas/laminas-stdlib",
- "version": "3.21.0",
+ "version": "3.20.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-stdlib.git",
- "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096"
+ "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b1c81514cfe158aadf724c42b34d3d0a8164c096",
- "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096",
+ "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/8974a1213be42c3e2f70b2c27b17f910291ab2f4",
+ "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4",
"shasum": ""
},
"require": {
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-stdlib": "*"
},
"require-dev": {
- "laminas/laminas-coding-standard": "^3.1.0",
- "phpbench/phpbench": "^1.4.1",
- "phpunit/phpunit": "^11.5.42",
- "psalm/plugin-phpunit": "^0.19.5",
- "vimeo/psalm": "^6.13.1"
+ "laminas/laminas-coding-standard": "^3.0",
+ "phpbench/phpbench": "^1.3.1",
+ "phpunit/phpunit": "^10.5.38",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "vimeo/psalm": "^5.26.1"
},
"type": "library",
"autoload": {
@@ -4725,26 +4795,26 @@
"type": "community_bridge"
}
],
- "time": "2025-10-11T18:13:12+00:00"
+ "time": "2024-10-29T13:46:07+00:00"
},
{
"name": "laminas/laminas-stratigility",
- "version": "3.14.0",
+ "version": "3.13.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-stratigility.git",
- "reference": "d23d128a22f79a67e1f9682df4c51719e3553c9d"
+ "reference": "3df57528b5c8e9d958515c51006825a83f76d62b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-stratigility/zipball/d23d128a22f79a67e1f9682df4c51719e3553c9d",
- "reference": "d23d128a22f79a67e1f9682df4c51719e3553c9d",
+ "url": "https://api.github.com/repos/laminas/laminas-stratigility/zipball/3df57528b5c8e9d958515c51006825a83f76d62b",
+ "reference": "3df57528b5c8e9d958515c51006825a83f76d62b",
"shasum": ""
},
"require": {
"fig/http-message-util": "^1.1",
"laminas/laminas-escaper": "^2.10.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"psr/http-message": "^1.0 || ^2.0",
"psr/http-server-middleware": "^1.0.2"
},
@@ -4752,9 +4822,9 @@
"zendframework/zend-stratigility": "*"
},
"require-dev": {
- "laminas/laminas-coding-standard": "^3.1.0",
- "laminas/laminas-diactoros": "^2.25 || ^3.8.0",
- "phpunit/phpunit": "^10.5.58",
+ "laminas/laminas-coding-standard": "~2.5.0",
+ "laminas/laminas-diactoros": "^2.25 || ^3.5.0",
+ "phpunit/phpunit": "^10.5.37",
"psalm/plugin-phpunit": "^0.19.0",
"vimeo/psalm": "^5.26.1"
},
@@ -4804,7 +4874,7 @@
"type": "community_bridge"
}
],
- "time": "2025-11-12T05:23:21+00:00"
+ "time": "2024-10-28T11:28:41+00:00"
},
{
"name": "laminas/laminas-text",
@@ -4869,24 +4939,24 @@
},
{
"name": "laminas/laminas-translator",
- "version": "1.2.0",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-translator.git",
- "reference": "c4c1637ea56afe812f1af3212656fd6f9c02c551"
+ "reference": "12897e710e21413c1f93fc38fe9dead6b51c5218"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-translator/zipball/c4c1637ea56afe812f1af3212656fd6f9c02c551",
- "reference": "c4c1637ea56afe812f1af3212656fd6f9c02c551",
+ "url": "https://api.github.com/repos/laminas/laminas-translator/zipball/12897e710e21413c1f93fc38fe9dead6b51c5218",
+ "reference": "12897e710e21413c1f93fc38fe9dead6b51c5218",
"shasum": ""
},
"require": {
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
- "laminas/laminas-coding-standard": "~3.1.0",
- "vimeo/psalm": "^6.13.1"
+ "laminas/laminas-coding-standard": "~3.0.0",
+ "vimeo/psalm": "^5.24.0"
},
"type": "library",
"autoload": {
@@ -4918,33 +4988,33 @@
"type": "community_bridge"
}
],
- "time": "2025-10-14T20:58:42+00:00"
+ "time": "2024-10-21T15:33:01+00:00"
},
{
"name": "laminas/laminas-uri",
- "version": "2.14.0",
+ "version": "2.13.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-uri.git",
- "reference": "e804288f4540988903dc0ede386ce5eec87198df"
+ "reference": "de53600ae8153b3605bb6edce8aeeef524eaafba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/e804288f4540988903dc0ede386ce5eec87198df",
- "reference": "e804288f4540988903dc0ede386ce5eec87198df",
+ "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/de53600ae8153b3605bb6edce8aeeef524eaafba",
+ "reference": "de53600ae8153b3605bb6edce8aeeef524eaafba",
"shasum": ""
},
"require": {
"laminas/laminas-escaper": "^2.9",
"laminas/laminas-validator": "^2.39 || ^3.0",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-uri": "*"
},
"require-dev": {
"laminas/laminas-coding-standard": "~2.4.0",
- "phpunit/phpunit": "^11.0"
+ "phpunit/phpunit": "^9.6.20"
},
"type": "library",
"autoload": {
@@ -4976,26 +5046,26 @@
"type": "community_bridge"
}
],
- "time": "2025-12-05T10:02:11+00:00"
+ "time": "2024-12-03T12:27:51+00:00"
},
{
"name": "laminas/laminas-validator",
- "version": "2.65.0",
+ "version": "2.64.4",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-validator.git",
- "reference": "f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49"
+ "reference": "e2e6631f599a9b0db1e23adb633c09a2f0c68bed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49",
- "reference": "f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49",
+ "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/e2e6631f599a9b0db1e23adb633c09a2f0c68bed",
+ "reference": "e2e6631f599a9b0db1e23adb633c09a2f0c68bed",
"shasum": ""
},
"require": {
"laminas/laminas-servicemanager": "^3.21.0",
"laminas/laminas-stdlib": "^3.19",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"psr/http-message": "^1.0.1 || ^2.0.0"
},
"conflict": {
@@ -5004,15 +5074,15 @@
"require-dev": {
"laminas/laminas-coding-standard": "^2.5",
"laminas/laminas-db": "^2.20",
- "laminas/laminas-filter": "^2.41.0",
- "laminas/laminas-i18n": "^2.30.0",
- "laminas/laminas-session": "^2.25.1",
- "laminas/laminas-uri": "^2.13.0",
- "phpunit/phpunit": "^10.5.58",
+ "laminas/laminas-filter": "^2.35.2",
+ "laminas/laminas-i18n": "^2.26.0",
+ "laminas/laminas-session": "^2.20",
+ "laminas/laminas-uri": "^2.11.0",
+ "phpunit/phpunit": "^10.5.20",
"psalm/plugin-phpunit": "^0.19.0",
"psr/http-client": "^1.0.3",
"psr/http-factory": "^1.1.0",
- "vimeo/psalm": "^5.26.1"
+ "vimeo/psalm": "^5.24.0"
},
"suggest": {
"laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator",
@@ -5060,7 +5130,7 @@
"type": "community_bridge"
}
],
- "time": "2025-10-13T14:40:30+00:00"
+ "time": "2025-06-16T14:38:00+00:00"
},
{
"name": "laminas/laminas-view",
@@ -5166,16 +5236,16 @@
},
{
"name": "monarc/core",
- "version": "v2.13.3-p1",
+ "version": "v2.13.4",
"source": {
"type": "git",
"url": "https://github.com/monarc-project/zm-core.git",
- "reference": "8ec9b355456746d838d1010aac033e0af9c2e38f"
+ "reference": "d42c6621fc68c916d010760657b907e27e0ab8d0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/monarc-project/zm-core/zipball/8ec9b355456746d838d1010aac033e0af9c2e38f",
- "reference": "8ec9b355456746d838d1010aac033e0af9c2e38f",
+ "url": "https://api.github.com/repos/monarc-project/zm-core/zipball/d42c6621fc68c916d010760657b907e27e0ab8d0",
+ "reference": "d42c6621fc68c916d010760657b907e27e0ab8d0",
"shasum": ""
},
"require": {
@@ -5183,6 +5253,7 @@
"ext-intl": "*",
"ext-json": "*",
"ext-openssl": "*",
+ "friendsofphp/proxy-manager-lts": "^1.0",
"laminas/laminas-dependency-plugin": "^2.0",
"laminas/laminas-di": "^3.1",
"laminas/laminas-filter": "^2.9",
@@ -5194,7 +5265,6 @@
"laminas/laminas-mvc": "^3.1",
"laminas/laminas-mvc-i18n": "^1.7",
"laminas/laminas-mvc-middleware": "^2.2",
- "ocramius/proxy-manager": "^2.13",
"php": "^8.0",
"ramsey/uuid-doctrine": "^1.5",
"robmorgan/phinx": "^0.13.4",
@@ -5255,9 +5325,9 @@
"description": "Core for monarc/monarc application",
"support": {
"issues": "https://github.com/monarc-project/zm-core/issues",
- "source": "https://github.com/monarc-project/zm-core/tree/v2.13.3-p1"
+ "source": "https://github.com/monarc-project/zm-core/tree/v2.13.4"
},
- "time": "2025-05-20T14:55:52+00:00"
+ "time": "2026-01-21T10:43:32+00:00"
},
{
"name": "monarc/frontoffice",
@@ -5415,87 +5485,6 @@
},
"time": "2025-12-06T11:56:16+00:00"
},
- {
- "name": "ocramius/proxy-manager",
- "version": "2.14.1",
- "source": {
- "type": "git",
- "url": "https://github.com/Ocramius/ProxyManager.git",
- "reference": "3990d60ef79001badbab4927a6a811682274a0d1"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/3990d60ef79001badbab4927a6a811682274a0d1",
- "reference": "3990d60ef79001badbab4927a6a811682274a0d1",
- "shasum": ""
- },
- "require": {
- "composer-runtime-api": "^2.1.0",
- "laminas/laminas-code": "^4.4.2",
- "php": "~8.0.0",
- "webimpress/safe-writer": "^2.2.0"
- },
- "conflict": {
- "thecodingmachine/safe": "<1.3.3"
- },
- "require-dev": {
- "codelicia/xulieta": "^0.1.6",
- "doctrine/coding-standard": "^9.0.0",
- "ext-phar": "*",
- "phpbench/phpbench": "^1.0.3",
- "phpunit/phpunit": "^9.5.6",
- "roave/infection-static-analysis-plugin": "^1.8",
- "squizlabs/php_codesniffer": "^3.6.0",
- "vimeo/psalm": "^4.8.1"
- },
- "suggest": {
- "laminas/laminas-json": "To have the JsonRpc adapter (Remote Object feature)",
- "laminas/laminas-soap": "To have the Soap adapter (Remote Object feature)",
- "laminas/laminas-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)",
- "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "ProxyManager\\": "src/ProxyManager"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Marco Pivetta",
- "email": "ocramius@gmail.com",
- "homepage": "https://ocramius.github.io/"
- }
- ],
- "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies",
- "homepage": "https://github.com/Ocramius/ProxyManager",
- "keywords": [
- "aop",
- "lazy loading",
- "proxy",
- "proxy pattern",
- "service proxies"
- ],
- "support": {
- "issues": "https://github.com/Ocramius/ProxyManager/issues",
- "source": "https://github.com/Ocramius/ProxyManager/tree/2.14.1"
- },
- "funding": [
- {
- "url": "https://github.com/Ocramius",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager",
- "type": "tidelift"
- }
- ],
- "time": "2022-03-05T18:43:14+00:00"
- },
{
"name": "phpoffice/phpword",
"version": "0.18.3",
@@ -6743,25 +6732,25 @@
},
{
"name": "symfony/filesystem",
- "version": "v7.4.0",
+ "version": "v6.4.30",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "d551b38811096d0be9c4691d406991b47c0c630a"
+ "reference": "441c6b69f7222aadae7cbf5df588496d5ee37789"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a",
- "reference": "d551b38811096d0be9c4691d406991b47c0c630a",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/441c6b69f7222aadae7cbf5df588496d5ee37789",
+ "reference": "441c6b69f7222aadae7cbf5df588496d5ee37789",
"shasum": ""
},
"require": {
- "php": ">=8.2",
+ "php": ">=8.1",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8"
},
"require-dev": {
- "symfony/process": "^6.4|^7.0|^8.0"
+ "symfony/process": "^5.4|^6.4|^7.0"
},
"type": "library",
"autoload": {
@@ -6789,7 +6778,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v7.4.0"
+ "source": "https://github.com/symfony/filesystem/tree/v6.4.30"
},
"funding": [
{
@@ -6809,7 +6798,7 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:27:24+00:00"
+ "time": "2025-11-26T14:43:45+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -8512,12 +8501,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
- "reference": "e5034c4df32edeafb119b2c1e2b58876d0286ea8"
+ "reference": "a2b02a3f02794e178258157c73431946c7eb9af3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/e5034c4df32edeafb119b2c1e2b58876d0286ea8",
- "reference": "e5034c4df32edeafb119b2c1e2b58876d0286ea8",
+ "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a2b02a3f02794e178258157c73431946c7eb9af3",
+ "reference": "a2b02a3f02794e178258157c73431946c7eb9af3",
"shasum": ""
},
"conflict": {
@@ -8532,12 +8521,14 @@
"aimeos/ai-cms-grapesjs": ">=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.9|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.10.8|>=2025.04.1,<2025.10.2",
"aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1",
"aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7",
+ "aimeos/aimeos-laravel": "==2021.10",
"aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5",
"airesvsg/acf-to-rest-api": "<=3.1",
"akaunting/akaunting": "<2.1.13",
"akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53",
- "alextselegidis/easyappointments": "<1.5.2.0-beta1",
+ "alextselegidis/easyappointments": "<=1.5.2",
"alexusmai/laravel-file-manager": "<=3.3.1",
+ "algolia/algoliasearch-magento-2": "<=3.16.1|>=3.17.0.0-beta1,<=3.17.1",
"alt-design/alt-redirect": "<1.6.4",
"altcha-org/altcha": "<1.3.1",
"alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1",
@@ -8570,7 +8561,7 @@
"automad/automad": "<2.0.0.0-alpha5",
"automattic/jetpack": "<9.8",
"awesome-support/awesome-support": "<=6.0.7",
- "aws/aws-sdk-php": "<3.288.1",
+ "aws/aws-sdk-php": "<3.368",
"azuracast/azuracast": "<=0.23.1",
"b13/seo_basics": "<0.8.2",
"backdrop/backdrop": "<=1.32",
@@ -8578,7 +8569,7 @@
"backpack/filemanager": "<2.0.2|>=3,<3.0.9",
"bacula-web/bacula-web": "<9.7.1",
"badaso/core": "<=2.9.11",
- "bagisto/bagisto": "<=2.3.7",
+ "bagisto/bagisto": "<2.3.10",
"barrelstrength/sprout-base-email": "<1.2.7",
"barrelstrength/sprout-forms": "<3.9",
"barryvdh/laravel-translation-manager": "<0.6.8",
@@ -8610,7 +8601,8 @@
"bvbmedia/multishop": "<2.0.39",
"bytefury/crater": "<6.0.2",
"cachethq/cachet": "<2.5.1",
- "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10",
+ "cadmium-org/cadmium-cms": "<=0.4.9",
+ "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10|>=5.2.10,<5.2.12|==5.3",
"cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10",
"cardgate/magento2": "<2.0.33",
"cardgate/woocommerce": "<=3.1.15",
@@ -8639,7 +8631,7 @@
"codingms/modules": "<4.3.11|>=5,<5.7.4|>=6,<6.4.2|>=7,<7.5.5",
"commerceteam/commerce": ">=0.9.6,<0.9.9",
"components/jquery": ">=1.0.3,<3.5",
- "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7",
+ "composer/composer": "<1.10.27|>=2,<2.2.26|>=2.3,<2.9.3",
"concrete5/concrete5": "<9.4.3",
"concrete5/core": "<8.5.8|>=9,<9.1",
"contao-components/mediaelement": ">=2.14.2,<2.21.1",
@@ -8649,11 +8641,12 @@
"contao/core-bundle": "<4.13.57|>=5,<5.3.42|>=5.4,<5.6.5",
"contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8",
"contao/managed-edition": "<=1.5",
+ "coreshop/core-shop": "<=4.1.7",
"corveda/phpsandbox": "<1.3.5",
"cosenary/instagram": "<=2.3",
"couleurcitron/tarteaucitron-wp": "<0.3",
- "craftcms/cms": "<=4.16.5|>=5,<=5.8.6",
- "croogo/croogo": "<4",
+ "craftcms/cms": "<=4.16.16|>=5,<=5.8.20",
+ "croogo/croogo": "<=4.0.7",
"cuyz/valinor": "<0.12",
"czim/file-handling": "<1.5|>=2,<2.3",
"czproject/git-php": "<4.0.3",
@@ -8700,7 +8693,7 @@
"drupal/commerce_alphabank_redirect": "<1.0.3",
"drupal/commerce_eurobank_redirect": "<2.1.1",
"drupal/config_split": "<1.10|>=2,<2.0.2",
- "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8",
+ "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8",
"drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8",
"drupal/currency": "<3.5",
"drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8",
@@ -8764,7 +8757,7 @@
"ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15",
"ezyang/htmlpurifier": "<=4.2",
"facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2",
- "facturascripts/facturascripts": "<=2022.08",
+ "facturascripts/facturascripts": "<=2025.4|==2025.11|==2025.41|==2025.43",
"fastly/magento2": "<1.2.26",
"feehi/cms": "<=2.1.1",
"feehi/feehicms": "<=2.1.1",
@@ -8815,7 +8808,7 @@
"geshi/geshi": "<=1.0.9.1",
"getformwork/formwork": "<2.2",
"getgrav/grav": "<1.11.0.0-beta1",
- "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<5.1.4",
+ "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<=5.2.1",
"getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1",
"getkirby/panel": "<2.5.14",
"getkirby/starterkit": "<=3.7.0.2",
@@ -8905,7 +8898,7 @@
"kelvinmo/simplexrd": "<3.1.1",
"kevinpapst/kimai2": "<1.16.7",
"khodakhah/nodcms": "<=3",
- "kimai/kimai": "<=2.20.1",
+ "kimai/kimai": "<2.46",
"kitodo/presentation": "<3.2.3|>=3.3,<3.3.4",
"klaviyo/magento2-extension": ">=1,<3",
"knplabs/knp-snappy": "<=1.4.2",
@@ -8936,11 +8929,12 @@
"leantime/leantime": "<3.3",
"lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3",
"libreform/libreform": ">=2,<=2.0.8",
- "librenms/librenms": "<25.11",
+ "librenms/librenms": "<25.12",
"liftkit/database": "<2.13.2",
"lightsaml/lightsaml": "<1.3.5",
"limesurvey/limesurvey": "<6.5.12",
"livehelperchat/livehelperchat": "<=3.91",
+ "livewire-filemanager/filemanager": "<=1.0.4",
"livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.6.4",
"livewire/volt": "<1.7",
"lms/routes": "<2.1.1",
@@ -9044,7 +9038,7 @@
"october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1",
"october/october": "<3.7.5",
"october/rain": "<1.0.472|>=1.1,<1.1.2",
- "october/system": "<3.7.5",
+ "october/system": "<=3.7.12|>=4,<=4.0.11",
"oliverklee/phpunit": "<3.5.15",
"omeka/omeka-s": "<4.0.3",
"onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1",
@@ -9071,6 +9065,7 @@
"pagekit/pagekit": "<=1.0.18",
"paragonie/ecc": "<2.0.1",
"paragonie/random_compat": "<2",
+ "paragonie/sodium_compat": "<1.24|>=2,<2.5",
"passbolt/passbolt_api": "<4.6.2",
"paypal/adaptivepayments-sdk-php": "<=3.9.2",
"paypal/invoice-sdk-php": "<=3.9",
@@ -9107,14 +9102,15 @@
"phpxmlrpc/extras": "<0.6.1",
"phpxmlrpc/phpxmlrpc": "<4.9.2",
"pi/pi": "<=2.5",
- "pimcore/admin-ui-classic-bundle": "<1.7.6",
+ "pimcore/admin-ui-classic-bundle": "<=1.7.15|>=2.0.0.0-RC1-dev,<=2.2.2",
"pimcore/customer-management-framework-bundle": "<4.2.1",
"pimcore/data-hub": "<1.2.4",
"pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3",
"pimcore/demo": "<10.3",
"pimcore/ecommerce-framework-bundle": "<1.0.10",
"pimcore/perspective-editor": "<1.5.1",
- "pimcore/pimcore": "<11.5.4",
+ "pimcore/pimcore": "<=11.5.13|>=12.0.0.0-RC1-dev,<12.3.1",
+ "pimcore/web2print-tools-bundle": "<=5.2.1|>=6.0.0.0-RC1-dev,<=6.1",
"piwik/piwik": "<1.11",
"pixelfed/pixelfed": "<0.12.5",
"plotly/plotly.js": "<2.25.2",
@@ -9138,7 +9134,7 @@
"processwire/processwire": "<=3.0.246",
"propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7",
"propel/propel1": ">=1,<=1.7.1",
- "pterodactyl/panel": "<=1.11.10",
+ "pterodactyl/panel": "<1.12",
"ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2",
"ptrofimov/beanstalk_console": "<1.7.14",
"pubnub/pubnub": "<6.1",
@@ -9156,7 +9152,7 @@
"rap2hpoutre/laravel-log-viewer": "<0.13",
"react/http": ">=0.7,<1.9",
"really-simple-plugins/complianz-gdpr": "<6.4.2",
- "redaxo/source": "<5.20.1",
+ "redaxo/source": "<=5.20.1",
"remdex/livehelperchat": "<4.29",
"renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1",
"reportico-web/reportico": "<=8.1",
@@ -9178,10 +9174,10 @@
"setasign/fpdi": "<2.6.4",
"sfroemken/url_redirect": "<=1.2.1",
"sheng/yiicms": "<1.2.1",
- "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.4.1-dev",
+ "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.6.1-dev",
"shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev",
"shopware/production": "<=6.3.5.2",
- "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.5.1-dev",
+ "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev",
"shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev",
"shopxo/shopxo": "<=6.4",
"showdoc/showdoc": "<2.10.4",
@@ -9226,7 +9222,7 @@
"snipe/snipe-it": "<=8.3.4",
"socalnick/scn-social-auth": "<1.15.2",
"socialiteproviders/steam": "<1.1",
- "solspace/craft-freeform": ">=5,<5.10.16",
+ "solspace/craft-freeform": "<4.1.29|>=5,<5.10.16",
"soosyze/soosyze": "<=2",
"spatie/browsershot": "<5.0.5",
"spatie/image-optimizer": "<1.7.3",
@@ -9315,7 +9311,7 @@
"thelia/thelia": ">=2.1,<2.1.3",
"theonedemon/phpwhois": "<=4.2.5",
"thinkcmf/thinkcmf": "<6.0.8",
- "thorsten/phpmyfaq": "<=4.0.13",
+ "thorsten/phpmyfaq": "<4.0.16|>=4.1.0.0-alpha,<=4.1.0.0-beta2",
"tikiwiki/tiki-manager": "<=17.1",
"timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1",
"tinymce/tinymce": "<7.2",
@@ -9334,10 +9330,10 @@
"twbs/bootstrap": "<3.4.1|>=4,<4.3.1",
"twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19",
"typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2",
- "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18",
+ "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1",
"typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2",
"typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18",
- "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18",
+ "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1",
"typo3/cms-dashboard": ">=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18",
"typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1",
"typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2",
@@ -9349,7 +9345,8 @@
"typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2",
"typo3/cms-lowlevel": ">=11,<=11.5.41",
"typo3/cms-recordlist": ">=11,<11.5.48",
- "typo3/cms-recycler": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18",
+ "typo3/cms-recycler": ">=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1",
+ "typo3/cms-redirects": ">=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1",
"typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30",
"typo3/cms-scheduler": ">=11,<=11.5.41",
"typo3/cms-setup": ">=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11",
@@ -9431,7 +9428,7 @@
"yiisoft/yii2-redis": "<2.0.20",
"yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6",
"yoast-seo-for-typo3/yoast_seo": "<7.2.3",
- "yourls/yourls": "<=1.8.2",
+ "yourls/yourls": "<=1.10.2",
"yuan1994/tpadmin": "<=1.3.12",
"yungifez/skuul": "<=2.6.5",
"z-push/z-push-dev": "<2.7.6",
@@ -9509,7 +9506,7 @@
"type": "tidelift"
}
],
- "time": "2025-12-17T21:06:23+00:00"
+ "time": "2026-01-20T19:24:42+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -10727,6 +10724,9 @@
"ext-bcmath": "*",
"ext-openssl": "*"
},
- "platform-dev": [],
- "plugin-api-version": "2.6.0"
+ "platform-dev": {},
+ "platform-overrides": {
+ "php": "8.1.0"
+ },
+ "plugin-api-version": "2.9.0"
}
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
new file mode 100644
index 0000000..b5a1c7b
--- /dev/null
+++ b/docker-compose.dev.yml
@@ -0,0 +1,151 @@
+services:
+ # MariaDB database for MONARC
+ fodb:
+ image: mariadb:10.11
+ container_name: monarc-fo-db
+ environment:
+ MYSQL_ROOT_PASSWORD: ${DBPASSWORD_ADMIN}
+ MYSQL_DATABASE: ${DBNAME_COMMON}
+ MYSQL_USER: ${DBUSER_MONARC}
+ MYSQL_PASSWORD: ${DBPASSWORD_MONARC}
+ command:
+ - --character-set-server=utf8mb4
+ - --collation-server=utf8mb4_general_ci
+ - --bind-address=0.0.0.0
+ - --sql-mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
+ ports:
+ - "${DBPORT_HOST:-3307}:3306"
+ volumes:
+ - db_data:/var/lib/mysql
+ - ./docker/db-init:/docker-entrypoint-initdb.d:ro
+ networks:
+ - monarc-network
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DBPASSWORD_ADMIN}"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ # PostgreSQL database for stats service
+ postgres:
+ image: postgres:15
+ container_name: monarc-fo-postgres
+ environment:
+ POSTGRES_USER: ${STATS_DB_USER}
+ POSTGRES_PASSWORD: ${STATS_DB_PASSWORD}
+ POSTGRES_DB: ${STATS_DB_NAME}
+ ports:
+ - "5432:5432"
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+ networks:
+ - monarc-network
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U ${STATS_DB_USER} -d ${STATS_DB_NAME}"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ # Stats service
+ stats-service:
+ build:
+ context: .
+ dockerfile: Dockerfile.stats
+ container_name: monarc-fo-stats
+ environment:
+ STATS_HOST: ${STATS_HOST}
+ STATS_PORT: ${STATS_PORT}
+ STATS_DB_HOST: postgres
+ STATS_DB_NAME: ${STATS_DB_NAME}
+ STATS_DB_USER: ${STATS_DB_USER}
+ STATS_DB_PASSWORD: ${STATS_DB_PASSWORD}
+ STATS_SECRET_KEY: ${STATS_SECRET_KEY}
+ ports:
+ - "5005:5005"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ networks:
+ - monarc-network
+ volumes:
+ - stats_data:/var/www/stats-service
+
+ # MailCatcher for email testing
+ mailcatcher:
+ image: sj26/mailcatcher:latest
+ container_name: monarc-fo-mailcatcher
+ ports:
+ - "1080:1080" # Web interface
+ - "8825:1025" # SMTP
+ networks:
+ - monarc-network
+
+ # Main MONARC FrontOffice application
+ monarcfoapp:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ args:
+ NODE_MAJOR: ${NODE_MAJOR:-16}
+ XDEBUG_ENABLED: ${XDEBUG_ENABLED:-1}
+ XDEBUG_MODE: ${XDEBUG_MODE:-debug}
+ XDEBUG_START_WITH_REQUEST: ${XDEBUG_START_WITH_REQUEST:-trigger}
+ XDEBUG_CLIENT_HOST: ${XDEBUG_CLIENT_HOST:-host.docker.internal}
+ XDEBUG_CLIENT_PORT: ${XDEBUG_CLIENT_PORT:-9003}
+ XDEBUG_IDEKEY: ${XDEBUG_IDEKEY:-IDEKEY}
+ XDEBUG_DISCOVER_CLIENT_HOST: ${XDEBUG_DISCOVER_CLIENT_HOST:-0}
+ container_name: monarc-fo-app
+ environment:
+ DBHOST: ${DBHOST:-fodb}
+ DBNAME_COMMON: ${DBNAME_COMMON}
+ DBNAME_CLI: ${DBNAME_CLI}
+ DBUSER_MONARC: ${DBUSER_MONARC}
+ DBPASSWORD_MONARC: ${DBPASSWORD_MONARC}
+ DBPASSWORD_ADMIN: ${DBPASSWORD_ADMIN}
+ USE_BO_COMMON: ${USE_BO_COMMON:-0}
+ STATS_API_KEY: ${STATS_API_KEY}
+ APP_ENV: development
+ APP_DIR: /var/www/html/monarc
+ ports:
+ - "5001:80"
+ depends_on:
+ fodb:
+ condition: service_healthy
+ # stats-service:
+ # condition: service_started
+ networks:
+ - monarc-network
+ volumes:
+ # Mount the application code for live development
+ - ./:/var/www/html/monarc
+ # Preserve vendor directory in named volume for better performance
+ - vendor_data:/var/www/html/monarc/vendor
+ - node_modules_data:/var/www/html/monarc/node_modules
+ working_dir: /var/www/html/monarc
+
+networks:
+ monarc-network:
+ name: ${MONARC_NETWORK_NAME:-monarc-network}
+ external: true
+
+volumes:
+ db_data:
+ driver: local
+ driver_opts:
+ type: none
+ device: ./docker/db_data
+ o: bind
+ postgres_data:
+ stats_data:
+ vendor_data:
+ driver: local
+ driver_opts:
+ type: none
+ device: ./vendor
+ o: bind
+ node_modules_data:
+ driver: local
+ driver_opts:
+ type: none
+ device: ./node_modules
+ o: bind
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100755
index 0000000..99acc57
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,185 @@
+#!/bin/bash
+set -e
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+echo -e "${GREEN}Starting MONARC FrontOffice setup...${NC}"
+
+# Wait for database to be ready
+echo -e "${YELLOW}Waiting for MariaDB to be ready...${NC}"
+while ! mysqladmin ping -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" --silent 2>/dev/null; do
+ echo "Waiting for MariaDB..."
+ sleep 2
+done
+echo -e "${GREEN}MariaDB is ready!${NC}"
+
+is_true() {
+ case "$1" in
+ 1|true|yes|on) return 0 ;;
+ *) return 1 ;;
+ esac
+}
+
+# Check if this is the first run
+if [ ! -f "/var/www/html/monarc/.docker-initialized" ]; then
+ echo -e "${GREEN}First run detected, initializing application...${NC}"
+
+ cd /var/www/html/monarc
+
+ # Install composer dependencies (always, to ensure binaries like Phinx are present)
+ echo -e "${YELLOW}Installing Composer dependencies...${NC}"
+ composer install --ignore-platform-req=php --no-interaction
+
+ # Create module symlinks
+ echo -e "${YELLOW}Creating module symlinks...${NC}"
+ mkdir -p module/Monarc
+ cd module/Monarc
+ ln -sfn ./../../vendor/monarc/core Core
+ ln -sfn ./../../vendor/monarc/frontoffice FrontOffice
+ cd /var/www/html/monarc
+
+ # Clone frontend repositories
+ echo -e "${YELLOW}Setting up frontend repositories...${NC}"
+ mkdir -p node_modules
+ cd node_modules
+
+ if [ ! -d "ng_client" ]; then
+ git clone --config core.fileMode=false https://github.com/monarc-project/ng-client.git ng_client
+ fi
+
+ if [ ! -d "ng_anr" ]; then
+ git clone --config core.fileMode=false https://github.com/monarc-project/ng-anr.git ng_anr
+ fi
+
+ cd /var/www/html/monarc
+
+ # Check if CLI database exists and create databases if needed
+ echo -e "${YELLOW}Setting up databases...${NC}"
+ DB_EXISTS=$(mysql -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" -e "SHOW DATABASES LIKE '${DBNAME_CLI}';" | grep -c "${DBNAME_CLI}" || true)
+ USE_BO_COMMON_ENABLED=0
+ if is_true "${USE_BO_COMMON}"; then
+ USE_BO_COMMON_ENABLED=1
+ fi
+
+ if [ "$DB_EXISTS" -eq 0 ]; then
+ echo -e "${YELLOW}Creating databases...${NC}"
+ mysql -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" -e "CREATE DATABASE IF NOT EXISTS ${DBNAME_CLI} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;"
+
+ if [ "$USE_BO_COMMON_ENABLED" -eq 0 ]; then
+ mysql -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" -e "CREATE DATABASE IF NOT EXISTS ${DBNAME_COMMON} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;"
+
+ echo -e "${YELLOW}Populating common database...${NC}"
+ export MYSQL_PWD="${DBPASSWORD_MONARC}"
+ mysql -h"${DBHOST}" -u"${DBUSER_MONARC}" ${DBNAME_COMMON} < db-bootstrap/monarc_structure.sql
+ mysql -h"${DBHOST}" -u"${DBUSER_MONARC}" ${DBNAME_COMMON} < db-bootstrap/monarc_data.sql
+ else
+ echo -e "${YELLOW}USE_BO_COMMON is enabled; skipping monarc_common creation and bootstrap.${NC}"
+ fi
+ fi
+
+ echo -e "${YELLOW}Ensuring privileges for ${DBUSER_MONARC}...${NC}"
+ mysql -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" -e "GRANT ALL PRIVILEGES ON ${DBNAME_CLI}.* TO '${DBUSER_MONARC}'@'%';"
+ if [ "$USE_BO_COMMON_ENABLED" -eq 0 ]; then
+ mysql -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" -e "GRANT ALL PRIVILEGES ON ${DBNAME_COMMON}.* TO '${DBUSER_MONARC}'@'%';"
+ fi
+ mysql -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" -e "FLUSH PRIVILEGES;"
+
+ if [ "$USE_BO_COMMON_ENABLED" -eq 1 ]; then
+ COMMON_EXISTS=$(mysql -h"${DBHOST}" -u"root" -p"${DBPASSWORD_ADMIN}" -e "SHOW DATABASES LIKE '${DBNAME_COMMON}';" | grep -c "${DBNAME_COMMON}" || true)
+ if [ "$COMMON_EXISTS" -eq 0 ]; then
+ echo -e "${RED}USE_BO_COMMON is enabled, but ${DBNAME_COMMON} was not found on ${DBHOST}.${NC}"
+ echo -e "${RED}Ensure the BackOffice database is reachable and contains ${DBNAME_COMMON}.${NC}"
+ exit 1
+ fi
+ fi
+
+ # Generate local config (always override to match container DB)
+ echo -e "${YELLOW}Creating local configuration...${NC}"
+ cat > config/autoload/local.php < [
+ 'connection' => [
+ 'orm_default' => [
+ 'params' => [
+ 'host' => '${DBHOST}',
+ 'user' => '${DBUSER_MONARC}',
+ 'password' => '${DBPASSWORD_MONARC}',
+ 'dbname' => '${DBNAME_COMMON}',
+ ],
+ ],
+ 'orm_cli' => [
+ 'params' => [
+ 'host' => '${DBHOST}',
+ 'user' => '${DBUSER_MONARC}',
+ 'password' => '${DBPASSWORD_MONARC}',
+ 'dbname' => '${DBNAME_CLI}',
+ ],
+ ],
+ ],
+ ],
+
+ 'activeLanguages' => array('fr','en','de','nl','es','ro','it','ja','pl','pt','ru','zh'),
+
+ 'appVersion' => \$package_json['version'],
+
+ 'checkVersion' => false,
+ 'appCheckingURL' => 'https://version.monarc.lu/check/MONARC',
+
+ 'email' => [
+ 'name' => 'MONARC',
+ 'from' => 'info@monarc.lu',
+ ],
+
+ 'mospApiUrl' => 'https://objects.monarc.lu/api/',
+
+ 'monarc' => [
+ 'ttl' => 60, // timeout
+ 'salt' => '', // private salt for password encryption
+ ],
+
+ 'statsApi' => [
+ 'baseUrl' => 'http://stats-service:5005',
+ 'apiKey' => '${STATS_API_KEY:-}',
+ ],
+
+ 'import' => [
+ 'uploadFolder' => '$appdir/data/import/files',
+ 'isBackgroundProcessActive' => false,
+ ],
+];
+EOF
+
+ # Update and build frontend and run DB migrations
+ echo -e "${YELLOW}Building frontend and running DB migrations...${NC}"
+ ./scripts/update-all.sh
+
+ # Seed database with initial user
+ echo -e "${YELLOW}Creating initial user and client...${NC}"
+ php ./vendor/robmorgan/phinx/bin/phinx seed:run -c ./module/Monarc/FrontOffice/migrations/phinx.php
+
+ # Set permissions
+ echo -e "${YELLOW}Setting permissions...${NC}"
+ chown -R www-data:www-data /var/www/html/monarc/data
+ chmod -R 775 /var/www/html/monarc/data
+
+ # Mark initialization as complete
+ touch /var/www/html/monarc/.docker-initialized
+ echo -e "${GREEN}Initialization complete!${NC}"
+else
+ echo -e "${GREEN}Application already initialized, starting services...${NC}"
+fi
+
+# Execute the main command
+exec apachectl -D FOREGROUND
diff --git a/docker-stats-entrypoint.sh b/docker-stats-entrypoint.sh
new file mode 100755
index 0000000..57df8b0
--- /dev/null
+++ b/docker-stats-entrypoint.sh
@@ -0,0 +1,321 @@
+#!/bin/bash
+set -e
+
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+echo -e "${GREEN}Starting MONARC Stats Service setup...${NC}"
+
+write_health_wrapper() {
+ cat > /var/www/stats-service/monarc_health_app.py <<'PY'
+import importlib
+import os
+import pkgutil
+import sys
+
+from flask.cli import ScriptInfo
+
+base_app = os.environ.get("STATS_BASE_APP") or "app"
+
+info = ScriptInfo(app_import_path=base_app)
+app = info.load_app()
+
+try:
+ import statsservice
+ for _, modname, _ in pkgutil.walk_packages(statsservice.__path__, statsservice.__name__ + "."):
+ try:
+ importlib.import_module(modname)
+ except Exception:
+ pass
+except Exception:
+ pass
+
+@app.get("/health")
+def health():
+ return {"status": "ok"}
+PY
+}
+
+detect_flask_app() {
+ if [ -f "/var/www/stats-service/app.py" ]; then
+ echo "app.py"
+ return 0
+ fi
+ if [ -f "/var/www/stats-service/runserver.py" ]; then
+ echo "runserver.py"
+ return 0
+ fi
+ if [ -f "/var/www/stats-service/statsservice/bootstrap.py" ]; then
+ echo "statsservice.bootstrap"
+ return 0
+ fi
+ return 1
+}
+
+ensure_flask_app() {
+ local detected_app
+ local flask_app_missing=0
+
+ if [ -n "${FLASK_APP}" ]; then
+ if [[ "${FLASK_APP}" == *.py ]]; then
+ [ -f "/var/www/stats-service/${FLASK_APP}" ] || flask_app_missing=1
+ else
+ local module_path="/var/www/stats-service/${FLASK_APP//.//}.py"
+ local module_dir="/var/www/stats-service/${FLASK_APP//.//}/__init__.py"
+ if [ ! -f "${module_path}" ] && [ ! -f "${module_dir}" ]; then
+ flask_app_missing=1
+ fi
+ fi
+ fi
+
+ if [ -z "${FLASK_APP}" ] || [ "${flask_app_missing}" -eq 1 ]; then
+ detected_app=$(detect_flask_app || true)
+ if [ -z "${detected_app}" ]; then
+ echo -e "${YELLOW}Could not detect Flask app entrypoint. Check stats-service repo layout.${NC}"
+ exit 1
+ fi
+ export FLASK_APP="${detected_app}"
+ echo -e "${GREEN}Using FLASK_APP=${FLASK_APP}${NC}"
+ fi
+
+ BASE_FLASK_APP="${FLASK_APP}"
+}
+
+ensure_python_deps() {
+ if ! poetry run python - <<'PY'
+import importlib
+import sys
+try:
+ importlib.import_module("flask")
+except Exception:
+ sys.exit(1)
+PY
+ then
+ echo -e "${YELLOW}Installing Python dependencies with Poetry...${NC}"
+ poetry install --only=main
+ fi
+}
+
+write_instance_config() {
+ echo -e "${YELLOW}Creating configuration...${NC}"
+ mkdir -p instance
+ STATS_INSTANCE_URL_VALUE="${STATS_INSTANCE_URL:-http://localhost:${STATS_PORT}}"
+ cat > instance/production.py </dev/null; do
+ echo "Waiting for PostgreSQL..."
+ sleep 2
+done
+echo -e "${GREEN}PostgreSQL is ready!${NC}"
+
+# Check if this is the first run
+if [ ! -f "/var/www/stats-service/.docker-initialized" ]; then
+ echo -e "${GREEN}First run detected, initializing stats service...${NC}"
+
+ # Clone the stats service repository
+ if [ ! -d "/var/www/stats-service/.git" ]; then
+ echo -e "${YELLOW}Cloning stats-service repository...${NC}"
+ cd /var/www
+ git clone https://github.com/monarc-project/stats-service stats-service-tmp
+ cp -r stats-service-tmp/* stats-service/
+ cp -r stats-service-tmp/.git stats-service/
+ rm -rf stats-service-tmp
+ cd /var/www/stats-service
+ fi
+
+ ensure_flask_app
+
+ flask_has_command() {
+ local cmd="$1"
+ poetry run flask --app "${FLASK_APP}" --help 2>/dev/null | grep -qE "^[[:space:]]+${cmd}[[:space:]]"
+ }
+
+ run_flask() {
+ poetry run flask --app "${FLASK_APP}" "$@"
+ }
+
+ run_db_upgrade() {
+ run_flask db upgrade
+ }
+
+ run_db_stamp_head() {
+ run_flask db stamp head
+ }
+
+ db_has_tables() {
+ local count
+ count=$(PGPASSWORD=$STATS_DB_PASSWORD psql -h "$STATS_DB_HOST" -U "$STATS_DB_USER" -d "$STATS_DB_NAME" -tAc "select count(*) from information_schema.tables where table_schema='public';" 2>/dev/null | tr -d '[:space:]')
+ if [ -z "${count}" ]; then
+ return 1
+ fi
+ [ "${count}" -gt 0 ]
+ }
+
+ create_schema_fallback() {
+ echo -e "${YELLOW}Attempting SQLAlchemy create_all() fallback...${NC}"
+ poetry run python - <<'PY'
+import importlib
+import os
+import sys
+import pkgutil
+
+from flask.cli import ScriptInfo
+
+app_path = os.environ.get("FLASK_APP")
+if not app_path:
+ print("FLASK_APP is not set; cannot load app.", file=sys.stderr)
+ sys.exit(1)
+
+info = ScriptInfo(app_import_path=app_path)
+app = info.load_app()
+
+with app.app_context():
+ try:
+ import statsservice
+ for _, modname, _ in pkgutil.walk_packages(statsservice.__path__, statsservice.__name__ + "."):
+ try:
+ importlib.import_module(modname)
+ except Exception:
+ pass
+ except Exception:
+ for mod in (
+ "statsservice.models",
+ "statsservice.model",
+ "statsservice.api.v1",
+ "statsservice.api",
+ ):
+ try:
+ importlib.import_module(mod)
+ except Exception:
+ pass
+
+ ext = app.extensions.get("sqlalchemy")
+ db = None
+ if ext is not None:
+ db = ext if hasattr(ext, "create_all") else getattr(ext, "db", None)
+ if db is None:
+ for mod in ("statsservice.extensions", "statsservice.models", "statsservice"):
+ try:
+ candidate = importlib.import_module(mod)
+ if hasattr(candidate, "db"):
+ db = candidate.db
+ break
+ except Exception:
+ pass
+
+ if db is None or not hasattr(db, "create_all"):
+ print("Could not resolve SQLAlchemy db instance.", file=sys.stderr)
+ sys.exit(1)
+
+ db.create_all()
+ print("Schema created via SQLAlchemy create_all().")
+PY
+ }
+
+ # Install npm dependencies
+ echo -e "${YELLOW}Installing npm dependencies...${NC}"
+ npm ci
+
+ ensure_python_deps
+
+ write_instance_config
+
+ # Initialize database
+ echo -e "${YELLOW}Initializing database...${NC}"
+ if flask_has_command "db_create"; then
+ run_flask db_create
+ fi
+
+ if flask_has_command "db_init"; then
+ run_flask db_init
+ fi
+
+ if flask_has_command "db"; then
+ if ! db_has_tables; then
+ if create_schema_fallback; then
+ echo -e "${YELLOW}Empty database detected. Stamping head after create_all...${NC}"
+ run_db_stamp_head
+ else
+ echo -e "${YELLOW}Schema fallback failed on empty database.${NC}"
+ exit 1
+ fi
+ else
+ if ! UPGRADE_OUT=$(run_db_upgrade 2>&1); then
+ if echo "${UPGRADE_OUT}" | grep -q "DuplicateColumn"; then
+ echo -e "${YELLOW}Migration already applied (duplicate column). Stamping head...${NC}"
+ run_db_stamp_head
+ elif create_schema_fallback; then
+ echo -e "${YELLOW}Schema created. Stamping head to match models...${NC}"
+ run_db_stamp_head
+ else
+ echo -e "${YELLOW}Database upgrade failed and schema fallback failed.${NC}"
+ exit 1
+ fi
+ fi
+ fi
+ elif ! flask_has_command "db_create" && ! flask_has_command "db_init"; then
+ echo -e "${YELLOW}No database init/create/upgrade command found in Flask CLI.${NC}"
+ fi
+
+ # Create admin client
+ echo -e "${YELLOW}Creating admin client...${NC}"
+ if flask_has_command "client_create"; then
+ run_flask client_create --name ADMIN --role admin
+
+ # Create client for MONARC and capture the API key
+ echo -e "${YELLOW}Creating MONARC client...${NC}"
+ run_flask client_create --name admin_localhost
+ else
+ echo -e "${YELLOW}No client_create command found in Flask CLI. Skipping client setup.${NC}"
+ fi
+
+ # Mark initialization as complete
+ touch /var/www/stats-service/.docker-initialized
+ echo -e "${GREEN}Stats service initialization complete!${NC}"
+else
+ echo -e "${GREEN}Stats service already initialized, starting...${NC}"
+ cd /var/www/stats-service
+ ensure_flask_app
+ ensure_python_deps
+ write_instance_config
+fi
+
+# Start the Flask application
+write_health_wrapper
+export STATS_BASE_APP="${BASE_FLASK_APP}"
+export FLASK_APP="monarc_health_app.py"
+echo -e "${GREEN}Starting Flask application on ${STATS_HOST}:${STATS_PORT}${NC}"
+exec poetry run flask --app "${FLASK_APP}" run --host=${STATS_HOST} --port=${STATS_PORT}
diff --git a/docker/db-init/10-dev-grants.sh b/docker/db-init/10-dev-grants.sh
new file mode 100644
index 0000000..edb0e81
--- /dev/null
+++ b/docker/db-init/10-dev-grants.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -e
+
+if [ -z "$DBUSER_MONARC" ]; then
+ echo "DBUSER_MONARC is not set; skipping dev grants."
+ exit 0
+fi
+
+mysql -u root -p"$MYSQL_ROOT_PASSWORD" <