diff --git a/.env.example b/.env.example index 709b64bd9..7ec0a7a8a 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ APP_ENV=local -APP_KEY=base64:ilyL17PxJTnZXqNBVr+P36ARu9NF7HgqJ87ciGWvo5E= +# Generate with: php artisan key:generate +APP_KEY= APP_DEBUG=false APP_TIMEZONE=UTC APP_URL=http://localhost @@ -16,10 +17,24 @@ BCRYPT_ROUNDS=12 DB_CONNECTION=sqlite DB_DATABASE=database/database.sqlite +# Deployed environments use mysql and need the following instead: +# DB_CONNECTION=mysql +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_USERNAME= +# DB_PASSWORD= +# Used only by the opt-in mysql_testing connection in phpunit.xml +# DB_DATABASE_TESTING= + +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_PASSWORD= CACHE_STORE=array SESSION_DRIVER=array -QUEUE_DRIVER=sync +# QUEUE_CONNECTION, not QUEUE_DRIVER: the old Laravel 4/5 name is ignored by the +# framework. Must not be 'sync' on any deployed environment. +QUEUE_CONNECTION=sync SCOUT_DRIVER=null ALGOLIA_APP_ID= @@ -29,8 +44,8 @@ ALGOLIA_SECRET= AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=eu-west-1 -AWS_BUCKET=codeweek-dev -AWS_URL=https://codeweek-s3.s3.amazonaws.com/ +AWS_BUCKET= +AWS_URL= LOG_CHANNEL=stack LOG_STACK=single @@ -48,6 +63,81 @@ SESSION_DOMAIN=null AUTH_MODEL=App\User +# --------------------------------------------------------------------------- +# Mail. Required in every deployed environment; the app sends certificates, +# contact form submissions and moderation notices. +# --------------------------------------------------------------------------- +MAIL_MAILER=smtp +MAIL_HOST= +MAIL_PORT=587 +MAIL_USERNAME= +MAIL_PASSWORD= +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS= +MAIL_FROM_NAME="EU Code Week" +# Falls back to ADMIN_EMAIL when unset +CONTACT_FORM_RECIPIENT_EMAIL= + +# --------------------------------------------------------------------------- +# Social login. LoginController allowlists twitter, github, google, facebook. +# --------------------------------------------------------------------------- +TWITTER_CLIENT_ID= +TWITTER_CLIENT_SECRET= +TWITTER_REDIRECT= +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +GITHUB_REDIRECT= +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT= +FACEBOOK_CLIENT_ID= +FACEBOOK_CLIENT_SECRET= +FACEBOOK_REDIRECT= + +# --------------------------------------------------------------------------- +# Maps and geolocation +# --------------------------------------------------------------------------- +MAPS_MAPBOX_ACCESS_TOKEN= +MAP_TILES= +MAXMIND_USER_ID= +MAXMIND_LICENSE_KEY= +IPAPI_KEY= + +# --------------------------------------------------------------------------- +# Certificates. PDFLATEX_PATH must point at a real pdflatex binary or the +# whole certificate pipeline fails. See docs/handover/08-certificates.md +# --------------------------------------------------------------------------- +PDFLATEX_PATH= + +# --------------------------------------------------------------------------- +# Storage. RESOURCES_BUCKET is a second bucket for Learn & Teach PDFs, and is +# commonly pointed at production even on dev - check before importing. +# --------------------------------------------------------------------------- +FILESYSTEM_CLOUD=s3 +RESOURCES_BUCKET= +RESOURCES_URL= +BULK_UPLOAD_TEMP_DISK=local +RESOURCES_IMPORT_TEMP_DISK=local + +# --------------------------------------------------------------------------- +# Bot protection (Cloudflare Turnstile). Either TURNSTILE_SECRET or +# TURNSTILE_SECRET_KEY is accepted; verification is skipped when both are blank. +# --------------------------------------------------------------------------- +TURNSTILE_SITEKEY= +TURNSTILE_SECRET= + +# --------------------------------------------------------------------------- +# Partner feeds and integrations +# --------------------------------------------------------------------------- +BLOG_URL= +EEDUCATION_CLIENTID= +RELOCATION_COUNTRY= +# Bearer token for the internal support API +SUPPORT_SERVICE_TOKEN= + +# Error monitoring +SENTRY_LARAVEL_DSN= + # --------------------------------------------------------------------------- # Support AI copilot (Phase 1: AI triage + frontend code PRs) # --------------------------------------------------------------------------- diff --git a/.env.travis b/.env.travis deleted file mode 100644 index 9179d2548..000000000 --- a/.env.travis +++ /dev/null @@ -1,32 +0,0 @@ -APP_ENV=local -APP_KEY=base64:ilyL17PxJTnZXqNBVr+P36ARu9NF7HgqJ87ciGWvo5E= -APP_DEBUG=false -APP_URL=http://127.0.0.1:8000 - -DB_CONNECTION=mysql -DB_HOST=127.0.0.1 -DB_PORT=3306 -DB_DATABASE=codeweek -DB_USERNAME=root -DB_PASSWORD= - -CACHE_STORE=array -SESSION_DRIVER=array -QUEUE_DRIVER=sync - -SCOUT_DRIVER=null -ALGOLIA_APP_ID= -ALGOLIA_KEY= -ALGOLIA_SECRET= - -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_DEFAULT_REGION=eu-west-1 -AWS_BUCKET=codeweek-dev -AWS_URL=https://codeweek-s3.s3.amazonaws.com/ - -LOG_CHANNEL=stack - -LOCALES=al,ba,bg,cs,da,de,el,en,es,et,fi,fr,hr,hu,it,lt,lv,me,mk,nl,pl,pt,ro,rs,sk,sl,sv - -ADMIN_EMAIL=admin@codeweek.test diff --git a/.github/workflows/laravel.yml b/.github/workflows/laravel.yml index 830376b1c..7eb2fb311 100644 --- a/.github/workflows/laravel.yml +++ b/.github/workflows/laravel.yml @@ -2,9 +2,9 @@ name: Laravel on: push: - branches: [ master ] + branches: [ master, dev ] pull_request: - branches: [ master ] + branches: [ master, dev ] jobs: laravel-tests: diff --git a/.gitignore b/.gitignore index cda6901a5..4906882fa 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,16 @@ vite.config.js resources/excel/example.xlsx resources/ai-agent-plans/* /docs/internal/ + +# pdflatex writes personalised .tex files and its logs into resources/latex at +# runtime. Only the templates belong in git. +resources/latex/*.aux +resources/latex/*.log +/texput.log + +# Debugging leftovers that have been committed by accident before +cookies.txt +changed_files.txt +differences.diff +/bom.json +/phpunit.xml.bak diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ba56f3984..000000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: php -php: -- 7.3 - -before_install: - - echo "memory_limit=2G" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - -install: - - echo "{\"http-basic\":{\"nova.laravel.com\":{\"username\":\"${NOVA_USERNAME}\",\"password\":\"${NOVA_PASSWORD}\"}}}" > auth.json - -before_script: - - cp .env.travis .env - - travis_retry composer self-update - - if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi; - - travis_retry composer install ${COMPOSER_FLAGS} --no-interaction --prefer-source - - php artisan vue-i18n:generate - - yarn install - - npm run build - - php artisan key:generate - -cache: - directories: - - $HOME/.composer/cache - -env: - matrix: - - COMPOSER_FLAGS="" - global: - - secure: PYuzncHIaUzAGEKdptcoFACE7YPtixwiy/2tN1lQRwszqnBh1pr6wGzoWMxZZxYrfZqnM10RO9kvcXcJPnnihfO/BTbEuvNkLC+Uquf7+0elO/we2UJr/BezIFW1WczZQGIxxJEpwNHYfb8zRaXjgecSaEGq+lcas4Vkny76WB1Ty5IeefSsQPOaeJ0J1ScRz7+wvsG6FE2DtIqHyvZ3P9+OCN+dbvfSnuIo3NM4/ysHuubHT8sy/sPx/D5jFefPXaApfXC8/alGPF3qadQptS5IJ+NhHU6qQKGS0lH3ZXI6YnHgykpdjLUMEfk3pwz90k3NjpsmI+goqExpI4kgfDej8NSBxiW+mM0s7Bp8gF+br7GcRHjhAe5um9ftnVzs2nt7GrartfVQY7uN527o6yrZkibHHB0ENwiTjgRhuAfypzgys5jLjWvf7868CPdqsnz9XdVfbeN2KwwFdUy6t67S3LEfG1jTObrCS3FNUnkNgBPuPA20QAyNYVLjuYmqmHG2GzIQrv/UmOGhWPg/ulr5N9uqqV28o3Bpf771TwXik92mlcQUGhszexuWc+CkTs/5DIRruzJkJgt0BUuw136mnHy6+3LMt7igyrFFMZITG/4RLjR3QaCrOkXFTPGjSWqiIBSqqlKlJWrl2k2Nav9O+ESMC37ABHzRxz03wNE= - - secure: WfOFwRSiwK5n+p8GbS/Lf/Tr4IEENodhUOXYjDQ6BhPdRFZVOlXRh/phHuLif8gwHEyztytzWDdP7sARjBt47u7X8qkYwsuz6B4yWrWbhuHiF4DKVN8474NaVUcksHK/8vV+41lIqIwk+ifMw+4bt+ldkYiLlgK5inenNHCmiUICcU7gio6D5jwJ1eLKc0GG+uduRkNAeFAGvOKWeD7Mf6yIl5iosuiXfsGtX9/6byg1XALhWuyruWU4d+vRjChZlt/8JPfgXytVOMCOqY2vMZtFit1JJAfNZiZQhBgl/2tKLmWfGpNG9weKnRYDxU+THz4LAn/SCMZMiFhWVZnU1zJ26Zx/DyrusZO5F9pr+ysBmyWD6NQAgRD4jFjIzCbRnjgPoxc1p1m8qIxTsPyf37MwS3sFHrAJe9pcAV8SZzqzN9FF1kWK8EWTYyQdoRmUS54buVuUi5C4p8wgmd3FOEhiME+C+xjTmVD/tWddRlv0Gh0cpf49N08Y/f6zf9dFBhFooAFKLiY6fpte9gi2jhj2g+UicYK7R8ZgcGEqVP/SkpiQNBdxVpDBkgMKXY/9MOgbN60yH/XHqvmaNHislHI2SL3vZmu96dpEhhcByEKJw4ar7GPJDCi4UNYwScPdplNMl29cS0gWxRPhGiB+z0imRRYNGS+Mx2l5bdpGSqM= \ No newline at end of file diff --git a/app/Console/Commands/CertificatePreflight.php b/app/Console/Commands/CertificatePreflight.php index e2241881d..4deafd7d9 100644 --- a/app/Console/Commands/CertificatePreflight.php +++ b/app/Console/Commands/CertificatePreflight.php @@ -9,7 +9,7 @@ class CertificatePreflight extends Command { protected $signature = 'certificate:preflight - {--edition=2025 : Target edition year} + {--edition= : Target edition year (defaults to the current year)} {--type=all : excellence|super-organiser|all} {--limit=0 : Max records to test (0 = all)} {--batch-size=500 : Process in batches; 0 = single run} @@ -22,7 +22,7 @@ class CertificatePreflight extends Command public function handle(): int { - $edition = (int) $this->option('edition'); + $edition = (int) ($this->option('edition') ?: date('Y')); $typeOption = strtolower(trim((string) $this->option('type'))); $limit = max(0, (int) $this->option('limit')); $batchSize = max(0, (int) $this->option('batch-size')); diff --git a/app/Http/Controllers/ContactFormController.php b/app/Http/Controllers/ContactFormController.php index cf61e826d..7068672a5 100644 --- a/app/Http/Controllers/ContactFormController.php +++ b/app/Http/Controllers/ContactFormController.php @@ -17,6 +17,8 @@ public function submit(Request $request) abort(403, 'Spam detected.'); } + $turnstileSecret = config('codeweek.turnstile_secret'); + $validated = $request->validate([ 'first_name' => 'required|string|max:255', 'last_name' => 'required|string|max:255', @@ -25,13 +27,13 @@ public function submit(Request $request) 'subject' => 'required|string', 'message' => 'required|string', 'terms' => 'accepted', - 'cf-turnstile-response' => env('TURNSTILE_SECRET_KEY') ? 'required' : 'nullable', + 'cf-turnstile-response' => $turnstileSecret ? 'required' : 'nullable', ]); // Verify CAPTCHA via Cloudflare Turnstile - if (env('TURNSTILE_SECRET_KEY')) { + if ($turnstileSecret) { $response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ - 'secret' => env('TURNSTILE_SECRET_KEY'), + 'secret' => $turnstileSecret, 'response' => $request->input('cf-turnstile-response'), 'remoteip' => $request->ip(), ]); @@ -51,7 +53,7 @@ public function submit(Request $request) $view = view()->exists("emails.$locale.contact") ? "emails.$locale.contact" : 'emails.en.contact'; Mail::send($view, ['data' => $validated], function ($message) use ($validated) { - $message->to(env('CONTACT_FORM_RECIPIENT_EMAIL', 'bernard@matrixinternet.ie')) + $message->to(config('codeweek.contact_form_recipient')) ->subject('New Contact Form Submission') ->replyTo($validated['email'], $validated['first_name'] . ' ' . $validated['last_name']); }); diff --git a/app/Http/Controllers/ImporterController.php b/app/Http/Controllers/ImporterController.php deleted file mode 100644 index ac7f4d834..000000000 --- a/app/Http/Controllers/ImporterController.php +++ /dev/null @@ -1,79 +0,0 @@ -year, 2014, -1); - - $selectedYear = $request->input('year') ? $request->input('year') : Carbon::now()->year; - - $iso_country_of_user = User::getGeoIPData()->iso_code; - - return view('map')->with([ - 'years' => $years, - 'selectedYear' => $selectedYear, - 'current_country_iso' => $iso_country_of_user, - - ]); - } -} diff --git a/app/Nova/Actions/PromoteAmbassador.php b/app/Nova/Actions/PromoteAmbassador.php deleted file mode 100644 index 43e4c94f7..000000000 --- a/app/Nova/Actions/PromoteAmbassador.php +++ /dev/null @@ -1,34 +0,0 @@ -where('country_iso', "=", $request->user()->country_iso); - return $query->join('model_has_roles', 'users.id', '=', 'model_has_roles.model_id') - ->where('model_has_roles.role_id', '=', 4) - ->select('users.*'); - + // Matched by role name, not id: the previous `role_id = 4` only held while + // the seeders had run in their original order. + return $query->whereHas('roles', function ($roles) { + $roles->where('name', 'ambassador'); + }); } } diff --git a/app/Nova/Country.php b/app/Nova/Country.php index bab50023f..cb2c83c9e 100644 --- a/app/Nova/Country.php +++ b/app/Nova/Country.php @@ -99,9 +99,9 @@ public static function indexQuery(NovaRequest $request, $query) if ($request->user()->isAmbassador()) { return $query - ->where('iso', '=', 'FR'); - + ->where('iso', '=', $request->user()->country_iso); } + return $query; } } diff --git a/app/Nova/Filters/UserStatus.php b/app/Nova/Filters/UserStatus.php deleted file mode 100644 index 0e3ac0529..000000000 --- a/app/Nova/Filters/UserStatus.php +++ /dev/null @@ -1,33 +0,0 @@ -join('model_has_roles', 'users.id', '=', 'model_has_roles.model_id') - ->where('model_has_roles.role_id', '=', $value) - ->select('users.*'); - } - - /** - * Get the filter's available options. - */ - public function options(Request $request): array - { - return [ - 'Administrator' => 5, - 'Ambassador' => 3, - ]; - } -} diff --git a/bom.json b/bom.json deleted file mode 100644 index 4a1cae4fe..000000000 --- a/bom.json +++ /dev/null @@ -1,8974 +0,0 @@ -{ - "$schema": "http://cyclonedx.org/schema/bom-1.3.schema.json", - "bomFormat": "CycloneDX", - "specVersion": "1.3", - "version": 1, - "metadata": { - "tools": [ - { - "vendor": "cyclonedx", - "name": "cyclonedx-php-composer", - "version": "3.11.0" - } - ], - "component": { - "bom-ref": "pkg:composer/laravel/laravel@dev-master", - "type": "application", - "name": "laravel", - "version": "dev-master", - "group": "laravel", - "description": "The Laravel Framework.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/laravel@dev-master" - } - }, - "components": [ - { - "bom-ref": "pkg:composer/asm89/stack-cors@2.1.1", - "type": "library", - "name": "stack-cors", - "version": "2.1.1", - "group": "asm89", - "description": "Cross-origin resource sharing library and stack middleware", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/asm89/stack-cors@2.1.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/asm89/stack-cors.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=73e5b88775c64ccc0b84fb60836b30dc9d92ac4a)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/asm89/stack-cors/zipball/73e5b88775c64ccc0b84fb60836b30dc9d92ac4a", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=73e5b88775c64ccc0b84fb60836b30dc9d92ac4a & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/asm89/stack-cors", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/asm89/stack-cors/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/asm89/stack-cors/tree/v2.1.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/aws/aws-crt-php@1.0.4", - "type": "library", - "name": "aws-crt-php", - "version": "1.0.4", - "group": "aws", - "description": "AWS Common Runtime for PHP", - "licenses": [ - { - "license": { - "id": "Apache-2.0" - } - } - ], - "purl": "pkg:composer/aws/aws-crt-php@1.0.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/awslabs/aws-crt-php.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f5c64ee7c5fce196e2519b3d9b7138649efe032d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/f5c64ee7c5fce196e2519b3d9b7138649efe032d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f5c64ee7c5fce196e2519b3d9b7138649efe032d & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://aws.amazon.com/sdkforphp", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/awslabs/aws-crt-php/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/awslabs/aws-crt-php/tree/v1.0.4", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/aws/aws-sdk-php@3.258.3", - "type": "library", - "name": "aws-sdk-php", - "version": "3.258.3", - "group": "aws", - "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", - "licenses": [ - { - "license": { - "id": "Apache-2.0" - } - } - ], - "purl": "pkg:composer/aws/aws-sdk-php@3.258.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/aws/aws-sdk-php.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=57cbc06827148d0d4d3f5dbe4b948daa20f82d70)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/57cbc06827148d0d4d3f5dbe4b948daa20f82d70", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=57cbc06827148d0d4d3f5dbe4b948daa20f82d70 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://aws.amazon.com/sdkforphp", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "support", - "url": "https://forums.aws.amazon.com/forum.jspa?forumID=80", - "comment": "As set via `support.forum` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/aws/aws-sdk-php/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/aws/aws-sdk-php/tree/3.258.3", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/brick/math@0.9.3", - "type": "library", - "name": "math", - "version": "0.9.3", - "group": "brick", - "description": "Arbitrary-precision arithmetic library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/brick/math@0.9.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/brick/math.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ca57d18f028f84f777b2168cd1911b0dee2343ae)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ca57d18f028f84f777b2168cd1911b0dee2343ae & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/brick/math/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/brick/math/tree/0.9.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/BenMorel", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/brick/math", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/brick/money@0.5.3", - "type": "library", - "name": "money", - "version": "0.5.3", - "group": "brick", - "description": "Money and currency library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/brick/money@0.5.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/brick/money.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=49e6597470da74f6a9f1dd7d5286ea3b4756b7e0)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/brick/money/zipball/49e6597470da74f6a9f1dd7d5286ea3b4756b7e0", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=49e6597470da74f6a9f1dd7d5286ea3b4756b7e0 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/brick/money/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/brick/money/tree/0.5.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/BenMorel", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/brick/money", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/cakephp/chronos@2.3.2", - "type": "library", - "name": "chronos", - "version": "2.3.2", - "group": "cakephp", - "description": "A simple API extension for DateTime.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/cakephp/chronos@2.3.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/cakephp/chronos.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=a21b7b633f483c4cf525d200219d200f551ee38b)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/a21b7b633f483c4cf525d200219d200f551ee38b", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=a21b7b633f483c4cf525d200219d200f551ee38b & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://cakephp.org", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/cakephp/chronos/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/cakephp/chronos", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/composer/semver@3.3.2", - "type": "library", - "name": "semver", - "version": "3.3.2", - "group": "composer", - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/composer/semver@3.3.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/composer/semver.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3953f23262f2bff1919fc82183ad9acb13ff62c9)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3953f23262f2bff1919fc82183ad9acb13ff62c9 & sha1=UNDEFINED)" - }, - { - "type": "chat", - "url": "irc://irc.freenode.org/composer", - "comment": "As set via `support.irc` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/composer/semver/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/composer/semver/tree/3.3.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://packagist.com", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/composer", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/ctessier/nova-advanced-image-field@1.3.3", - "type": "library", - "name": "nova-advanced-image-field", - "version": "1.3.3", - "group": "ctessier", - "description": "An advanced image field for Nova with crop and resize.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/ctessier/nova-advanced-image-field@1.3.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/ctessier/nova-advanced-image-field.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=9d52c56d92192638cd775b4800d6f73cfc0978fb)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/ctessier/nova-advanced-image-field/zipball/9d52c56d92192638cd775b4800d6f73cfc0978fb", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=9d52c56d92192638cd775b4800d6f73cfc0978fb & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/ctessier/nova-advanced-image-field/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/ctessier/nova-advanced-image-field/tree/v1.3.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sponsors/ctessier", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/dflydev/dot-access-data@3.0.2", - "type": "library", - "name": "dot-access-data", - "version": "3.0.2", - "group": "dflydev", - "description": "Given a deep data structure, access data by dot notation.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/dflydev/dot-access-data@3.0.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f41715465d65213d644d3141a6a93081be5d3549)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f41715465d65213d644d3141a6a93081be5d3549 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/dflydev/dflydev-dot-access-data", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/doctrine/cache@2.2.0", - "type": "library", - "name": "cache", - "version": "2.2.0", - "group": "doctrine", - "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/doctrine/cache@2.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/doctrine/cache.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=1ca8f21980e770095a31456042471a57bc4c68fb)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=1ca8f21980e770095a31456042471a57bc4c68fb & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.doctrine-project.org/projects/cache.html", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/doctrine/cache/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/doctrine/cache/tree/2.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.doctrine-project.org/sponsorship.html", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.patreon.com/phpdoctrine", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/doctrine/dbal@2.13.9", - "type": "library", - "name": "dbal", - "version": "2.13.9", - "group": "doctrine", - "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/doctrine/dbal@2.13.9", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/doctrine/dbal.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c480849ca3ad6706a39c970cdfe6888fa8a058b8)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/c480849ca3ad6706a39c970cdfe6888fa8a058b8", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c480849ca3ad6706a39c970cdfe6888fa8a058b8 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.doctrine-project.org/projects/dbal.html", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/doctrine/dbal/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/doctrine/dbal/tree/2.13.9", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.doctrine-project.org/sponsorship.html", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.patreon.com/phpdoctrine", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/doctrine/deprecations@1.0.0", - "type": "library", - "name": "deprecations", - "version": "1.0.0", - "group": "doctrine", - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/doctrine/deprecations@1.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/doctrine/deprecations.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.doctrine-project.org/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/doctrine/deprecations/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/doctrine/deprecations/tree/v1.0.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/doctrine/event-manager@1.2.0", - "type": "library", - "name": "event-manager", - "version": "1.2.0", - "group": "doctrine", - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/doctrine/event-manager@1.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/doctrine/event-manager.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=95aa4cb529f1e96576f3fda9f5705ada4056a520)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=95aa4cb529f1e96576f3fda9f5705ada4056a520 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.doctrine-project.org/projects/event-manager.html", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/doctrine/event-manager/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/doctrine/event-manager/tree/1.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.doctrine-project.org/sponsorship.html", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.patreon.com/phpdoctrine", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/doctrine/inflector@2.0.6", - "type": "library", - "name": "inflector", - "version": "2.0.6", - "group": "doctrine", - "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/doctrine/inflector@2.0.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/doctrine/inflector.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=d9d313a36c872fd6ee06d9a6cbcf713eaa40f024)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=d9d313a36c872fd6ee06d9a6cbcf713eaa40f024 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.doctrine-project.org/projects/inflector.html", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/doctrine/inflector/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/doctrine/inflector/tree/2.0.6", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.doctrine-project.org/sponsorship.html", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.patreon.com/phpdoctrine", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/doctrine/lexer@3.0.0", - "type": "library", - "name": "lexer", - "version": "3.0.0", - "group": "doctrine", - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/doctrine/lexer@3.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/doctrine/lexer.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=84a527db05647743d50373e0ec53a152f2cde568)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=84a527db05647743d50373e0ec53a152f2cde568 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.doctrine-project.org/projects/lexer.html", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/doctrine/lexer/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/doctrine/lexer/tree/3.0.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.doctrine-project.org/sponsorship.html", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.patreon.com/phpdoctrine", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/dragonmantank/cron-expression@3.3.2", - "type": "library", - "name": "cron-expression", - "version": "3.3.2", - "group": "dragonmantank", - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/dragonmantank/cron-expression@3.3.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/dragonmantank/cron-expression.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=782ca5968ab8b954773518e9e49a6f892a34b2a8)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=782ca5968ab8b954773518e9e49a6f892a34b2a8 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/dragonmantank/cron-expression/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/dragonmantank", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/egulias/email-validator@4.0.1", - "type": "library", - "name": "email-validator", - "version": "4.0.1", - "group": "egulias", - "description": "A library for validating emails against several RFCs", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/egulias/email-validator@4.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/egulias/EmailValidator.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3a85486b709bc384dae8eb78fb2eec649bdb64ff)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3a85486b709bc384dae8eb78fb2eec649bdb64ff & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/egulias/EmailValidator", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/egulias/EmailValidator/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/egulias/EmailValidator/tree/4.0.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/egulias", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/ezyang/htmlpurifier@4.16.0", - "type": "library", - "name": "htmlpurifier", - "version": "4.16.0", - "group": "ezyang", - "description": "Standards compliant HTML filter written in PHP", - "licenses": [ - { - "license": { - "id": "LGPL-2.1-or-later" - } - } - ], - "purl": "pkg:composer/ezyang/htmlpurifier@4.16.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/ezyang/htmlpurifier.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=523407fb06eb9e5f3d59889b3978d5bfe94299c8)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=523407fb06eb9e5f3d59889b3978d5bfe94299c8 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://htmlpurifier.org/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/ezyang/htmlpurifier/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/fruitcake/laravel-cors@2.2.0", - "type": "library", - "name": "laravel-cors", - "version": "2.2.0", - "group": "fruitcake", - "description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Laravel application", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/fruitcake/laravel-cors@2.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/fruitcake/laravel-cors.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=783a74f5e3431d7b9805be8afb60fd0a8f743534)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/fruitcake/laravel-cors/zipball/783a74f5e3431d7b9805be8afb60fd0a8f743534", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=783a74f5e3431d7b9805be8afb60fd0a8f743534 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/fruitcake/laravel-cors/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/fruitcake/laravel-cors/tree/v2.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://fruitcake.nl", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/barryvdh", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/fruitcake/php-cors@1.2.0", - "type": "library", - "name": "php-cors", - "version": "1.2.0", - "group": "fruitcake", - "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/fruitcake/php-cors@1.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/fruitcake/php-cors.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=58571acbaa5f9f462c9c77e911700ac66f446d4e)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=58571acbaa5f9f462c9c77e911700ac66f446d4e & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/fruitcake/php-cors", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/fruitcake/php-cors/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/fruitcake/php-cors/tree/v1.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://fruitcake.nl", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/barryvdh", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/graham-campbell/result-type@1.1.0", - "type": "library", - "name": "result-type", - "version": "1.1.0", - "group": "graham-campbell", - "description": "An Implementation Of The Result Type", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/graham-campbell/result-type@1.1.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/GrahamCampbell/Result-Type.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=a878d45c1914464426dc94da61c9e1d36ae262a8)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/a878d45c1914464426dc94da61c9e1d36ae262a8", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=a878d45c1914464426dc94da61c9e1d36ae262a8 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/GrahamCampbell/Result-Type/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/GrahamCampbell", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/guzzlehttp/guzzle@7.5.0", - "type": "library", - "name": "guzzle", - "version": "7.5.0", - "group": "guzzlehttp", - "description": "Guzzle is a PHP HTTP client library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/guzzlehttp/guzzle@7.5.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/guzzle/guzzle.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=b50a2a1251152e43f6a37f0fa053e730a67d25ba)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=b50a2a1251152e43f6a37f0fa053e730a67d25ba & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/guzzle/guzzle/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/guzzle/guzzle/tree/7.5.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/GrahamCampbell", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://github.com/Nyholm", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/guzzlehttp/promises@1.5.2", - "type": "library", - "name": "promises", - "version": "1.5.2", - "group": "guzzlehttp", - "description": "Guzzle promises library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/guzzlehttp/promises@1.5.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/guzzle/promises.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=b94b2807d85443f9719887892882d0329d1e2598)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=b94b2807d85443f9719887892882d0329d1e2598 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/guzzle/promises/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/guzzle/promises/tree/1.5.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/GrahamCampbell", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://github.com/Nyholm", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/guzzlehttp/psr7@2.4.3", - "type": "library", - "name": "psr7", - "version": "2.4.3", - "group": "guzzlehttp", - "description": "PSR-7 message implementation that also provides common utility methods", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/guzzlehttp/psr7@2.4.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/guzzle/psr7.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=67c26b443f348a51926030c83481b85718457d3d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=67c26b443f348a51926030c83481b85718457d3d & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/guzzle/psr7/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/guzzle/psr7/tree/2.4.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/GrahamCampbell", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://github.com/Nyholm", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/intervention/image@2.7.2", - "type": "library", - "name": "image", - "version": "2.7.2", - "group": "intervention", - "description": "Image handling and manipulation library with support for Laravel integration", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/intervention/image@2.7.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/Intervention/image.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=04be355f8d6734c826045d02a1079ad658322dad)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=04be355f8d6734c826045d02a1079ad658322dad & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://image.intervention.io/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/Intervention/image/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/Intervention/image/tree/2.7.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://paypal.me/interventionio", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/Intervention", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/laravel-lang/lang@10.9.5", - "type": "library", - "name": "lang", - "version": "10.9.5", - "group": "laravel-lang", - "description": "Languages for Laravel", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel-lang/lang@10.9.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/Laravel-Lang/lang.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=e341421d40f2cd28feca24ab2cb84fa5cb5ddaf6)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/e341421d40f2cd28feca24ab2cb84fa5cb5ddaf6", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=e341421d40f2cd28feca24ab2cb84fa5cb5ddaf6 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/Laravel-Lang/lang/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/Laravel-Lang/lang", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://opencollective.com/laravel-lang", - "comment": "As set via `funding` in composer package definition. (type=open_collective)" - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/framework@9.50.2", - "type": "library", - "name": "framework", - "version": "9.50.2", - "group": "laravel", - "description": "The Laravel Framework.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/framework@9.50.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/framework.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=39932773c09658ddea9045958f305e67f9304995)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/framework/zipball/39932773c09658ddea9045958f305e67f9304995", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=39932773c09658ddea9045958f305e67f9304995 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://laravel.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/laravel/framework/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laravel/framework", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/helpers@1.6.0", - "type": "library", - "name": "helpers", - "version": "1.6.0", - "group": "laravel", - "description": "Provides backwards compatibility for helpers in the latest Laravel release.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/helpers@1.6.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/helpers.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=4dd0f9436d3911611622a6ced8329a1710576f60)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/helpers/zipball/4dd0f9436d3911611622a6ced8329a1710576f60", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=4dd0f9436d3911611622a6ced8329a1710576f60 & sha1=UNDEFINED)" - }, - { - "type": "distribution", - "url": "https://github.com/laravel/helpers/tree/v1.6.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/legacy-factories@1.3.2", - "type": "library", - "name": "legacy-factories", - "version": "1.3.2", - "group": "laravel", - "description": "The legacy version of the Laravel Eloquent factories.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/legacy-factories@1.3.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/legacy-factories.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=cc6720da81094c82ea9f4737d615dd3d71f7f43d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/cc6720da81094c82ea9f4737d615dd3d71f7f43d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=cc6720da81094c82ea9f4737d615dd3d71f7f43d & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://laravel.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/laravel/framework/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laravel/framework", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/nova@3.32.0?checksum=sha1:e0406c9d5eb280fa71eaa685e822089491e44b0c", - "type": "library", - "name": "nova", - "version": "3.32.0", - "group": "laravel", - "description": "A wonderful administration interface for Laravel.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "hashes": [ - { - "alg": "SHA-1", - "content": "e0406c9d5eb280fa71eaa685e822089491e44b0c" - } - ], - "purl": "pkg:composer/laravel/nova@3.32.0?checksum=sha1:e0406c9d5eb280fa71eaa685e822089491e44b0c", - "externalReferences": [ - { - "type": "distribution", - "url": "git@github.com:laravel/nova.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=32cec324c30f6ebc8e97c877ba5ca0ae95a18cee)" - }, - { - "type": "distribution", - "url": "https://nova.laravel.com/dist/laravel/nova/laravel-nova-32cec324c30f6ebc8e97c877ba5ca0ae95a18cee-zip-35edd0.zip", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=32cec324c30f6ebc8e97c877ba5ca0ae95a18cee & sha1=e0406c9d5eb280fa71eaa685e822089491e44b0c)", - "hashes": [ - { - "alg": "SHA-1", - "content": "e0406c9d5eb280fa71eaa685e822089491e44b0c" - } - ] - }, - { - "type": "distribution", - "url": "https://github.com/laravel/nova/tree/v3.32.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/sanctum@2.15.1", - "type": "library", - "name": "sanctum", - "version": "2.15.1", - "group": "laravel", - "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/sanctum@2.15.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/sanctum.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/laravel/sanctum/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laravel/sanctum", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/serializable-closure@1.3.0", - "type": "library", - "name": "serializable-closure", - "version": "1.3.0", - "group": "laravel", - "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/serializable-closure@1.3.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/serializable-closure.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f23fe9d4e95255dacee1bf3525e0810d1a1b0f37)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f23fe9d4e95255dacee1bf3525e0810d1a1b0f37 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/laravel/serializable-closure/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laravel/serializable-closure", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/socialite@5.6.1", - "type": "library", - "name": "socialite", - "version": "5.6.1", - "group": "laravel", - "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/socialite@5.6.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/socialite.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=a14a177f2cc71d8add71e2b19e00800e83bdda09)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/socialite/zipball/a14a177f2cc71d8add71e2b19e00800e83bdda09", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=a14a177f2cc71d8add71e2b19e00800e83bdda09 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://laravel.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/laravel/socialite/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laravel/socialite", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/tinker@2.8.0", - "type": "library", - "name": "tinker", - "version": "2.8.0", - "group": "laravel", - "description": "Powerful REPL for the Laravel framework.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/tinker@2.8.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/tinker.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=74d0b287cc4ae65d15c368dd697aae71d62a73ad)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/tinker/zipball/74d0b287cc4ae65d15c368dd697aae71d62a73ad", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=74d0b287cc4ae65d15c368dd697aae71d62a73ad & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/laravel/tinker/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laravel/tinker/tree/v2.8.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/ui@3.4.6", - "type": "library", - "name": "ui", - "version": "3.4.6", - "group": "laravel", - "description": "Laravel UI utilities and presets.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/ui@3.4.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/ui.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=65ec5c03f7fee2c8ecae785795b829a15be48c2c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/ui/zipball/65ec5c03f7fee2c8ecae785795b829a15be48c2c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=65ec5c03f7fee2c8ecae785795b829a15be48c2c & sha1=UNDEFINED)" - }, - { - "type": "distribution", - "url": "https://github.com/laravel/ui/tree/v3.4.6", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/league/commonmark@2.3.8", - "type": "library", - "name": "commonmark", - "version": "2.3.8", - "group": "league", - "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/league/commonmark@2.3.8", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/thephpleague/commonmark.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c493585c130544c4e91d2e0e131e6d35cb0cbc47)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c493585c130544c4e91d2e0e131e6d35cb0cbc47", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c493585c130544c4e91d2e0e131e6d35cb0cbc47 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://commonmark.thephpleague.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "documentation", - "url": "https://commonmark.thephpleague.com/", - "comment": "As set via `support.docs` in composer package definition." - }, - { - "type": "support", - "url": "https://github.com/thephpleague/commonmark/discussions", - "comment": "As set via `support.forum` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/thephpleague/commonmark/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "support", - "url": "https://github.com/thephpleague/commonmark/releases.atom", - "comment": "As set via `support.rss` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/thephpleague/commonmark", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.colinodell.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.paypal.me/colinpodell/10.00", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/colinodell", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/league/commonmark", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/league/config@1.2.0", - "type": "library", - "name": "config", - "version": "1.2.0", - "group": "league", - "description": "Define configuration arrays with strict schemas and access values with dot notation", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/league/config@1.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/thephpleague/config.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=754b3604fb2984c71f4af4a9cbe7b57f346ec1f3)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=754b3604fb2984c71f4af4a9cbe7b57f346ec1f3 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://config.thephpleague.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "documentation", - "url": "https://config.thephpleague.com/", - "comment": "As set via `support.docs` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/thephpleague/config/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "support", - "url": "https://github.com/thephpleague/config/releases.atom", - "comment": "As set via `support.rss` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/thephpleague/config", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.colinodell.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.paypal.me/colinpodell/10.00", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/colinodell", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/league/flysystem@3.12.2", - "type": "library", - "name": "flysystem", - "version": "3.12.2", - "group": "league", - "description": "File storage abstraction for PHP", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/league/flysystem@3.12.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/thephpleague/flysystem.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f6377c709d2275ed6feaf63e44be7a7162b0e77f)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/f6377c709d2275ed6feaf63e44be7a7162b0e77f", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f6377c709d2275ed6feaf63e44be7a7162b0e77f & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/thephpleague/flysystem/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/thephpleague/flysystem/tree/3.12.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://ecologi.com/frankdejonge", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/frankdejonge", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/league/flysystem-aws-s3-v3@3.12.2", - "type": "library", - "name": "flysystem-aws-s3-v3", - "version": "3.12.2", - "group": "league", - "description": "AWS S3 filesystem adapter for Flysystem.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/league/flysystem-aws-s3-v3@3.12.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=645e14e4a80bd2da8b01e57388e7296a695a80c2)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/645e14e4a80bd2da8b01e57388e7296a695a80c2", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=645e14e4a80bd2da8b01e57388e7296a695a80c2 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.12.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://ecologi.com/frankdejonge", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/frankdejonge", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/league/mime-type-detection@1.11.0", - "type": "library", - "name": "mime-type-detection", - "version": "1.11.0", - "group": "league", - "description": "Mime-type detection for Flysystem", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/league/mime-type-detection@1.11.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/thephpleague/mime-type-detection.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ff6248ea87a9f116e78edd6002e39e5128a0d4dd)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ff6248ea87a9f116e78edd6002e39e5128a0d4dd & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/thephpleague/mime-type-detection/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/frankdejonge", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/league/oauth1-client@1.10.1", - "type": "library", - "name": "oauth1-client", - "version": "1.10.1", - "group": "league", - "description": "OAuth 1.0 Client Library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/league/oauth1-client@1.10.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/thephpleague/oauth1-client.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=d6365b901b5c287dd41f143033315e2f777e1167)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=d6365b901b5c287dd41f143033315e2f777e1167 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/thephpleague/oauth1-client/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/livewire/livewire@2.11.2", - "type": "library", - "name": "livewire", - "version": "2.11.2", - "group": "livewire", - "description": "A front-end framework for Laravel.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/livewire/livewire@2.11.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/livewire/livewire.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=e92233f31dc1b88fb1ac242b32afe1ab7525ee05)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/livewire/livewire/zipball/e92233f31dc1b88fb1ac242b32afe1ab7525ee05", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=e92233f31dc1b88fb1ac242b32afe1ab7525ee05 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/livewire/livewire/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/livewire/livewire/tree/v2.11.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/livewire", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/maatwebsite/excel@3.1.46", - "type": "library", - "name": "excel", - "version": "3.1.46", - "group": "maatwebsite", - "description": "Supercharged Excel exports and imports in Laravel", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/maatwebsite/excel@3.1.46", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/SpartnerNL/Laravel-Excel.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ba0b9b9305d5b603c3938d4d1d4a13025c92c241)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/ba0b9b9305d5b603c3938d4d1d4a13025c92c241", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ba0b9b9305d5b603c3938d4d1d4a13025c92c241 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/SpartnerNL/Laravel-Excel/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.46", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://laravel-excel.com/commercial-support", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/patrickbrouwers", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/maennchen/zipstream-php@2.4.0", - "type": "library", - "name": "zipstream-php", - "version": "2.4.0", - "group": "maennchen", - "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/maennchen/zipstream-php@2.4.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/maennchen/ZipStream-PHP.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/maennchen/ZipStream-PHP/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/maennchen/ZipStream-PHP/tree/v2.4.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/maennchen", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://opencollective.com/zipstream", - "comment": "As set via `funding` in composer package definition. (type=open_collective)" - } - ] - }, - { - "bom-ref": "pkg:composer/markbaker/complex@3.0.2", - "type": "library", - "name": "complex", - "version": "3.0.2", - "group": "markbaker", - "description": "PHP Class for working with complex numbers", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/markbaker/complex@3.0.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/MarkBaker/PHPComplex.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=95c56caa1cf5c766ad6d65b6344b807c1e8405b9)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=95c56caa1cf5c766ad6d65b6344b807c1e8405b9 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/MarkBaker/PHPComplex", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/MarkBaker/PHPComplex/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/markbaker/matrix@3.0.1", - "type": "library", - "name": "matrix", - "version": "3.0.1", - "group": "markbaker", - "description": "PHP Class for working with matrices", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/markbaker/matrix@3.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/MarkBaker/PHPMatrix.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=728434227fe21be27ff6d86621a1b13107a2562c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=728434227fe21be27ff6d86621a1b13107a2562c & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/MarkBaker/PHPMatrix", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/MarkBaker/PHPMatrix/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/martinlindhe/laravel-vue-i18n-generator@dev-l9", - "type": "library", - "name": "laravel-vue-i18n-generator", - "version": "dev-l9", - "group": "martinlindhe", - "description": "Generates a vue-i18n compatible include file from your Laravel translations.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/martinlindhe/laravel-vue-i18n-generator@dev-l9", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/codeeu/laravel-vue-i18n-generator.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=53b45822beac98e635c18509c090c038afe81618)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/codeeu/laravel-vue-i18n-generator/zipball/53b45822beac98e635c18509c090c038afe81618", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=53b45822beac98e635c18509c090c038afe81618 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://github.com/martinlindhe/laravel-vue-i18n-generator", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/codeeu/laravel-vue-i18n-generator/tree/l9", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/mediconesystems/livewire-datatables@0.9.5", - "type": "library", - "name": "livewire-datatables", - "version": "0.9.5", - "group": "mediconesystems", - "description": "Fork of the livewire-datatables package. This has several modifications and additions.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/mediconesystems/livewire-datatables@0.9.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/MedicOneSystems/livewire-datatables.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c2931fb62f0e7d482dc73976fd87f3c0eb44905b)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/MedicOneSystems/livewire-datatables/zipball/c2931fb62f0e7d482dc73976fd87f3c0eb44905b", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c2931fb62f0e7d482dc73976fd87f3c0eb44905b & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/mediconesystems/livewire-datatables", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/MedicOneSystems/livewire-datatables/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/MedicOneSystems/livewire-datatables/tree/v0.9.5", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/monolog/monolog@2.9.1", - "type": "library", - "name": "monolog", - "version": "2.9.1", - "group": "monolog", - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/monolog/monolog@2.9.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/Seldaek/monolog.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f259e2b15fb95494c83f52d3caad003bbf5ffaa1)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f259e2b15fb95494c83f52d3caad003bbf5ffaa1 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/Seldaek/monolog", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/Seldaek/monolog/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/Seldaek/monolog/tree/2.9.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/Seldaek", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/mtdowling/jmespath.php@2.6.1", - "type": "library", - "name": "jmespath.php", - "version": "2.6.1", - "group": "mtdowling", - "description": "Declaratively specify how to extract elements from a JSON document", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/mtdowling/jmespath.php@2.6.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/jmespath/jmespath.php.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=9b87907a81b87bc76d19a7fb2d61e61486ee9edb)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=9b87907a81b87bc76d19a7fb2d61e61486ee9edb & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/jmespath/jmespath.php/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/jmespath/jmespath.php/tree/2.6.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/myclabs/php-enum@1.8.4", - "type": "library", - "name": "php-enum", - "version": "1.8.4", - "group": "myclabs", - "description": "PHP Enum implementation", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/myclabs/php-enum@1.8.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/myclabs/php-enum.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=a867478eae49c9f59ece437ae7f9506bfaa27483)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=a867478eae49c9f59ece437ae7f9506bfaa27483 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://github.com/myclabs/php-enum", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/myclabs/php-enum/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/myclabs/php-enum/tree/1.8.4", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/mnapoli", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/nesbot/carbon@2.66.0", - "type": "library", - "name": "carbon", - "version": "2.66.0", - "group": "nesbot", - "description": "An API extension for DateTime that supports 281 different languages.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/nesbot/carbon@2.66.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/briannesbitt/Carbon.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=496712849902241f04902033b0441b269effe001)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=496712849902241f04902033b0441b269effe001 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://carbon.nesbot.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "documentation", - "url": "https://carbon.nesbot.com/docs", - "comment": "As set via `support.docs` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/briannesbitt/Carbon/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/briannesbitt/Carbon", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sponsors/kylekatarnls", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://opencollective.com/Carbon#sponsor", - "comment": "As set via `funding` in composer package definition. (type=opencollective)" - }, - { - "type": "other", - "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/nette/schema@1.2.3", - "type": "library", - "name": "schema", - "version": "1.2.3", - "group": "nette", - "description": "\ud83d\udcd0 Nette Schema: validating data structures against a given Schema.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - }, - { - "license": { - "id": "GPL-2.0-only" - } - }, - { - "license": { - "id": "GPL-3.0-only" - } - } - ], - "purl": "pkg:composer/nette/schema@1.2.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/nette/schema.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=abbdbb70e0245d5f3bf77874cea1dfb0c930d06f)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=abbdbb70e0245d5f3bf77874cea1dfb0c930d06f & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://nette.org", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/nette/schema/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/nette/schema/tree/v1.2.3", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/nette/utils@4.0.0", - "type": "library", - "name": "utils", - "version": "4.0.0", - "group": "nette", - "description": "\ud83d\udee0 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - }, - { - "license": { - "id": "GPL-2.0-only" - } - }, - { - "license": { - "id": "GPL-3.0-only" - } - } - ], - "purl": "pkg:composer/nette/utils@4.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/nette/utils.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=cacdbf5a91a657ede665c541eda28941d4b09c1e)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=cacdbf5a91a657ede665c541eda28941d4b09c1e & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://nette.org", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/nette/utils/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/nette/utils/tree/v4.0.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/nikic/php-parser@4.15.3", - "type": "library", - "name": "php-parser", - "version": "4.15.3", - "group": "nikic", - "description": "A PHP parser written in PHP", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/nikic/php-parser@4.15.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/nikic/PHP-Parser.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=570e980a201d8ed0236b0a62ddf2c9cbb2034039)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=570e980a201d8ed0236b0a62ddf2c9cbb2034039 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/nikic/PHP-Parser/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/nikic/PHP-Parser/tree/v4.15.3", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/nunomaduro/termwind@1.15.0", - "type": "library", - "name": "termwind", - "version": "1.15.0", - "group": "nunomaduro", - "description": "Its like Tailwind CSS, but for the console.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/nunomaduro/termwind@1.15.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/nunomaduro/termwind.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=594ab862396c16ead000de0c3c38f4a5cbe1938d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/594ab862396c16ead000de0c3c38f4a5cbe1938d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=594ab862396c16ead000de0c3c38f4a5cbe1938d & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/nunomaduro/termwind/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/nunomaduro/termwind/tree/v1.15.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.paypal.com/paypalme/enunomaduro", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/nunomaduro", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://github.com/xiCO2k", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/phpoffice/phpspreadsheet@1.27.0", - "type": "library", - "name": "phpspreadsheet", - "version": "1.27.0", - "group": "phpoffice", - "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/phpoffice/phpspreadsheet@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=eeb8582f9cabf5a7f4ef78015691163233a1834f)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/eeb8582f9cabf5a7f4ef78015691163233a1834f", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=eeb8582f9cabf5a7f4ef78015691163233a1834f & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/PHPOffice/PhpSpreadsheet", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.27.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/phpoption/phpoption@1.9.0", - "type": "library", - "name": "phpoption", - "version": "1.9.0", - "group": "phpoption", - "description": "Option Type for PHP", - "licenses": [ - { - "license": { - "id": "Apache-2.0" - } - } - ], - "purl": "pkg:composer/phpoption/phpoption@1.9.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/schmittjoh/php-option.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=dc5ff11e274a90cc1c743f66c9ad700ce50db9ab)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dc5ff11e274a90cc1c743f66c9ad700ce50db9ab", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=dc5ff11e274a90cc1c743f66c9ad700ce50db9ab & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/schmittjoh/php-option/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/schmittjoh/php-option/tree/1.9.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/GrahamCampbell", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/predis/predis@1.1.10", - "type": "library", - "name": "predis", - "version": "1.1.10", - "group": "predis", - "description": "Flexible and feature-complete Redis client for PHP and HHVM", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/predis/predis@1.1.10", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/predis/predis.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/predis/predis/zipball/a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://github.com/predis/predis", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/predis/predis/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/predis/predis/tree/v1.1.10", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sponsors/tillkruss", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/psr/container@2.0.2", - "type": "library", - "name": "container", - "version": "2.0.2", - "group": "psr", - "description": "Common Container Interface (PHP FIG PSR-11)", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psr/container@2.0.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/php-fig/container.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c71ecc56dfe541dbd90c5360474fbc405f8d5963)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c71ecc56dfe541dbd90c5360474fbc405f8d5963 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/php-fig/container", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/php-fig/container/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/php-fig/container/tree/2.0.2", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/psr/event-dispatcher@1.0.0", - "type": "library", - "name": "event-dispatcher", - "version": "1.0.0", - "group": "psr", - "description": "Standard interfaces for event handling.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psr/event-dispatcher@1.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/php-fig/event-dispatcher.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=dbefd12671e8a14ec7f180cab83036ed26714bb0)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=dbefd12671e8a14ec7f180cab83036ed26714bb0 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/php-fig/event-dispatcher/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/php-fig/event-dispatcher/tree/1.0.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/psr/http-client@1.0.1", - "type": "library", - "name": "http-client", - "version": "1.0.1", - "group": "psr", - "description": "Common interface for HTTP clients", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psr/http-client@1.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/php-fig/http-client.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=2dfb5f6c5eff0e91e20e913f8c5452ed95b86621)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=2dfb5f6c5eff0e91e20e913f8c5452ed95b86621 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/php-fig/http-client", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/php-fig/http-client/tree/master", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/psr/http-factory@1.0.1", - "type": "library", - "name": "http-factory", - "version": "1.0.1", - "group": "psr", - "description": "Common interfaces for PSR-7 HTTP message factories", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psr/http-factory@1.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/php-fig/http-factory.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=12ac7fcd07e5b077433f5f2bee95b3a771bf61be)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=12ac7fcd07e5b077433f5f2bee95b3a771bf61be & sha1=UNDEFINED)" - }, - { - "type": "distribution", - "url": "https://github.com/php-fig/http-factory/tree/master", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/psr/http-message@1.0.1", - "type": "library", - "name": "http-message", - "version": "1.0.1", - "group": "psr", - "description": "Common interface for HTTP messages", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psr/http-message@1.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/php-fig/http-message.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f6561bf28d520154e4b0ec72be95418abe6d9363)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f6561bf28d520154e4b0ec72be95418abe6d9363 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/php-fig/http-message", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/php-fig/http-message/tree/master", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/psr/log@3.0.0", - "type": "library", - "name": "log", - "version": "3.0.0", - "group": "psr", - "description": "Common interface for logging libraries", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psr/log@3.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/php-fig/log.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=fe5ea303b0887d5caefd3d431c3e61ad47037001)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=fe5ea303b0887d5caefd3d431c3e61ad47037001 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/php-fig/log", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/php-fig/log/tree/3.0.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/psr/simple-cache@3.0.0", - "type": "library", - "name": "simple-cache", - "version": "3.0.0", - "group": "psr", - "description": "Common interfaces for simple caching", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psr/simple-cache@3.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/php-fig/simple-cache.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=764e0b3939f5ca87cb904f570ef9be2d78a07865)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=764e0b3939f5ca87cb904f570ef9be2d78a07865 & sha1=UNDEFINED)" - }, - { - "type": "distribution", - "url": "https://github.com/php-fig/simple-cache/tree/3.0.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/psy/psysh@0.11.12", - "type": "library", - "name": "psysh", - "version": "0.11.12", - "group": "psy", - "description": "An interactive shell for modern PHP.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/psy/psysh@0.11.12", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/bobthecow/psysh.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=52cb7c47d403c31c0adc9bf7710fc355f93c20f7)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/52cb7c47d403c31c0adc9bf7710fc355f93c20f7", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=52cb7c47d403c31c0adc9bf7710fc355f93c20f7 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://psysh.org", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/bobthecow/psysh/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/bobthecow/psysh/tree/v0.11.12", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/ralouphie/getallheaders@3.0.3", - "type": "library", - "name": "getallheaders", - "version": "3.0.3", - "group": "ralouphie", - "description": "A polyfill for getallheaders.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/ralouphie/getallheaders@3.0.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/ralouphie/getallheaders.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=120b605dfeb996808c31b6477290a714d356e822)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=120b605dfeb996808c31b6477290a714d356e822 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/ralouphie/getallheaders/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/ralouphie/getallheaders/tree/develop", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/ramsey/collection@2.0.0", - "type": "library", - "name": "collection", - "version": "2.0.0", - "group": "ramsey", - "description": "A PHP library for representing and manipulating collections.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/ramsey/collection@2.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/ramsey/collection.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/ramsey/collection/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/ramsey/collection/tree/2.0.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/ramsey", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/ramsey/uuid@4.7.3", - "type": "library", - "name": "uuid", - "version": "4.7.3", - "group": "ramsey", - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/ramsey/uuid@4.7.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/ramsey/uuid.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=433b2014e3979047db08a17a205f410ba3869cf2)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=433b2014e3979047db08a17a205f410ba3869cf2 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/ramsey/uuid/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/ramsey/uuid/tree/4.7.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/ramsey", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/rappasoft/laravel-livewire-tables@2.10.0", - "type": "library", - "name": "laravel-livewire-tables", - "version": "2.10.0", - "group": "rappasoft", - "description": "A dynamic table component for Laravel Livewire", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/rappasoft/laravel-livewire-tables@2.10.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/rappasoft/laravel-livewire-tables.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=8d6e6aa10585b7f66f29e3895ae6bac12a7bc2dc)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/rappasoft/laravel-livewire-tables/zipball/8d6e6aa10585b7f66f29e3895ae6bac12a7bc2dc", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=8d6e6aa10585b7f66f29e3895ae6bac12a7bc2dc & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/rappasoft/laravel-livewire-tables", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/rappasoft/laravel-livewire-tables/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/rappasoft/laravel-livewire-tables/tree/v2.10.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/rappasoft", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/reedware/laravel-relation-joins@3.0.0", - "type": "library", - "name": "laravel-relation-joins", - "version": "3.0.0", - "group": "reedware", - "description": "Adds the ability to join on a relationship by name.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/reedware/laravel-relation-joins@3.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/tylernathanreed/laravel-relation-joins.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=943620791dbf991dda06963967b0aaa28445768c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/tylernathanreed/laravel-relation-joins/zipball/943620791dbf991dda06963967b0aaa28445768c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=943620791dbf991dda06963967b0aaa28445768c & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/tylernathanreed/laravel-relation-joins/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/tylernathanreed/laravel-relation-joins/tree/v3.0.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/simplepie/simplepie@1.8.0", - "type": "library", - "name": "simplepie", - "version": "1.8.0", - "group": "simplepie", - "description": "A simple Atom/RSS parsing library for PHP", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/simplepie/simplepie@1.8.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/simplepie/simplepie.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=65b095d87bc00898d8fa7737bdbcda93a3fbcc55)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/simplepie/simplepie/zipball/65b095d87bc00898d8fa7737bdbcda93a3fbcc55", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=65b095d87bc00898d8fa7737bdbcda93a3fbcc55 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://simplepie.org/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/simplepie/simplepie/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/simplepie/simplepie/tree/1.8.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/socialiteproviders/manager@4.3.0", - "type": "library", - "name": "manager", - "version": "4.3.0", - "group": "socialiteproviders", - "description": "Easily add new or override built-in providers in Laravel Socialite.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/socialiteproviders/manager@4.3.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/SocialiteProviders/Manager.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=47402cbc5b7ef445317e799bf12fd5a12062206c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/47402cbc5b7ef445317e799bf12fd5a12062206c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=47402cbc5b7ef445317e799bf12fd5a12062206c & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://socialiteproviders.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/socialiteproviders/manager/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/socialiteproviders/manager", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/socialiteproviders/microsoft-azure@4.2.1", - "type": "library", - "name": "microsoft-azure", - "version": "4.2.1", - "group": "socialiteproviders", - "description": "Microsoft Azure OAuth2 Provider for Laravel Socialite", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/socialiteproviders/microsoft-azure@4.2.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/SocialiteProviders/Microsoft-Azure.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=64779ec21db0bee3111039a67c0fa0ab550a3462)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/64779ec21db0bee3111039a67c0fa0ab550a3462", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=64779ec21db0bee3111039a67c0fa0ab550a3462 & sha1=UNDEFINED)" - }, - { - "type": "distribution", - "url": "https://github.com/SocialiteProviders/Microsoft-Azure/tree/4.2.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/backtrace@1.2.1", - "type": "library", - "name": "backtrace", - "version": "1.2.1", - "group": "spatie", - "description": "A better backtrace", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/backtrace@1.2.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/backtrace.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=4ee7d41aa5268107906ea8a4d9ceccde136dbd5b)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/4ee7d41aa5268107906ea8a4d9ceccde136dbd5b", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=4ee7d41aa5268107906ea8a4d9ceccde136dbd5b & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/backtrace", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/spatie/backtrace/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/backtrace/tree/1.2.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sponsors/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://spatie.be/open-source/support-us", - "comment": "As set via `funding` in composer package definition. (type=other)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/commonmark-shiki-highlighter@2.1.1", - "type": "library", - "name": "commonmark-shiki-highlighter", - "version": "2.1.1", - "group": "spatie", - "description": "Highlight code blocks with league/commonmark and Shiki", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/commonmark-shiki-highlighter@2.1.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/commonmark-shiki-highlighter.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=52528a02f61d84030b313b5c7b0c4dd8edca7187)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/commonmark-shiki-highlighter/zipball/52528a02f61d84030b313b5c7b0c4dd8edca7187", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=52528a02f61d84030b313b5c7b0c4dd8edca7187 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/commonmark-shiki-highlighter", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/commonmark-shiki-highlighter/tree/2.1.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/flare-client-php@1.3.5", - "type": "library", - "name": "flare-client-php", - "version": "1.3.5", - "group": "spatie", - "description": "Send PHP errors to Flare", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/flare-client-php@1.3.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/flare-client-php.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3e5dd5ac4928f3d2d036bd02de5eb83fd0ef1f42)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/3e5dd5ac4928f3d2d036bd02de5eb83fd0ef1f42", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3e5dd5ac4928f3d2d036bd02de5eb83fd0ef1f42 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/flare-client-php", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/spatie/flare-client-php/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/flare-client-php/tree/1.3.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/ignition@1.4.3", - "type": "library", - "name": "ignition", - "version": "1.4.3", - "group": "spatie", - "description": "A beautiful error page for PHP applications.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/ignition@1.4.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/ignition.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=2cf3833220cfe8fcf639544f8d7067b6469a00b0)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/ignition/zipball/2cf3833220cfe8fcf639544f8d7067b6469a00b0", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=2cf3833220cfe8fcf639544f8d7067b6469a00b0 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://flareapp.io/ignition", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "documentation", - "url": "https://flareapp.io/docs/ignition-for-laravel/introduction", - "comment": "As set via `support.docs` in composer package definition." - }, - { - "type": "support", - "url": "https://twitter.com/flareappio", - "comment": "As set via `support.forum` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/spatie/ignition/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/ignition", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/laravel-activitylog@4.7.3", - "type": "library", - "name": "laravel-activitylog", - "version": "4.7.3", - "group": "spatie", - "description": "A very simple activity logger to monitor the users of your website or application", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/laravel-activitylog@4.7.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-activitylog.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ec65a478a909b8df1b4f0c3c45de2592ca7639e5)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/ec65a478a909b8df1b4f0c3c45de2592ca7639e5", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ec65a478a909b8df1b4f0c3c45de2592ca7639e5 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/activitylog", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/spatie/laravel-activitylog/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-activitylog/tree/4.7.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://spatie.be/open-source/support-us", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/laravel-cookie-consent@3.2.4", - "type": "library", - "name": "laravel-cookie-consent", - "version": "3.2.4", - "group": "spatie", - "description": "Make your Laravel app comply with the crazy EU cookie law", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/laravel-cookie-consent@3.2.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-cookie-consent.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=5a5b2f0a03cc66d8ffede42037a7f3e00f95aaa9)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/laravel-cookie-consent/zipball/5a5b2f0a03cc66d8ffede42037a7f3e00f95aaa9", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=5a5b2f0a03cc66d8ffede42037a7f3e00f95aaa9 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/cookie-consent", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-cookie-consent/tree/3.2.4", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://spatie.be/open-source/support-us", - "comment": "As set via `funding` in composer package definition. (type=custom)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/laravel-feed@4.2.1", - "type": "library", - "name": "laravel-feed", - "version": "4.2.1", - "group": "spatie", - "description": "Generate rss feeds", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/laravel-feed@4.2.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-feed.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=0b9b7df3f716c6067b082cd6a985126c2189a6c4)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/laravel-feed/zipball/0b9b7df3f716c6067b082cd6a985126c2189a6c4", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=0b9b7df3f716c6067b082cd6a985126c2189a6c4 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/laravel-feed", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-feed/tree/4.2.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://spatie.be/open-source/support-us", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/laravel-ignition@1.6.4", - "type": "library", - "name": "laravel-ignition", - "version": "1.6.4", - "group": "spatie", - "description": "A beautiful error page for Laravel applications.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/laravel-ignition@1.6.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-ignition.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=1a2b4bd3d48c72526c0ba417687e5c56b5cf49bc)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/1a2b4bd3d48c72526c0ba417687e5c56b5cf49bc", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=1a2b4bd3d48c72526c0ba417687e5c56b5cf49bc & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://flareapp.io/ignition", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "documentation", - "url": "https://flareapp.io/docs/ignition-for-laravel/introduction", - "comment": "As set via `support.docs` in composer package definition." - }, - { - "type": "support", - "url": "https://twitter.com/flareappio", - "comment": "As set via `support.forum` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/spatie/laravel-ignition/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-ignition", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/laravel-markdown@2.2.6", - "type": "library", - "name": "laravel-markdown", - "version": "2.2.6", - "group": "spatie", - "description": "A highly configurable markdown renderer and Blade component for Laravel", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/laravel-markdown@2.2.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-markdown.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=d3542637093fa661f1d5bd58eff509b3868bace8)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/laravel-markdown/zipball/d3542637093fa661f1d5bd58eff509b3868bace8", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=d3542637093fa661f1d5bd58eff509b3868bace8 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/laravel-markdown", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-markdown/tree/2.2.6", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/laravel-package-tools@1.14.1", - "type": "library", - "name": "laravel-package-tools", - "version": "1.14.1", - "group": "spatie", - "description": "Tools for creating Laravel packages", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/laravel-package-tools@1.14.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-package-tools.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=b477dd2f89d0751f0e51ffb3a70181f0bc7ab8df)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/b477dd2f89d0751f0e51ffb3a70181f0bc7ab8df", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=b477dd2f89d0751f0e51ffb3a70181f0bc7ab8df & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/laravel-package-tools", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/spatie/laravel-package-tools/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-package-tools/tree/1.14.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/laravel-permission@5.8.0", - "type": "library", - "name": "laravel-permission", - "version": "5.8.0", - "group": "spatie", - "description": "Permission handling for Laravel 6.0 and up", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/laravel-permission@5.8.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-permission.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=9f5a74f858d22ecefd9ee44a0ac64a95fd0bf755)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/9f5a74f858d22ecefd9ee44a0ac64a95fd0bf755", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=9f5a74f858d22ecefd9ee44a0ac64a95fd0bf755 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/laravel-permission", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/spatie/laravel-permission/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/laravel-permission/tree/5.8.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/once@3.1.0", - "type": "library", - "name": "once", - "version": "3.1.0", - "group": "spatie", - "description": "A magic memoization function", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/once@3.1.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/once.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=aab3fc8694e7caec98b2d330c4a8420391c7559d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/once/zipball/aab3fc8694e7caec98b2d330c4a8420391c7559d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=aab3fc8694e7caec98b2d330c4a8420391c7559d & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/once", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/once/tree/3.1.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://spatie.be/open-source/support-us", - "comment": "As set via `funding` in composer package definition. (type=custom)" - } - ] - }, - { - "bom-ref": "pkg:composer/spatie/shiki-php@1.3.0", - "type": "library", - "name": "shiki-php", - "version": "1.3.0", - "group": "spatie", - "description": "Highlight code using Shiki in PHP", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/spatie/shiki-php@1.3.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/spatie/shiki-php.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=34fe61405b405c735c82a9c56feffd3f7c5544ff)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/spatie/shiki-php/zipball/34fe61405b405c735c82a9c56feffd3f7c5544ff", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=34fe61405b405c735c82a9c56feffd3f7c5544ff & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/spatie/shiki-php", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/spatie/shiki-php/tree/1.3.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/spatie", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/stevebauman/purify@5.1.1", - "type": "library", - "name": "purify", - "version": "5.1.1", - "group": "stevebauman", - "description": "An HTML Purifier / Sanitizer for Laravel", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/stevebauman/purify@5.1.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/stevebauman/purify.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=639cf2fe17b45f2afaf0688be1316d4aad801f9a)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/stevebauman/purify/zipball/639cf2fe17b45f2afaf0688be1316d4aad801f9a", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=639cf2fe17b45f2afaf0688be1316d4aad801f9a & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/stevebauman/purify/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/stevebauman/purify/tree/v5.1.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/console@6.2.5", - "type": "library", - "name": "console", - "version": "6.2.5", - "group": "symfony", - "description": "Eases the creation of beautiful and testable command line interfaces", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/console@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/console.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3e294254f2191762c1d137aed4b94e966965e985)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3e294254f2191762c1d137aed4b94e966965e985 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/console/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/css-selector@6.2.5", - "type": "library", - "name": "css-selector", - "version": "6.2.5", - "group": "symfony", - "description": "Converts CSS selectors to XPath expressions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/css-selector@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/css-selector.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/css-selector/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/deprecation-contracts@3.2.0", - "type": "library", - "name": "deprecation-contracts", - "version": "3.2.0", - "group": "symfony", - "description": "A generic function and convention to trigger deprecation notices", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/deprecation-contracts@3.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/deprecation-contracts.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=1ee04c65529dea5d8744774d474e7cbd2f1206d3)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=1ee04c65529dea5d8744774d474e7cbd2f1206d3 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/error-handler@6.2.5", - "type": "library", - "name": "error-handler", - "version": "6.2.5", - "group": "symfony", - "description": "Provides tools to manage errors and ease debugging PHP code", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/error-handler@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/error-handler.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=0092696af0be8e6124b042fbe2890ca1788d7b28)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/0092696af0be8e6124b042fbe2890ca1788d7b28", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=0092696af0be8e6124b042fbe2890ca1788d7b28 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/error-handler/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/event-dispatcher@6.2.5", - "type": "library", - "name": "event-dispatcher", - "version": "6.2.5", - "group": "symfony", - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/event-dispatcher@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/event-dispatcher.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/event-dispatcher/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/event-dispatcher-contracts@3.2.0", - "type": "library", - "name": "event-dispatcher-contracts", - "version": "3.2.0", - "group": "symfony", - "description": "Generic abstractions related to dispatching event", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/event-dispatcher-contracts@3.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=0782b0b52a737a05b4383d0df35a474303cabdae)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0782b0b52a737a05b4383d0df35a474303cabdae", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=0782b0b52a737a05b4383d0df35a474303cabdae & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/finder@6.2.5", - "type": "library", - "name": "finder", - "version": "6.2.5", - "group": "symfony", - "description": "Finds files and directories via an intuitive fluent interface", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/finder@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/finder.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c90dc446976a612e3312a97a6ec0069ab0c2099c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c90dc446976a612e3312a97a6ec0069ab0c2099c & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/finder/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/http-foundation@6.2.6", - "type": "library", - "name": "http-foundation", - "version": "6.2.6", - "group": "symfony", - "description": "Defines an object-oriented layer for the HTTP specification", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/http-foundation@6.2.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/http-foundation.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=e8dd1f502bc2b3371d05092aa233b064b03ce7ed)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e8dd1f502bc2b3371d05092aa233b064b03ce7ed", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=e8dd1f502bc2b3371d05092aa233b064b03ce7ed & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/http-foundation/tree/v6.2.6", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/http-kernel@6.2.6", - "type": "library", - "name": "http-kernel", - "version": "6.2.6", - "group": "symfony", - "description": "Provides a structured process for converting a Request into a Response", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/http-kernel@6.2.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/http-kernel.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=7122db07b0d8dbf0de682267c84217573aee3ea7)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7122db07b0d8dbf0de682267c84217573aee3ea7", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=7122db07b0d8dbf0de682267c84217573aee3ea7 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/http-kernel/tree/v6.2.6", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/intl@6.2.5", - "type": "library", - "name": "intl", - "version": "6.2.5", - "group": "symfony", - "description": "Provides a PHP replacement layer for the C intl extension that includes additional data from the ICU library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/intl@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/intl.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3e5671e7676723db90a1b3c0b8b27e00407d69d5)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/intl/zipball/3e5671e7676723db90a1b3c0b8b27e00407d69d5", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3e5671e7676723db90a1b3c0b8b27e00407d69d5 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/intl/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/mailer@6.2.5", - "type": "library", - "name": "mailer", - "version": "6.2.5", - "group": "symfony", - "description": "Helps sending emails", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/mailer@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/mailer.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=29729ac0b4e5113f24c39c46746bd6afb79e0aaa)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/mailer/zipball/29729ac0b4e5113f24c39c46746bd6afb79e0aaa", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=29729ac0b4e5113f24c39c46746bd6afb79e0aaa & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/mailer/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/mime@6.2.5", - "type": "library", - "name": "mime", - "version": "6.2.5", - "group": "symfony", - "description": "Allows manipulating MIME messages", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/mime@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/mime.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=4b7b349f67d15cd0639955c8179a76c89f6fd610)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/mime/zipball/4b7b349f67d15cd0639955c8179a76c89f6fd610", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=4b7b349f67d15cd0639955c8179a76c89f6fd610 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/mime/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-ctype@1.27.0", - "type": "library", - "name": "polyfill-ctype", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill for ctype functions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-ctype@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-ctype.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=5bbc823adecdae860bb64756d639ecfec17b050a)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=5bbc823adecdae860bb64756d639ecfec17b050a & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-intl-grapheme@1.27.0", - "type": "library", - "name": "polyfill-intl-grapheme", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill for intl's grapheme_* functions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-intl-grapheme@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=511a08c03c1960e08a883f4cffcacd219b758354)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=511a08c03c1960e08a883f4cffcacd219b758354 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-intl-idn@1.27.0", - "type": "library", - "name": "polyfill-intl-idn", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-intl-idn@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=639084e360537a19f9ee352433b84ce831f3d2da)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=639084e360537a19f9ee352433b84ce831f3d2da & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-intl-normalizer@1.27.0", - "type": "library", - "name": "polyfill-intl-normalizer", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-intl-normalizer@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=19bd1e4fcd5b91116f14d8533c57831ed00571b6)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=19bd1e4fcd5b91116f14d8533c57831ed00571b6 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-mbstring@1.27.0", - "type": "library", - "name": "polyfill-mbstring", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill for the Mbstring extension", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-mbstring@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=8ad114f6b39e2c98a8b0e3bd907732c207c2b534)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=8ad114f6b39e2c98a8b0e3bd907732c207c2b534 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-php72@1.27.0", - "type": "library", - "name": "polyfill-php72", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-php72@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-php72.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=869329b1e9894268a8a61dabb69153029b7a8c97)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=869329b1e9894268a8a61dabb69153029b7a8c97 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-php72/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-php80@1.27.0", - "type": "library", - "name": "polyfill-php80", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-php80@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-php80.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-php80/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/polyfill-uuid@1.27.0", - "type": "library", - "name": "polyfill-uuid", - "version": "1.27.0", - "group": "symfony", - "description": "Symfony polyfill for uuid functions", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/polyfill-uuid@1.27.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-uuid.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f3cf1a645c2734236ed1e2e671e273eeb3586166)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/f3cf1a645c2734236ed1e2e671e273eeb3586166", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f3cf1a645c2734236ed1e2e671e273eeb3586166 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/polyfill-uuid/tree/v1.27.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/process@6.2.5", - "type": "library", - "name": "process", - "version": "6.2.5", - "group": "symfony", - "description": "Executes commands in sub-processes", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/process@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/process.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=9ead139f63dfa38c4e4a9049cc64a8b2748c83b7)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/process/zipball/9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=9ead139f63dfa38c4e4a9049cc64a8b2748c83b7 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/process/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/routing@6.2.5", - "type": "library", - "name": "routing", - "version": "6.2.5", - "group": "symfony", - "description": "Maps an HTTP request to a set of configuration variables", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/routing@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/routing.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=589bd742d5d03c192c8521911680fe88f61712fe)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/routing/zipball/589bd742d5d03c192c8521911680fe88f61712fe", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=589bd742d5d03c192c8521911680fe88f61712fe & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/routing/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/service-contracts@3.2.0", - "type": "library", - "name": "service-contracts", - "version": "3.2.0", - "group": "symfony", - "description": "Generic abstractions related to writing services", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/service-contracts@3.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/service-contracts.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=aac98028c69df04ee77eb69b96b86ee51fbf4b75)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=aac98028c69df04ee77eb69b96b86ee51fbf4b75 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/service-contracts/tree/v3.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/string@6.2.5", - "type": "library", - "name": "string", - "version": "6.2.5", - "group": "symfony", - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/string@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/string.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/string/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/translation@6.2.5", - "type": "library", - "name": "translation", - "version": "6.2.5", - "group": "symfony", - "description": "Provides tools to internationalize your application", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/translation@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/translation.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=60556925a703cfbc1581cde3b3f35b0bb0ea904c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/translation/zipball/60556925a703cfbc1581cde3b3f35b0bb0ea904c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=60556925a703cfbc1581cde3b3f35b0bb0ea904c & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/translation/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/translation-contracts@3.2.0", - "type": "library", - "name": "translation-contracts", - "version": "3.2.0", - "group": "symfony", - "description": "Generic abstractions related to translation", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/translation-contracts@3.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/translation-contracts.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=68cce71402305a015f8c1589bfada1280dc64fe7)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/68cce71402305a015f8c1589bfada1280dc64fe7", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=68cce71402305a015f8c1589bfada1280dc64fe7 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/translation-contracts/tree/v3.2.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/uid@6.2.5", - "type": "library", - "name": "uid", - "version": "6.2.5", - "group": "symfony", - "description": "Provides an object-oriented API to generate and represent UIDs", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/uid@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/uid.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=8ace895bded57d6496638c9b2d3b788e05b7395b)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/uid/zipball/8ace895bded57d6496638c9b2d3b788e05b7395b", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=8ace895bded57d6496638c9b2d3b788e05b7395b & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/uid/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/symfony/var-dumper@6.2.5", - "type": "library", - "name": "var-dumper", - "version": "6.2.5", - "group": "symfony", - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/symfony/var-dumper@6.2.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/symfony/var-dumper.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=44b7b81749fd20c1bdf4946c041050e22bc8da27)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/44b7b81749fd20c1bdf4946c041050e22bc8da27", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=44b7b81749fd20c1bdf4946c041050e22bc8da27 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://symfony.com", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/symfony/var-dumper/tree/v6.2.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://symfony.com/sponsor", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/fabpot", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/tijsverkoyen/css-to-inline-styles@2.2.6", - "type": "library", - "name": "css-to-inline-styles", - "version": "2.2.6", - "group": "tijsverkoyen", - "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/tijsverkoyen/css-to-inline-styles@2.2.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c42125b83a4fa63b187fdf29f9c93cb7733da30c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c42125b83a4fa63b187fdf29f9c93cb7733da30c & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/tijsverkoyen/CssToInlineStyles", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/torann/geoip@dev-master", - "type": "library", - "name": "geoip", - "version": "dev-master", - "group": "torann", - "description": "Support for multiple GeoIP services.", - "licenses": [ - { - "license": { - "id": "BSD-2-Clause" - } - } - ], - "purl": "pkg:composer/torann/geoip@dev-master", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/Torann/laravel-geoip.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3d730ae7470b86ae828b4128b1757a96156fd941)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/Torann/laravel-geoip/zipball/3d730ae7470b86ae828b4128b1757a96156fd941", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3d730ae7470b86ae828b4128b1757a96156fd941 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/Torann/laravel-geoip/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/Torann/laravel-geoip/tree/3.0.4", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/vlucas/phpdotenv@5.5.0", - "type": "library", - "name": "phpdotenv", - "version": "5.5.0", - "group": "vlucas", - "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/vlucas/phpdotenv@5.5.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/vlucas/phpdotenv.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/vlucas/phpdotenv/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/vlucas/phpdotenv/tree/v5.5.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/GrahamCampbell", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/voku/portable-ascii@2.0.1", - "type": "library", - "name": "portable-ascii", - "version": "2.0.1", - "group": "voku", - "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/voku/portable-ascii@2.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/voku/portable-ascii.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=b56450eed252f6801410d810c8e1727224ae0743)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=b56450eed252f6801410d810c8e1727224ae0743 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/voku/portable-ascii", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/voku/portable-ascii/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/voku/portable-ascii/tree/2.0.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.paypal.me/moelleken", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/voku", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://opencollective.com/portable-ascii", - "comment": "As set via `funding` in composer package definition. (type=open_collective)" - }, - { - "type": "other", - "url": "https://www.patreon.com/voku", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/webmozart/assert@1.11.0", - "type": "library", - "name": "assert", - "version": "1.11.0", - "group": "webmozart", - "description": "Assertions to validate method input/output with nice error messages.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/webmozart/assert@1.11.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/webmozarts/assert.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=11cb2199493b2f8a3b53e7f19068fc6aac760991)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=11cb2199493b2f8a3b53e7f19068fc6aac760991 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/webmozarts/assert/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/webmozarts/assert/tree/1.11.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/willvincent/feeds@2.4.0", - "type": "library", - "name": "feeds", - "version": "2.4.0", - "group": "willvincent", - "description": "Laravel 5 Service Provider for the SimplePie library", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/willvincent/feeds@2.4.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/willvincent/feeds.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ae4c1b55761c9c4cf665d447fdbc9a0fb25dde0d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/willvincent/feeds/zipball/ae4c1b55761c9c4cf665d447fdbc9a0fb25dde0d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ae4c1b55761c9c4cf665d447fdbc9a0fb25dde0d & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/willvincent/feeds/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/willvincent/feeds/tree/v2.4.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/barryvdh/laravel-debugbar@3.8.0", - "type": "library", - "name": "laravel-debugbar", - "version": "3.8.0", - "group": "barryvdh", - "description": "PHP Debugbar integration for Laravel", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/barryvdh/laravel-debugbar@3.8.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/barryvdh/laravel-debugbar.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=eb01216141e62433178c52b0cbdb785b45bae871)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/eb01216141e62433178c52b0cbdb785b45bae871", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=eb01216141e62433178c52b0cbdb785b45bae871 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/barryvdh/laravel-debugbar/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/barryvdh/laravel-debugbar/tree/v3.8.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://fruitcake.nl", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/barryvdh", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/barryvdh/laravel-ide-helper@2.13.0", - "type": "library", - "name": "laravel-ide-helper", - "version": "2.13.0", - "group": "barryvdh", - "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/barryvdh/laravel-ide-helper@2.13.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=81d5b223ff067a1f38e14c100997e153b837fe4a)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/81d5b223ff067a1f38e14c100997e153b837fe4a", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=81d5b223ff067a1f38e14c100997e153b837fe4a & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/barryvdh/laravel-ide-helper/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.13.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://fruitcake.nl", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/barryvdh", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/barryvdh/reflection-docblock@2.1.0", - "type": "library", - "name": "reflection-docblock", - "version": "2.1.0", - "group": "barryvdh", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/barryvdh/reflection-docblock@2.1.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/barryvdh/ReflectionDocBlock.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=bf44b757feb8ba1734659029357646466ded673e)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/bf44b757feb8ba1734659029357646466ded673e", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=bf44b757feb8ba1734659029357646466ded673e & sha1=UNDEFINED)" - }, - { - "type": "distribution", - "url": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.1.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/brianium/paratest@6.8.1", - "type": "library", - "name": "paratest", - "version": "6.8.1", - "group": "brianium", - "description": "Parallel testing for PHP", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/brianium/paratest@6.8.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/paratestphp/paratest.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=168c1cfdf79e5b19b57cb03060fc9a6a79c5f582)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/168c1cfdf79e5b19b57cb03060fc9a6a79c5f582", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=168c1cfdf79e5b19b57cb03060fc9a6a79c5f582 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/paratestphp/paratest", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/paratestphp/paratest/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/paratestphp/paratest/tree/v6.8.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sponsors/Slamdunk", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://paypal.me/filippotessarotto", - "comment": "As set via `funding` in composer package definition. (type=paypal)" - } - ] - }, - { - "bom-ref": "pkg:composer/composer/class-map-generator@1.0.0", - "type": "library", - "name": "class-map-generator", - "version": "1.0.0", - "group": "composer", - "description": "Utilities to scan PHP code and generate class maps.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/composer/class-map-generator@1.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/composer/class-map-generator.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=1e1cb2b791facb2dfe32932a7718cf2571187513)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/1e1cb2b791facb2dfe32932a7718cf2571187513", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=1e1cb2b791facb2dfe32932a7718cf2571187513 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/composer/class-map-generator/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/composer/class-map-generator/tree/1.0.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://packagist.com", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/composer", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/composer/pcre@3.1.0", - "type": "library", - "name": "pcre", - "version": "3.1.0", - "group": "composer", - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/composer/pcre@3.1.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/composer/pcre.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=4bff79ddd77851fe3cdd11616ed3f92841ba5bd2)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=4bff79ddd77851fe3cdd11616ed3f92841ba5bd2 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/composer/pcre/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/composer/pcre/tree/3.1.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://packagist.com", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/composer", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/doctrine/instantiator@2.0.0", - "type": "library", - "name": "instantiator", - "version": "2.0.0", - "group": "doctrine", - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/doctrine/instantiator@2.0.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/doctrine/instantiator.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c6222283fa3f4ac679f8b9ced9a4e23f163e80d0)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c6222283fa3f4ac679f8b9ced9a4e23f163e80d0 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.doctrine-project.org/projects/instantiator.html", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/doctrine/instantiator/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/doctrine/instantiator/tree/2.0.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.doctrine-project.org/sponsorship.html", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://www.patreon.com/phpdoctrine", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/fakerphp/faker@1.21.0", - "type": "library", - "name": "faker", - "version": "1.21.0", - "group": "fakerphp", - "description": "Faker is a PHP library that generates fake data for you.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/fakerphp/faker@1.21.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/FakerPHP/Faker.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=92efad6a967f0b79c499705c69b662f738cc9e4d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=92efad6a967f0b79c499705c69b662f738cc9e4d & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/FakerPHP/Faker/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/FakerPHP/Faker/tree/v1.21.0", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/fidry/cpu-core-counter@0.4.1", - "type": "library", - "name": "cpu-core-counter", - "version": "0.4.1", - "group": "fidry", - "description": "Tiny utility to get the number of CPU cores.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/fidry/cpu-core-counter@0.4.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=79261cc280aded96d098e1b0e0ba0c4881b432c2)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/79261cc280aded96d098e1b0e0ba0c4881b432c2", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=79261cc280aded96d098e1b0e0ba0c4881b432c2 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/theofidry/cpu-core-counter/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/theofidry/cpu-core-counter/tree/0.4.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/theofidry", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/filp/whoops@2.14.6", - "type": "library", - "name": "whoops", - "version": "2.14.6", - "group": "filp", - "description": "php error handling for cool kids", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/filp/whoops@2.14.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/filp/whoops.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f7948baaa0330277c729714910336383286305da)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/filp/whoops/zipball/f7948baaa0330277c729714910336383286305da", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f7948baaa0330277c729714910336383286305da & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://filp.github.io/whoops/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/filp/whoops/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/filp/whoops/tree/2.14.6", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/denis-sokolov", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/hamcrest/hamcrest-php@2.0.1", - "type": "library", - "name": "hamcrest-php", - "version": "2.0.1", - "group": "hamcrest", - "description": "This is the PHP port of Hamcrest Matchers", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/hamcrest/hamcrest-php@2.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=8c3d0a3f6af734494ad8f6fbbee0ba92422859f3)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=8c3d0a3f6af734494ad8f6fbbee0ba92422859f3 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/hamcrest/hamcrest-php/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/jean85/pretty-package-versions@2.0.5", - "type": "library", - "name": "pretty-package-versions", - "version": "2.0.5", - "group": "jean85", - "description": "A library to get pretty versions strings of installed dependencies", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/jean85/pretty-package-versions@2.0.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ae547e455a3d8babd07b96966b17d7fd21d9c6af)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ae547e455a3d8babd07b96966b17d7fd21d9c6af & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/Jean85/pretty-package-versions/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laracasts/generators@2.0.1", - "type": "library", - "name": "generators", - "version": "2.0.1", - "group": "laracasts", - "description": "Advanced Laravel generators, that include schema information.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laracasts/generators@2.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laracasts/Laravel-5-Generators-Extended.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=5527f50260c6d3bf50a8e62e902e3fb44e87c4d8)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laracasts/Laravel-5-Generators-Extended/zipball/5527f50260c6d3bf50a8e62e902e3fb44e87c4d8", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=5527f50260c6d3bf50a8e62e902e3fb44e87c4d8 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/laracasts/Laravel-5-Generators-Extended/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laracasts/Laravel-5-Generators-Extended/tree/2.0.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/laravel/sail@1.19.0", - "type": "library", - "name": "sail", - "version": "1.19.0", - "group": "laravel", - "description": "Docker files for running a basic Laravel application.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/laravel/sail@1.19.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/laravel/sail.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=4f230634a3163f3442def6a4e6ffdb02b02e14d6)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/laravel/sail/zipball/4f230634a3163f3442def6a4e6ffdb02b02e14d6", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=4f230634a3163f3442def6a4e6ffdb02b02e14d6 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/laravel/sail/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/laravel/sail", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/maximebf/debugbar@1.18.2", - "type": "library", - "name": "debugbar", - "version": "1.18.2", - "group": "maximebf", - "description": "Debug bar in the browser for php application", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/maximebf/debugbar@1.18.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/maximebf/php-debugbar.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=17dcf3f6ed112bb85a37cf13538fd8de49f5c274)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/17dcf3f6ed112bb85a37cf13538fd8de49f5c274", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=17dcf3f6ed112bb85a37cf13538fd8de49f5c274 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/maximebf/php-debugbar", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/maximebf/php-debugbar/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/maximebf/php-debugbar/tree/v1.18.2", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/mockery/mockery@1.5.1", - "type": "library", - "name": "mockery", - "version": "1.5.1", - "group": "mockery", - "description": "Mockery is a simple yet flexible PHP mock object framework", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/mockery/mockery@1.5.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/mockery/mockery.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=e92dcc83d5a51851baf5f5591d32cb2b16e3684e)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=e92dcc83d5a51851baf5f5591d32cb2b16e3684e & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/mockery/mockery", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/mockery/mockery/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/mockery/mockery/tree/1.5.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/myclabs/deep-copy@1.11.0", - "type": "library", - "name": "deep-copy", - "version": "1.11.0", - "group": "myclabs", - "description": "Create deep copies (clones) of your objects", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/myclabs/deep-copy@1.11.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/myclabs/DeepCopy.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=14daed4296fae74d9e3201d2c4925d1acb7aa614)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=14daed4296fae74d9e3201d2c4925d1acb7aa614 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/myclabs/DeepCopy/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/myclabs/DeepCopy/tree/1.11.0", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/nunomaduro/collision@6.4.0", - "type": "library", - "name": "collision", - "version": "6.4.0", - "group": "nunomaduro", - "description": "Cli error handling for console/command-line PHP applications.", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/nunomaduro/collision@6.4.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/nunomaduro/collision.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=f05978827b9343cba381ca05b8c7deee346b6015)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f05978827b9343cba381ca05b8c7deee346b6015", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=f05978827b9343cba381ca05b8c7deee346b6015 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/nunomaduro/collision/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/nunomaduro/collision", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://www.paypal.com/paypalme/enunomaduro", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/nunomaduro", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://www.patreon.com/nunomaduro", - "comment": "As set via `funding` in composer package definition. (type=patreon)" - } - ] - }, - { - "bom-ref": "pkg:composer/phar-io/manifest@2.0.3", - "type": "library", - "name": "manifest", - "version": "2.0.3", - "group": "phar-io", - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phar-io/manifest@2.0.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/phar-io/manifest.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=97803eca37d319dfa7826cc2437fc020857acb53)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=97803eca37d319dfa7826cc2437fc020857acb53 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/phar-io/manifest/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/phar-io/manifest/tree/2.0.3", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/phar-io/version@3.2.1", - "type": "library", - "name": "version", - "version": "3.2.1", - "group": "phar-io", - "description": "Library for handling version information and constraints", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phar-io/version@3.2.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/phar-io/version.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=4f7fd7836c6f332bb2933569e566a0d6c4cbed74)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=4f7fd7836c6f332bb2933569e566a0d6c4cbed74 & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/phar-io/version/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/phar-io/version/tree/3.2.1", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/phpdocumentor/reflection-common@2.2.0", - "type": "library", - "name": "reflection-common", - "version": "2.2.0", - "group": "phpdocumentor", - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/phpdocumentor/reflection-common@2.2.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=1d01c49d4ed62f25aa84a747ad35d5a16924662b)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=1d01c49d4ed62f25aa84a747ad35d5a16924662b & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://www.phpdoc.org", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/phpdocumentor/type-resolver@1.6.2", - "type": "library", - "name": "type-resolver", - "version": "1.6.2", - "group": "phpdocumentor", - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ], - "purl": "pkg:composer/phpdocumentor/type-resolver@1.6.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=48f445a408c131e38cab1c235aa6d2bb7a0bb20d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=48f445a408c131e38cab1c235aa6d2bb7a0bb20d & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/phpDocumentor/TypeResolver/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2", - "comment": "As set via `support.source` in composer package definition." - } - ] - }, - { - "bom-ref": "pkg:composer/phpunit/php-code-coverage@9.2.24", - "type": "library", - "name": "php-code-coverage", - "version": "9.2.24", - "group": "phpunit", - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phpunit/php-code-coverage@9.2.24", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=2cf940ebc6355a9d430462811b5aaa308b174bed)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=2cf940ebc6355a9d430462811b5aaa308b174bed & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/php-code-coverage", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/phpunit/php-file-iterator@3.0.6", - "type": "library", - "name": "php-file-iterator", - "version": "3.0.6", - "group": "phpunit", - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phpunit/php-file-iterator@3.0.6", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/php-file-iterator/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/phpunit/php-invoker@3.1.1", - "type": "library", - "name": "php-invoker", - "version": "3.1.1", - "group": "phpunit", - "description": "Invoke callables with a timeout", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phpunit/php-invoker@3.1.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=5a10147d0aaf65b58940a0b72f71c9ac0423cc67)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=5a10147d0aaf65b58940a0b72f71c9ac0423cc67 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/php-invoker/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/php-invoker/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/phpunit/php-text-template@2.0.4", - "type": "library", - "name": "php-text-template", - "version": "2.0.4", - "group": "phpunit", - "description": "Simple template engine.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phpunit/php-text-template@2.0.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/php-text-template/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/php-text-template/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/phpunit/php-timer@5.0.3", - "type": "library", - "name": "php-timer", - "version": "5.0.3", - "group": "phpunit", - "description": "Utility class for timing", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phpunit/php-timer@5.0.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/php-timer/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/php-timer/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/phpunit/phpunit@9.6.3", - "type": "library", - "name": "phpunit", - "version": "9.6.3", - "group": "phpunit", - "description": "The PHP Unit Testing framework.", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/phpunit/phpunit@9.6.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=e7b1615e3e887d6c719121c6d4a44b0ab9645555)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7b1615e3e887d6c719121c6d4a44b0ab9645555", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=e7b1615e3e887d6c719121c6d4a44b0ab9645555 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://phpunit.de/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/phpunit/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/phpunit/tree/9.6.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://phpunit.de/sponsors.html", - "comment": "As set via `funding` in composer package definition. (type=custom)" - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - }, - { - "type": "other", - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "comment": "As set via `funding` in composer package definition. (type=tidelift)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/cli-parser@1.0.1", - "type": "library", - "name": "cli-parser", - "version": "1.0.1", - "group": "sebastian", - "description": "Library for parsing CLI options", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/cli-parser@1.0.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=442e7c7e687e42adc03470c7b668bc4b2402c0b2)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=442e7c7e687e42adc03470c7b668bc4b2402c0b2 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/cli-parser", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/cli-parser/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/code-unit@1.0.8", - "type": "library", - "name": "code-unit", - "version": "1.0.8", - "group": "sebastian", - "description": "Collection of value objects that represent the PHP code units", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/code-unit@1.0.8", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=1fc9f64c0927627ef78ba436c9b17d967e68e120)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=1fc9f64c0927627ef78ba436c9b17d967e68e120 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/code-unit", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/code-unit/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/code-unit-reverse-lookup@2.0.3", - "type": "library", - "name": "code-unit-reverse-lookup", - "version": "2.0.3", - "group": "sebastian", - "description": "Looks up which function or method a line of code belongs to", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/code-unit-reverse-lookup@2.0.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/comparator@4.0.8", - "type": "library", - "name": "comparator", - "version": "4.0.8", - "group": "sebastian", - "description": "Provides the functionality to compare PHP values for equality", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/comparator@4.0.8", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/comparator.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=fa0f136dd2334583309d32b62544682ee972b51a)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=fa0f136dd2334583309d32b62544682ee972b51a & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/comparator", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/comparator/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/comparator/tree/4.0.8", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/complexity@2.0.2", - "type": "library", - "name": "complexity", - "version": "2.0.2", - "group": "sebastian", - "description": "Library for calculating the complexity of PHP code units", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/complexity@2.0.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/complexity.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=739b35e53379900cc9ac327b2147867b8b6efd88)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=739b35e53379900cc9ac327b2147867b8b6efd88 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/complexity", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/complexity/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/complexity/tree/2.0.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/diff@4.0.4", - "type": "library", - "name": "diff", - "version": "4.0.4", - "group": "sebastian", - "description": "Diff implementation", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/diff@4.0.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/diff.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=3461e3fccc7cfdfc2720be910d3bd73c69be590d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=3461e3fccc7cfdfc2720be910d3bd73c69be590d & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/diff", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/diff/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/diff/tree/4.0.4", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/environment@5.1.5", - "type": "library", - "name": "environment", - "version": "5.1.5", - "group": "sebastian", - "description": "Provides functionality to handle HHVM/PHP environments", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/environment@5.1.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/environment.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=830c43a844f1f8d5b7a1f6d6076b784454d8b7ed)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=830c43a844f1f8d5b7a1f6d6076b784454d8b7ed & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://www.github.com/sebastianbergmann/environment", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/environment/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/environment/tree/5.1.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/exporter@4.0.5", - "type": "library", - "name": "exporter", - "version": "4.0.5", - "group": "sebastian", - "description": "Provides the functionality to export PHP variables for visualization", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/exporter@4.0.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/exporter.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.github.com/sebastianbergmann/exporter", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/exporter/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/exporter/tree/4.0.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/global-state@5.0.5", - "type": "library", - "name": "global-state", - "version": "5.0.5", - "group": "sebastian", - "description": "Snapshotting of global state", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/global-state@5.0.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/global-state.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=0ca8db5a5fc9c8646244e629625ac486fa286bf2)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=0ca8db5a5fc9c8646244e629625ac486fa286bf2 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "http://www.github.com/sebastianbergmann/global-state", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/global-state/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/global-state/tree/5.0.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/lines-of-code@1.0.3", - "type": "library", - "name": "lines-of-code", - "version": "1.0.3", - "group": "sebastian", - "description": "Library for counting the lines of code in PHP source code", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/lines-of-code@1.0.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c1c2e997aa3146983ed888ad08b15470a2e22ecc)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c1c2e997aa3146983ed888ad08b15470a2e22ecc & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/lines-of-code", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/lines-of-code/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/object-enumerator@4.0.4", - "type": "library", - "name": "object-enumerator", - "version": "4.0.4", - "group": "sebastian", - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/object-enumerator@4.0.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=5c9eeac41b290a3712d88851518825ad78f45c71)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=5c9eeac41b290a3712d88851518825ad78f45c71 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/object-enumerator/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/object-enumerator/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/object-reflector@2.0.4", - "type": "library", - "name": "object-reflector", - "version": "2.0.4", - "group": "sebastian", - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/object-reflector@2.0.4", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=b4f479ebdbf63ac605d183ece17d8d7fe49c15c7)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=b4f479ebdbf63ac605d183ece17d8d7fe49c15c7 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/object-reflector/", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/object-reflector/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/recursion-context@4.0.5", - "type": "library", - "name": "recursion-context", - "version": "4.0.5", - "group": "sebastian", - "description": "Provides functionality to recursively process PHP variables", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/recursion-context@4.0.5", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/recursion-context", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/recursion-context/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/resource-operations@3.0.3", - "type": "library", - "name": "resource-operations", - "version": "3.0.3", - "group": "sebastian", - "description": "Provides a list of PHP built-in functions that operate on resources", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/resource-operations@3.0.3", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://www.github.com/sebastianbergmann/resource-operations", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/resource-operations/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/type@3.2.1", - "type": "library", - "name": "type", - "version": "3.2.1", - "group": "sebastian", - "description": "Collection of value objects that represent the types of the PHP type system", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/type@3.2.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/type.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7 & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/type", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/type/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/type/tree/3.2.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/sebastian/version@3.0.2", - "type": "library", - "name": "version", - "version": "3.0.2", - "group": "sebastian", - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/sebastian/version@3.0.2", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/version.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=c6c1022351a901512170118436c764e473f6de8c)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=c6c1022351a901512170118436c764e473f6de8c & sha1=UNDEFINED)" - }, - { - "type": "website", - "url": "https://github.com/sebastianbergmann/version", - "comment": "As set via `homepage` in composer package definition." - }, - { - "type": "issue-tracker", - "url": "https://github.com/sebastianbergmann/version/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/sebastianbergmann/version/tree/3.0.2", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/sebastianbergmann", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - }, - { - "bom-ref": "pkg:composer/theseer/tokenizer@1.2.1", - "type": "library", - "name": "tokenizer", - "version": "1.2.1", - "group": "theseer", - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "licenses": [ - { - "license": { - "id": "BSD-3-Clause" - } - } - ], - "purl": "pkg:composer/theseer/tokenizer@1.2.1", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/theseer/tokenizer.git", - "comment": "As detected by composer's `getSourceUrls()` (type=git & reference=34a41e998c2183e22995f158c581e7b5e755ab9e)" - }, - { - "type": "distribution", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "comment": "As detected by composer's `getDistUrls()` (type=zip & reference=34a41e998c2183e22995f158c581e7b5e755ab9e & sha1=UNDEFINED)" - }, - { - "type": "issue-tracker", - "url": "https://github.com/theseer/tokenizer/issues", - "comment": "As set via `support.issues` in composer package definition." - }, - { - "type": "distribution", - "url": "https://github.com/theseer/tokenizer/tree/1.2.1", - "comment": "As set via `support.source` in composer package definition." - }, - { - "type": "other", - "url": "https://github.com/theseer", - "comment": "As set via `funding` in composer package definition. (type=github)" - } - ] - } - ], - "dependencies": [ - { - "ref": "pkg:composer/asm89/stack-cors@2.1.1", - "dependsOn": [ - "pkg:composer/symfony/http-foundation@6.2.6", - "pkg:composer/symfony/http-kernel@6.2.6" - ] - }, - { - "ref": "pkg:composer/aws/aws-crt-php@1.0.4" - }, - { - "ref": "pkg:composer/aws/aws-sdk-php@3.258.3", - "dependsOn": [ - "pkg:composer/aws/aws-crt-php@1.0.4", - "pkg:composer/guzzlehttp/guzzle@7.5.0", - "pkg:composer/guzzlehttp/promises@1.5.2", - "pkg:composer/guzzlehttp/psr7@2.4.3", - "pkg:composer/mtdowling/jmespath.php@2.6.1" - ] - }, - { - "ref": "pkg:composer/brick/math@0.9.3" - }, - { - "ref": "pkg:composer/brick/money@0.5.3", - "dependsOn": [ - "pkg:composer/brick/math@0.9.3" - ] - }, - { - "ref": "pkg:composer/cakephp/chronos@2.3.2" - }, - { - "ref": "pkg:composer/composer/semver@3.3.2" - }, - { - "ref": "pkg:composer/ctessier/nova-advanced-image-field@1.3.3", - "dependsOn": [ - "pkg:composer/intervention/image@2.7.2" - ] - }, - { - "ref": "pkg:composer/dflydev/dot-access-data@3.0.2" - }, - { - "ref": "pkg:composer/doctrine/cache@2.2.0" - }, - { - "ref": "pkg:composer/doctrine/dbal@2.13.9", - "dependsOn": [ - "pkg:composer/doctrine/cache@2.2.0", - "pkg:composer/doctrine/deprecations@1.0.0", - "pkg:composer/doctrine/event-manager@1.2.0" - ] - }, - { - "ref": "pkg:composer/doctrine/deprecations@1.0.0" - }, - { - "ref": "pkg:composer/doctrine/event-manager@1.2.0", - "dependsOn": [ - "pkg:composer/doctrine/deprecations@1.0.0" - ] - }, - { - "ref": "pkg:composer/doctrine/inflector@2.0.6" - }, - { - "ref": "pkg:composer/doctrine/lexer@3.0.0" - }, - { - "ref": "pkg:composer/dragonmantank/cron-expression@3.3.2", - "dependsOn": [ - "pkg:composer/webmozart/assert@1.11.0" - ] - }, - { - "ref": "pkg:composer/egulias/email-validator@4.0.1", - "dependsOn": [ - "pkg:composer/doctrine/lexer@3.0.0", - "pkg:composer/symfony/polyfill-intl-idn@1.27.0" - ] - }, - { - "ref": "pkg:composer/ezyang/htmlpurifier@4.16.0" - }, - { - "ref": "pkg:composer/fruitcake/laravel-cors@2.2.0", - "dependsOn": [ - "pkg:composer/asm89/stack-cors@2.1.1" - ] - }, - { - "ref": "pkg:composer/fruitcake/php-cors@1.2.0", - "dependsOn": [ - "pkg:composer/symfony/http-foundation@6.2.6" - ] - }, - { - "ref": "pkg:composer/graham-campbell/result-type@1.1.0", - "dependsOn": [ - "pkg:composer/phpoption/phpoption@1.9.0" - ] - }, - { - "ref": "pkg:composer/guzzlehttp/guzzle@7.5.0", - "dependsOn": [ - "pkg:composer/guzzlehttp/promises@1.5.2", - "pkg:composer/guzzlehttp/psr7@2.4.3", - "pkg:composer/psr/http-client@1.0.1", - "pkg:composer/symfony/deprecation-contracts@3.2.0" - ] - }, - { - "ref": "pkg:composer/guzzlehttp/promises@1.5.2" - }, - { - "ref": "pkg:composer/guzzlehttp/psr7@2.4.3", - "dependsOn": [ - "pkg:composer/psr/http-factory@1.0.1", - "pkg:composer/psr/http-message@1.0.1", - "pkg:composer/ralouphie/getallheaders@3.0.3" - ] - }, - { - "ref": "pkg:composer/intervention/image@2.7.2", - "dependsOn": [ - "pkg:composer/guzzlehttp/psr7@2.4.3" - ] - }, - { - "ref": "pkg:composer/laravel-lang/lang@10.9.5" - }, - { - "ref": "pkg:composer/laravel/framework@9.50.2", - "dependsOn": [ - "pkg:composer/brick/math@0.9.3", - "pkg:composer/doctrine/inflector@2.0.6", - "pkg:composer/dragonmantank/cron-expression@3.3.2", - "pkg:composer/egulias/email-validator@4.0.1", - "pkg:composer/fruitcake/php-cors@1.2.0", - "pkg:composer/laravel/serializable-closure@1.3.0", - "pkg:composer/league/commonmark@2.3.8", - "pkg:composer/league/flysystem@3.12.2", - "pkg:composer/monolog/monolog@2.9.1", - "pkg:composer/nesbot/carbon@2.66.0", - "pkg:composer/nunomaduro/termwind@1.15.0", - "pkg:composer/psr/container@2.0.2", - "pkg:composer/psr/log@3.0.0", - "pkg:composer/psr/simple-cache@3.0.0", - "pkg:composer/ramsey/uuid@4.7.3", - "pkg:composer/symfony/console@6.2.5", - "pkg:composer/symfony/error-handler@6.2.5", - "pkg:composer/symfony/finder@6.2.5", - "pkg:composer/symfony/http-foundation@6.2.6", - "pkg:composer/symfony/http-kernel@6.2.6", - "pkg:composer/symfony/mailer@6.2.5", - "pkg:composer/symfony/mime@6.2.5", - "pkg:composer/symfony/process@6.2.5", - "pkg:composer/symfony/routing@6.2.5", - "pkg:composer/symfony/uid@6.2.5", - "pkg:composer/symfony/var-dumper@6.2.5", - "pkg:composer/tijsverkoyen/css-to-inline-styles@2.2.6", - "pkg:composer/vlucas/phpdotenv@5.5.0", - "pkg:composer/voku/portable-ascii@2.0.1" - ] - }, - { - "ref": "pkg:composer/laravel/helpers@1.6.0" - }, - { - "ref": "pkg:composer/laravel/legacy-factories@1.3.2", - "dependsOn": [ - "pkg:composer/symfony/finder@6.2.5" - ] - }, - { - "ref": "pkg:composer/laravel/nova@3.32.0?checksum=sha1:e0406c9d5eb280fa71eaa685e822089491e44b0c", - "dependsOn": [ - "pkg:composer/brick/money@0.5.3", - "pkg:composer/cakephp/chronos@2.3.2", - "pkg:composer/doctrine/dbal@2.13.9", - "pkg:composer/laravel/ui@3.4.6", - "pkg:composer/spatie/once@3.1.0", - "pkg:composer/symfony/console@6.2.5", - "pkg:composer/symfony/finder@6.2.5", - "pkg:composer/symfony/intl@6.2.5", - "pkg:composer/symfony/process@6.2.5" - ] - }, - { - "ref": "pkg:composer/laravel/sanctum@2.15.1" - }, - { - "ref": "pkg:composer/laravel/serializable-closure@1.3.0" - }, - { - "ref": "pkg:composer/laravel/socialite@5.6.1", - "dependsOn": [ - "pkg:composer/guzzlehttp/guzzle@7.5.0", - "pkg:composer/league/oauth1-client@1.10.1" - ] - }, - { - "ref": "pkg:composer/laravel/tinker@2.8.0", - "dependsOn": [ - "pkg:composer/psy/psysh@0.11.12", - "pkg:composer/symfony/var-dumper@6.2.5" - ] - }, - { - "ref": "pkg:composer/laravel/ui@3.4.6" - }, - { - "ref": "pkg:composer/league/commonmark@2.3.8", - "dependsOn": [ - "pkg:composer/league/config@1.2.0", - "pkg:composer/psr/event-dispatcher@1.0.0", - "pkg:composer/symfony/deprecation-contracts@3.2.0", - "pkg:composer/symfony/polyfill-php80@1.27.0" - ] - }, - { - "ref": "pkg:composer/league/config@1.2.0", - "dependsOn": [ - "pkg:composer/dflydev/dot-access-data@3.0.2", - "pkg:composer/nette/schema@1.2.3" - ] - }, - { - "ref": "pkg:composer/league/flysystem@3.12.2", - "dependsOn": [ - "pkg:composer/league/mime-type-detection@1.11.0" - ] - }, - { - "ref": "pkg:composer/league/flysystem-aws-s3-v3@3.12.2", - "dependsOn": [ - "pkg:composer/aws/aws-sdk-php@3.258.3", - "pkg:composer/league/flysystem@3.12.2", - "pkg:composer/league/mime-type-detection@1.11.0" - ] - }, - { - "ref": "pkg:composer/league/mime-type-detection@1.11.0" - }, - { - "ref": "pkg:composer/league/oauth1-client@1.10.1", - "dependsOn": [ - "pkg:composer/guzzlehttp/guzzle@7.5.0", - "pkg:composer/guzzlehttp/psr7@2.4.3" - ] - }, - { - "ref": "pkg:composer/livewire/livewire@2.11.2", - "dependsOn": [ - "pkg:composer/league/mime-type-detection@1.11.0", - "pkg:composer/symfony/http-kernel@6.2.6" - ] - }, - { - "ref": "pkg:composer/maatwebsite/excel@3.1.46", - "dependsOn": [ - "pkg:composer/composer/semver@3.3.2", - "pkg:composer/phpoffice/phpspreadsheet@1.27.0", - "pkg:composer/psr/simple-cache@3.0.0" - ] - }, - { - "ref": "pkg:composer/maennchen/zipstream-php@2.4.0", - "dependsOn": [ - "pkg:composer/myclabs/php-enum@1.8.4", - "pkg:composer/psr/http-message@1.0.1" - ] - }, - { - "ref": "pkg:composer/markbaker/complex@3.0.2" - }, - { - "ref": "pkg:composer/markbaker/matrix@3.0.1" - }, - { - "ref": "pkg:composer/martinlindhe/laravel-vue-i18n-generator@dev-l9" - }, - { - "ref": "pkg:composer/mediconesystems/livewire-datatables@0.9.5", - "dependsOn": [ - "pkg:composer/livewire/livewire@2.11.2", - "pkg:composer/maatwebsite/excel@3.1.46", - "pkg:composer/reedware/laravel-relation-joins@3.0.0" - ] - }, - { - "ref": "pkg:composer/monolog/monolog@2.9.1", - "dependsOn": [ - "pkg:composer/psr/log@3.0.0" - ] - }, - { - "ref": "pkg:composer/mtdowling/jmespath.php@2.6.1", - "dependsOn": [ - "pkg:composer/symfony/polyfill-mbstring@1.27.0" - ] - }, - { - "ref": "pkg:composer/myclabs/php-enum@1.8.4" - }, - { - "ref": "pkg:composer/nesbot/carbon@2.66.0", - "dependsOn": [ - "pkg:composer/symfony/polyfill-mbstring@1.27.0", - "pkg:composer/symfony/polyfill-php80@1.27.0", - "pkg:composer/symfony/translation@6.2.5" - ] - }, - { - "ref": "pkg:composer/nette/schema@1.2.3", - "dependsOn": [ - "pkg:composer/nette/utils@4.0.0" - ] - }, - { - "ref": "pkg:composer/nette/utils@4.0.0" - }, - { - "ref": "pkg:composer/nikic/php-parser@4.15.3" - }, - { - "ref": "pkg:composer/nunomaduro/termwind@1.15.0", - "dependsOn": [ - "pkg:composer/symfony/console@6.2.5" - ] - }, - { - "ref": "pkg:composer/phpoffice/phpspreadsheet@1.27.0", - "dependsOn": [ - "pkg:composer/ezyang/htmlpurifier@4.16.0", - "pkg:composer/maennchen/zipstream-php@2.4.0", - "pkg:composer/markbaker/complex@3.0.2", - "pkg:composer/markbaker/matrix@3.0.1", - "pkg:composer/psr/http-client@1.0.1", - "pkg:composer/psr/http-factory@1.0.1", - "pkg:composer/psr/simple-cache@3.0.0" - ] - }, - { - "ref": "pkg:composer/phpoption/phpoption@1.9.0" - }, - { - "ref": "pkg:composer/predis/predis@1.1.10" - }, - { - "ref": "pkg:composer/psr/container@2.0.2" - }, - { - "ref": "pkg:composer/psr/event-dispatcher@1.0.0" - }, - { - "ref": "pkg:composer/psr/http-client@1.0.1", - "dependsOn": [ - "pkg:composer/psr/http-message@1.0.1" - ] - }, - { - "ref": "pkg:composer/psr/http-factory@1.0.1", - "dependsOn": [ - "pkg:composer/psr/http-message@1.0.1" - ] - }, - { - "ref": "pkg:composer/psr/http-message@1.0.1" - }, - { - "ref": "pkg:composer/psr/log@3.0.0" - }, - { - "ref": "pkg:composer/psr/simple-cache@3.0.0" - }, - { - "ref": "pkg:composer/psy/psysh@0.11.12", - "dependsOn": [ - "pkg:composer/nikic/php-parser@4.15.3", - "pkg:composer/symfony/console@6.2.5", - "pkg:composer/symfony/var-dumper@6.2.5" - ] - }, - { - "ref": "pkg:composer/ralouphie/getallheaders@3.0.3" - }, - { - "ref": "pkg:composer/ramsey/collection@2.0.0" - }, - { - "ref": "pkg:composer/ramsey/uuid@4.7.3", - "dependsOn": [ - "pkg:composer/brick/math@0.9.3", - "pkg:composer/ramsey/collection@2.0.0" - ] - }, - { - "ref": "pkg:composer/rappasoft/laravel-livewire-tables@2.10.0", - "dependsOn": [ - "pkg:composer/livewire/livewire@2.11.2", - "pkg:composer/spatie/laravel-package-tools@1.14.1" - ] - }, - { - "ref": "pkg:composer/reedware/laravel-relation-joins@3.0.0" - }, - { - "ref": "pkg:composer/simplepie/simplepie@1.8.0" - }, - { - "ref": "pkg:composer/socialiteproviders/manager@4.3.0", - "dependsOn": [ - "pkg:composer/laravel/socialite@5.6.1" - ] - }, - { - "ref": "pkg:composer/socialiteproviders/microsoft-azure@4.2.1", - "dependsOn": [ - "pkg:composer/socialiteproviders/manager@4.3.0" - ] - }, - { - "ref": "pkg:composer/spatie/backtrace@1.2.1" - }, - { - "ref": "pkg:composer/spatie/commonmark-shiki-highlighter@2.1.1", - "dependsOn": [ - "pkg:composer/league/commonmark@2.3.8", - "pkg:composer/spatie/shiki-php@1.3.0", - "pkg:composer/symfony/process@6.2.5" - ] - }, - { - "ref": "pkg:composer/spatie/flare-client-php@1.3.5", - "dependsOn": [ - "pkg:composer/spatie/backtrace@1.2.1", - "pkg:composer/symfony/http-foundation@6.2.6", - "pkg:composer/symfony/mime@6.2.5", - "pkg:composer/symfony/process@6.2.5", - "pkg:composer/symfony/var-dumper@6.2.5" - ] - }, - { - "ref": "pkg:composer/spatie/ignition@1.4.3", - "dependsOn": [ - "pkg:composer/spatie/flare-client-php@1.3.5", - "pkg:composer/symfony/console@6.2.5", - "pkg:composer/symfony/var-dumper@6.2.5" - ] - }, - { - "ref": "pkg:composer/spatie/laravel-activitylog@4.7.3", - "dependsOn": [ - "pkg:composer/spatie/laravel-package-tools@1.14.1" - ] - }, - { - "ref": "pkg:composer/spatie/laravel-cookie-consent@3.2.4", - "dependsOn": [ - "pkg:composer/spatie/laravel-package-tools@1.14.1" - ] - }, - { - "ref": "pkg:composer/spatie/laravel-feed@4.2.1", - "dependsOn": [ - "pkg:composer/spatie/laravel-package-tools@1.14.1" - ] - }, - { - "ref": "pkg:composer/spatie/laravel-ignition@1.6.4", - "dependsOn": [ - "pkg:composer/monolog/monolog@2.9.1", - "pkg:composer/spatie/flare-client-php@1.3.5", - "pkg:composer/spatie/ignition@1.4.3", - "pkg:composer/symfony/console@6.2.5", - "pkg:composer/symfony/var-dumper@6.2.5" - ] - }, - { - "ref": "pkg:composer/spatie/laravel-markdown@2.2.6", - "dependsOn": [ - "pkg:composer/league/commonmark@2.3.8", - "pkg:composer/spatie/commonmark-shiki-highlighter@2.1.1", - "pkg:composer/spatie/laravel-package-tools@1.14.1" - ] - }, - { - "ref": "pkg:composer/spatie/laravel-package-tools@1.14.1" - }, - { - "ref": "pkg:composer/spatie/laravel-permission@5.8.0" - }, - { - "ref": "pkg:composer/spatie/once@3.1.0" - }, - { - "ref": "pkg:composer/spatie/shiki-php@1.3.0" - }, - { - "ref": "pkg:composer/stevebauman/purify@5.1.1", - "dependsOn": [ - "pkg:composer/ezyang/htmlpurifier@4.16.0" - ] - }, - { - "ref": "pkg:composer/symfony/console@6.2.5", - "dependsOn": [ - "pkg:composer/symfony/deprecation-contracts@3.2.0", - "pkg:composer/symfony/polyfill-mbstring@1.27.0", - "pkg:composer/symfony/service-contracts@3.2.0", - "pkg:composer/symfony/string@6.2.5" - ] - }, - { - "ref": "pkg:composer/symfony/css-selector@6.2.5" - }, - { - "ref": "pkg:composer/symfony/deprecation-contracts@3.2.0" - }, - { - "ref": "pkg:composer/symfony/error-handler@6.2.5", - "dependsOn": [ - "pkg:composer/psr/log@3.0.0", - "pkg:composer/symfony/var-dumper@6.2.5" - ] - }, - { - "ref": "pkg:composer/symfony/event-dispatcher@6.2.5", - "dependsOn": [ - "pkg:composer/symfony/event-dispatcher-contracts@3.2.0" - ] - }, - { - "ref": "pkg:composer/symfony/event-dispatcher-contracts@3.2.0", - "dependsOn": [ - "pkg:composer/psr/event-dispatcher@1.0.0" - ] - }, - { - "ref": "pkg:composer/symfony/finder@6.2.5" - }, - { - "ref": "pkg:composer/symfony/http-foundation@6.2.6", - "dependsOn": [ - "pkg:composer/symfony/deprecation-contracts@3.2.0", - "pkg:composer/symfony/polyfill-mbstring@1.27.0" - ] - }, - { - "ref": "pkg:composer/symfony/http-kernel@6.2.6", - "dependsOn": [ - "pkg:composer/psr/log@3.0.0", - "pkg:composer/symfony/deprecation-contracts@3.2.0", - "pkg:composer/symfony/error-handler@6.2.5", - "pkg:composer/symfony/event-dispatcher@6.2.5", - "pkg:composer/symfony/http-foundation@6.2.6", - "pkg:composer/symfony/polyfill-ctype@1.27.0" - ] - }, - { - "ref": "pkg:composer/symfony/intl@6.2.5" - }, - { - "ref": "pkg:composer/symfony/mailer@6.2.5", - "dependsOn": [ - "pkg:composer/egulias/email-validator@4.0.1", - "pkg:composer/psr/event-dispatcher@1.0.0", - "pkg:composer/psr/log@3.0.0", - "pkg:composer/symfony/event-dispatcher@6.2.5", - "pkg:composer/symfony/mime@6.2.5", - "pkg:composer/symfony/service-contracts@3.2.0" - ] - }, - { - "ref": "pkg:composer/symfony/mime@6.2.5", - "dependsOn": [ - "pkg:composer/symfony/polyfill-intl-idn@1.27.0", - "pkg:composer/symfony/polyfill-mbstring@1.27.0" - ] - }, - { - "ref": "pkg:composer/symfony/polyfill-ctype@1.27.0" - }, - { - "ref": "pkg:composer/symfony/polyfill-intl-grapheme@1.27.0" - }, - { - "ref": "pkg:composer/symfony/polyfill-intl-idn@1.27.0", - "dependsOn": [ - "pkg:composer/symfony/polyfill-intl-normalizer@1.27.0", - "pkg:composer/symfony/polyfill-php72@1.27.0" - ] - }, - { - "ref": "pkg:composer/symfony/polyfill-intl-normalizer@1.27.0" - }, - { - "ref": "pkg:composer/symfony/polyfill-mbstring@1.27.0" - }, - { - "ref": "pkg:composer/symfony/polyfill-php72@1.27.0" - }, - { - "ref": "pkg:composer/symfony/polyfill-php80@1.27.0" - }, - { - "ref": "pkg:composer/symfony/polyfill-uuid@1.27.0" - }, - { - "ref": "pkg:composer/symfony/process@6.2.5" - }, - { - "ref": "pkg:composer/symfony/routing@6.2.5" - }, - { - "ref": "pkg:composer/symfony/service-contracts@3.2.0", - "dependsOn": [ - "pkg:composer/psr/container@2.0.2" - ] - }, - { - "ref": "pkg:composer/symfony/string@6.2.5", - "dependsOn": [ - "pkg:composer/symfony/polyfill-ctype@1.27.0", - "pkg:composer/symfony/polyfill-intl-grapheme@1.27.0", - "pkg:composer/symfony/polyfill-intl-normalizer@1.27.0", - "pkg:composer/symfony/polyfill-mbstring@1.27.0" - ] - }, - { - "ref": "pkg:composer/symfony/translation@6.2.5", - "dependsOn": [ - "pkg:composer/symfony/polyfill-mbstring@1.27.0", - "pkg:composer/symfony/translation-contracts@3.2.0" - ] - }, - { - "ref": "pkg:composer/symfony/translation-contracts@3.2.0" - }, - { - "ref": "pkg:composer/symfony/uid@6.2.5", - "dependsOn": [ - "pkg:composer/symfony/polyfill-uuid@1.27.0" - ] - }, - { - "ref": "pkg:composer/symfony/var-dumper@6.2.5", - "dependsOn": [ - "pkg:composer/symfony/polyfill-mbstring@1.27.0" - ] - }, - { - "ref": "pkg:composer/tijsverkoyen/css-to-inline-styles@2.2.6", - "dependsOn": [ - "pkg:composer/symfony/css-selector@6.2.5" - ] - }, - { - "ref": "pkg:composer/torann/geoip@dev-master" - }, - { - "ref": "pkg:composer/vlucas/phpdotenv@5.5.0", - "dependsOn": [ - "pkg:composer/graham-campbell/result-type@1.1.0", - "pkg:composer/phpoption/phpoption@1.9.0", - "pkg:composer/symfony/polyfill-ctype@1.27.0", - "pkg:composer/symfony/polyfill-mbstring@1.27.0", - "pkg:composer/symfony/polyfill-php80@1.27.0" - ] - }, - { - "ref": "pkg:composer/voku/portable-ascii@2.0.1" - }, - { - "ref": "pkg:composer/webmozart/assert@1.11.0" - }, - { - "ref": "pkg:composer/willvincent/feeds@2.4.0", - "dependsOn": [ - "pkg:composer/simplepie/simplepie@1.8.0" - ] - }, - { - "ref": "pkg:composer/barryvdh/laravel-debugbar@3.8.0", - "dependsOn": [ - "pkg:composer/maximebf/debugbar@1.18.2", - "pkg:composer/symfony/finder@6.2.5" - ] - }, - { - "ref": "pkg:composer/barryvdh/laravel-ide-helper@2.13.0", - "dependsOn": [ - "pkg:composer/barryvdh/reflection-docblock@2.1.0", - "pkg:composer/composer/class-map-generator@1.0.0", - "pkg:composer/doctrine/dbal@2.13.9", - "pkg:composer/nikic/php-parser@4.15.3", - "pkg:composer/phpdocumentor/type-resolver@1.6.2" - ] - }, - { - "ref": "pkg:composer/barryvdh/reflection-docblock@2.1.0" - }, - { - "ref": "pkg:composer/brianium/paratest@6.8.1", - "dependsOn": [ - "pkg:composer/fidry/cpu-core-counter@0.4.1", - "pkg:composer/jean85/pretty-package-versions@2.0.5", - "pkg:composer/phpunit/php-code-coverage@9.2.24", - "pkg:composer/phpunit/php-file-iterator@3.0.6", - "pkg:composer/phpunit/php-timer@5.0.3", - "pkg:composer/phpunit/phpunit@9.6.3", - "pkg:composer/sebastian/environment@5.1.5", - "pkg:composer/symfony/console@6.2.5", - "pkg:composer/symfony/process@6.2.5" - ] - }, - { - "ref": "pkg:composer/composer/class-map-generator@1.0.0", - "dependsOn": [ - "pkg:composer/composer/pcre@3.1.0", - "pkg:composer/symfony/finder@6.2.5" - ] - }, - { - "ref": "pkg:composer/composer/pcre@3.1.0" - }, - { - "ref": "pkg:composer/doctrine/instantiator@2.0.0" - }, - { - "ref": "pkg:composer/fakerphp/faker@1.21.0", - "dependsOn": [ - "pkg:composer/psr/container@2.0.2", - "pkg:composer/symfony/deprecation-contracts@3.2.0" - ] - }, - { - "ref": "pkg:composer/fidry/cpu-core-counter@0.4.1" - }, - { - "ref": "pkg:composer/filp/whoops@2.14.6", - "dependsOn": [ - "pkg:composer/psr/log@3.0.0" - ] - }, - { - "ref": "pkg:composer/hamcrest/hamcrest-php@2.0.1" - }, - { - "ref": "pkg:composer/jean85/pretty-package-versions@2.0.5" - }, - { - "ref": "pkg:composer/laracasts/generators@2.0.1" - }, - { - "ref": "pkg:composer/laravel/sail@1.19.0" - }, - { - "ref": "pkg:composer/maximebf/debugbar@1.18.2", - "dependsOn": [ - "pkg:composer/psr/log@3.0.0", - "pkg:composer/symfony/var-dumper@6.2.5" - ] - }, - { - "ref": "pkg:composer/mockery/mockery@1.5.1", - "dependsOn": [ - "pkg:composer/hamcrest/hamcrest-php@2.0.1" - ] - }, - { - "ref": "pkg:composer/myclabs/deep-copy@1.11.0" - }, - { - "ref": "pkg:composer/nunomaduro/collision@6.4.0", - "dependsOn": [ - "pkg:composer/filp/whoops@2.14.6", - "pkg:composer/symfony/console@6.2.5" - ] - }, - { - "ref": "pkg:composer/phar-io/manifest@2.0.3", - "dependsOn": [ - "pkg:composer/phar-io/version@3.2.1" - ] - }, - { - "ref": "pkg:composer/phar-io/version@3.2.1" - }, - { - "ref": "pkg:composer/phpdocumentor/reflection-common@2.2.0" - }, - { - "ref": "pkg:composer/phpdocumentor/type-resolver@1.6.2", - "dependsOn": [ - "pkg:composer/phpdocumentor/reflection-common@2.2.0" - ] - }, - { - "ref": "pkg:composer/phpunit/php-code-coverage@9.2.24", - "dependsOn": [ - "pkg:composer/nikic/php-parser@4.15.3", - "pkg:composer/phpunit/php-file-iterator@3.0.6", - "pkg:composer/phpunit/php-text-template@2.0.4", - "pkg:composer/sebastian/code-unit-reverse-lookup@2.0.3", - "pkg:composer/sebastian/complexity@2.0.2", - "pkg:composer/sebastian/environment@5.1.5", - "pkg:composer/sebastian/lines-of-code@1.0.3", - "pkg:composer/sebastian/version@3.0.2", - "pkg:composer/theseer/tokenizer@1.2.1" - ] - }, - { - "ref": "pkg:composer/phpunit/php-file-iterator@3.0.6" - }, - { - "ref": "pkg:composer/phpunit/php-invoker@3.1.1" - }, - { - "ref": "pkg:composer/phpunit/php-text-template@2.0.4" - }, - { - "ref": "pkg:composer/phpunit/php-timer@5.0.3" - }, - { - "ref": "pkg:composer/phpunit/phpunit@9.6.3", - "dependsOn": [ - "pkg:composer/doctrine/instantiator@2.0.0", - "pkg:composer/myclabs/deep-copy@1.11.0", - "pkg:composer/phar-io/manifest@2.0.3", - "pkg:composer/phar-io/version@3.2.1", - "pkg:composer/phpunit/php-code-coverage@9.2.24", - "pkg:composer/phpunit/php-file-iterator@3.0.6", - "pkg:composer/phpunit/php-invoker@3.1.1", - "pkg:composer/phpunit/php-text-template@2.0.4", - "pkg:composer/phpunit/php-timer@5.0.3", - "pkg:composer/sebastian/cli-parser@1.0.1", - "pkg:composer/sebastian/code-unit@1.0.8", - "pkg:composer/sebastian/comparator@4.0.8", - "pkg:composer/sebastian/diff@4.0.4", - "pkg:composer/sebastian/environment@5.1.5", - "pkg:composer/sebastian/exporter@4.0.5", - "pkg:composer/sebastian/global-state@5.0.5", - "pkg:composer/sebastian/object-enumerator@4.0.4", - "pkg:composer/sebastian/resource-operations@3.0.3", - "pkg:composer/sebastian/type@3.2.1", - "pkg:composer/sebastian/version@3.0.2" - ] - }, - { - "ref": "pkg:composer/sebastian/cli-parser@1.0.1" - }, - { - "ref": "pkg:composer/sebastian/code-unit@1.0.8" - }, - { - "ref": "pkg:composer/sebastian/code-unit-reverse-lookup@2.0.3" - }, - { - "ref": "pkg:composer/sebastian/comparator@4.0.8", - "dependsOn": [ - "pkg:composer/sebastian/diff@4.0.4", - "pkg:composer/sebastian/exporter@4.0.5" - ] - }, - { - "ref": "pkg:composer/sebastian/complexity@2.0.2", - "dependsOn": [ - "pkg:composer/nikic/php-parser@4.15.3" - ] - }, - { - "ref": "pkg:composer/sebastian/diff@4.0.4" - }, - { - "ref": "pkg:composer/sebastian/environment@5.1.5" - }, - { - "ref": "pkg:composer/sebastian/exporter@4.0.5", - "dependsOn": [ - "pkg:composer/sebastian/recursion-context@4.0.5" - ] - }, - { - "ref": "pkg:composer/sebastian/global-state@5.0.5", - "dependsOn": [ - "pkg:composer/sebastian/object-reflector@2.0.4", - "pkg:composer/sebastian/recursion-context@4.0.5" - ] - }, - { - "ref": "pkg:composer/sebastian/lines-of-code@1.0.3", - "dependsOn": [ - "pkg:composer/nikic/php-parser@4.15.3" - ] - }, - { - "ref": "pkg:composer/sebastian/object-enumerator@4.0.4", - "dependsOn": [ - "pkg:composer/sebastian/object-reflector@2.0.4", - "pkg:composer/sebastian/recursion-context@4.0.5" - ] - }, - { - "ref": "pkg:composer/sebastian/object-reflector@2.0.4" - }, - { - "ref": "pkg:composer/sebastian/recursion-context@4.0.5" - }, - { - "ref": "pkg:composer/sebastian/resource-operations@3.0.3" - }, - { - "ref": "pkg:composer/sebastian/type@3.2.1" - }, - { - "ref": "pkg:composer/sebastian/version@3.0.2" - }, - { - "ref": "pkg:composer/theseer/tokenizer@1.2.1" - }, - { - "ref": "pkg:composer/laravel/laravel@dev-master", - "dependsOn": [ - "pkg:composer/aws/aws-sdk-php@3.258.3", - "pkg:composer/ctessier/nova-advanced-image-field@1.3.3", - "pkg:composer/doctrine/dbal@2.13.9", - "pkg:composer/fruitcake/laravel-cors@2.2.0", - "pkg:composer/guzzlehttp/guzzle@7.5.0", - "pkg:composer/intervention/image@2.7.2", - "pkg:composer/laravel-lang/lang@10.9.5", - "pkg:composer/laravel/framework@9.50.2", - "pkg:composer/laravel/helpers@1.6.0", - "pkg:composer/laravel/legacy-factories@1.3.2", - "pkg:composer/laravel/nova@3.32.0?checksum=sha1:e0406c9d5eb280fa71eaa685e822089491e44b0c", - "pkg:composer/laravel/sanctum@2.15.1", - "pkg:composer/laravel/socialite@5.6.1", - "pkg:composer/laravel/tinker@2.8.0", - "pkg:composer/league/flysystem-aws-s3-v3@3.12.2", - "pkg:composer/livewire/livewire@2.11.2", - "pkg:composer/maatwebsite/excel@3.1.46", - "pkg:composer/martinlindhe/laravel-vue-i18n-generator@dev-l9", - "pkg:composer/mediconesystems/livewire-datatables@0.9.5", - "pkg:composer/predis/predis@1.1.10", - "pkg:composer/rappasoft/laravel-livewire-tables@2.10.0", - "pkg:composer/socialiteproviders/microsoft-azure@4.2.1", - "pkg:composer/spatie/laravel-activitylog@4.7.3", - "pkg:composer/spatie/laravel-cookie-consent@3.2.4", - "pkg:composer/spatie/laravel-feed@4.2.1", - "pkg:composer/spatie/laravel-ignition@1.6.4", - "pkg:composer/spatie/laravel-markdown@2.2.6", - "pkg:composer/spatie/laravel-permission@5.8.0", - "pkg:composer/stevebauman/purify@5.1.1", - "pkg:composer/torann/geoip@dev-master", - "pkg:composer/willvincent/feeds@2.4.0", - "pkg:composer/barryvdh/laravel-debugbar@3.8.0", - "pkg:composer/barryvdh/laravel-ide-helper@2.13.0", - "pkg:composer/brianium/paratest@6.8.1", - "pkg:composer/fakerphp/faker@1.21.0", - "pkg:composer/laracasts/generators@2.0.1", - "pkg:composer/laravel/sail@1.19.0", - "pkg:composer/mockery/mockery@1.5.1", - "pkg:composer/nunomaduro/collision@6.4.0", - "pkg:composer/phpunit/phpunit@9.6.3" - ] - } - ] -} \ No newline at end of file diff --git a/changed_files.txt b/changed_files.txt deleted file mode 100644 index b4af72d8e..000000000 --- a/changed_files.txt +++ /dev/null @@ -1,4 +0,0 @@ -app/Console/Commands/excel/CoderDojoEvents.php -app/Imports/IrelandDreamSpaceImport.php -resources/excel/Code Week Events - Ireland Dream Space .xlsx -resources/excel/events.xlsx diff --git a/config/codeweek.php b/config/codeweek.php index 1fcc4ef28..834baebcd 100644 --- a/config/codeweek.php +++ b/config/codeweek.php @@ -13,4 +13,15 @@ 'EEDUCATION_CLIENTID' => env('EEDUCATION_CLIENTID', null), 'LOCALES' => env('LOCALES', null), 'blog_url' => env('BLOG_URL', 'https://codeweek.eu/blog'), + + // Cloudflare Turnstile. Deployed environments set TURNSTILE_SECRET, while the + // code used to read TURNSTILE_SECRET_KEY, which silently disabled verification. + // Both names are accepted so neither spelling can turn the CAPTCHA off. + 'turnstile_sitekey' => env('TURNSTILE_SITEKEY', env('TURNSTILE_SITE_KEY')), + 'turnstile_secret' => env('TURNSTILE_SECRET_KEY', env('TURNSTILE_SECRET')), + + 'contact_form_recipient' => env( + 'CONTACT_FORM_RECIPIENT_EMAIL', + env('ADMIN_EMAIL', 'admin@codeweek.test') + ), ]; diff --git a/cookies.txt b/cookies.txt deleted file mode 100644 index 9527a6ed3..000000000 --- a/cookies.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Netscape HTTP Cookie File -# https://curl.se/docs/http-cookies.html -# This file was generated by libcurl! Edit at your own risk. - -#HttpOnly_.meet-and-code.org TRUE / FALSE 1788477322 visid_incap_3121020 DjQAJk4QRTyB0Vo80qF2WXlnuWgAAAAAQUIPAAAAAAAkQU6WmVNhelG0XaW50dIn -.meet-and-code.org TRUE / FALSE 0 incap_ses_1319_3121020 AtB4cG2hayc5QizfzwdOEnlnuWgAAAAAKWyRmc7B7kyWoQnTu62uiw== diff --git a/differences.diff b/differences.diff deleted file mode 100644 index a42f1e13d..000000000 --- a/differences.diff +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/app/Console/Commands/excel/CoderDojoEvents.php b/app/Console/Commands/excel/CoderDojoEvents.php -index c9fb706d..71787585 100644 ---- a/app/Console/Commands/excel/CoderDojoEvents.php -+++ b/app/Console/Commands/excel/CoderDojoEvents.php -@@ -42,7 +42,7 @@ class CoderDojoEvents extends Command - - Excel::import( - new CoderDojoEventsImport(), -- 'events-coderdojobelgium.xlsx', -+ 'events.xlsx', - 'excel' - ); - } -diff --git a/app/Imports/IrelandDreamSpaceImport.php b/app/Imports/IrelandDreamSpaceImport.php -index a6c0b1fe..1dd8ebe2 100644 ---- a/app/Imports/IrelandDreamSpaceImport.php -+++ b/app/Imports/IrelandDreamSpaceImport.php -@@ -42,7 +42,7 @@ class IrelandDreamSpaceImport extends DefaultValueBinder implements ToModel, Wit - 'description' => '', - 'organizer_type' => $row['type_of_organization'], - 'activity_type' => $row['activity_type'], -- 'location' => '', -+ 'location' => $row['address'], - 'event_url' => '', - 'contact_person' => $row['email'], - 'user_email' => '', -@@ -55,9 +55,9 @@ class IrelandDreamSpaceImport extends DefaultValueBinder implements ToModel, Wit - 'codeweek_for_all_participation_code' => '', - 'start_date' => $this->parseDate($row['date']), - 'end_date' => $this->parseDate($row['date']), -- 'geoposition' => '', -- 'longitude' => '', -- 'latitude' => '', -+ 'geoposition' => $row['latitude'].','.$row['longitude'], -+ 'longitude' => $row['latitude'], -+ 'latitude' => $row['latitude'], - 'language' => '', - 'approved_by' => 19588, - 'mass_added_for' => 'Excel', -diff --git a/resources/excel/Code Week Events - Ireland Dream Space .xlsx b/resources/excel/Code Week Events - Ireland Dream Space .xlsx -index cb2a6484..dfc79c5b 100644 -Binary files a/resources/excel/Code Week Events - Ireland Dream Space .xlsx and b/resources/excel/Code Week Events - Ireland Dream Space .xlsx differ -diff --git a/resources/excel/events.xlsx b/resources/excel/events.xlsx -new file mode 100644 -index 00000000..acc5e6b3 -Binary files /dev/null and b/resources/excel/events.xlsx differ diff --git a/docs/handover/.gitignore b/docs/handover/.gitignore new file mode 100644 index 000000000..6b105e60f --- /dev/null +++ b/docs/handover/.gitignore @@ -0,0 +1,7 @@ +# Generated by _pdf/build.py. Regenerate rather than commit it: the markdown +# chapters are the source of truth and this is a 7 MB build artefact. +codeweek-technical-handover.pdf + +# PDF build scratch +_pdf/build/ +_pdf/preview/ diff --git a/docs/handover/00-access-checklist.md b/docs/handover/00-access-checklist.md new file mode 100644 index 000000000..296c9311b --- /dev/null +++ b/docs/handover/00-access-checklist.md @@ -0,0 +1,132 @@ +# 00 — Access checklist + +Work through this before anything else. Most of the platform cannot be operated read-only, and several areas will actively refuse you until the outgoing team hands over or changes a setting. + +**No credential values appear in this folder.** Each row below names *what* you need and *where it is held*. The values come from the credentials vault during handover. + +## 1. Source control and project + +| What | Why you need it | Notes | +|------|-----------------|-------| +| GitHub access to [codeeu/codeweek](https://github.com/codeeu/codeweek) | All application code | Push rights plus permission to merge to `dev` and `master` | +| GitHub wiki edit rights | The [partner import guide](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek) is the contract national partners work from | Must be kept in sync with the validator; see [06](06-bulk-uploads-and-imports.md) | +| Branch protection / review settings | To understand what can reach live | Confirm who can approve to `master` | + +## 2. Hosting and servers + +Production and staging run on **Laravel Forge** over AWS EC2. See [02](02-environments-and-deployment.md). + +| What | Why you need it | +|------|-----------------| +| Laravel Forge account on the team/circle that owns both sites | Deploys, queue workers, scheduler, SSL, server `.env` editing | +| AWS account access | EC2 instances, S3 buckets, IAM | +| SSH key added to the `forge` user on both servers | Running Artisan commands, imports, log inspection | +| The dev and live server addresses | Held in the vault, not in this repo. See the note in section 9. | +| DNS / registrar access for `codeweek.eu` | Certificates, subdomains, mail records | + +The server `.env` files, the `schedule:run` cron entry, and the queue worker definitions exist **only in Forge**. Nothing in this repository will recreate them. Export or screenshot them during handover. + +## 3. Database + +| What | Why | +|------|-----| +| MySQL credentials for dev and live | Held in the server `.env` and in Forge | +| A recent verified restore of a live backup | Do not take backups on trust — restore one and check row counts before you change anything | +| Backup schedule and retention details | Confirm what is actually running and where it lands | + +## 4. Object storage (S3) + +Two separate buckets are used. Names and credentials are in the vault and the server `.env`. + +| Disk name in code | Holds | +|-------------------|-------| +| `s3` | Activity images, user avatars, generated certificate PDFs | +| `resources` | Learn & Teach resource PDFs and thumbnails | + +You need the AWS access key pair, region, and both bucket names. See [03](03-configuration.md) for the variable names. + +## 5. Laravel Nova licence + +Nova is a paid Laravel product. `composer install` will fail without it. + +| What | Where it is used | +|------|------------------| +| Nova licence username and password | `auth.json` locally, Forge deploy environment, and the `NOVA_USERNAME` / `NOVA_PASSWORD` GitHub Actions secrets | + +`auth.json` is gitignored, so it will not arrive with the clone — you must create it locally or run `composer config http-basic.nova.laravel.com `. Confirm the licence itself is transferred to the incoming organisation, not just the credentials. + +## 6. Third-party services + +| Service | Used for | Credential type | +|---------|----------|-----------------| +| Sentry | Error monitoring | DSN | +| Mapbox | Map tiles on the activity and community maps | Access token | +| Cloudflare Turnstile | Bot protection on the contact form | Site key and secret | +| Mail transport (SMTP, Mailgun-compatible) | All transactional email and mailings | Host, username, password, from-address | +| Google Cloud project | OAuth login and the support mailbox integration | OAuth client, plus a Gmail OAuth token | +| Facebook, X, GitHub developer apps | Social login | OAuth client and secret per provider | +| IP geolocation provider | Default country on the map page | API key | +| Matomo | Web analytics | Dashboard login | +| Site24x7 | Uptime monitoring | Dashboard login | +| CookieScript | Cookie consent banner | Account ID | +| Cursor | The AI support copilot subsystem, if you keep it running | API key and a GitHub token | + +Azure OAuth variables also exist in the deployed environment but are **not** wired into the login flow. See [12](12-risks-and-known-issues.md). + +## 7. WordPress blog + +The blog at [codeweek.eu/blog](https://codeweek.eu/blog/) is a separate application on the live server. See [09](09-wordpress-blog.md). + +| What | Why | +|------|-----| +| WordPress admin login | Content publishing, plugin and core updates | +| Filesystem access to the blog directory on the live server | The theme is patched with shell scripts in [scripts/](../../scripts) | +| Theme source or a backup of it | The active theme has been hand-patched; changes are not in this repo | + +## 8. Application-level access + +You will need an account on both dev and live with the right roles. Roles are managed with spatie/laravel-permission; see [04](04-domain-model.md). + +| Role | Grants | +|------|--------| +| `super admin` | Everything, including Nova, the bulk uploaders, and the admin screens | +| `ambassador` | Nova access scoped to their own country, plus activity moderation | +| `resource editor` | Nova access for the resource catalogue | + +Ask for a `super admin` account on both environments. + +### Day-one blocker you must resolve + +The certificate administration area at `/admin/certificate-backend/*` is gated on a **single hardcoded email address** belonging to the outgoing team: + +```11:11:app/Http/Middleware/EnsureSuperCertificateAdmin.php + private const ALLOWED_EMAIL = 'bernard@matrixinternet.ie'; +``` + +No role will get you in. This constant has to be changed (ideally replaced with a role or permission check) before you can operate the certificate tooling at all. The same address is also the fallback recipient for the contact form in `app/Http/Controllers/ContactFormController.php`. Both are listed in [12](12-risks-and-known-issues.md). + +## 9. A note on server addresses in git history + +The runbook at [docs/ops/learn-and-teach-resource-import.md](../ops/learn-and-teach-resource-import.md) previously contained the live and dev server IP addresses together with working `ssh` command lines, committed to this **public** repository. Those have been replaced with placeholders. + +Redacting the file does not remove the addresses from git history. If these are considered sensitive, the real mitigation is one of: + +- rotating the `forge` SSH keys on both servers, and/or +- restricting SSH ingress at the AWS security group to known addresses. + +Treat this as an action item for the incoming team rather than something already solved. + +## 10. Handover verification + +Before you consider the handover complete, prove each of these yourself: + +- [ ] Clone the repo and get `composer install` to complete, including Nova. +- [ ] Bring up a local environment and load the homepage ([11](11-testing-and-local-dev.md)). +- [ ] Run the test suite green. +- [ ] Log into Nova on dev as a super admin. +- [ ] Deploy a trivial change to dev and see it live ([02](02-environments-and-deployment.md)). +- [ ] Restore a live database backup into a scratch environment and compare row counts. +- [ ] Generate one certificate end to end on dev, confirming `pdflatex` works and the PDF reaches S3 ([08](08-certificates.md)). +- [ ] Run a small bulk activity upload on dev through to the report screen ([06](06-bulk-uploads-and-imports.md)). +- [ ] Confirm the scheduler is running on live and the queue has a worker. +- [ ] Log into the WordPress blog admin and confirm `app:sync-blogs` pulls a new post through. diff --git a/docs/handover/01-architecture.md b/docs/handover/01-architecture.md new file mode 100644 index 000000000..e1fc1b751 --- /dev/null +++ b/docs/handover/01-architecture.md @@ -0,0 +1,241 @@ +# 01 — Architecture + +## The short version + +Code Week is one Laravel monolith. There is no microservice layer, no separate API service, and no external search cluster. Almost everything you need to understand lives in `app/`, `routes/web.php`, and `resources/views/`. + +The two things that sit outside the monolith are the **WordPress blog** (a separate application, synced in nightly over HTTP) and **S3** (file storage). Everything else is the Laravel app, its MySQL database, and Redis. + +## Stack + +| Layer | Technology | Notes | +|-------|------------|-------| +| Language | PHP 8.2+ | `composer.json` requires `^8.2`; CI runs 8.3 | +| Framework | Laravel 11 | Uses the Laravel 11 slim skeleton — see "Bootstrap" below | +| Admin | Laravel Nova 4 | Paid licence required to install. At `/nova` | +| Database | MySQL 8 | 173 migrations. SQLite is used for tests only | +| Cache / queue / session | Redis available, drivers set per environment | `predis` client. No Horizon | +| Server rendering | Blade | The bulk of the public site | +| Interactive server components | Livewire 3 | Tables, filters, signup forms | +| Client components | Vue 3 | Mounted as islands into Blade, not a single-page app | +| Asset build | Vite 5 | Entries in `vite.config.js` | +| Styling | Tailwind 3 **and** legacy SCSS | Both are live; see "Two styling systems" below | +| Object storage | S3 | Two buckets, two disks | +| PDF generation | `pdflatex` via TeX Live | Certificates. See [08](08-certificates.md) | +| Spreadsheets | `maatwebsite/excel` | All bulk import and export | +| Error monitoring | Sentry | Wired in `bootstrap/app.php` | +| Auth | Laravel UI + Socialite + Sanctum | Local accounts plus four social providers | +| Roles | `spatie/laravel-permission` | Nine roles. See [04](04-domain-model.md) | + +## How the pieces fit + +Two views, because the request path and the background work are largely independent. + +**Serving a request.** Four user-facing surfaces, one database. + +```mermaid +flowchart TB + Visitors[Public
visitors] + Organisers[Organisers
and teachers] + Admins[Ambassadors
and admins] + + Web[Blade pages
+ Livewire 3] + VueIslands[Vue 3
components] + NovaAdmin[Nova 4 admin
/nova] + AdminBlade[Bespoke admin
/admin/*] + + MySQL[(MySQL 8)] + Redis[(Redis
cache, session, queue)] + S3[(S3
images, PDFs, resources)] + + Visitors --> Web + Organisers --> Web + Admins --> NovaAdmin + Admins --> AdminBlade + + Web --> VueIslands + Web --> MySQL + VueIslands --> MySQL + NovaAdmin --> MySQL + AdminBlade --> MySQL + + Web --> Redis + Web --> S3 + AdminBlade --> S3 +``` + +**Background work and integrations.** Everything below is driven by the scheduler in `routes/console.php` and executed by queue workers. + +```mermaid +flowchart TB + Cron[Scheduler
routes/console.php] + + Cron --> DEfeed[events.codeweek.de
hourly] + Cron --> Eedu[eeducation.at
daily] + Cron --> MaC[Meet and Code
RSS, hourly] + Cron --> WP[WordPress blog
nightly] + Cron --> Queue[Queue
workers] + + DEfeed --> DB[(MySQL)] + Eedu --> DB + MaC --> DB + WP --> DB + + Queue --> Certs[Certificate PDFs
pdflatex, then S3] + Queue --> Mail[Transactional
email] + DB --> PublicApi[Public API and
podcast RSS] +``` + +Browser-side third parties sit outside both diagrams: Mapbox and OpenStreetMap supply map tiles, ArcGIS does geocoding through a server-side proxy, and Sentry collects errors. + +## Bootstrap and wiring + +Laravel 11's slim skeleton means there is **no `app/Http/Kernel.php` and no `app/Console/Kernel.php`**. If you are used to older Laravel, this is the main orientation shock. Everything is in two files. + +[bootstrap/app.php](../../bootstrap/app.php) registers routing, middleware, and exception handling: + +```16:21:bootstrap/app.php + ->withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + // channels: __DIR__.'/../routes/channels.php', + health: '/up', +``` + +Notable consequences: + +- **The cron schedule lives in [routes/console.php](../../routes/console.php)**, not a kernel class. See [10](10-scheduled-jobs-and-runbooks.md). +- **There is a health endpoint at `/up`** provided by the framework. Useful for uptime monitoring. +- Four custom middleware run on every web request: + +```31:36:bootstrap/app.php + $middleware->web([ + \App\Http\Middleware\EnsureNovaMainDashboard::class, + \App\Http\Middleware\CheckBrowser::class, + \App\Http\Middleware\Locale::class, + \App\Http\Middleware\CheckConsent::class + ]); +``` + +`CheckConsent` is the one that will surprise you: any logged-in user without a recorded GDPR consent is redirected to `/consent` before they can use the site. `Locale` resolves the display language. `EnsureNovaMainDashboard` re-registers the Nova dashboard, a workaround for a Nova sidebar quirk. + +Middleware aliases worth knowing: + +| Alias | Class | Used for | +|-------|-------|----------| +| `role`, `permission` | spatie | Route-level role gating | +| `super.certificate.admin` | `EnsureSuperCertificateAdmin` | The certificate admin area. **Hardcoded to one email** — see [12](12-risks-and-known-issues.md) | +| `support.service.token` | `SupportServiceToken` | Bearer-token gate on the internal support API | + +[bootstrap/providers.php](../../bootstrap/providers.php) registers five application providers: `AchievementsServiceProvider`, `AppServiceProvider`, `CalendarServiceProvider`, `NovaServiceProvider`, and `RouteServiceProvider`. Package providers for Intervention Image, GeoIP, the Vue i18n generator, Feeds, and the IDE helper are registered inline in `bootstrap/app.php`. + +## Repository layout + +### `app/` — the thing to understand first + +**Models live at the top level of `app/`, not in `app/Models/`.** There are 60 PHP files directly in `app/`, most of them Eloquent models: `app/Event.php`, `app/User.php`, `app/Country.php`, `app/Excellence.php`, `app/Blog.php`, and so on. This is a legacy layout carried forward from early Laravel versions. + +`app/Models/` exists too, but only holds newer additions: the navigation models (`MenuSection`, `MenuItem`) and the support copilot models under `app/Models/Support/`. So when looking for a model, check both places. See [04](04-domain-model.md) for the full inventory. + +Everything else is conventional: + +| Directory | Contents | +|-----------|----------| +| `app/Achievements/` | Badge and gamification definitions for leading teachers | +| `app/Certificates/` | Certificate support classes | +| `app/Console/Commands/` | ~150 Artisan commands. The largest and messiest directory in the repo | +| `app/Enums/` | Enum types, e.g. global search filters | +| `app/Exports/` | `maatwebsite/excel` export classes | +| `app/Filters/` | Query filter objects — `EventFilters` and `ResourceFilters` drive all search | +| `app/Helpers/` | Static helper classes, including `CertificatesHelper` and `Codeweek4AllHelper` | +| `app/Http/Controllers/` | Controllers, including the bespoke admin screens | +| `app/Http/Middleware/` | The custom middleware listed above | +| `app/Importers/` | Remote feed parsers (e.g. eeducation) | +| `app/Imports/` | Spreadsheet import classes, including the bulk activity uploader | +| `app/Jobs/` | Twelve queued jobs — certificates, bulk imports, user deletion | +| `app/Livewire/` | Livewire 3 components | +| `app/Nova/` | 47 Nova resources plus actions, filters, metrics, dashboards | +| `app/Policies/` | Authorisation policies. `EventPolicy` is the important one | +| `app/RSSItems/` | Staging models for the legacy German city feeds | +| `app/Services/` | The newer, better-organised business logic. Bulk upload, support copilot, workbook parsing | + +The rough rule: **`app/Services/` is where recent work went, `app/Console/Commands/` is where historical work accumulated.** Code quality follows that split. + +### Everything else + +| Path | Contents | +|------|----------| +| `routes/web.php` | 892 lines. The public site, activity CRUD, admin screens, auth | +| `routes/api.php` | 53 lines. Public map/event endpoints and the internal support API | +| `routes/console.php` | 62 lines. The cron schedule | +| `config/` | 22 config files, several bespoke. See [03](03-configuration.md) | +| `database/migrations/` | 173 migrations | +| `database/seeders/` | Roles, taxonomies, CMS page content, dev fixtures | +| `database/factories/` | 31 factories for tests | +| `resources/views/` | Blade templates, including Livewire views and mail templates | +| `resources/js/` | Vue 3 components; `app.js` is the entry point | +| `resources/assets/sass/` | Legacy SCSS | +| `resources/css/app.css` | Tailwind entry point | +| `resources/lang/` | Translation files, one directory per locale | +| `resources/latex/` | Certificate templates. See [08](08-certificates.md) | +| `resources/excel/` | Source spreadsheets for the legacy CLI importers | +| `devspace/` + `devspace.yaml` | **Local development only.** See [02](02-environments-and-deployment.md) | +| `scripts/` | Python helpers for resource imports, plus WordPress theme patch scripts | +| `docs/ops/` | Existing operational runbooks | +| `docs/internal/` | Gitignored working files | +| `tests/` | ~102 test files | + +## Two styling systems + +Both Tailwind and a legacy SCSS bundle are compiled and shipped. [vite.config.js](../../vite.config.js) has three entry points: + +```19:23:vite.config.js + input: [ + 'resources/assets/sass/app.scss', + 'resources/css/app.css', + 'resources/js/app.js', + ], +``` + +`resources/css/app.css` is just the Tailwind directives. `resources/assets/sass/app.scss` is the older hand-written stylesheet that still carries much of the site's appearance. Newer pages lean on Tailwind, older ones on the SCSS. When changing styling, check which system the page you are editing actually uses before adding classes. + +## Front end model + +This is **not** a single-page application, and treating it like one will cause confusion. + +- Most pages are Blade, server-rendered, with a full page load between navigations. +- Vue 3 is mounted into specific containers for the richer interfaces: the activity registration form, the map, the resource catalogue, the matchmaking tool. +- Livewire 3 handles server-driven interactivity such as filterable tables, without writing an API endpoint. +- The Vue components talk back to endpoints defined in `routes/web.php` under `/api/*` (not `routes/api.php`), which is a slightly unusual arrangement worth remembering when you go looking for an endpoint. + +Because Blade, Livewire, and Vue coexist on some pages, there have been repeated bugs from mounting conflicts, where a Livewire component inside the Vue root stops working. If a filter or a calendar mysteriously stops responding, check whether it sits inside the Vue mount point. + +## Search and geocoding + +There is **no external search index.** Despite `SCOUT_DRIVER` and Algolia variables existing in the configuration, they are unused. All activity and resource search is plain MySQL through query filter objects: + +- `app/Filters/EventFilters.php` — country, free text, theme, audience, activity type, year, tag, format, age, language, date. Format and age filters use MySQL `JSON_CONTAINS` against JSON columns; language uses `FIND_IN_SET`. +- `app/Filters/ResourceFilters.php` — the Learn & Teach catalogue, joining across pivot tables. + +Map data uses the same filters, selects only the columns the map needs, groups by country, and is cached for 300 seconds. + +**Geocoding is ArcGIS**, proxied through `GeocodeController` so the browser never calls it directly. Note that the WP5 architecture document states Nominatim; that is incorrect, the code calls the ArcGIS World GeocodeServer. Map *tiles* are Mapbox and OpenStreetMap behind Leaflet. + +IP geolocation is a separate concern, handled by `torann/geoip`, and is only used to pick a sensible default country on the map page and during leading teacher signup. + +## Where the data comes from + +Activities reach the database three ways, and knowing which one produced a row is often the first step in debugging it: + +| Source | Mechanism | Marker on the row | +|--------|-----------|-------------------| +| Organiser self-registration | The public activity form | `mass_added_for` is null | +| Admin bulk upload | Spreadsheet through `/admin/bulk-upload` | `mass_added_for = 'Excel'` | +| National partner feed | Scheduled Artisan commands | `mass_added_for` names the feed; `source_ref` holds the upstream ID | + +See [06](06-bulk-uploads-and-imports.md) and [07](07-partner-feeds-and-apis.md). + +## Certificates need a TeX installation + +This catches everyone. Certificates are not generated with a PHP PDF library. They are generated by running `pdflatex` against `.tex` templates in `resources/latex/`, then uploading the resulting PDF to S3. A server without TeX Live installed will fail every certificate operation. The required packages are listed in [08](08-certificates.md). diff --git a/docs/handover/02-environments-and-deployment.md b/docs/handover/02-environments-and-deployment.md new file mode 100644 index 000000000..8b078ddf7 --- /dev/null +++ b/docs/handover/02-environments-and-deployment.md @@ -0,0 +1,213 @@ +# 02 — Environments and deployment + +## Read this first + +The repository contains a `devspace.yaml` and a `devspace/` directory full of Kubernetes manifests, Helm chart references, and a Traefik ingress. It is very easy to conclude that Code Week runs on Kubernetes. + +**It does not.** Production and staging both run on **Laravel Forge** over AWS EC2, with a conventional nginx + PHP-FPM setup and Forge's own deployment scripts. The DevSpace and Kubernetes tooling in this repo is **local development only** and is not used to serve any public environment. + +Nothing about the production infrastructure is version controlled in this repository. The server `.env` files, the cron entry that runs the scheduler, the queue worker definitions, and the nginx configuration all live in Forge. Treat Forge as the source of truth, and export what you need during handover. + +## Environments + +| Environment | URL | Purpose | +|-------------|-----|---------| +| Local | `codeweek.local.europa.eu` (DevSpace) or a standard local PHP setup | Development | +| Dev / staging | [dev.codeweek.eu](https://dev.codeweek.eu/) | QA before anything reaches live. Every change goes through here | +| Live | [codeweek.eu](https://codeweek.eu) | Production | + +Both hosted environments are Forge sites on separate EC2 instances, each with its own application directory under `/home/forge/`. The SSH addresses are held in the credentials vault and deliberately not written down here — see [00](00-access-checklist.md) section 9 for why. + +Two things about dev are worth knowing before you use it: + +- **Dev often points at the same `RESOURCES_BUCKET` as production.** Importing resources on dev will write PDFs into the production bucket, even though the database rows stay on dev. This is documented in [docs/ops/learn-and-teach-resource-import.md](../ops/learn-and-teach-resource-import.md) and is a real foot-gun. +- Dev has its own database, so data diverges from live over time. It is not a reliable place to reproduce data-specific bugs unless you refresh it first. + +## Branch model and deploy flow + +```mermaid +flowchart LR + Feature[feature branch] -->|PR| Dev[dev] + Dev -->|Forge auto-deploy| DevSite[dev.codeweek.eu] + DevSite -->|QA passes| Master[master] + Dev -->|PR| Master + Master -->|Forge auto-deploy| LiveSite[codeweek.eu] +``` + +The process, as actually practised: + +1. Branch off `dev` for the work. +2. Open a PR into `dev`. +3. Forge deploys `dev` to [dev.codeweek.eu](https://dev.codeweek.eu/). +4. QA on dev. +5. Open a PR from `dev` into `master`. +6. Forge deploys `master` to [codeweek.eu](https://codeweek.eu). + +### Branch naming is inconsistent, and it matters + +The repository has accumulated a large number of long-lived branches, many named after a date or an import batch (`bulk_11_11_25`, `2-sept-imports`, `coderdojo-import-april`, `30-june-25-imports`). These were working branches for one-off data jobs and are mostly abandoned. Do not assume an unfamiliar branch is active. + +More importantly, the production branch is referred to inconsistently across the project. CI triggers on `master` and `dev`: + +```3:7:.github/workflows/laravel.yml +on: + push: + branches: [ master, dev ] + pull_request: + branches: [ master, dev ] +``` + +while [docs/ops/learn-and-teach-resource-import.md](../ops/learn-and-teach-resource-import.md) describes the final step as a "PR → `main` / `master`". **Confirm in Forge which branch each site actually deploys from** before your first release, and consider standardising the naming. + +## Continuous integration + +One GitHub Actions workflow: [.github/workflows/laravel.yml](../../.github/workflows/laravel.yml). It runs on pushes and pull requests against `master`. + +| Step | Detail | +|------|--------| +| PHP | 8.3 with Composer v2 | +| Node | 21 | +| Env | Copies `.env.example` to `.env` | +| Dependencies | **Overwrites `composer.json` with [composer-test.json](../../composer-test.json)** | +| Nova auth | `secrets.NOVA_USERNAME` and `secrets.NOVA_PASSWORD` | +| Database | A SQLite file at `database/database.sqlite` | +| Assets | `npm install && npm run build` | +| Tests | `php artisan test --parallel` with `DB_CONNECTION=sqlite` | + +The `composer-test.json` swap is the part to remember. It exists to strip the Nova-only packages so CI does not need the full paid dependency tree. The consequence is that **CI does not exercise any Nova code**. Nova resources, actions, and filters are only ever tested by a human clicking through the admin. If you change Nova, QA it on dev manually. + +CI triggers on pushes and pull requests for both `master` and `dev`. It previously only ran for `master`, so pull requests into `dev` ran no tests at all. + +Two tests fail on `master` for reasons unrelated to any recent change: `UserRestoreServiceTest` and `CommunityAmbassadorFilteringTest`, the latter because `database/factories/CityFactory.php` is still in the pre-Laravel-8 `$factory->define()` format. Everything else passes. Fix or quarantine these, because a suite that is normally red is a suite nobody reads. + +An abandoned Travis configuration targeting PHP 7.3 has been removed, along with `.env.travis`. GitHub Actions is the only pipeline. + +## Deploying + +Forge handles deployment on push. The sequence it runs is essentially the script documented in the original infrastructure handover: + +```bash +cd /home/forge/ + +git pull origin $BRANCH + +composer install --no-interaction --prefer-dist --optimize-autoloader + +npm install +npm run build + +php artisan migrate --force + +php artisan nova:publish +php artisan view:clear +php artisan cache:clear +php artisan config:clear +php artisan queue:restart +``` + +Points that matter: + +- **`php artisan queue:restart` is essential, not optional.** Queue workers hold the old code in memory. Certificates in particular will keep using last year's LaTeX templates until workers restart. See [08](08-certificates.md). +- `php artisan nova:publish` republishes Nova's assets and must run after a Nova version change. +- `migrate --force` runs migrations non-interactively. With 173 migrations and a large `events` table, some migrations are slow. Check what a release contains before deploying during October. +- `npm run build` runs on the server, so Node must be installed and the instance needs enough memory. PHP memory limit should be at least 2048 MB per the original setup notes. + +## Server requirements + +From the original infrastructure handover, still accurate: + +| Requirement | Value | +|-------------|-------| +| PHP | 8.2+ (the original doc says 8.3; `composer.json` requires `^8.2`) | +| Composer | v2 | +| Node.js | 20+ (CI uses 21) | +| PHP memory limit | 2048 MB | +| MySQL | 8 | +| Nova credentials | In `auth.json` | +| TeX Live | Required for certificates | + +TeX Live packages needed for certificate generation: + +```bash +sudo apt-get install texlive-latex-base -y +sudo apt-get install texlive-fonts-recommended -y +sudo apt-get install texlive-fonts-extra -y +sudo apt-get install texlive-lang-cyrillic -y +sudo apt-get install texlive-lang-greek -y +sudo apt-get install texlive-latex-extra -y +sudo apt-get install texlive-font-utils -y +``` + +The Cyrillic and Greek language packages are not optional — certificates are issued in Greek, Ukrainian, and other non-Latin scripts, and those templates will fail to compile without them. + +## Things Forge owns that the repo does not + +Check and document each of these in Forge during handover, because losing them means losing functionality silently: + +| Item | Why it matters | +|------|----------------| +| The `schedule:run` cron entry | Without it, none of the ~18 scheduled commands run: no partner imports, no blog sync, no reminder emails. See [10](10-scheduled-jobs-and-runbooks.md) | +| Queue worker(s) | Certificates and all queued mail stop without a worker. There is no Horizon and no supervisor config in the repo | +| The server `.env` | The only complete record of production configuration. `.env.example` is significantly incomplete — see [03](03-configuration.md) | +| SSL certificates | Managed by Forge | +| Deploy script contents | May have drifted from the script above | +| Daily database backups | Confirm these exist, and restore one to prove it | + +## Local development with DevSpace + +This is the optional Kubernetes-based local setup. It is genuinely useful because it avoids installing PHP, MySQL, Redis, and Node locally, but it is not required — a standard local PHP environment works too. + +Prerequisites: Rancher Desktop (or Docker Desktop plus k3d), `kubectl`, and the DevSpace CLI. + +What [devspace.yaml](../../devspace.yaml) deploys into the `codeweek` namespace: + +| Deployment | Detail | +|------------|--------| +| `laravel-app` | `nginx:alpine` sidecar plus a PHP-FPM container, sharing an `emptyDir` at `/assets` | +| `mysql` | Bitnami MySQL chart 9.4.5, image tag 8.0.31 | +| `redis` | Bitnami Redis chart 17.4.0, image tag 6.0.12 | +| `nginx-config` | ConfigMap for nginx | +| `codeweek-config` | Traefik `IngressRoute` and security middleware | + +Setup, per [devspace/README.md](../../devspace/README.md): + +```bash +kubectl create ns codeweek + +# self-signed cert for the local .europa.eu hostname +kubectl create secret generic certificates-secret \ + --from-file=tls.crt=./localhost.crt --from-file=tls.key=./localhost.key + +# add to /etc/hosts +# 127.0.0.1 codeweek.local.europa.eu + +cp devspace/.env.devspace .env +devspace dev # prompts for a GitHub token and Nova credentials +devspace run artisan migrate:fresh --seed +``` + +Then open `https://codeweek.local.europa.eu`. + +Convenience wrappers defined in `devspace.yaml`: + +```bash +devspace run artisan +devspace run composer +devspace run npm +devspace run mysql +devspace run generate-key +``` + +MySQL is port-forwarded to `localhost:3308`. + +### Known problem with the DevSpace setup + +The PHP image is pinned to PHP 8.0: + +```3:5:devspace.yaml +vars: + - name: APP_IMAGE + value: public.ecr.aws/cnect-prime/php-8.0-fpm:latest +``` + +The application requires PHP 8.2 or newer. This image predates the Laravel 11 upgrade and will not satisfy `composer install`. Expect to have to point `APP_IMAGE` at a PHP 8.2+ FPM image before the DevSpace path works. This is tracked in [12](12-risks-and-known-issues.md). diff --git a/docs/handover/03-configuration.md b/docs/handover/03-configuration.md new file mode 100644 index 000000000..fd6057ab2 --- /dev/null +++ b/docs/handover/03-configuration.md @@ -0,0 +1,319 @@ +# 03 — Configuration + +Every variable below is documented by **name and purpose**. No values appear in this folder. Real values live in the server `.env` (managed through Forge) and in the credentials vault. + +## Before you trust `.env.example` + +[.env.example](../../.env.example) is **significantly incomplete**. It reflects a minimal test configuration, not a working deployment. Roughly forty variables that production depends on are absent from it, including the entire mail configuration, all OAuth secrets, the Mapbox token, the resources bucket, the `pdflatex` path, and the database host and credentials. + +The practical consequence: **copying `.env.example` will not give you a working application.** Use the server `.env` from Forge as your reference when setting up a new environment. + +The file used to ship a real base64 `APP_KEY` along with live S3 bucket names. Those are now blank, and the variables the application actually reads are listed by name with empty values. Still: **always run `php artisan key:generate`** for a new environment, and treat the key that was previously committed as compromised anywhere it was used. + +### Variables present in the deployed environment but missing from `.env.example` + +Add these when reconstructing an environment: + +``` +AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_REDIRECT +CACHE_DRIVER +CONTACT_FORM_RECIPIENT_EMAIL +DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD +FACEBOOK_CLIENT_SECRET +GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_REDIRECT +GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT +MAIL_MAILER, MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, +MAIL_ENCRYPTION, MAIL_FROM_ADDRESS, MAIL_FROM_NAME +MAPS_MAPBOX_ACCESS_TOKEN +PDFLATEX_PATH +QUEUE_CONNECTION +RELOCATION_COUNTRY +RESOURCES_BUCKET, RESOURCES_URL +SANCTUM_STATEFUL_DOMAINS +SESSION_LIFETIME, SESSION_SECURE_COOKIE +TURNSTILE_SECRET, TURNSTILE_SITEKEY +TWITTER_CLIENT_ID, TWITTER_CLIENT_SECRET, TWITTER_REDIRECT +VITE_DEV_SERVER_URL, VITE_RESOURCES_URL +``` + +## Laravel 11 variable renames + +The Laravel 11 upgrade renamed two variables. Both old and new names are present in the deployed environment, which is harmless but confusing: + +| Old name | Current name | +|----------|--------------| +| `QUEUE_DRIVER` | `QUEUE_CONNECTION` | +| `CACHE_DRIVER` | `CACHE_STORE` | + +`.env.example` used to list only `QUEUE_DRIVER`, which the framework ignores — a value set under the old name has **no effect** and queues fall back to the default. It now uses `QUEUE_CONNECTION`. Check the server `.env` for the same mistake. Also added during that upgrade: `AUTH_MODEL` (must be `App\User`, because the User model is not in `app/Models/`), `MAIL_ENCRYPTION`, and `CACHE_PREFIX`. + +## Variables by concern + +### Application core + +| Variable | Purpose | +|----------|---------| +| `APP_ENV` | Environment name — `local`, `production` | +| `APP_KEY` | Encryption key. Generate per environment, never share | +| `APP_DEBUG` | Debug mode. Must be false on live | +| `APP_TIMEZONE` | Application timezone (UTC) | +| `APP_URL` | Canonical site URL. Used for links, Nova, and the `codeweek.app_url` config | +| `VITE_APP_URL` | Base URL exposed to the front-end build | +| `VITE_DEV_SERVER_URL` | Vite hot-reload server during development | +| `APP_LOCALE`, `APP_FALLBACK_LOCALE`, `APP_FAKER_LOCALE` | Laravel locale defaults | +| `APP_MAINTENANCE_DRIVER`, `APP_MAINTENANCE_STORE` | Maintenance mode storage | +| `AUTH_MODEL` | The User model class. Must be `App\User` | +| `LOCALES` | Comma-separated list of site locales. Drives the language switcher | +| `ADMIN_EMAIL` | Recipient for system warnings and admin notifications | +| `BLOG_URL` | Base URL of the WordPress blog, used by the sync command | +| `SANCTUM_STATEFUL_DOMAINS` | Domains allowed to use session-based API auth | +| `BCRYPT_ROUNDS` | Password hashing cost | + +### Database + +| Variable | Purpose | +|----------|---------| +| `DB_CONNECTION` | Driver. `mysql` in all real environments, `sqlite` for tests | +| `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` | MySQL connection | +| `DB_DATABASE_TESTING` | Separate database name for the `mysql_testing` connection | +| `MYSQL_ATTR_SSL_CA` | Path to a CA bundle if the database requires TLS | + +### Cache, session, queue, Redis + +| Variable | Purpose | +|----------|---------| +| `CACHE_STORE` | Cache driver | +| `SESSION_DRIVER` | Session storage backend | +| `SESSION_ENCRYPT`, `SESSION_PATH`, `SESSION_DOMAIN` | Session cookie scope | +| `SESSION_LIFETIME`, `SESSION_SECURE_COOKIE` | Session expiry and HTTPS-only flag | +| `QUEUE_CONNECTION` | Queue driver. **Must not be `sync` on live** — see note below | +| `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD` | Redis connection, via the `predis` client | + +A `sync` queue connection means every queued job runs inline during the web request. For certificate batches and bulk imports that will time out the request. Confirm the live value is a real queue driver. + +### Mail + +| Variable | Purpose | +|----------|---------| +| `MAIL_MAILER` | Transport — `smtp` or `mailgun` | +| `MAIL_HOST`, `MAIL_PORT`, `MAIL_USERNAME`, `MAIL_PASSWORD`, `MAIL_ENCRYPTION` | SMTP connection | +| `MAIL_FROM_ADDRESS`, `MAIL_FROM_NAME` | Default From header | +| `MAIL_URL`, `MAIL_EHLO_DOMAIN` | Advanced SMTP options | +| `MAILGUN_DOMAIN`, `MAILGUN_SECRET`, `MAILGUN_ENDPOINT` | Mailgun API transport | +| `CONTACT_FORM_RECIPIENT_EMAIL` | Where the public contact form delivers | + +The default SMTP host in `config/mail.php` is Mailgun's. Almost all mail in the application is sent with `Mail::queue()`, so **mail depends on a running queue worker**. + +`CONTACT_FORM_RECIPIENT_EMAIL` has a hardcoded fallback to an outgoing-team address in `app/Http/Controllers/ContactFormController.php`. Set it explicitly or contact form submissions will keep going to the wrong place. + +### AWS and file storage + +| Variable | Purpose | +|----------|---------| +| `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` | Credentials, shared by both S3 disks | +| `AWS_DEFAULT_REGION` | Region | +| `AWS_BUCKET` | Main bucket — activity images, avatars, certificate PDFs | +| `AWS_URL` | Public base URL for the main bucket | +| `AWS_ENDPOINT`, `AWS_USE_PATH_STYLE_ENDPOINT` | For S3-compatible alternatives | +| `FILESYSTEM_CLOUD` | Default cloud disk name | +| `RESOURCES_BUCKET` | Separate bucket for Learn & Teach PDFs and thumbnails | +| `RESOURCES_URL` | Public base URL for the resources bucket | +| `VITE_RESOURCES_URL` | The resources URL exposed to the front end | +| `RESOURCES_IMPORT_TEMP_DISK` | Temporary disk for resource imports. Defaults to `local` | +| `BULK_UPLOAD_TEMP_DISK` | Temporary disk for bulk uploads. Defaults to `local` | + +Disks are defined in [config/filesystems.php](../../config/filesystems.php): + +| Disk | Driver | Location | Holds | +|------|--------|----------|-------| +| `latex` | local | `resources/latex/` | Certificate templates and intermediate TeX artefacts | +| `excel` | local | `resources/excel/` | Source spreadsheets for the legacy CLI importers | +| `meet-and-code` | local | `public/rss/` | Generated Meet and Code RSS | +| `s3` | S3 | `AWS_BUCKET` | Activity images, avatars, certificate PDFs at `certificates/{id}.pdf` | +| `resources` | S3 | `RESOURCES_BUCKET` | Learn & Teach resource PDFs and thumbnails | + +### OAuth and social login + +Configured in [config/services.php](../../config/services.php), consumed by `app/Http/Controllers/Auth/LoginController.php`. + +| Provider | Variables | Wired into login? | +|----------|-----------|-------------------| +| Google | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT` | Yes | +| Facebook | `FACEBOOK_CLIENT_ID`, `FACEBOOK_CLIENT_SECRET`, `FACEBOOK_REDIRECT` | Yes | +| X / Twitter | `TWITTER_CLIENT_ID`, `TWITTER_CLIENT_SECRET`, `TWITTER_REDIRECT` | Yes | +| GitHub | `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `GITHUB_REDIRECT` | Yes | +| Azure | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_REDIRECT` | **No** | + +The allowlist is hardcoded in the controller: + +```70:70:app/Http/Controllers/Auth/LoginController.php + $allowed_providers = ['twitter', 'github', 'google', 'facebook']; +``` + +Azure credentials are configured in the deployed environment but the provider is not in that list, so Azure login is unreachable. Either finish wiring it or remove the variables. + +### Maps, geocoding, and geolocation + +| Variable | Purpose | +|----------|---------| +| `MAPS_MAPBOX_ACCESS_TOKEN` | Mapbox tile access for the activity and community maps | +| `MAP_TILES` | Alternative tile server URL | +| `RELOCATION_COUNTRY` | Used by the `relocate` commands when fixing bad coordinates | +| `MAXMIND_USER_ID`, `MAXMIND_LICENSE_KEY` | MaxMind, if the GeoIP service is switched to it | +| `IPAPI_KEY` | The default IP geolocation provider | + +Geocoding of addresses during activity registration goes through ArcGIS via `GeocodeController` and needs no key. + +### Certificates + +| Variable | Purpose | +|----------|---------| +| `PDFLATEX_PATH` | Absolute path to the `pdflatex` binary | + +If this is wrong or empty, every certificate operation fails. See [08](08-certificates.md). + +### Bot protection + +| Variable | Purpose | +|----------|---------| +| `TURNSTILE_SITEKEY` | Cloudflare Turnstile widget key, rendered in the contact form | +| `TURNSTILE_SECRET` | Server-side verification secret. `TURNSTILE_SECRET_KEY` is also accepted | +| `HONEYPOT_ENABLED` | Toggle for `spatie/laravel-honeypot` | +| `HONEYPOT_NAME`, `HONEYPOT_RANDOMIZE` | Honeypot field naming | +| `HONEYPOT_VALID_FROM_TIMESTAMP`, `HONEYPOT_VALID_FROM`, `HONEYPOT_SECONDS` | Timing-based bot detection | + +**This used to be silently broken and is worth understanding.** The controller read `TURNSTILE_SECRET_KEY` while deployed environments set `TURNSTILE_SECRET`. Because the verification was wrapped in a truthiness test on a variable that was never set, the CAPTCHA response was validated as `nullable` and **never checked against Cloudflare** — while the widget still rendered, so it looked healthy. + +Both spellings now resolve through one config value, so neither can turn verification off by accident: + +```20:21:config/codeweek.php + 'turnstile_sitekey' => env('TURNSTILE_SITEKEY', env('TURNSTILE_SITE_KEY')), + 'turnstile_secret' => env('TURNSTILE_SECRET_KEY', env('TURNSTILE_SECRET')), +``` + +Verification is skipped only when both are blank, which is the intended behaviour locally. Read them with `config('codeweek.turnstile_secret')`, not `env()`, so they survive config caching. + +### Monitoring and logging + +| Variable | Purpose | +|----------|---------| +| `SENTRY_LARAVEL_DSN` | Sentry project DSN | +| `SENTRY_TRACES_SAMPLE_RATE` | Performance trace sampling | +| `LOG_CHANNEL`, `LOG_STACK`, `LOG_LEVEL` | Logging destination and verbosity | + +There is no published `config/sentry.php`; the package auto-discovers its configuration. A custom log channel `mails_sent` writes to `storage/logs/mails_sent.log` and is a useful first stop when someone claims an email never arrived. + +### Nova + +| Variable | Purpose | +|----------|---------| +| `NOVA_USERNAME`, `NOVA_PASSWORD` | Composer authentication for the paid packages. Build-time only, not runtime | +| `NOVA_SHOW_ERRORS` | Surfaces Nova form validation errors in API responses. Useful for debugging a failing Nova save | +| `NOVA_GUARD` | Auth guard for Nova | +| `NOVA_STORAGE_DISK` | Disk for Nova file upload fields | + +### Search (configured but unused) + +| Variable | Purpose | +|----------|---------| +| `SCOUT_DRIVER` | Laravel Scout driver | +| `ALGOLIA_APP_ID`, `ALGOLIA_KEY`, `ALGOLIA_SECRET` | Algolia credentials | + +**These are not in use.** Search is plain MySQL. Do not assume an index exists. + +### Cookie consent + +| Variable | Purpose | +|----------|---------| +| `COOKIESCRIPT_ID` | CookieScript account ID, injected into the base layouts | + +### Support copilot subsystem + +An AI-assisted support pipeline ingests tickets from a Gmail mailbox, triages them, and can open pull requests. It is feature-flagged off by default. Full variable list in [.env.example](../../.env.example) and [config/support_ai.php](../../config/support_ai.php) / [config/support_gmail.php](../../config/support_gmail.php). + +| Group | Variables | +|-------|-----------| +| Master switch | `SUPPORT_AI_ENABLED`, `CURSOR_API_KEY` | +| Triage | `SUPPORT_AI_TRIAGE_ENABLED`, `SUPPORT_AI_CLI_BIN`, `SUPPORT_AI_CLI_MODEL`, `SUPPORT_AI_CLI_TIMEOUT` | +| Code changes | `SUPPORT_AI_CODE_CHANGE_ENABLED`, `SUPPORT_AI_CURSOR_API_BASE`, `SUPPORT_AI_CLOUD_MODEL`, `SUPPORT_AI_REPO_URL`, `SUPPORT_AI_DEV_BRANCH`, `SUPPORT_AI_AUTO_CREATE_PR`, `SUPPORT_AI_MAX_POLL_MINUTES` | +| Promotion to live | `SUPPORT_AI_LIVE_PROMOTION`, `SUPPORT_AI_LIVE_BRANCH`, `SUPPORT_GITHUB_REPO`, `SUPPORT_GITHUB_TOKEN` | +| Artisan phase | `SUPPORT_AI_ARTISAN_ENABLED`, `SUPPORT_AI_ARTISAN_ALLOW_RAW`, `SUPPORT_AI_ARTISAN_TIMEOUT`, `SUPPORT_AI_ARTISAN_OUTPUT_LIMIT` | +| Content phase | `SUPPORT_AI_CONTENT_ENABLED`, `SUPPORT_AI_CONTENT_MAX_FIELD_LENGTH` | +| Gmail ingest | `SUPPORT_GMAIL_ENABLED`, `SUPPORT_GMAIL_CREDENTIALS`, `SUPPORT_GMAIL_TOKEN`, plus poll interval, query, label, and allowlist settings | +| Internal API | `SUPPORT_SERVICE_TOKEN` — bearer token for `/api/internal/support/*` | + +If you do not intend to operate this subsystem, leave `SUPPORT_AI_ENABLED` and `SUPPORT_GMAIL_ENABLED` false. The scheduled entries for it are conditional on those flags and will not run. + +### Miscellaneous + +| Variable | Purpose | +|----------|---------| +| `EEDUCATION_CLIENTID` | Client ID for the eeducation.at activity feed. See [07](07-partner-feeds-and-apis.md) | +| `RESOURCES_DROPDOWN_USE_NOVA` | Switches the resources navigation dropdown between Nova-managed and static | +| `STRIPE_KEY`, `STRIPE_SECRET` | Present in `config/services.php` but with no usage found in application code | + +## The `config/` directory + +22 files. Several are bespoke to this project rather than published Laravel defaults. + +| File | Custom? | Notes | +|------|---------|-------| +| `app.php` | Yes | Trimmed right down. Only `locales` and facade aliases for Calendar, Feeds, GeoIP, Image | +| `auth.php` | Yes | Minimal — only the `api` token guard is defined | +| `charts.php` | Yes | Default chart library for the Nova analytics cards | +| [`codeweek.php`](../../config/codeweek.php) | Yes | **The project's own config.** See below | +| `database.php` | No | MySQL, SQLite, Postgres, SQL Server, plus Redis. Includes a `mysql_testing` connection | +| `feed.php` | Yes | `spatie/laravel-feed` — the outbound podcast RSS at `/feed/podcasts` | +| `feeds.php` | Yes | `willvincent/feeds` — the inbound RSS reader. Caching disabled | +| `filesystems.php` | Yes | The five disks listed above | +| `geoip.php` | Yes | Default service is `ipapi`. Falls back to a Brussels location | +| `hashing.php` | No | | +| `honeypot.php` | No | | +| `livewire.php` | Yes | Components resolve from `App\Livewire`, views from `resources/views/livewire` | +| `logging.php` | Yes | Adds the `mails_sent` channel | +| `mail.php` | No | SMTP and Mailgun mailers | +| `menus.php` | Yes | The resources dropdown toggle | +| `nova.php` | No | Nova mounted at `/nova` | +| `permission.php` | No | spatie tables and models | +| `purify.php` | No | HTML sanitisation profiles, used on CMS-editable fields | +| `services.php` | No | Mailgun, Stripe, and the Socialite providers | +| `support_ai.php` | Yes | Support copilot phases | +| `support_gmail.php` | Yes | Support mailbox ingest | +| `vue-i18n-generator.php` | Yes | Output path for the legacy Vue translation generator | + +**Not published, so framework defaults apply:** `queue.php`, `cache.php`, `session.php`, `broadcasting.php`, `sanctum.php`, `sentry.php`, `activitylog.php`, `scout.php`. If you need to change queue or cache behaviour beyond what the environment variables allow, you will need to publish the relevant file first. + +### `config/codeweek.php` + +The project's own configuration file, and the first place to look when a project-specific value seems to come from nowhere: + +```1:15:config/codeweek.php + env('ADMIN_EMAIL', 'admin@codeweek.test'), + 'app_url' => env('APP_URL', 'http://codeweek.test'), + 'resources_url' => env('RESOURCES_URL', 'http://codeweek.test/resources'), + 'pdflatex_path' => env('PDFLATEX_PATH', ''), + 'db_connection' => env('DB_CONNECTION', ''), + 'aws_url' => env('AWS_URL', ''), + 'relocation_country' => env('RELOCATION_COUNTRY', ''), + 'MAPS_MAPBOX_ACCESS_TOKEN' => env('MAPS_MAPBOX_ACCESS_TOKEN', ''), + 'MAP_TILES' => env('MAP_TILES', ''), + 'EEDUCATION_CLIENTID' => env('EEDUCATION_CLIENTID', null), + 'LOCALES' => env('LOCALES', null), + 'blog_url' => env('BLOG_URL', 'https://codeweek.eu/blog'), +``` + +Note that `blog_url` defaults to the live blog. On a dev or local environment the blog sync command will therefore pull from **production** unless `BLOG_URL` is set explicitly. See [09](09-wordpress-blog.md). + +## Locales + +`LOCALES` currently lists 27 codes: + +``` +al, ba, bg, cs, da, de, el, en, es, et, fi, fr, hr, hu, it, lt, lv, +me, mk, nl, pl, pt, ro, rs, sk, sl, sv +``` + +`resources/lang/` contains directories beyond this list (including `tr`, `ua`, and `mt`). Those translations exist but are not exposed in the language switcher, because the switcher is driven by `LOCALES`. If a stakeholder asks why a language they translated is not appearing, this variable is the answer. See [11](11-testing-and-local-dev.md) for how translation files are structured. diff --git a/docs/handover/04-domain-model.md b/docs/handover/04-domain-model.md new file mode 100644 index 000000000..af3da7dc2 --- /dev/null +++ b/docs/handover/04-domain-model.md @@ -0,0 +1,357 @@ +# 04 — Domain model + +## What the platform is, in domain terms + +Organisers register **activities** (called "events" in the code and database). Ambassadors, who are scoped to a country, **moderate** them. Approved activities appear on the public map and in search. After an activity finishes, the organiser files a **report**, which triggers a **certificate**. Separately, users can generate certificates for their participants, and the most prolific organisers and networks receive recognition certificates each year. + +Alongside that sits a substantial CMS: the Learn & Teach resource catalogue, podcasts, training pages, the Grassroots Grants programme, and a large number of editorially managed landing pages, all administered through Nova. + +## Terminology mismatch to internalise + +| Public / stakeholder term | Code and database term | +|---------------------------|------------------------| +| Activity | `Event`, `events` table | +| Edition | A calendar **year**. There is no `Edition` model | +| Code Week 4 All | The `codeweek_for_all_participation_code` column on `events` | +| Hub | Two unrelated meanings — see below | + +**"Edition" is not a model.** It is just a year. `excellences.edition` is an integer year, and activity year filtering is done on `end_date`. Commands take it as an argument, for example `php artisan notify:winners 2026`. Do not go looking for an editions table. + +**"Hub" is ambiguous.** `GrassrootsGrantsHub` is a CMS record for the Grassroots Grants programme. It has nothing to do with the national or regional Code Week hubs that stakeholders talk about; those are not modelled as a separate entity. + +## Where models live + +Most models are at the **top level of `app/`**, not `app/Models/`. There are 60 PHP files directly in `app/`, the majority of them Eloquent models. `app/Models/` holds only the newer additions: + +| Location | Contents | +|----------|----------| +| `app/*.php` | `Event`, `User`, `Country`, `City`, `Audience`, `Theme`, `Tag`, `Excellence`, `Participation`, `Moderation`, `Location`, `Volunteer`, `School`, `Experience`, `Blog`, `Podcast*`, `ResourceItem` and its taxonomies, `Importer`, `MeetAndCodeRSSItem`, all the CMS page models | +| `app/Models/` | `MenuSection`, `MenuItem` | +| `app/Models/Support/` | `SupportCase`, `SupportCaseMessage`, `SupportCaseAction`, `SupportApproval`, `SupportGmailCursor` | + +Check both locations when hunting for a model. Because `App\User` is not in `app/Models/`, the environment must set `AUTH_MODEL=App\User` — see [03](03-configuration.md). + +Note also that three classes in `app/` are **not** models despite sitting alongside them: `Certificate.php`, `CertificateExcellence.php`, and `CertificateParticipation.php` are service classes that drive PDF generation. See [08](08-certificates.md). + +## The activity lifecycle + +```mermaid +stateDiagram-v2 + [*] --> PENDING: organiser submits, bulk upload, or partner feed + PENDING --> APPROVED: ambassador or super admin approves + PENDING --> REJECTED: ambassador or super admin rejects + REJECTED --> PENDING: organiser edits and resubmits + APPROVED --> Reported: organiser files report after end_date + Reported --> Certified: certificate PDF generated and stored + Certified --> [*] +``` + +Statuses are the strings `PENDING`, `APPROVED`, and `REJECTED` on `events.status`. + +Key behaviour on the model, in [app/Event.php](../../app/Event.php): + +| Method | Effect | +|--------|--------| +| `approve()` | Sets status to `APPROVED` and emails the organiser | +| `reject()` | Sets status to `REJECTED` and writes a `Moderation` record capturing the reason | +| `notifyAmbassadors()` | Emails the ambassadors for the activity's country when a new activity needs review | +| `promote()` | Marks an online activity for promotion in the online calendar | +| `feature()` | Marks an activity as featured | +| `relocate()` | Repairs missing or nonsense coordinates | +| `createLocation()` | Saves the venue as a reusable `Location` for that organiser | + +Important asymmetry: **rejection records a reason, approval does not.** And the Nova reject action has no message field, so rejecting through Nova writes an empty reason. The Blade moderation screens at `/pending` and `/review` do capture it. See [05](05-nova-admin.md). + +Two policy behaviours that generate support tickets: + +- `EventPolicy::edit()` returns false once `reported_at` is set. Organisers **lose the ability to edit after reporting**, with no explanatory message. This is by design (the report feeds the certificate) but users find it baffling. +- `EventPolicy::feature()` always returns false, so featuring only works via the super-admin bypass in the policy's `before()` hook. + +## `events` — the central table + +Defined in `database/migrations/2017_05_19_094208_create_events_table.php` and extended by dozens of later migrations. Soft-deleted. + +### Content and identity + +| Column | Notes | +|--------|-------| +| `title`, `slug` | Slug is generated from the title and used in the public URL | +| `description` | Sanitised on read via `stevebauman/purify` | +| `organizer` | Free-text organisation name | +| `organizer_type` | School, library, private business, non-profit, other | +| `activity_type` | `open-online`, `invite-online`, `invite-in-person`, `open-in-person`, `other` | +| `event_url` | The organiser's own link. Mandatory for open-online activities | +| `contact_person`, `user_email` | Contact details | +| `picture`, `picture_detail` | S3 paths | +| `language` | Comma-separated language codes. Filtered with `FIND_IN_SET` | + +### Scheduling + +| Column | Notes | +|--------|-------| +| `start_date`, `end_date` | `end_date` is what year filtering uses | +| `pub_date`, `created`, `updated` | Legacy timestamp columns, separate from Laravel's own | +| `recurring_event` | `daily`, `weekly`, or `monthly` | +| `recurring_type` | `consecutive` or `individual` | +| `duration` | `0-1`, `1-2`, `2-4`, `over-4` | + +### Geography + +| Column | Notes | +|--------|-------| +| `location` | Free-text address | +| `latitude`, `longitude`, `geoposition` | `geoposition` is the combined value the map reads | +| `country_iso` | Two-letter code, foreign key to `countries.iso` | +| `location_id` | Optional link to a saved `Location` | + +### Ownership and moderation + +| Column | Notes | +|--------|-------| +| `creator_id` | The owning user | +| `approved_by` | Who approved it | +| `status` | `PENDING` / `APPROVED` / `REJECTED` | + +### Reporting and certificates + +| Column | Notes | +|--------|-------| +| `reported_at` | Set when the organiser files the report. Locks editing | +| `participants_count`, `males_count`, `females_count`, `other_count` | Attendance breakdown | +| `average_participant_age`, `percentage_of_females` | Legacy report fields | +| `name_for_certificate` | The name printed on the certificate | +| `certificate_url`, `certificate_generated_at` | The generated PDF | +| `report_notifications_count`, `last_report_notification_sent_at` | Reminder throttling | + +Certificates of Recognition have **no separate table** — their data lives on `events`. + +### Code Week 4 All + +| Column | Notes | +|--------|-------| +| `codeweek_for_all_participation_code` | The `cw20-xxxx` style code linking activities into a network | + +Aggregation logic is in `app/Helpers/Codeweek4AllHelper.php` and `Codeweek4AllController`. This is what drives Certificates of Excellence: a network qualifies at 10 linked activities from 10 different organisers, or 3 countries involved. + +### Newer structured fields (2025 form revision) + +| Column | Notes | +|--------|-------| +| `activity_format` | **JSON array.** Filtered with `JSON_CONTAINS` | +| `ages` | **JSON array.** Filtered with `JSON_CONTAINS` | +| `is_extracurricular_event`, `is_standard_school_curriculum`, `is_use_resource` | Booleans | + +Permitted values are class constants on the model: + +```87:98:app/Event.php + public const ACTIVITY_FORMATS = [ + 'coding-camp', + 'summer-camp', + 'weekend-course', + 'evening-course', + 'careerday', + 'university-visit', + 'coding-home', + 'code-week-challenge', + 'competition', + 'other', + ]; +``` + +`AGES` is `under-5`, `6-9`, `10-12`, `13-15`, `16-18`, `19-25`, `over-25`. `DURATIONS` is `0-1`, `1-2`, `2-4`, `over-4`. + +These constants are the **single source of truth**, and the bulk uploader and partner importers validate against them. If you add a value, update the constant, the front-end form, the translation files, and the public wiki page that partners work from. + +### Import provenance + +| Column | Notes | +|--------|-------| +| `mass_added_for` | Which mechanism created the row: `Excel`, `API codeweek_de`, `RSS Eeducation`, etc. Null for self-registered activities | +| `source_ref` | Upstream identifier, namespaced as `{feed}:{uid}` | +| `source_synced_at` | Last time the feed touched the row | + +`source_ref` is the deduplication key for feed imports and the reason a re-run does not duplicate rows. The namespacing matters: `codeweek-de:{uid}` from the central German export deliberately does not collide with legacy `berlin:{uid}` rows. See [07](07-partner-feeds-and-apis.md). + +### Online activity promotion + +| Column | Notes | +|--------|-------| +| `highlighted_status`, `playback_url` | Featured online activities and recordings | +| `leading_teacher_tag` | Links an activity to a leading teacher's `tag` | + +### Relationships + +| Relationship | Target | +|--------------|--------| +| `owner()` | `User` via `creator_id` | +| `country()` | `Country` via `country_iso` | +| `audiences()` | `Audience`, pivot `audience_event` | +| `themes()` | `Theme`, pivot `event_theme` | +| `tags()` | `Tag`, pivot `event_tag` | +| `leadingTeacher()` | `User`, matched on `leading_teacher_tag` = `users.tag` | +| `moderations()` | `Moderation` | +| `extractedLocation()` | `Location` | +| `notification()` | `Notification` | + +Note `leadingTeacher()` joins on a **string tag**, not a foreign key. A typo in a tag silently breaks the link rather than erroring. + +## `users` + +Soft-deleted, implements `MustVerifyEmail`, uses spatie's `HasRoles`. + +| Column group | Columns | +|--------------|---------| +| Identity | `firstname`, `lastname`, `username`, `email`, `password` | +| Geography | `country_iso`, `city_id` | +| Social login | `provider`, `provider_id`, `avatar_path` | +| Preferences | `privacy`, `email_display`, `receive_emails` | +| GDPR | `consent_given_at`, `future_consent_given_at` | +| Verification | `email_verified_at`, `pending_email` | +| Leading teacher | `tag` | +| Admin tooling | `current_country`, `approved`, `magic_key` | + +Notes: + +- There is **no `name` column** — it is `firstname` and `lastname`. The Nova `User` resource searches a non-existent `name` column, so that search is broken. See [12](12-risks-and-known-issues.md). +- `current_country` lets a super admin view the site as though they were in a given country. +- `magic_key` powers tokenised unsubscribe and notification links, regenerated hourly by `php artisan magic:key`. +- `approved` is a legacy boolean that coexists with spatie roles. Roles are the real source of truth; treat `approved` with suspicion. +- **`$guarded = []` on the User model**, so every attribute is mass-assignable. Be careful with `fill()` and `update()` on user-supplied input. + +### Key relationships + +`events()`, `country()`, `excellences()` and `superOrganisers()` (both `Excellence`, filtered by `type`), `participations()`, `schools()`, `locations()`, `experience()`, `actions()`, `expertises()`, `levels()`, `subjects()`, and `taggedActivities()`. + +## Roles and permissions + +Managed by `spatie/laravel-permission`. Seeded across four seeders. + +From [database/seeders/RolesAndPermissionsSeeder.php](../../database/seeders/RolesAndPermissionsSeeder.php): + +| Role | Permissions | +|------|-------------| +| `member` | create event, create school | +| `event owner` | update event, generate certificate | +| `ambassador` | moderate event | +| `school manager` | update school, generate certificate | +| `resource editor` | moderate resource | +| `super admin` | all permissions | + +Added by the other seeders: + +| Role | Source | Permissions | +|------|--------|-------------| +| `leading teacher` | `LeadingTeacherRoleSeeder` | submit resource | +| `leading teacher admin` | `LeadingTeacherRoleSeeder` | manage the leading teacher list | +| `activities admin` | `ActivitiesAdministratorRoleSeeder` | feature event | + +Full permission list: `create event`, `update event`, `moderate event`, `create school`, `update school`, `generate certificate`, `view activities`, `moderate resource`, `submit resource`, `feature event`. + +**Do not rely on role IDs.** Two places in the Nova layer hardcode numeric role IDs, which only hold if the seeders ran in their original order. Use role *names*. See [12](12-risks-and-known-issues.md). + +## Ambassadors + +An ambassador is a user with the `ambassador` role and a `country_iso`. They moderate activities for their own country and nothing else — the scoping is applied in `EventPolicy` and in the Nova `Event` resource's `indexQuery`. + +There are two routes to becoming one, which is itself a wrinkle: + +1. **The volunteer pipeline.** A user applies, creating a `Volunteer` record. A super admin approves it at `/volunteer/{id}/approve`, which assigns the role. +2. **Directly in Nova.** The `Ambassador` resource exposes a boolean, and `User::setAmbassadorAttribute()` assigns or removes the role when it is toggled. + +## Taxonomies + +| Model | Table | Notes | +|-------|-------|-------| +| `Audience` | `audiences` | Target group. Nine values | +| `Theme` | `themes` | Topic. Has an `order` column for display sequence | +| `Tag` | `tags` | Free-form, slug-based | + +The numeric IDs matter because partners submit them directly in bulk upload spreadsheets. The authoritative public list is on the [wiki](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek), and it must be kept in step with the database. + +Theme IDs are **not contiguous** — the published list skips 7, 10, 12, and 15. That is the residue of a theme consolidation done with `php artisan themes:sync-finalized`, which replaced the master list and remapped `event_theme`. Do not renumber themes casually; historical activities point at these IDs and partner spreadsheets hardcode them. + +## Learn & Teach resources + +| Model | Purpose | +|-------|---------| +| `ResourceItem` | A resource — a PDF or an external link | +| `ResourceCategory`, `ResourceType`, `ResourceSubject`, `ResourceLevel`, `ResourceLanguage`, `ResourceProgrammingLanguage` | Filter taxonomies, each with its own `resourceitem_*` pivot | + +Files go to the `resources` S3 disk. A resource's `source` is either an S3 PDF URL or an external `https://` link, and the front-end card behaves differently for each. Import is covered in [06](06-bulk-uploads-and-imports.md) and the existing runbook at [docs/ops/learn-and-teach-resource-import.md](../ops/learn-and-teach-resource-import.md). + +## Certificates and recognition + +Four certificate types, three storage locations. Full detail in [08](08-certificates.md). + +| Type | Data lives in | Awarded to | +|------|---------------|------------| +| Recognition | Columns on `events` | An organiser after reporting an approved activity | +| Participation | `participations` table | Attendees, generated in bulk by the organiser | +| Excellence | `excellences` table, `type = 'Excellence'` | Code Week 4 All networks meeting the threshold | +| Super Organiser | `excellences` table, `type = 'SuperOrganiser'` | Organisers running 10+ activities in an edition | + +`excellences` has a unique constraint on `(user_id, edition, type)`, which is what stops duplicate awards. It also carries `notified_at`, `certificate_url`, `name_for_certificate`, and error columns added in 2025 (`certificate_generation_error`, `certificate_sent_error`) for diagnosing failed runs. + +`participations` stores `names` (the whole list for one batch), `event_name`, `event_date`, `participation_url` for the generated ZIP, and a `status` that starts at `PENDING`. + +## Leading teachers and gamification + +Leading teachers are users with the `leading teacher` role and a `tag`. Activities carrying that tag in `leading_teacher_tag` are credited to them, linked up by `php artisan link:lt`. + +| Model | Purpose | +|-------|---------| +| `Experience` | Points per user per year | +| `LeadingTeacherAction` | Logged actions that earn points | +| `LeadingTeacherExpertise` | Expertise areas on the profile | + +Badge definitions live in `app/Achievements/`, registered by `AchievementsServiceProvider`. + +## GDPR, consent, and deletion + +This area is unusually consequential, so read it before touching user records. + +**Consent is enforced by middleware.** `CheckConsent` runs on every web request. A logged-in user with a null `consent_given_at` is redirected to `/consent` and cannot use the site until they answer. Declining logs them out. + +Two consent fields exist because two separate data transfers had to be consented to: the Code4Europe transfer, and a later prospective transfer to the Commission and HaDEA. `giveConsent()` and `giveFutureConsent()` record them. + +**Deletion reassigns rather than cascades.** `php artisan users:delete-without-consent` and the accompanying [soft_delete_users_without_consent.sql](../../soft_delete_users_without_consent.sql) reparent a departing user's records to a magic legacy user with id `1000000` (`legacy@codeweek.eu`), then soft-delete the user. This preserves historical activity counts. + +Cautions: + +- The SQL script performs both soft deletes and hard deletes in one pass and is **not reversible without a database backup**. Take one first, every time. +- The literal `1000000` appears in several places in the codebase. It is load-bearing. +- Soft deletes are in play on `users`, `events`, `locations`, and `importers`. A "missing" record is often just soft-deleted; query with `withTrashed()` before concluding data loss. + +## Moderation audit + +`Moderation` rows (`moderations` table) hold `event_id`, `status`, `message`, and `status_by`. They are written on rejection and are the only record of why something was turned down. + +## Geography + +| Model | Notes | +|-------|-------| +| `Country` | **Primary key is `iso`**, not an auto-increment id. Carries centroid latitude and longitude, plus Facebook and website links | +| `City` | Reference data for user profiles | +| `Location` | Saved venues belonging to an organiser. Soft-deleted | + +Because `Country` is keyed on `iso`, relationships use the ISO string throughout. Country centroids are used by `relocate:country` to give activities with missing coordinates a plausible position rather than dropping them at the origin. + +## Database at a glance + +173 migrations. Other tables worth knowing: + +| Table | Purpose | +|-------|---------| +| `importers` | Tracks feed-imported activities so `clean:remote` can remove ones that vanish upstream | +| `*_rss_items` | Per-city staging tables for the legacy German feeds | +| `support_cases`, `support_approvals`, and siblings | The support copilot subsystem | +| `menu_sections`, `menu_items` | Editable site navigation | +| `activity_log` | `spatie/laravel-activitylog`, currently only used by `School` | +| `experiences`, `user_achievements`, `leading_teacher_actions` | Gamification | +| `blogs` | Mirror of the WordPress blog. See [09](09-wordpress-blog.md) | + +### Seeders and factories + +`DatabaseSeeder` orchestrates countries, cities, roles, users, taxonomies, activities, resources, CMS pages, and schools. `UserSeeder` creates a super admin plus 60 ambassadors for development. There are 31 factories in `database/factories/`, including `EventFactory`, `ExcellenceFactory`, `ParticipationFactory`, and `ModerationFactory`. + +`php artisan migrate:fresh --seed` gives a usable development dataset. Never run it against a real database. diff --git a/docs/handover/05-nova-admin.md b/docs/handover/05-nova-admin.md new file mode 100644 index 000000000..339e050e5 --- /dev/null +++ b/docs/handover/05-nova-admin.md @@ -0,0 +1,214 @@ +# 05 — Nova admin + +## Access + +Nova is mounted at **`/nova`**, configured in [config/nova.php](../../config/nova.php). It uses the same `users` table and web guard as the public site, with its own authentication and password reset routes registered in [app/Providers/NovaServiceProvider.php](../../app/Providers/NovaServiceProvider.php). + +Access is controlled by a single gate: + +```97:99:app/Providers/NovaServiceProvider.php + Gate::define('viewNova', function ($user) { + return $user->hasRole('super admin') || $user->hasRole('ambassador') || $user->hasRole('resource editor'); + }); +``` + +So three roles can get in: `super admin`, `ambassador`, and `resource editor`. There is no finer-grained gating at the gate level — restriction happens per resource, via `indexQuery` scoping and `authorizedToSee` checks. + +The dashboard is at `/nova/dashboards/main`. The `EnsureNovaMainDashboard` middleware (registered in [bootstrap/app.php](../../bootstrap/app.php)) re-registers it on each request, and `NovaServiceProvider::boot()` also calls `Nova::dashboards()` directly. Both exist to work around the dashboard occasionally not being available. If you see duplicate-looking dashboard registration, that is why. + +## Nova is not covered by CI + +Worth stating plainly: CI replaces `composer.json` with `composer-test.json`, which strips the Nova packages. **No Nova code is exercised by the test suite.** Every Nova change must be QA'd by hand on dev. See [02](02-environments-and-deployment.md). + +## Resources + +48 resources in [app/Nova/](../../app/Nova). Note that `app/Nova/Resource.php` is not a resource — it is the abstract base class all the others extend, providing a default `indexQuery`. + +Several newer resources are registered **explicitly** in `NovaServiceProvider::boot()` rather than relying on Nova's directory auto-discovery, because discovery was missing them from the sidebar: + +```41:56:app/Providers/NovaServiceProvider.php + Nova::resources([ + CsrCampaignPageNova::class, + DancePageNova::class, + GetInvolvedPageNova::class, + GrassrootsGrantsPageNova::class, + OnlineCoursesPageNova::class, + TreasureHuntPageNova::class, + TrainingResourceNova::class, + MediaUploadNova::class, + SupportCaseNova::class, + SupportCaseActionNova::class, + SupportApprovalNova::class, + SupportCaseMessageNova::class, + SupportGmailCursorNova::class, + MenuSectionNova::class, + ]); +``` + +**If you add a resource and it does not appear in the sidebar, add it to this array.** This is the single most likely cause of a "my new Nova resource is invisible" problem. + +### Community + +| Resource | Model | Notes | +|----------|-------|-------| +| `Event` | `App\Event` | The main activity admin. Status, geography, dates, certificate fields, the JSON format and age fields, and the audience/theme/tag relations. **Scoped for ambassadors** — see below | +| `User` | `App\User` | Deliberately minimal: avatar, name, email, password. No role fields are exposed, so **roles cannot be changed from here** | +| `Ambassador` | `App\User` | The ambassador toggle and country. Admin-only in the sidebar | +| `Country` | `App\Country` | Name, ISO, Facebook, website | + +The `Event` resource scopes ambassadors to their own country: + +```292:300:app/Nova/Event.php + public static function indexQuery(NovaRequest $request, $query) + { + if ($request->user()->hasRole('ambassador')) { + return $query->where('country_iso', '=', $request->user()->country_iso); + } + + return $query; + + } +``` + +### Resources (Learn & Teach) + +| Resource | Purpose | +|----------|---------| +| `ResourceItem` | The resource records themselves, with all their taxonomy relations | +| `ResourceCategory`, `ResourceType`, `ResourceSubject`, `ResourceLevel`, `ResourceLanguage`, `ResourceProgrammingLanguage` | Filter taxonomies | +| `TrainingResource` | Dynamic training module pages, with locale overrides and hero fields | +| `OnlineCourse` | MOOC listings | +| `MediaUpload` | Uploaded media assets, with a bulk upload action | + +### Content (editorial CMS) + +A large part of the public site is editorially managed through these. Each corresponds to a page or a repeatable block on a page. + +| Resource | Page | +|----------|------| +| `HomeSlide` | Homepage carousel, with locale override import/export | +| `StaticPage` | Registry of static content | +| `GetInvolvedPage` | Get Involved | +| `DreamJobsPage`, `DreamJobRoleModel`, `DreamJobsResource` | Careers in Digital | +| `GirlsInDigitalPage`, `GirlsInDigitalFaqItem` | Girls in Digital | +| `GrassrootsGrantsPage`, `GrassrootsGrantsHub`, `GrassrootsGrantsProject`, `GrassrootsGrantsProjectImage`, `GrassrootsGrantsProjectLink` | Grassroots Grants programme | +| `HackathonsPage` | Hackathons | +| `DancePage` | Dance Challenge | +| `TreasureHuntPage` | Treasure Hunt | +| `OnlineCoursesPage` | Online Courses landing | +| `CsrCampaignPage`, `CsrCampaignResource` | Future Ready CSR | +| `Partner` | Partners and sponsors | + +Content fields on these pass through `stevebauman/purify` on save, so raw HTML from editors is sanitised. If an editor complains that their markup was stripped, check [config/purify.php](../../config/purify.php). + +### Podcasts + +`Podcast`, `PodcastGuest`, `PodcastResource`. The `Podcast` model implements `Feedable` and drives the outbound RSS feed at `/feed/podcasts`, configured in [config/feed.php](../../config/feed.php). + +### Taxonomies + +`Audience`, `Theme` (has an `order` field), `Tag`. Minimal UIs. Be careful here — see the warning about theme IDs in [04](04-domain-model.md). + +### Site, matchmaking, and support + +| Resource | Purpose | +|----------|---------| +| `MenuSection`, `MenuItem` | Editable site navigation | +| `MatchmakingProfile` | The matchmaking tool directory, with template download and bulk import actions | +| `SupportCase`, `SupportCaseMessage`, `SupportCaseAction`, `SupportApproval`, `SupportGmailCursor` | The AI support copilot subsystem | + +## Actions + +Ten actions in [app/Nova/Actions/](../../app/Nova/Actions). + +| Action | Attached to | Effect | +|--------|-------------|--------| +| `ApproveEvent` | `Event` | Calls `$model->approve()` — sets status and emails the organiser | +| `RejectEvent` | `Event` | Calls `$model->reject()`. **No message field** — see below | +| `BulkUploadMediaFiles` | `MediaUpload` | Multi-file upload to S3 | +| `ExportHomeSlideLocaleOverrides` | `HomeSlide` | CSV export of slide translations | +| `ImportHomeSlideLocaleOverrides` | `HomeSlide` | CSV import of slide translations | +| `DownloadMatchmakingTemplate` | `MatchmakingProfile` | Downloads the import template | +| `ImportMatchmakingProfiles` | `MatchmakingProfile` | Bulk profile import | +| `ApproveSupportApproval` | `SupportApproval` | Support copilot workflow | +| `RejectSupportApproval` | `SupportApproval` | Support copilot workflow | +### Rejecting through Nova loses the reason + +`RejectEvent` exposes no fields, so the `Moderation` record it creates has an empty `message`. The organiser gets a rejection with no explanation. The legacy Blade moderation screens at `/pending` and `/review` do capture a reason. + +**Guidance for ambassadors: use `/pending` and `/review` to reject, not Nova.** Or fix the action to include a message field. Either way, decide and communicate it, because right now the two paths behave differently and ambassadors use both. + +## Filters + +| Filter | Applied to | Options | +|--------|------------|---------| +| `EventStatus` | `Event` | APPROVED, PENDING, REJECTED | +| `EventCountry` | `Event` | Countries that have activities. Admin only | +| `UserCountry` | `Ambassador` | Countries that have activities | +| `ResourceActive` | `ResourceItem` | Published / Pending | +## Metrics and dashboard + +One dashboard, `app/Nova/Dashboards/Main.php`, with five cards: + +| Metric | Measures | +|--------|----------| +| `EventCount` | Total activities, over 30 / 60 / 365 day ranges | +| `EventsPerDay` | Activity creation trend | +| `UsersPerDay` | Registration trend | +| `ImporterTrend` | Daily count of feed-imported activities | +| `MeetCodeTrend` | Daily count of Meet and Code RSS items | + +There are **no lenses** anywhere in the Nova layer — every resource returns an empty `lenses()` array. If you need a saved, filtered view, a lens is the idiomatic way to add one. + +## Nova runs stock + +There is no custom theme and no analytics tool. The repository used to contain a `nova-components/` directory holding two local packages — `codeeu/nova-theme` and `acme/analytics` — that were never installed: no `path` repository in `composer.json`, absent from `composer.lock`, and neither service provider referenced outside its own directory. `Analytics` also had its component registration commented out, so it would have built an empty bundle. Both directories and the `npm run build-analytics*` scripts have been deleted, because they read as working features and were not. + +If you want a custom Nova theme or a dashboard tool, start fresh with the current Nova documentation rather than reviving those. + +## Two ambassador-scoping bugs, now fixed + +Both are worth knowing about because they shaped what ambassadors saw for a long time, and because the pattern can recur. + +`Country::indexQuery` scoped **every** ambassador to France, regardless of their own country — leftover debug code. It now uses the ambassador's `country_iso`, and returns a query rather than `null` on the fall-through path: + +```100:106:app/Nova/Country.php + if ($request->user()->isAmbassador()) { + return $query + ->where('iso', '=', $request->user()->country_iso); + } + + return $query; + } +``` + +`Ambassador::indexQuery` filtered on `model_has_roles.role_id = 4`, which only held while the seeders had run in their original order. It now matches by role name: + +```114:118:app/Nova/Ambassador.php + // Matched by role name, not id: the previous `role_id = 4` only held while + // the seeders had run in their original order. + return $query->whereHas('roles', function ($roles) { + $roles->where('name', 'ambassador'); + }); +``` + +**Never filter on a role or permission ID in this codebase.** IDs depend on seeder ordering; names do not. + +### `User` resource search is broken + +The `User` resource searches a `name` column. The `users` table has `firstname` and `lastname`, not `name`. Searching users in Nova therefore does not work as expected. + +## The other admin: bespoke Blade screens + +Not everything admin-related is in Nova. A significant set of screens live in `routes/web.php` under `/admin/*` and elsewhere, gated by role middleware: + +| Area | Route | Gate | +|------|-------|------| +| Bulk activity upload | `/admin/bulk-upload` | `role:super admin` | +| Bulk user changes | `/admin/bulk-user-changes` | `role:super admin` | +| Resource import | `/admin/resources-import` | `role:super admin` | +| Activity moderation queue | `/pending`, `/review` | `role:super admin\|ambassador` | +| Excellence winners and certificate batches | various | `role:super admin` | +| Certificate backend | `/admin/certificate-backend/*` | **Single hardcoded email** | + +The rule of thumb: **Nova for CRUD and content, Blade admin screens for anything involving a workflow, a file upload, or a batch job.** The bulk uploaders are covered in [06](06-bulk-uploads-and-imports.md), certificates in [08](08-certificates.md). diff --git a/docs/handover/06-bulk-uploads-and-imports.md b/docs/handover/06-bulk-uploads-and-imports.md new file mode 100644 index 000000000..1bc1f5056 --- /dev/null +++ b/docs/handover/06-bulk-uploads-and-imports.md @@ -0,0 +1,330 @@ +# 06 — Bulk uploads and imports + +Three separate bulk tools exist, all super-admin only, all built on `maatwebsite/excel`: + +| Tool | Route | Purpose | +|------|-------|---------| +| Bulk activity upload | `/admin/bulk-upload` | Import activities from a spreadsheet | +| Bulk user changes | `/admin/bulk-user-changes` | Apply role, email, and country changes to existing users | +| Resource import | `/admin/resources-import` | Publish Learn & Teach resources | + +Activities that arrive automatically from national partner feeds are a different mechanism — see [07](07-partner-feeds-and-apis.md). + +--- + +## Bulk activity upload + +This is the tool national partners' data ends up going through, and the one you will use most. It is the modern, well-structured part of the import codebase. + +### Components + +| Piece | File | +|-------|------| +| Controller | [app/Http/Controllers/BulkEventUploadController.php](../../app/Http/Controllers/BulkEventUploadController.php) | +| Header validator | [app/Services/BulkEventUploadValidator.php](../../app/Services/BulkEventUploadValidator.php) | +| Row importer | [app/Imports/GenericEventsImport.php](../../app/Imports/GenericEventsImport.php) | +| State store | [app/Services/BulkEventUploadCache.php](../../app/Services/BulkEventUploadCache.php) | +| Duplicate matcher | [app/Services/BulkEventDuplicateFinder.php](../../app/Services/BulkEventDuplicateFinder.php) | +| Result accumulator | `app/Services/BulkEventImportResult.php` | +| Jobs | `app/Jobs/ValidateBulkEventUploadJob.php`, `app/Jobs/ProcessBulkEventImportJob.php` | +| Views | `resources/views/admin/bulk-upload/{index,processing,preview,report}.blade.php` | + +### Routes + +All behind `auth` and `role:super admin` in [routes/web.php](../../routes/web.php) lines 80-87. + +| Method | Path | Name | +|--------|------|------| +| GET | `/admin/bulk-upload` | `admin.bulk-upload.index` | +| POST | `/admin/bulk-upload/validate` | `admin.bulk-upload.validate` | +| GET | `/admin/bulk-upload/processing/{token}` | `admin.bulk-upload.processing` | +| GET | `/admin/bulk-upload/status/{token}` | `admin.bulk-upload.status` | +| GET | `/admin/bulk-upload/preview` | `admin.bulk-upload.preview` | +| POST | `/admin/bulk-upload/import` | `admin.bulk-upload.import` | +| GET | `/admin/bulk-upload/report` | `admin.bulk-upload.report` | + +### The flow + +```mermaid +flowchart TD + Upload["POST /validate
file + optional default_creator_email"] --> HeaderCheck{Header check
synchronous} + HeaderCheck -->|missing columns| Reject[Back to form with
the missing column names] + HeaderCheck -->|ok| Token["Store file on temp disk
mint a 64-char token
phase = validating"] + Token --> VJob["ValidateBulkEventUploadJob
afterResponse, previewMode = true"] + VJob --> Validated[phase = validated] + Validated --> Preview["GET /preview
per-row pass/fail"] + Preview --> Import["POST /import
phase = importing"] + Import --> IJob["ProcessBulkEventImportJob
afterResponse, previewMode = false"] + IJob --> Completed["phase = completed
temp file deleted"] + Completed --> Report["GET /report
created + failures"] + Processing["GET /processing/{token}
polls /status/{token}"] -.-> Validated + Processing -.-> Completed +``` + +The key design point: **the file is parsed twice.** The first pass runs in preview mode, which validates every row and records pass or fail without writing anything. The second pass does the same validation and then persists. This means the preview is trustworthy — it is the real importer, not a separate validation routine that could drift. + +State lives in the cache under `bulk_upload_{token}` with a **two-hour TTL**. Phases are `validating`, `validated`, `importing`, `completed`, `failed`. The processing page polls the `status` endpoint and redirects when the phase changes. + +### Upload constraints + +| Constraint | Value | +|------------|-------| +| Formats | `.csv`, `.xlsx`, `.xls` | +| Max size | 50 MB (`max:51200`) | +| Temp disk | `config('filesystems.bulk_upload_temp_disk')`, default `local` | +| Temp path | `temp/bulk_events_{timestamp}.{ext}` | +| Validation job timeout | 1800 seconds | +| Import job timeout | 3600 seconds | +| Retries | `tries = 1` on both. **A failed import does not retry** | + +Both jobs are dispatched with `->afterResponse()`, so they start after the HTTP response is sent. They still need a **queue worker** unless the queue connection is `sync`. If uploads appear to hang on the processing page forever, check the worker first. + +Row counts above **500** switch the preview to failures-only, to keep the page renderable. That threshold is `BulkEventUploadCache::PREVIEW_FAILURES_ONLY_THRESHOLD`. The public guide states a maximum of 10,000 rows per sheet. + +### Required columns + +Validated up front against `BulkEventUploadValidator::REQUIRED_COLUMNS`. A missing header aborts before any row is read, and the error message names exactly which ones are absent. + +```14:32:app/Services/BulkEventUploadValidator.php + public const REQUIRED_COLUMNS = [ + 'activity_title', + 'name_of_organisation', + 'type_of_organisation', + 'activity_type', + 'description', + 'address', + 'country', + 'start_date', + 'end_date', + 'longitude', + 'latitude', + 'contact_email', + 'organiser_website', + 'participants_count', + 'males_count', + 'females_count', + 'other_count', + ]; +``` + +Note the distinction between **required headers** and **required values**. All 17 headers must be present. Only a subset must be non-empty per row: `activity_title`, `name_of_organisation`, `description`, `type_of_organisation`, `activity_type`, `country`, `start_date`, `end_date`. So `address` may be blank for an online activity even though the column must exist. + +### Column to database mapping + +| Spreadsheet column | `events` column | Transformation | +|--------------------|-----------------|----------------| +| `activity_title` | `title` and `slug` | Slug via `Str::slug()` | +| `name_of_organisation` | `organizer` | Trimmed | +| `type_of_organisation` | `organizer_type` | Trimmed | +| `activity_type` | `activity_type` | Trimmed | +| `description` | `description` | Trimmed | +| `address` | `location` | Trimmed | +| `country` | `country_iso` | Uppercased | +| `start_date`, `end_date` | `start_date`, `end_date` | Excel serial numbers or parseable strings | +| `longitude`, `latitude` | `longitude`, `latitude`, `geoposition` | Range-checked; `geoposition` becomes `"lat,lon"` | +| `contact_email` | `user_email`, and `contact_person` | Also drives creator resolution | +| `organiser_website` | `event_url` | Trimmed | +| `participants_count` | `participants_count` | Cast to int | +| `males_count`, `females_count`, `other_count` | same | Cast to int | + +Optional columns: + +| Spreadsheet column | `events` column | Notes | +|--------------------|-----------------|-------| +| `creator_id` | `creator_id` | Numeric ID or an email address | +| `image_path` | `picture` | Relative paths get a hardcoded S3 base URL prefixed | +| `language` | `language` | Takes the part before any `_`, lowercased. Defaults to `en` | +| `audience_comma_separated_ids` | `audience_event` pivot | Numeric IDs, 1-100, deduplicated | +| `theme_comma_separated_ids` | `event_theme` pivot | Passed through theme ID remapping | +| `tags` | `event_tag` pivot | Comma-separated names | +| `activity_format` | `activity_format` | Comma-separated, validated against `Event::ACTIVITY_FORMATS`, stored as JSON | +| `ages` | `ages` | Comma-separated, validated against `Event::AGES`, stored as JSON | +| `duration` | `duration` | Single value from `Event::DURATIONS` | +| `recurring_event` | `recurring_event` | Boolean-ish | +| `recurring_type` | `recurring_type` | From `Event::RECURRING_TYPES` | +| `is_extracurricular_event`, `is_standard_school_curriculum`, `is_use_resource` | same | TRUE / FALSE | +| `leading_teacher_tag` | `leading_teacher_tag` | Must match an existing user's `tag` exactly | +| `codeweek_for_all_participation_code` | `codeweek_for_all_participation_code` | Code Week 4 All code | + +Values set by the importer regardless of the spreadsheet: + +| Column | Value | Why it matters | +|--------|-------|----------------| +| `status` | `APPROVED` | **Bulk-uploaded activities skip moderation entirely** | +| `mass_added_for` | `Excel` | How you identify bulk-uploaded rows later | +| `pub_date`, `created`, `updated` | `now()` | | + +That auto-approval is deliberate — an admin uploading a partner's vetted data does not want 3,000 rows in the moderation queue — but it means **a bad upload publishes immediately**. Always use the preview. + +### Creator resolution + +Every activity needs an owning user. `resolveCreatorId()` tries, in order: + +1. The row's `creator_id`, if it is an integer. +2. The row's `creator_id`, if it is an email that matches an existing user. +3. The form's `default_creator_email`, if it matches an existing user. +4. The row's `contact_email`: exact match, then a match on the local part before the `@`, and finally **creating a new user** via `UserHelper::createUser()`. + +Two consequences to be aware of. First, **a bulk upload can create user accounts as a side effect.** Second, the local-part fallback is fuzzy: a `contact_email` of `jane@school-a.be` can match an existing `jane@anything-else.com`, attributing the activity to the wrong person. That fallback exists because partner spreadsheets often carry slightly different addresses for the same person, but it is a real source of misattribution. If ownership looks wrong after an import, this is why. + +### Duplicate handling + +Re-running the same file **updates rather than duplicates**, provided the match succeeds. `BulkEventDuplicateFinder` matches on `title` + `start_date` + `country_iso` + `organizer`, and then additionally on either the same `location` string or coordinates within a small epsilon. + +On a match the existing row's attributes are overwritten, and the audience and theme pivots are **detached and re-attached** so they reflect the new file exactly. + +The corollary: change any one of title, start date, country, or organiser in the spreadsheet and the match fails, producing a second activity rather than an update. When a partner sends a corrected file, expect duplicates if they also cleaned up titles. + +### Reading the report + +The report screen lists created or updated rows plus failures. Failure messages name the row number and the offending column, for example: + +``` +Row 42 — missing required columns: country, start_date +Row 87 — columns latitude, longitude: latitude must be -90 to 90, longitude -180 to 180 +Row 91 — invalid date format in column(s): end_date +Row 103 — could not resolve or create user (column: contact_email) +``` + +Above 500 rows the created list is not itemised (`created_details_truncated`), only counted. + +### Gotcha: the import flushes the entire application cache + +```65:66:app/Jobs/ProcessBulkEventImportJob.php + Storage::disk($disk)->delete($path); + Cache::flush(); +``` + +`Cache::flush()` clears **everything**, not just this upload's state — the map data cache, GeoIP lookups, and any other cached values. The intent is presumably to make new activities appear on the map immediately, but the blast radius is the whole cache. On live, expect a brief performance dip after a large import as caches rebuild. Worth narrowing to targeted cache keys. Tracked in [12](12-risks-and-known-issues.md). + +### Keeping the partner guide in sync + +National partners prepare files from the public wiki page [Publish your events into Codeweek](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek). That page is the **contract**: it documents the same 17 required columns, the optional columns, the audience IDs, and the theme IDs. + +**If you change `REQUIRED_COLUMNS`, the `Event` constants, or the taxonomy IDs, update that wiki page in the same change.** Partners build export scripts against it, and drift produces failures that look like our bug from their side. The wiki also links a template spreadsheet, which needs regenerating when columns change. + +Checked at the time of writing: the wiki's 17 mandatory columns match `REQUIRED_COLUMNS` exactly, the optional column list matches what `GenericEventsImport` reads, the nine audience IDs match, and the published theme IDs (1-6, 8, 9, 11, 13, 14, 16-19, skipping 7, 10, 12, and 15) match the post-consolidation set. **No drift.** + +One thing on that page *does* need changing on handover: Step 2 directs partners to email the outgoing team's support address. Repoint it at whoever will field these requests. + +--- + +## Bulk user changes + +Applies role, email, and country changes to **existing** users in bulk. Built for the periodic ambassador and leading-teacher list updates that arrive as spreadsheets. + +| Piece | Location | +|-------|----------| +| Controller | `app/Http/Controllers/BulkUserChangesController.php` | +| Services | `app/Services/BulkUserChanges/` — sheet reader, text parser, row normaliser, country resolver, planner, result | +| Routes | `/admin/bulk-user-changes/{validate,preview,apply,report}`, lines 89-93 of `routes/web.php` | +| Views | `resources/views/admin/bulk-user-changes/{index,preview,report}.blade.php` | + +### Two input modes + +**Excel.** The workbook must contain a sheet named **`Changes`** (`BulkUserChangesSheetReader::resolveChangesSheet()`; if absent, the error lists the sheet names it did find). The header row is detected by looking for `country`, `email`, and `action` columns. An optional `ignore_through_row` setting skips everything up to a given row, for workbooks with preamble. + +**Pasted text.** Records of five lines each: country, name, email, action, then role or new email. Handy for a handful of changes from an email thread. + +### Operations + +`BulkUserChangesRowNormalizer` classifies each row into an operation: + +| Operation | Triggered by | Effect | +|-----------|--------------|--------| +| `role_add` | Action containing add / grant / assign, with a role | Assigns the spatie role | +| `role_remove` | Action containing delete / remove / revoke, with a role | Removes the role | +| `email_update` | Action indicating an email change, with both old and new addresses | Updates `email`, and `email_display` if it matched | +| Country change | A resolvable country value | Updates `country_iso` | +| `manual_review` | Anything unrecognised, or a required piece missing | **Skipped**, flagged for a human | + +Safety properties worth relying on: + +- **Users are never created.** An address that matches nothing is skipped, not registered. +- **Ambiguous email matches are skipped** rather than guessed at. +- Anything the normaliser cannot confidently classify becomes `manual_review`. + +The flow is validate, then preview the plan as a dry run, then apply, then report. **Read the preview.** The planner tells you exactly which users it will touch and how, and `manual_review` rows in that list are work someone has to do by hand afterwards. + +Reference material from past runs (report and applied-address list) is kept under `docs/internal/`, which is gitignored. + +--- + +## Resource import (Learn & Teach) + +Publishes resources to [the Learn & Teach catalogue](https://codeweek.eu/resources/learn-and-teach). There is a **detailed existing runbook** at [docs/ops/learn-and-teach-resource-import.md](../ops/learn-and-teach-resource-import.md) — read that for the full procedure. Summary here for orientation only. + +| Piece | Location | +|-------|----------| +| Controller | `app/Http/Controllers/ResourcesImportController.php` | +| Imports | `app/Imports/ResourcesImport.php`, plus preview, learn, and teach variants | +| Workbook parser | `app/Services/LearnTeachWorkbookParser.php` | +| CLI | `php artisan resources:import {file}` | +| Routes | `/admin/resources-import/{verify,preview,import,report}` | +| Storage | The `resources` S3 disk | + +Only one column is strictly required: `name_of_the_resource`. + +Two things reliably cause trouble: + +- **Excel hyperlinks.** Some cells display a course title while the real URL is the cell's hyperlink target. The parser reads the hyperlink, not the visible text. Anything that bypasses the parser will publish the title as the link. +- **Dev writes to the production bucket.** Dev commonly shares `RESOURCES_BUCKET` with live, so a dev import uploads real PDFs to the production bucket while the database rows stay on dev. + +Helper scripts, both Python: + +| Script | Purpose | +|--------|---------| +| `scripts/prepare-learn-teach-upload.py` | Flattens a multi-sheet workbook into `metadata.csv` plus `images/` and `links/{group}/` folders | +| `scripts/download-learn-teach-from-sharepoint.py` | Downloads PDFs from SharePoint via Microsoft Graph, using `az login` | + +--- + +## Legacy Excel importers + +`app/Console/Commands/excel/` holds **20 partner-specific CLI importers**, each reading a spreadsheet from the `excel` disk (`resources/excel/`). They are the historical predecessors of the web uploader. + +| Command | Partner | +|---------|---------| +| `excel:generic` | The generic importer, reads `example.xlsx` | +| `excel:events-import` | Events 2025 | +| `excel:reported` | Reported activities | +| `excel:apple`, `excel:apple2024` | Apple | +| `excel:avande` | Avanade | +| `excel:charity` | UK DigitAll Charity | +| `excel:coderdojo` | CoderDojo | +| `excel:coding_activities_eu` | codingactivities.eu | +| `excel:digitale_wolven` | Digitale Wolven | +| `excel:dutch`, `excel:dutch-2023`, `excel:dutchdance`, `excel:simone` | Various Dutch partners | +| `excel:eventi` | Italy | +| `excel:hamburg` | Hamburg | +| `excel:ireland`, `excel:ireland-dream-space` | Ireland | +| `excel:lux` | Luxembourg | +| `excel:magenta` | Magenta Moon | +| `import:bulgaria`, `import:telerik` | Bulgaria, Telerik | + +Most extend `BaseEventsImport` and each expects its own column layout, frequently with a hardcoded filename. + +**Treat these as archaeology.** They were written for one delivery each and mostly will not run again. Do not use them as a model for new work, and do not spend time maintaining them — use `/admin/bulk-upload` for new partner data. If you need to reimplement a one-off, `GenericEventsImport` is the class to build on. + +## Nova-based imports + +Two smaller import paths live as Nova actions rather than admin screens: + +| Action | Imports | +|--------|---------| +| `ImportMatchmakingProfiles` | Matchmaking directory profiles from CSV or XLSX | +| `ImportHomeSlideLocaleOverrides` | Homepage carousel translations from CSV | + +## Exports + +| Mechanism | Output | +|-----------|--------| +| `UsersExport` via the Livewire leading teachers table | `users.xlsx` with tag, country, email, approved, first and last name | +| `ExcellenceExport` | Code Week 4 All metrics for the excellence flow | +| `VotesExport` | Hackathon votes | +| `php artisan cw:export-certificates-proof` | CSV manifest of issued certificates, to `storage/app/exports/` | +| `php artisan resources:export-s3-urls` | Resource S3 URLs as CSV or JSON | +| `php artisan certificate:failures-report` | Certificate generation failures | +| `GET /admin/home-slides/export-locale-overrides` | Carousel translation CSV | + +Public API endpoints that partners consume are covered in [07](07-partner-feeds-and-apis.md). diff --git a/docs/handover/07-partner-feeds-and-apis.md b/docs/handover/07-partner-feeds-and-apis.md new file mode 100644 index 000000000..6639d7ffb --- /dev/null +++ b/docs/handover/07-partner-feeds-and-apis.md @@ -0,0 +1,235 @@ +# 07 — Partner feeds and APIs + +National Code Week organisations run their own registration sites. Rather than asking their organisers to register twice, we ingest their activities automatically. This chapter covers the inbound feeds, and the outbound endpoints partners consume from us. + +Spreadsheet-based partner data goes through the bulk uploader instead — see [06](06-bulk-uploads-and-imports.md). + +## Live inbound feeds + +Four feeds run on a schedule. Everything else in this area is historical. + +| Feed | Command | Frequency | Source | +|------|---------|-----------|--------| +| Germany (central) | `api:germany-central --import` | Hourly at :15 | `events.codeweek.de` | +| Germany (legacy per-city) | `api:germany` | Hourly at :10 | Per-city endpoints. **Largely a no-op** | +| eeducation.at | `import:eeducation` | Daily at 04:00 | `eeducation.at` | +| Meet and Code | `rss:meetandcode` | Hourly at :05 | `meet-and-code.org` RSS | + +Plus one cleanup job: + +| Command | Frequency | Purpose | +|---------|-----------|---------| +| `clean:remote` | Daily at 12:05 | Deletes previously imported activities that have disappeared from their upstream feed | + +## How deduplication works + +Every feed-imported activity carries two provenance columns: + +| Column | Content | +|--------|---------| +| `mass_added_for` | Which mechanism created it, e.g. `API codeweek_de`, `RSS Eeducation` | +| `source_ref` | The upstream identifier, namespaced as `{feed-key}:{uid}` | + +`source_ref` is what makes the imports idempotent. Running a feed twice updates rows rather than duplicating them. + +The namespacing is deliberate and load-bearing. The central German export writes `codeweek-de:{uid}`, while the legacy city feeds wrote `berlin:{uid}`, `hamburg:{uid}` and so on. Because the namespaces differ, **the central import does not overwrite or collide with legacy rows**. This is called out in the scheduler: + +```28:30:routes/console.php +// New central codeweek.de export (single URL). Uses source_ref "codeweek-de:{uid}" +// so it does not overwrite legacy "berlin:{uid}" / "hamburg:{uid}" events. +Schedule::command('api:germany-central --import')->hourlyAt(15); +``` + +A side effect worth knowing: the same real-world activity can exist twice, once from a legacy city feed and once from the central export, because the two have different `source_ref` namespaces. If Germany reports duplicates, that is the first thing to check. + +--- + +## Germany: the central export + +This is the important one and the pattern to follow for any new national partner. + +**File:** [app/Console/Commands/api/GermanyCentral.php](../../app/Console/Commands/api/GermanyCentral.php) + +```17:20:app/Console/Commands/api/GermanyCentral.php + protected $signature = 'api:germany-central + {--url=https://events.codeweek.de/api/v1/export/ : Central DE export URL} + {--limit=0 : Limit number of items (0 = all)} + {--import : Persist events (default is dry-run only)}'; +``` + +### It is dry-run by default + +**Without `--import` the command writes nothing.** This is the single most useful property of the tool and how you should always start: + +```bash +# Inspect the feed, validate every row, change nothing +php artisan api:germany-central + +# Same but only the first 20 items +php artisan api:germany-central --limit=20 + +# Actually write +php artisan api:germany-central --import +``` + +The dry run reports counts of created, updated, skipped, and failed rows, plus a per-row issue list keyed by upstream `uid`. When a partner asks why some of their activities are missing, run the dry run and send them the issue list. + +### Expected payload shape + +A **top-level JSON array** of activity objects. The command explicitly rejects the `{"events": [...]}` wrapper with a clear error, because partners get this wrong: + +```53:57:app/Console/Commands/api/GermanyCentral.php + if (is_array($json) && isset($json['events']) && is_array($json['events']) && ! array_is_list($json)) { + $this->error('Top-level is {"events":[...]} — importer expects a JSON array.'); + + return self::FAILURE; + } +``` + +Fetch settings: 90 second timeout, `Accept: application/json`, and a `codeweek-eu-germany-central/1.0` User-Agent (useful if a partner needs to allowlist us). + +### Per-item validation + +`validateItem()` requires, per activity: + +| Field | Requirement | +|-------|-------------| +| `uid` | Present and numeric | +| `user` | An object | +| `user.email` | Non-empty | +| `user.type.identifier` | Non-empty | +| `type.identifier` | Present | +| `title`, `description`, `organizer` | Non-empty | +| `eventStartDate`, `eventEndDate` | Exactly `YYYY-MM-DD HH:MM:SS` | +| `tags` | If present, an array of objects each having a `title` | + +The date format is strict — matched with a regular expression, not parsed leniently. A partner sending ISO 8601 with a `T` separator or a timezone suffix will have **every row rejected**. This is the most common integration failure. + +### Mapping and ownership + +`mapAttrs()` maps the payload onto `events`, covering title, slug, organiser, description, organiser type, activity type, location, URL, contact, country (defaulting to `DE`), picture, language, dates, coordinates, participant and gender counts, the boolean curriculum flags, duration, activity format, ages, leading teacher tag, and the Code Week 4 All code. Audience, theme, and tag pivots are synced on every upsert, so removing a tag upstream removes it here. + +Activities are owned by a dedicated technical user resolved through `ImporterHelper::getTechnicalUser('codeweek-de-technical')`, used as a fallback when the payload's user cannot be matched. **Do not delete that account** — imported German activities hang off it. + +--- + +## Germany: the legacy per-city pipeline + +Historical, and mostly dormant. Kept scheduled so that any city still serving data continues to work. + +It is a **two-stage** design, unlike the central export: + +```mermaid +flowchart LR + CityAPI["City JSON endpoint
e.g. berlin.codeweek.de"] -->|"api:berlin"| Staging["{City}RSSItem
staging table"] + Staging -->|"import:berlin"| Events["events table
source_ref = berlin:{uid}"] +``` + +Stage one (`api:{city}`) fetches JSON and upserts staging rows via `GermanTraits::createRSSItem()`. Stage two (`import:{city}`) reads rows with a null `imported_at` and creates or updates activities through `GermanTraits::createGermanEvent()`. + +`api:germany` orchestrates stage one across every city in `ImporterHelper::getGermanCities()`: `niedersachsen`, `hamburg`, `baden`, `berlin`, `bremen`, `muensterland`, `nordhessen`, `bayern`, `bonn`. Additional commands exist for `dresden`, `leipzig`, and `thueringen`. + +The scheduler is honest about the state of it: + +```25:26:routes/console.php +// Keep legacy per-city German feeds (no-op while they return [] / 404). +Schedule::command('api:germany')->hourlyAt(10); +``` + +**Do not invest in this pipeline.** If Germany needs changes, they go in the central export. The main reason to understand it at all is that historical German activities carry city-namespaced `source_ref` values, and the per-city `*_rss_items` staging tables still hold data. + +--- + +## eeducation.at + +| Item | Detail | +|------|--------| +| Command | `import:eeducation` ([app/Console/Commands/Importers/Eeducation.php](../../app/Console/Commands/Importers/Eeducation.php)) | +| Schedule | Daily at 04:00 | +| Endpoint | `https://eeducation.at/rest-api/codeweek-activities/?clientid={EEDUCATION_CLIENTID}` | +| Parser | `App\Importers\Eeducation`, driven by `app/Imports/RemoteImporter.php` | +| Provenance | `mass_added_for = 'RSS Eeducation'` | + +This feed uses the **`importers` tracker table** rather than `source_ref`. Each imported activity gets a row recording `website`, `original_id`, `event_id`, and `seen_at`. `clean:remote` then deletes activities whose tracker row has not been refreshed in 24 hours — the mechanism by which an activity cancelled upstream disappears from our map. + +Requires `EEDUCATION_CLIENTID` in the environment. Without it the endpoint returns nothing and the import silently does no work. + +`RemoteImporter` resolves its parser dynamically from the website name to a class under `App\Importers\`, so adding a similar feed means adding a parser class there. + +--- + +## Meet and Code + +| Item | Detail | +|------|--------| +| Command | `rss:meetandcode` ([app/Console/Commands/rss/MeetAndCode.php](../../app/Console/Commands/rss/MeetAndCode.php)) | +| Schedule | Hourly at :05 | +| Feed | `https://www.meet-and-code.org/de/de/events/rss/` | +| Staging model | `App\MeetAndCodeRSSItem` | +| Disk | Generated XML written to the `meet-and-code` local disk (`public/rss/`) | + +Reads the RSS with `willvincent/feeds`, stages items, then creates or updates activities. + +Three follow-up commands exist for data quality on these rows, run manually: + +| Command | Purpose | +|---------|---------| +| `meetandcode:users` | Links imported activities to user accounts | +| `meetandcode:languages` | Fills in activity languages | +| `meetandcode:themes` | Fills in themes and audiences | + +--- + +## Outbound: what partners can consume from us + +### Public API + +Three endpoints in [routes/api.php](../../routes/api.php), handled by [app/Http/Controllers/Api/EventsController.php](../../app/Http/Controllers/Api/EventsController.php). + +| Endpoint | Returns | +|----------|---------| +| `GET /api/events/geobox` | Activities within a latitude/longitude bounding box. Powers map panning. Span is capped at 10 degrees | +| `GET /api/events/germany?year={year}` | All approved German activities for a year | +| `GET /api/event-detail/{event}` | A single activity | + +**These are unauthenticated.** There is no API key, no token, and no per-consumer rate limit beyond the global API throttle of 60 requests per minute configured in [bootstrap/app.php](../../bootstrap/app.php). + +`/api/events/germany` is notable: it is a country-specific export endpoint with no equivalent for any other country, built for the German partner's reciprocal integration. If another national partner asks for the same thing, generalise it rather than adding a second country-specific route. + +### Endpoints used by our own front end + +The Vue components call `/api/*` routes defined in **`routes/web.php`**, not `routes/api.php` — `/api/event/list`, `/api/event/detail`, `/api/event/closest`, `/api/event/eeducation`, and the geocoding proxies at `/api/proxy/geocode` and `/api/proxy/suggest`. They are session-authenticated where they need to be. Worth knowing so you look in the right file. + +### Internal support API + +`/api/internal/support/*`, gated by the `support.service.token` middleware (a bearer token in `SUPPORT_SERVICE_TOKEN`). Covers case intake, triage, diagnostics, tool calls (user audit, user restore, profile update, event audit), and the approval workflow. Only relevant if you operate the support copilot subsystem. + +### Podcast RSS + +`GET /feed/podcasts`, generated by `spatie/laravel-feed` from the `Podcast` model. Configured in [config/feed.php](../../config/feed.php). + +--- + +## Onboarding a new national partner + +The path of least resistance, in order of preference: + +1. **Spreadsheet through `/admin/bulk-upload`.** No code. Point them at the [public wiki guide](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek) and the template it links. Right answer for annual or occasional deliveries. +2. **A JSON export shaped like the German one.** If they want continuous sync, ask them to match the `events.codeweek.de` payload shape. Then it is a near-copy of `GermanyCentral.php` with a new feed key, and you inherit the dry-run and validation behaviour for free. +3. **A bespoke importer.** Only if neither of the above fits. Put the parser in `app/Importers/` and drive it through `RemoteImporter` so it participates in the `importers` tracker and therefore in `clean:remote`. + +Whichever route, settle these before writing code: + +- **A stable unique ID per activity.** Without one, deduplication is impossible and every run creates duplicates. +- **The exact date format**, and whether times are local or UTC. +- **Who owns the imported activities** — real user accounts matched by email, or a technical user. +- **What happens when an activity is cancelled upstream.** Does it vanish from the feed (so `clean:remote` handles it) or get flagged? +- **Whether imported activities should be auto-approved.** Feed imports currently bypass moderation. + +Then add the schedule entry to `routes/console.php`, and document it in [10](10-scheduled-jobs-and-runbooks.md). + +## Sample payloads + +`docs/internal/` holds captured samples, including a full `codeweek.de` export and an EU export sample. That directory is **gitignored**, so these files must be transferred outside git during handover. They are the quickest way to see a real payload without waiting for a live fetch. + +Note that `api:germany-central --url=` expects an HTTP URL, not a local file path, so replaying a saved sample means serving it over HTTP locally. diff --git a/docs/handover/08-certificates.md b/docs/handover/08-certificates.md new file mode 100644 index 000000000..f1f1dee07 --- /dev/null +++ b/docs/handover/08-certificates.md @@ -0,0 +1,295 @@ +# 08 — Certificates + +Certificates are the most operationally demanding part of the platform. They matter a lot to users, they are generated in bursts after October, and the pipeline depends on a system-level TeX installation that has nothing to do with PHP. + +This chapter consolidates and corrects the two certificate documents we inherited (`Certificates - Technical Documentation.docx` and `Certificate Generation Process Documentation`). Where those documents referenced a developer's local absolute paths or a retired private staging host, paths here are repo-relative and URLs are public. + +## The four types + +| Type | Awarded to | Trigger | Data stored in | +|------|------------|---------|----------------| +| **Recognition** | The activity organiser | Organiser files a report on an approved activity | Columns on `events` | +| **Participation** | Attendees, usually a class of students | Organiser generates them in bulk on the site | `participations` table | +| **Excellence** | Code Week 4 All networks | Artisan command per edition | `excellences`, `type = 'Excellence'` | +| **Super Organiser** | Organisers running 10+ activities | Artisan command per edition | `excellences`, `type = 'SuperOrganiser'` | + +Eligibility rules: + +- **Excellence** — a Code Week 4 All network with 10 activities linked from 10 different organisers, or with 3 countries involved. +- **Super Organiser** — 10 or more activities in a single edition. + +Remember that "edition" is just a **calendar year** ([04](04-domain-model.md)). + +## How generation works + +```mermaid +flowchart TD + Trigger["Trigger:
report submitted, user request,
or artisan command"] --> Pick["Pick a LaTeX template
by type, edition and language"] + Pick --> Fill["Read template from the latex disk
substitute placeholders
write a personalised .tex"] + Fill --> Compile["Run pdflatex"] + Compile -->|success| Upload["Upload PDF to the s3 disk"] + Compile -->|failure| Keep["Keep .tex and .log for debugging
record the error on the row"] + Upload --> Store["Store the URL on the row"] + Store --> Notify["Email the recipient a download link"] +``` + +The important thing to internalise: **this shells out to `pdflatex`.** There is no PHP PDF library involved. Failures are usually TeX failures — a missing font package, an unescaped character in a user-supplied name, a missing template file — and they are diagnosed by reading a `.log` file, not a PHP stack trace. + +## Code map + +| Concern | File | +|---------|------| +| Recognition generation | [app/Certificate.php](../../app/Certificate.php) | +| Excellence and Super Organiser generation | [app/CertificateExcellence.php](../../app/CertificateExcellence.php) | +| Participation generation | [app/CertificateParticipation.php](../../app/CertificateParticipation.php) | +| Shared helper, ZIP packaging, S3 upload | [app/Helpers/CertificatesHelper.php](../../app/Helpers/CertificatesHelper.php) | +| Supporting classes | `app/Certificates/` | +| Controllers | `CertificateController`, `ExcellenceController`, `ParticipationController`, `AdminController`, `CertificateBackendController` | +| Batch jobs | `app/Jobs/GenerateCertificateBatchJob.php`, `SendCertificateBatchJob.php`, `GenerateCertificatesOfParticipation.php`, `MissingCertificate.php` | +| Templates | `resources/latex/` | +| Excellence model | [app/Excellence.php](../../app/Excellence.php) | + +Note that `Certificate.php`, `CertificateExcellence.php`, and `CertificateParticipation.php` sit alongside the Eloquent models in `app/` but are **service classes, not models**. + +## Templates + +41 `.tex` files in `resources/latex/`, plus an `images/` directory for the artwork. + +Naming conventions: + +| Pattern | Example | Used for | +|---------|---------|----------| +| `template.tex` | | Recognition, general | +| `template_greek.tex` | | Recognition, Greek | +| `participation.tex` | | Participation, general | +| `participation_greek.tex` | | Participation, Greek | +| `participation_ukrainian.tex` | | Participation, Ukrainian | +| `participation_multilang.tex` | | Participation, multi-language | +| `excellence-{year}.tex` | `excellence-2026.tex` | Excellence, general | +| `excellence_greek-{year}.tex` | `excellence_greek-2026.tex` | Excellence, Greek | +| `super-organiser-{year}.tex` | `super-organiser-2026.tex` | Super Organiser, general | +| `super-organiser_greek-{year}.tex` | `super-organiser_greek-2026.tex` | Super Organiser, Greek | + +Excellence and Super Organiser templates are **versioned per edition**: excellence templates exist for 2018 through 2026, super-organiser for 2020 through 2026. Recognition and Participation templates are **not** versioned — there is one of each, updated in place. + +The template name is derived from type and edition at runtime: + +```48:49:app/CertificateExcellence.php + // e.g. "excellence-2025.tex" or "super-organiser-2025.tex" + $this->templateName = "{$this->type}-{$this->edition}.tex"; +``` + +with a language override: + +```205:205:app/CertificateExcellence.php + $this->templateName = "{$this->type}_greek-{$this->edition}.tex"; +``` + +**So a missing template for the current edition is a hard failure.** Running `notify:winners 2027` without `excellence-2027.tex` present will fail for every recipient. Creating next year's templates is the first task of the annual rollover. + +### Two oddities in the template directory + +- **`SuperOrganiser-2024.tex` and `SuperOrganiser_greek-2024.tex`** use a different capitalisation from every other file (`super-organiser-2024.tex` also exists). On a case-sensitive filesystem these are distinct files. Do not assume they are duplicates without checking which one the code actually resolves. +- **`nH8nokEg9c-1.tex`**, along with matching `.aux` and `.log` files, is **leftover build debris** from a failed compile that got committed. It can be deleted. Note that personalised `.tex` files are written into this same directory during generation, so debris accumulating here is expected at runtime — it is only a problem when it gets committed. + +## Language handling + +Certificates are issued across the whole EU, and some scripts need TeX packages and encoding that the default template does not provide. The approach is **a separate template per problem script** rather than one clever template. + +Greek has a dedicated variant for every type. Participation additionally has Ukrainian and multi-language variants. `CertificateParticipation` selects between them: + +```103:110:app/CertificateParticipation.php + // --- Choose base template (per detected script in any field) --- + if ($this->is_greek_text($this->event_name) || $this->is_greek_text($this->event_date) || $this->is_greek_text($this->name_of_certificate_holder)) { + $this->templateName = 'participation_greek.tex'; + } elseif ($this->is_ukrainian_text($this->event_name) || $this->is_ukrainian_text($this->event_date) || $this->is_ukrainian_text($this->name_of_certificate_holder)) { + $this->templateName = 'participation_ukrainian.tex'; + } else { + $this->templateName = 'participation.tex'; + } +``` + +Note that the script is **detected from the content of the recipient's name, activity name, and date** — not from a locale setting. A Greek name on an otherwise English activity gets the Greek template. + +Consequences to plan for: + +- Adding a language that needs special encoding means **a new template file per certificate type**, not a configuration change. +- When you refresh the design each year, **the language variants must be refreshed too** or Greek recipients get last year's artwork. +- `php artisan certificate:test-two` exists specifically to preflight names in Greek, Cyrillic, and other scripts. Use it before a big run. + +## Server requirements + +Certificates will not generate without TeX Live. From the original infrastructure handover: + +```bash +sudo apt-get install texlive-latex-base -y +sudo apt-get install texlive-fonts-recommended -y +sudo apt-get install texlive-fonts-extra -y +sudo apt-get install texlive-lang-cyrillic -y +sudo apt-get install texlive-lang-greek -y +sudo apt-get install texlive-latex-extra -y +sudo apt-get install texlive-font-utils -y +``` + +`texlive-lang-cyrillic` and `texlive-lang-greek` are **required, not optional**. Also set: + +| Requirement | Value | +|-------------|-------| +| `PDFLATEX_PATH` | Absolute path to the `pdflatex` binary | +| PHP memory limit | 2048 MB | + +If `PDFLATEX_PATH` is empty or wrong, every certificate fails. That is the first thing to check on a new server. + +## Storage + +| What | Where | +|------|-------| +| Templates and intermediate `.tex` / `.log` / `.aux` | The `latex` local disk (`resources/latex/`) | +| Finished individual PDFs | The `s3` disk, at `certificates/{id}.pdf` | +| Participation ZIP bundles | The `s3` disk, at `participation/{zipname}` | + +```74:75:app/Helpers/CertificatesHelper.php + $s3Path = 'participation/' . $zipname; + $upload = Storage::disk('s3')->put($s3Path, $zipStream); +``` + +On failure the `.tex` and `.log` are deliberately **left in place** so you can debug. That is your primary diagnostic artefact. + +## User-facing flows + +### Recognition + +1. Organiser submits an activity; an ambassador approves it. +2. After the end date the organiser is prompted (and reminded by `remind:creators`, daily at 10:00) to file a report. +3. The report captures `name_for_certificate`, `participants_count`, `average_participant_age`, and `percentage_of_females`. +4. On submission the certificate generates and a download link is shown. + +Filing the report sets `reported_at`, which **locks the activity against further edits** by the owner. Organisers regularly raise support tickets about this. + +### Participation + +Self-service at [codeweek.eu/participation](https://codeweek.eu/participation). The organiser enters the list of student names, the activity name, and the date. One PDF is generated per name and the batch is delivered as a single ZIP. + +Routes: `GET`/`POST /participation` → `ParticipationController::show` and `generate`. + +Rows start with `status = 'PENDING'` and move on as generation completes, or to an error state. A stuck batch is usually a missing queue worker. + +### Excellence and Super Organiser + +Eligible users see them at `/certificates`, with per-edition report pages: + +| Route | Purpose | +|-------|---------| +| `GET /certificates` | The user's certificate list | +| `GET`/`POST /certificates/excellence/{edition}` | Excellence report and generation | +| `GET`/`POST /certificates/super-organiser/{edition}` | Super Organiser report and generation | + +Access is controlled by two custom gates registered in `AppServiceProvider`: `report-excellence` and `report-super-organiser`, each checking for an `excellences` row of the right type and edition. + +## The certificate backend admin + +A dedicated batch operations UI at `/admin/certificate-backend/*`, handled by `CertificateBackendController`: + +| Route | Purpose | +|-------|---------| +| `/` | Dashboard | +| `/list` | Recipients | +| `/status` | Progress of a running batch | +| `/generate/start`, `/generate/cancel` | Start and cancel generation | +| `/send/start` | Start sending emails | +| `/regenerate/{id}`, `/resend/{id}` | Single-recipient fixes | +| `/resend-all-failed` | Retry every failure | +| `/errors` | Failure list | +| `/manual-create-send` | Create and send one certificate by hand | + +Batch progress is tracked in the cache with a 24-hour TTL. + +**This area is gated on a single hardcoded email address**, not a role: + +```11:11:app/Http/Middleware/EnsureSuperCertificateAdmin.php + private const ALLOWED_EMAIL = 'bernard@matrixinternet.ie'; +``` + +A new developer, even as super admin, gets a 403. **Change this constant before the first certificate run** — ideally replacing it with a role or permission check. See [00](00-access-checklist.md) and [12](12-risks-and-known-issues.md). + +## Artisan commands + +### Awarding and notifying + +| Command | Purpose | +|---------|---------| +| `php artisan excellence {edition}` | Tags the excellence winners for an edition | +| `php artisan superorganisers {edition}` | Allows super organisers for an edition to generate certificates | +| `php artisan notify:winners {edition}` | Identifies Excellence winners, generates certificates, emails recipients | +| `php artisan notify:superorganisers {edition}` | Same for Super Organisers | + +### Batch generation and sending + +| Command | Purpose | +|---------|---------| +| `certificate:generate-window` | Generates certificates in batches until none are pending | +| `certificate:send-window` | Sends certificate emails in batches | +| `certificate:clear-generation` | Clears stuck generation state | +| `certificate:preflight` | **Dry-run compiles and reports failures without sending anything** | +| `certificate:test-two` | Preflights awkward names (Greek, Cyrillic, and similar) | + +### Diagnosis and repair + +| Command | Purpose | +|---------|---------| +| `certificate:stats` | Counts by state | +| `certificate:issues` | Finds certificates with generation problems. **Scheduled hourly at :33** | +| `certificate:failures-report` | Lists or exports failures | +| `certificate:retry-failed` | Resets failed rows for another attempt | +| `certificate:regenerate-in-place` | Regenerates a single PDF, keeping the URL | +| `certificate:reassign-user` | Moves certificate rows between users | +| `certificate:fill-empty-names` | Fills blank `name_for_certificate` on Excellence rows | +| `certificate:orphaned-excellence` | Finds and fixes Excellence rows whose user is gone | +| `certificate:regenerate-greek-super-organiser` | Targeted fix for Greek Super Organiser PDFs | +| `certificates:fix` | Generates missing participation certificates | +| `missing:certificates` | Generates missing certificates | +| `cw:export-certificates-proof` | CSV manifest of everything issued, to `storage/app/exports/` | +| `support:certificate-kpi-report` | KPI counts for reporting | + +`certificates:fix` is **commented out** in the scheduler: + +```40:40:routes/console.php +//Schedule::command('certificates:fix')->everyFiveMinutes(); +``` + +Leave it that way unless you know why it was disabled — running a generation command every five minutes will queue work faster than TeX can complete it. + +The number of repair commands here is itself a signal: this pipeline fails often enough that a substantial toolkit grew around it. Expect to use them. + +## Error tracking + +The `excellences` table carries `certificate_generation_error` and `certificate_sent_error` (added 2025). When a run goes wrong, query those columns first, then look at the `.log` file left behind on the `latex` disk. + +## The annual rollover + +Do this at the start of each edition, before any generation runs. + +1. **Create the new templates.** Copy the previous year's and apply the new design: + - `excellence-{year}.tex` + - `excellence_greek-{year}.tex` + - `super-organiser-{year}.tex` + - `super-organiser_greek-{year}.tex` +2. **Refresh the non-versioned templates** in place for the new design: `template.tex`, `template_greek.tex`, `participation.tex`, `participation_greek.tex`, `participation_ukrainian.tex`, `participation_multilang.tex`. +3. **Update the artwork** in `resources/latex/images/`. +4. **Deploy.** +5. **Restart the PHP workers** — see the warning below. `php artisan queue:restart`. +6. **Preflight** with `php artisan certificate:preflight` and `php artisan certificate:test-two`. +7. **Generate and notify** with `notify:winners {edition}` and `notify:superorganisers {edition}`. +8. **Check for failures** with `certificate:failures-report`, and retry with `certificate:retry-failed`. + +### The worker restart gotcha + +This is the single most valuable piece of institutional knowledge in the inherited documentation, so it is repeated here: + +> After updating the LaTeX templates, you **must** restart the PHP workers before running the certificate commands. Long-running workers hold the previous templates in memory. Skip the restart and certificates are generated **silently using last year's design** — no error, no warning, just wrong output that has already been emailed to recipients. + +`php artisan queue:restart` is in the deploy script for exactly this reason ([02](02-environments-and-deployment.md)). Verify it actually ran. + +### Test with a real name before the bulk run + +Before generating thousands, generate one for a recipient whose name contains characters likely to break TeX — Greek, Cyrillic, or an accented Latin name. `certificate:test-two` is built for this. A run that fails at recipient 400 of 2,000 leaves you reconciling which half went out. diff --git a/docs/handover/09-wordpress-blog.md b/docs/handover/09-wordpress-blog.md new file mode 100644 index 000000000..d1b6323a2 --- /dev/null +++ b/docs/handover/09-wordpress-blog.md @@ -0,0 +1,161 @@ +# 09 — The WordPress blog + +## What it is + +[codeweek.eu/blog](https://codeweek.eu/blog/) is **a separate WordPress application**, not part of the Laravel codebase. It shares the `codeweek.eu` domain but is a distinct install with its own admin, database, theme, and plugins. + +It is where the communications team publishes news, ambassador profiles, activity highlights, and campaign announcements. It is not in this repository, and nothing in the Laravel deploy touches it. + +```mermaid +flowchart LR + Editor[Comms team] -->|writes posts| WP["WordPress at /blog
own DB, theme, plugins"] + WP -->|"/wp-json/wp/v2/posts"| Sync["app:sync-blogs
daily at 01:00"] + Sync -->|upsert on post URL| Blogs[("blogs table")] + Blogs --> Search["Global search
Blogs category"] + WP -->|served directly| Visitor[Visitors reading the blog] + Scripts["scripts/*.sh
run on the server"] -->|patch theme files| WP +``` + +Two entirely separate things are going on, and conflating them causes confusion: + +1. **Visitors read the blog from WordPress directly.** Laravel does not render blog posts. +2. **Laravel keeps a read-only mirror** of post metadata in its own `blogs` table, purely so blog posts appear in site-wide search. + +## The sync + +**Command:** `php artisan app:sync-blogs` — [app/Console/Commands/SyncBlogs.php](../../app/Console/Commands/SyncBlogs.php) +**Schedule:** daily at 01:00, in [routes/console.php](../../routes/console.php) + +It walks the WordPress REST API, ten posts at a time, until it runs out of pages: + +```31:37:app/Console/Commands/SyncBlogs.php + $url = config('codeweek.blog_url') . '/wp-json/wp/v2/posts'; + $perPage = 10; + $page = 1; + + try { + do { + $response = Http::get("{$url}?per_page={$perPage}&page={$page}"); +``` + +Pagination is driven by the `X-WP-TotalPages` response header. Each post is upserted into `blogs` **keyed on the post's URL** (`path`): + +| `blogs` column | Source in the WordPress payload | +|----------------|---------------------------------| +| `name` | `title.rendered` | +| `description` | `excerpt.rendered`, with tags stripped | +| `content` | `content.rendered` | +| `categories`, `tags` | The ID arrays, JSON-encoded | +| `thumbnail` | Resolved via a second request — see below | +| `path` | `link` (the match key) | +| `date`, `modified`, `status`, `type`, `slug` | Direct copies | + +Thumbnails need an **extra HTTP request per post**. The posts response only carries a `_links.wp:featuredmedia` href, so `fetchThumbnail()` fetches that and reads `source_url`. With a few hundred posts that is a few hundred extra requests per run — which is why this is a nightly job and not something you want to trigger casually in a loop. + +The `Blog` model is deliberately thin: `public $timestamps = false`, everything fillable, no relationships. It is a mirror, not a domain object. + +### What the mirror is used for + +Exactly one thing: **global search**. `app/Enums/GlobalSearchFiltersEnum.php` registers a `Blogs` category that queries the `blogs` table and renders results as **external links** back to WordPress. + +```107:115:app/Enums/GlobalSearchFiltersEnum.php + self::BLOGS => [ + 'model' => Blog::class, + 'map_fields' => [ + 'name' => '{name}', + 'category' => 'Blog', + 'description' => '{description}', + 'thumbnail' => '{thumbnail}', + 'path' => '{path}', + 'link_type' => 'external', +``` + +So a blog post that does not show up in site search is almost always a sync problem, not a search problem. + +### Failure modes + +The command is resilient but quiet about it, which is the main operational risk: + +| Situation | Behaviour | +|-----------|-----------| +| A page request fails | Prints an error and **breaks the loop** — later pages are not synced at all | +| A thumbnail fetch fails | Logs and returns null; the post still syncs, without an image | +| Any exception | Logged to `Log::error` and the command exits cleanly | + +**Deletions are never propagated.** Because it only ever upserts, a post deleted or unpublished in WordPress stays in the `blogs` table forever and keeps appearing in search, linking to a 404. There is no reconciliation pass. If someone reports a dead search result pointing at the blog, this is why, and the fix is to delete the row manually. + +Nothing alerts on a failed sync. If search results go stale, check the scheduler ran and look for `Error syncing blogs` in the logs. + +### The `BLOG_URL` trap on non-production environments + +```15:15:config/codeweek.php + 'blog_url' => env('BLOG_URL', 'https://codeweek.eu/blog'), +``` + +The default is the **live** blog. On dev or local, unless `BLOG_URL` is set explicitly, `app:sync-blogs` pulls production content into that environment's database. Harmless for a read-only fetch, but it means dev search results silently reflect live content, and it puts load on the production blog from non-production environments. + +### Running it manually + +```bash +php artisan app:sync-blogs +``` + +Safe to run any time — it is idempotent, upserting on the post URL. It prints one line per page. The slow part is the per-post thumbnail lookups. + +## Theme patching + +This is the part that will surprise you. There are **18 shell scripts in [scripts/](../../scripts)** that modify the live WordPress theme's files on the server. + +| Group | Scripts | +|-------|---------| +| Mobile menu | `fix-blog-mobile-menu-clickability.sh`, `-js-syntax`, `-laravel-match`, `-snappy`, `-v2`, `patch-blog-mobile-menu-parent-links.sh` | +| Hero and CTA | `fix-blog-hero-cta-render.sh`, `patch-blog-hero-cta-and-menu-icon.sh`, `patch-blog-hero-height.sh` | +| Chevron and buttons | `fix-blog-chevron-and-button.sh`, `-and-hero-cta`, `-left-cta`, `-position-remove-arrow` | +| Footer | `fix-blog-footer-js-stray-brace.sh`, `fix-blog-footer-script-clean.sh` | +| Feature deploy | `deploy-blog-share-stories.sh`, `patch-blog-menu-share-stories-icon.sh` | + +They share a pattern: locate the WordPress root and the theme directory, then rewrite `new.css`, `header.php`, `header-v2.php`, and `footer.php` in place using embedded Python, and cache-bust the CSS with a timestamp. + +```5:11:scripts/fix-blog-mobile-menu-v2.sh +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +THEME="${THEME:-$WP_ROOT/wp-content/themes/eucodewe-1389}" +CACHE_VER="${CACHE_VER:-$(date +%Y%m%d%H%M)}" + +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found"; exit 1; } +[[ -f "$THEME/new.css" ]] || { echo "Theme new.css not found"; exit 1; } +command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } +``` + +Both `WP_ROOT` and `THEME` are overridable environment variables. `wp-cli` must be installed on the server. Some scripts fall back to searching under `/var/www` for the theme directory if the default path misses. + +### Why this matters + +The consequences of this arrangement are significant, so be explicit about them: + +- **The blog theme's real state exists only on the server.** The scripts are patches against whatever the theme happened to look like at the time. They are not a source of truth, and running them out of order or against an updated theme may not apply. +- **A WordPress theme update can silently undo all of it.** Every one of these fixes lives in theme files that an update overwrites. If the mobile menu, hero, or footer suddenly regresses after a WordPress maintenance window, an update reverted the patches. +- **They are not idempotent in any guaranteed way.** Several insert CSS blocks keyed on a marker comment, so a second run may be a no-op or may duplicate, depending on the script. +- **They run as root on the live server.** There is no dry-run mode and no rollback. + +### Guidance + +Before running any of them: + +1. **Back up the theme directory.** `tar` it up first. There is no undo. +2. Read the script. They are short, and the embedded Python shows exactly what will be rewritten. +3. Prefer the highest-numbered or most recent variant where several address the same thing (for example `fix-blog-mobile-menu-v2.sh` over the earlier mobile menu scripts). +4. Verify on the live blog immediately afterwards, on both desktop and mobile, since most of these are mobile-specific. + +**The better long-term fix** is to move these changes into a proper child theme kept in version control, so a WordPress update cannot revert them and the current state is reviewable. Until that happens, treat this directory as a set of emergency patches with institutional knowledge attached, and keep a note of which were last applied. + +## Redirects + +`routes/web.php` contains a long list of `Route::permanentRedirect()` entries mapping old top-level URLs to `/blog/...` paths — the residue of a content migration when posts moved under the blog. Keep them; they are load-bearing for SEO and for links in old newsletters and press coverage. + +## Handover items + +- WordPress admin credentials, and confirmation of who else has accounts. +- Filesystem and `wp-cli` access to the blog directory on the live server. +- A backup of the current theme, since the patched state is not in version control. +- The WordPress and plugin update policy — updates are what revert the theme patches. +- Confirmation that the blog has its own backup schedule. It is a separate application, so a Laravel database backup does not cover it. diff --git a/docs/handover/10-scheduled-jobs-and-runbooks.md b/docs/handover/10-scheduled-jobs-and-runbooks.md new file mode 100644 index 000000000..a1ec695c5 --- /dev/null +++ b/docs/handover/10-scheduled-jobs-and-runbooks.md @@ -0,0 +1,239 @@ +# 10 — Scheduled jobs and runbooks + +## Where the schedule lives + +Laravel 11 has no `app/Console/Kernel.php`. **The entire cron schedule is in [routes/console.php](../../routes/console.php)** — 62 lines. That is the whole thing. + +It only runs if Forge has a `schedule:run` cron entry on the server. That entry is **not in this repository**. If every automated behaviour on the site stops at once, check that first. + +```bash +# on the server +php artisan schedule:list +``` + +## The schedule + +| Time | Command | What it does | Chapter | +|------|---------|--------------|---------| +| Hourly | `inspire` | Prints a quote. Harmless framework default | | +| Hourly :05 | `rss:meetandcode` | Imports Meet and Code activities from RSS | [07](07-partner-feeds-and-apis.md) | +| Hourly :10 | `api:germany` | Legacy per-city German feeds. Largely a no-op | [07](07-partner-feeds-and-apis.md) | +| Hourly :13 | `magic:key` | Regenerates user `magic_key` tokens for unsubscribe links | [04](04-domain-model.md) | +| Hourly :15 | `api:germany-central --import` | The live German import | [07](07-partner-feeds-and-apis.md) | +| Hourly :30 | `notify:administrators` | Emails admins about activities awaiting attention | | +| Hourly :30 | `relocate` | Fixes activities with missing or bad coordinates | | +| Hourly :33 | `certificate:issues` | Detects certificates with generation problems | [08](08-certificates.md) | +| Every 2 min | `relocate:country` | Country-level coordinate repair | | +| Every minute | `support:gmail:poll --max=10` | Support mailbox ingest. **Only if `support_gmail.enabled`** | | +| Every minute | `support:ai:poll-agents` | Support copilot agent polling. **Only if both support AI flags are on** | | +| Daily 01:00 | `app:sync-blogs` | Pulls WordPress posts into the `blogs` table | [09](09-wordpress-blog.md) | +| Daily 01:00 | `events:generate-recurring` | Materialises recurring activity instances | | +| Daily 04:00 | `import:eeducation` | eeducation.at activity feed | [07](07-partner-feeds-and-apis.md) | +| Daily 09:00 | `remind:ambassadors` | Reminds ambassadors about pending moderation | | +| Daily 10:00 | `remind:creators` | Reminds organisers to report on finished activities | [08](08-certificates.md) | +| Daily 12:05 | `clean:remote` | Removes imported activities that vanished upstream | [07](07-partner-feeds-and-apis.md) | +| Weekly Thu 08:00 | `delete:unactiveusers` | GDPR deletion of users without consent | [04](04-domain-model.md) | +| Disabled | `certificates:fix` | Commented out. Leave it that way | [08](08-certificates.md) | + +### A removed entry, for context + +The schedule used to contain `app:export-search-data-to-json` at 02:00 daily. No such command was registered anywhere in the codebase — the only occurrence of that string was the scheduler line itself — so the scheduler failed every night. Nothing depended on it and the line has been deleted. + +Worth noting as a pattern: a permanently failing schedule entry trains everyone to ignore scheduler errors, which is how a real failure goes unnoticed. If you add a `Schedule::command()` line, confirm it appears in `php artisan schedule:list` and actually runs. + +### Two entries with a name collision worth double-checking + +`relocate` (hourly at :30) and `relocate:country` (every two minutes) both resolve to `app/Console/Commands/RelocateCountry.php`. Confirm on the server with `php artisan schedule:list` which signature each actually binds to, and whether running a geocoding repair **every two minutes** is still intended. + +### Support subsystem entries are conditional + +```47:48:routes/console.php +$supportGmailPoll = Schedule::command('support:gmail:poll --max=10') + ->when(fn () => (bool) config('support_gmail.enabled')); +``` + +Both support entries are wrapped in `->when()` checks on their feature flags, so with the flags off they cost nothing. See [03](03-configuration.md). + +## Queues + +There is **no Horizon and no supervisor configuration in this repository.** Worker definitions live in Forge. Check them during handover and note the queue names, worker count, and timeout. + +### What depends on a worker + +| Work | Job class | +|------|-----------| +| Bulk activity upload validation | `ValidateBulkEventUploadJob` (timeout 1800s, `tries = 1`) | +| Bulk activity import | `ProcessBulkEventImportJob` (timeout 3600s, `tries = 1`) | +| Certificate batch generation | `GenerateCertificateBatchJob` | +| Certificate batch sending | `SendCertificateBatchJob` | +| Participation certificates | `GenerateCertificatesOfParticipation` | +| Missing certificates | `MissingCertificate` | +| GDPR user deletion | `ProcessUserDeletion` | +| Activity relocation | `RelocateEvent` | +| Support copilot work | `app/Jobs/Support/` | +| **Almost all outgoing mail** | Sent with `Mail::queue()` | + +That last row is the one people forget. **No worker means no email** — not just certificates, but moderation notifications, reminders, and contact form delivery. + +Both bulk jobs have `tries = 1`, so a transient failure is final. Re-upload rather than waiting for a retry. + +`php artisan queue:restart` must run on every deploy. Workers hold code in memory, and for certificates that means LaTeX templates — see [08](08-certificates.md). + +## About the ~150 Artisan commands + +`app/Console/Commands/` holds **151 files**. Most are one-off partner imports or data fixes that will never run again. + +The ones that matter divide into three groups: + +1. **Scheduled** — the table above. These run whether you think about them or not. +2. **Operational** — run deliberately during the annual cycle: the certificate commands ([08](08-certificates.md)), `notify:winners`, `notify:superorganisers`, `link:lt`. +3. **Everything else** — archaeology. Partner-specific importers, historical backfills, and abandoned experiments. + +When you meet an unfamiliar command, check whether it appears in `routes/console.php` or in a runbook. If it does not, assume it is group three until proven otherwise. + +--- + +# Runbooks + +## Deploy a change + +1. Branch from `dev`, open a PR into `dev`. +2. Forge deploys `dev` to [dev.codeweek.eu](https://dev.codeweek.eu/). +3. QA on dev. **Nova changes must be checked by hand** — CI does not cover Nova ([02](02-environments-and-deployment.md)). +4. PR from `dev` into `master`. +5. Forge deploys to [codeweek.eu](https://codeweek.eu). +6. Confirm `php artisan queue:restart` ran. +7. If the release contained migrations, confirm they applied: `php artisan migrate:status`. + +Avoid deploying during October unless it is a fix. Traffic, registrations, and moderation volume all peak then. + +## Run a bulk activity upload + +Full detail in [06](06-bulk-uploads-and-imports.md). + +1. **Do it on dev first.** Every time, no exceptions. +2. Check the file's headers against `BulkEventUploadValidator::REQUIRED_COLUMNS`. All 17 must be present. +3. Go to `/admin/bulk-upload` as a super admin. +4. Set a `default_creator_email` if the file has no `creator_id`, to avoid accidental user creation. +5. Upload. The header check is immediate; validation is queued. +6. **Read the preview.** Bulk-uploaded activities are created `APPROVED` and skip moderation, so a bad file publishes instantly. +7. Fix the spreadsheet and re-upload if failures are significant. Re-running updates matched rows rather than duplicating them. +8. Import, then read the report. +9. Repeat on live. +10. Spot-check a few activities on the public map. + +Expect a brief performance dip on live afterwards — the import calls `Cache::flush()`. + +## Onboard a national partner + +Covered in [07](07-partner-feeds-and-apis.md). In short: prefer the spreadsheet route, point them at the [public wiki guide](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek), and only build a feed importer if they need continuous sync. + +## New edition rollover + +Do this before the new Code Week year begins. + +1. **Certificate templates.** Create `excellence-{year}.tex`, `excellence_greek-{year}.tex`, `super-organiser-{year}.tex`, `super-organiser_greek-{year}.tex`. Refresh the non-versioned Recognition and Participation templates and the artwork. [08](08-certificates.md) +2. **Deploy, then restart workers.** Non-negotiable — otherwise last year's design goes out silently. +3. **Preflight** with `certificate:preflight` and `certificate:test-two`. +4. **Check the taxonomies.** If themes or audiences changed, update the database, the `Event` constants, the translation files, and the wiki page — together. +5. **Refresh the wiki guide and the template spreadsheet** if the column contract changed. +6. **Check the CMS pages.** Homepage slides, campaign pages, and the Grassroots Grants content are all editorially managed in Nova and usually need a refresh. +7. **Capacity check** ahead of October: queue workers, database headroom, S3 costs. + +## Issue certificates for an edition + +[08](08-certificates.md) has the detail. The shape: + +```bash +php artisan excellence {edition} +php artisan superorganisers {edition} +php artisan certificate:preflight +php artisan notify:winners {edition} +php artisan notify:superorganisers {edition} +php artisan certificate:failures-report +php artisan certificate:retry-failed +``` + +Remember the `/admin/certificate-backend/*` UI is gated on a hardcoded email address that must be changed first ([00](00-access-checklist.md)). + +## Regenerate one certificate + +1. Find the recipient in `/admin/certificate-backend/list`. +2. Use `/regenerate/{id}` for a new PDF at the same URL, or `/resend/{id}` to re-email the existing one. +3. From the CLI: `php artisan certificate:regenerate-in-place`. +4. If it fails, read the `.log` left behind on the `latex` disk and check `certificate_generation_error` on the row. + +## Publish Learn & Teach resources + +Follow [docs/ops/learn-and-teach-resource-import.md](../ops/learn-and-teach-resource-import.md) — it is the most complete runbook we have. Two reminders: read Excel **hyperlink targets**, not cell text, and be aware that dev usually writes to the **production** resources bucket. + +## October readiness + +Roughly a quarter of the year's visits land in October — October 2024 recorded 99,770 visits, 32,669 unique visitors and 1.49 million actions in that single month. Traffic then holds through spring rather than collapsing, because teachers discover Code Week in October and return when planning the next term. + +A fortnight beforehand: + +- [ ] Confirm the schedule is registered: `php artisan schedule:list`. +- [ ] Confirm queue workers are alive and draining. See the note below — **`queue:monitor` needs arguments and will not alert anyone on its own.** +- [ ] Check for a backlog of failures: `php artisan queue:failed`. Clear anything stale before the spike so October failures are visible. +- [ ] Confirm the German central import is succeeding: `php artisan api:germany-central`. **Dry-run is the default**; it only writes when you add `--import`, so this is safe to run against live. +- [ ] Verify the database backup by restoring one. Backups are not configured in this repository — there is no `spatie/laravel-backup` — so they are managed in Forge/AWS. The commitment is nightly database and weekly file backups with restore tests. +- [ ] Check Sentry for recurring errors and clear the noise so real ones stand out. +- [ ] Confirm Site24x7 monitoring is active and alerting somewhere someone reads. +- [ ] Check the moderation queue at `/pending` is empty and ambassadors know volume is coming. `/pending` and `/review` are both behind `role:super admin|ambassador`. +- [ ] Freeze non-essential deploys. +- [ ] Confirm `certificate:preflight` passes, since reporting begins as soon as October activities end. It defaults to the current year; pass `--edition` when checking any other. + +### Checking the queue properly + +`php artisan queue:monitor` on its own fails with `Not enough arguments (missing: "queues")`. It takes a required `connection:queue` list: + +```bash +# confirm what the connection actually is first - it must not be 'sync' on live +php artisan tinker --execute="echo config('queue.default');" + +php artisan queue:monitor database:default # or redis:default, matching the above +``` + +Two caveats that make this weaker than it looks: + +- `queue:monitor` only **dispatches a `QueueBusy` event** when a queue exceeds `--max`. There is no listener for that event anywhere in this application, so nothing is emailed or alerted. Run interactively and read the table, or register a listener if you want real alerting. +- No job in this codebase calls `onQueue()`, so everything sits on the single default queue. There is no Horizon either. Worker health is therefore a Forge question, not an application one — check the daemon status in Forge alongside the commands above. + +A useful one-liner for confirming the effective drivers on a host: + +```bash +php artisan about +``` + +## An activity is not appearing on the map + +Work through in order: + +1. Is `status` `APPROVED`? Pending activities are invisible to the public. +2. Does it have valid `latitude`, `longitude`, and `geoposition`? Missing coordinates are the most common cause. `relocate` and `relocate:country` run on a schedule to repair these. +3. Is it soft-deleted? Query with `withTrashed()`. +4. Map data is cached for **300 seconds** — wait, or clear the cache. +5. Is `end_date` in the year being filtered on? Year filtering uses `end_date`, not `start_date`. + +## Someone says they never received an email + +1. Is there a **running queue worker**? Nearly all mail is queued. +2. Check `storage/logs/mails_sent.log` — the `mails_sent` channel is there for exactly this. +3. Check the mail provider's own logs for a bounce or suppression. +4. For certificate mail specifically, check `certificate_sent_error` on the `excellences` row. + +## A user has disappeared + +1. Query `users` with `withTrashed()` — they are probably soft-deleted. +2. Check whether `delete:unactiveusers` removed them for missing consent ([04](04-domain-model.md)). +3. Their activities will have been reassigned to the legacy user with id `1000000`. +4. Restoring means undoing the soft delete **and** reassigning records back. There is no one-step restore. If it is a GDPR deletion, confirm restoring it is lawful before you do. + +## The whole site is misbehaving after a deploy + +1. `php artisan migrate:status` — did migrations apply? +2. Did `queue:restart` run? Stale workers cause behaviour that looks like the deploy did not happen. +3. `php artisan config:clear`, `cache:clear`, `view:clear`. +4. Check Sentry. +5. Confirm `npm run build` succeeded on the server — a failed asset build leaves the site styled with stale files. diff --git a/docs/handover/11-testing-and-local-dev.md b/docs/handover/11-testing-and-local-dev.md new file mode 100644 index 000000000..babb348fc --- /dev/null +++ b/docs/handover/11-testing-and-local-dev.md @@ -0,0 +1,194 @@ +# 11 — Testing and local development + +## Running the tests + +```bash +php artisan test # everything +php artisan test --parallel # what CI runs +php artisan test --filter=BulkEventUploadCacheTest +php artisan test tests/Feature/CertificatesTest.php +vendor/bin/phpunit # equivalent, via PHPUnit directly +``` + +## Test configuration + +[phpunit.xml](../../phpunit.xml) defines two suites, `Unit` and `Feature`, both matching files with a `Test.php` suffix. + +The environment it sets up: + +| Variable | Value | Effect | +|----------|-------|--------| +| `APP_ENV` | `testing` | | +| `APP_URL` | `http://codeweek.test` | | +| `DB_CONNECTION` | `sqlite` | | +| `DB_DATABASE` | `:memory:` | Fast, and nothing to clean up | +| `CACHE_STORE`, `SESSION_DRIVER` | `array` | In-memory | +| `MAIL_MAILER` | `array` | No mail leaves the process | +| `QUEUE_CONNECTION` | `sync` | Jobs run inline | +| `BCRYPT_ROUNDS` | `4` | Fast password hashing | + +Coverage is scoped to `./app`. + +**Tests run against SQLite, production runs MySQL 8.** That gap matters for the parts of this codebase that use MySQL-specific SQL — `JSON_CONTAINS` for the `activity_format` and `ages` filters, and `FIND_IN_SET` for the language filter, both in `app/Filters/EventFilters.php`. Those paths cannot be meaningfully covered by the SQLite suite. A `mysql_testing` connection exists in `config/database.php` and there is a commented-out line in `phpunit.xml` to use it: + +```18:18:phpunit.xml + +``` + +If you are changing the filter layer, switch to it locally and run the suite against MySQL before shipping. + +## Test layout + +102 test files. + +| Location | Contents | +|----------|----------| +| `tests/Unit/` | Bulk upload cache and duplicate finder, bulk user changes services, social login, user email change, avatar URL, factory behaviour | +| `tests/Feature/` | The bulk of the suite — activity CRUD and approval, certificates, excellence, Code Week 4 All, ambassadors, leading teachers, imports, locations, countries, GDPR deletion | +| `tests/Feature/API/` | The public events API | +| `tests/Feature/Auth/` | Password validation | +| `tests/Feature/Achievements/` | Nine files covering badges, experience, and influence | +| `tests/Feature/InternalSupport/` | Support copilot auth and pipeline | +| `tests/utilities/functions.php` | Shared test helpers | +| `tests/DuskTestCase.php` | Dusk base class. **There are no Dusk tests** — the scaffolding exists but is unused | + +### `tests/TestCase.php` + +Every test inherits setup that is worth knowing about, because it changes what you are actually testing: + +```10:18:tests/TestCase.php + protected function setUp(): void + { + parent::setUp(); + //$this->withoutExceptionHandling(); + $this->mockLocale(); + $this->mockBrowserCheck(); + $this->withoutVite(); + Mail::fake(); + } +``` + +- The `Locale` and `CheckBrowser` middleware are **mocked out**, so neither is exercised by the suite. +- `withoutVite()` skips asset manifest resolution, so tests do not need a build. +- `Mail::fake()` is global — assert with `Mail::assertQueued()` rather than expecting real delivery. +- A `signIn($user)` helper creates and authenticates a user in one call. + +### Five test files that never run + +These are in `tests/Feature/` but lack the `Test.php` suffix, so PHPUnit ignores them: + +- `GermanImports.php` +- `GermanUsersCreation.php` +- `RelocateCenteredActivities.php` +- `RelocateOnlineActivities.php` +- `NullEmails.php` + +They look like tests and will show up when you grep, but they are dead weight. Some are probably worth reviving by renaming — the German import ones in particular, given [07](07-partner-feeds-and-apis.md) — but check they still pass before trusting them. + +## What the suite does not cover + +Be realistic about this before relying on a green run: + +| Area | Status | +|------|--------| +| Nova | **Not covered at all.** CI swaps in `composer-test.json`, which strips Nova ([02](02-environments-and-deployment.md)) | +| Certificate PDF generation | Not covered end to end — it needs a TeX installation | +| MySQL-specific query paths | Not covered, per the SQLite note above | +| Browser behaviour | No Dusk tests, despite the base class | +| The Vue front end | No JavaScript test setup at all | +| Locale and browser middleware | Mocked out in `TestCase` | + +A green suite means the core domain logic is intact. It does not mean the admin, the certificates, or the front end work. + +## Local development + +Two options. Neither is mandated. + +### Option A: standard local PHP + +Conventional and probably faster to get going if you already have PHP 8.2+, MySQL 8, Redis, and Node 20+. + +```bash +git clone git@github.com:codeeu/codeweek.git +cd codeweek + +# Nova credentials are required for composer install +composer config http-basic.nova.laravel.com +composer install + +cp .env.example .env +php artisan key:generate +``` + +Then fill in `.env` properly. `.env.example` now lists the variables the application actually reads, grouped by concern, with empty values — but it is a template, not a working configuration. Run `php artisan key:generate`, and use [03](03-configuration.md) plus the server `.env` as the reference for anything that needs a real value. + +```bash +php artisan migrate:fresh --seed +npm install +npm run dev # Vite dev server with hot reload +php artisan serve +``` + +Seeding gives you countries, cities, roles, a super admin, 60 ambassadors, taxonomies, sample activities, resources, and CMS content. + +For certificate work you also need TeX Live and a correct `PDFLATEX_PATH` ([08](08-certificates.md)). Without it everything else still works; only certificates fail. + +### Option B: DevSpace on Kubernetes + +Avoids installing PHP, MySQL, Redis, and Node locally. Setup is in [02](02-environments-and-deployment.md) and [devspace/README.md](../../devspace/README.md). + +```bash +kubectl create ns codeweek +kubectl create secret generic certificates-secret \ + --from-file=tls.crt=./localhost.crt --from-file=tls.key=./localhost.key +# add "127.0.0.1 codeweek.local.europa.eu" to /etc/hosts +cp devspace/.env.devspace .env +devspace dev +devspace run artisan migrate:fresh --seed +``` + +Then `https://codeweek.local.europa.eu`. + +**Known blocker:** `devspace.yaml` pins a PHP **8.0** FPM image while the application requires 8.2+. You will need to point `APP_IMAGE` at a newer image before this path works. + +## Asset build + +| Command | Purpose | +|---------|---------| +| `npm run dev` | Vite dev server with hot module reload | +| `npm run build` | Production build. Runs on deploy | +| `npm run build-analytics` | Builds the Nova Analytics package in dev mode | +| `npm run build-analytics-prod` | Production build of the same | + +The analytics scripts are vestigial — that package is **not installed** and its component registration is commented out. See [05](05-nova-admin.md). + +Three Vite entry points: `resources/js/app.js`, `resources/css/app.css` (Tailwind), and `resources/assets/sass/app.scss` (legacy SCSS). Both stylesheets are live; check which one governs the page you are editing before adding classes ([01](01-architecture.md)). + +## Translations and locales + +### Where translations live + +`resources/lang/` has one directory per language, each containing PHP array files split by area: `base.php`, `auth.php`, `about.php`, `ambassador.php`, `certificates.php`, `challenges.php`, `codeweek4all.php`, and others. + +30 directories exist. `LOCALES` exposes 27. The three translated but **not exposed** in the language switcher are `mt` (Maltese), `tr` (Turkish), and `ua` (Ukrainian). If someone asks why a language they translated is not appearing, that variable is the answer ([03](03-configuration.md)). + +### Two translation systems + +Blade views use Laravel's `__()` and `trans()` directly. + +Vue components use `laravel-vue-i18n`, wired through the Vite plugin in [vite.config.js](../../vite.config.js), which reads `resources/lang` at build time. **So adding a translation key used by a Vue component requires rebuilding assets**, not just editing the file. + +There is also an older generator, `php artisan vue-i18n:generate` (`martinlindhe/laravel-vue-i18n-generator`, from a [codeeu fork](https://github.com/codeeu/laravel-vue-i18n-generator)), configured in [config/vue-i18n-generator.php](../../config/vue-i18n-generator.php). It predates the Vite plugin approach. Check which mechanism the component you are working on actually uses before running it. + +### Adding a language + +1. Create `resources/lang/{code}/` and populate it, using `en` as the reference. +2. Add the code to `LOCALES` in the environment. +3. Rebuild assets so the Vue side picks it up. +4. For certificates, the script may need a **dedicated LaTeX template** — Greek, Ukrainian, and multi-language variants exist for exactly this reason ([08](08-certificates.md)). + +## Code style + +There is no enforced formatter or linter in CI. Laravel Pint is available through the framework's dependencies but is not run automatically. + +Match the surrounding file. Be aware that the codebase has two distinct eras: older code in `app/Console/Commands/` and the top-level models is looser, while newer code in `app/Services/` is typed, small, and closer to modern Laravel conventions. Follow the latter for new work. diff --git a/docs/handover/12-risks-and-known-issues.md b/docs/handover/12-risks-and-known-issues.md new file mode 100644 index 000000000..c06649d6a --- /dev/null +++ b/docs/handover/12-risks-and-known-issues.md @@ -0,0 +1,119 @@ +# 12 — Risks and known issues + +This chapter used to be a long inventory. Most of it has since been fixed — see [what was fixed](#what-was-fixed) at the end for the list, so you are not surprised by references to it elsewhere. + +What remains is grouped by what you need to do about it: one thing that will lock you out, a few open security actions, behaviour you must understand before touching imports or certificates, and a set of accepted gaps. + +## This will lock you out on day one + +The certificate backend is gated on a single hardcoded email address belonging to the outgoing developer: + +```11:11:app/Http/Middleware/EnsureSuperCertificateAdmin.php + private const ALLOWED_EMAIL = 'bernard@matrixinternet.ie'; +``` + +Everything under `/admin/certificate-backend/*` — batch generation, sending, retrying failures, manual creation — depends on it. **No role grants access.** A new super admin gets a 403 and cannot operate certificates at all. + +This was deliberately left unchanged so the incoming team can decide the access model rather than inherit ours. The options, roughly in order of preference: + +- an environment-driven allowlist, so access follows a variable rather than a deploy; +- a Spatie permission such as `generate certificate`, which already exists in the seeder; +- the `super admin` role, which is simplest but means every super admin can send certificate batches. + +Whichever you choose, do it before October, because certificate reporting starts the moment October activities end. [08](08-certificates.md) + +## Open security actions + +**The `forge` SSH keys should be rotated.** [docs/ops/learn-and-teach-resource-import.md](../ops/learn-and-teach-resource-import.md) contained the live and dev server IP addresses with working `ssh -i ~/.ssh/id_rsa forge@…` command lines, in the **public** `codeeu/codeweek` repository. The file now uses placeholders, but **redaction does not remove them from git history.** The real mitigation is rotating those keys, restricting SSH ingress at the AWS security group, or both. + +**The previously committed `APP_KEY` should be treated as compromised.** `.env.example` shipped a real base64 key for a long time. It is blank now, but any environment created by copying that file is using a publicly known encryption key. Rotate it wherever it was used, and remember that rotating `APP_KEY` invalidates existing encrypted values and sessions. + +**`User` is fully mass-assignable.** + +```110:110:app/User.php + protected $guarded = []; +``` + +Every attribute is mass-assignable, including `approved` and `magic_key`. Any `fill()` or `update()` reached with unfiltered request data is a privilege-escalation path. This was left alone because moving to an explicit `$fillable` touches a lot of call sites and is exactly the kind of change that breaks quietly. Audit the call sites first, then narrow it. + +**The public API is unauthenticated.** `/api/events/geobox`, `/api/events/germany`, and `/api/event-detail/{event}` have no API key, no token, and no per-consumer limit beyond a global 60-requests-per-minute throttle. That may well be right for open data, but it should be a decision someone has made rather than an accident. [07](07-partner-feeds-and-apis.md) + +## Behaviour to understand before you touch imports + +None of these are bugs exactly. They are design choices with sharp edges, and each one has surprised somebody. + +**Restart workers after changing LaTeX templates.** The most important piece of inherited knowledge in this document. Long-running queue workers hold templates in memory, so if you skip `php artisan queue:restart` after a template change, certificates are generated **silently using last year's design** — no error, and by the time anyone notices they have been emailed. [08](08-certificates.md) + +**Bulk-uploaded activities skip moderation.** `GenericEventsImport` sets `status = 'APPROVED'` on every row, so a bad spreadsheet publishes straight to the live map. The preview step is the only safeguard — always read it. [06](06-bulk-uploads-and-imports.md) + +**Creator resolution can misattribute activities.** When a row's `contact_email` does not match a user exactly, the importer falls back to matching on the local part before the `@`: + +```152:156:app/Imports/GenericEventsImport.php + [$local] = explode('@', $contactEmail, 2); + $user = User::where('email', 'like', "{$local}@%")->first(); + if ($user) { + return $this->creatorIdByEmail[$contactEmail] = $user->id; + } +``` + +So `jane@school-a.be` can be attributed to an existing `jane@somewhere-else.com`. If ownership looks wrong after an import, this is why. It also means **a bulk upload can create user accounts as a side effect**. + +**A bulk import flushes the entire application cache.** + +```65:66:app/Jobs/ProcessBulkEventImportJob.php + Storage::disk($disk)->delete($path); + Cache::flush(); +``` + +Not just the upload's own entries — everything, including the map cache and GeoIP lookups. Expect a performance dip on live after a large import. Narrowing this to targeted keys would be a cheap improvement, but it needs someone to enumerate the keys safely rather than guess. + +**Duplicate detection is strict.** `BulkEventDuplicateFinder` matches on title, start date, country and organiser, plus address or coordinates. Change any one of those in a corrected spreadsheet and you get a second activity rather than an update. + +**Theme and audience IDs are a public contract.** Partners submit numeric theme and audience IDs taken from the [public wiki page](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek). Theme IDs are deliberately non-contiguous after a past consolidation. **Renumbering them breaks historical activities and every partner's export script simultaneously.** [04](04-domain-model.md) + +The column list is now pinned by `tests/Unit/BulkEventUploadColumnContractTest.php`, so changing `REQUIRED_COLUMNS` fails the suite with a reminder to update the wiki. Nothing pins the theme and audience IDs; treat them as frozen. + +**The GDPR deletion script is irreversible.** [soft_delete_users_without_consent.sql](../../soft_delete_users_without_consent.sql) performs soft deletes and hard deletes in one pass, reassigning records to the legacy user with id `1000000`. There is no undo without a database backup. Take one first, every time. That literal `1000000` is load-bearing in several places. [04](04-domain-model.md) + +**Blog sync never removes anything.** `app:sync-blogs` only upserts, so a post deleted or unpublished in WordPress stays in the `blogs` table forever and keeps appearing in site search, linking to a 404. There is no reconciliation pass. [09](09-wordpress-blog.md) + +## Accepted gaps + +These are known, and were left alone deliberately — either because fixing them needs access or decisions we do not have, or because the fix is riskier than the problem. + +**Testing and CI.** CI swaps in `composer-test.json`, which strips the Nova packages, so **no Nova code is exercised by CI at all** — every Nova change needs manual QA on dev. Tests also run on SQLite while production is MySQL, so the JSON and `FIND_IN_SET` paths in `app/Filters/EventFilters.php` cannot be covered by the default suite; a `mysql_testing` connection exists, commented out, in `phpunit.xml`. Two tests currently fail on `master` for unrelated legacy reasons: `UserRestoreServiceTest`, and `CommunityAmbassadorFilteringTest` because `database/factories/CityFactory.php` is still in the pre-Laravel-8 `$factory->define()` format. Five further files in `tests/Feature/` lack the `Test.php` suffix and never run; renaming them would make them execute, so check they pass before doing it. [11](11-testing-and-local-dev.md) + +**Nothing alerts on a backed-up queue.** `queue:monitor` only dispatches Laravel's `QueueBusy` event above a threshold, and no listener for it exists. There is no Horizon, and no job calls `onQueue()`, so all work shares one uninstrumented queue. In practice a stalled worker during October is noticed by a human wondering why activities are not appearing. A `QueueBusy` listener that notifies the on-call address is a small, high-value change. Related: both `ValidateBulkEventUploadJob` and `ProcessBulkEventImportJob` set `tries = 1`, so a transient failure in a long import is final and you re-upload. [10](10-scheduled-jobs-and-runbooks.md) + +**Environment and infrastructure.** The `schedule:run` cron entry, the queue workers, the server `.env` and the nginx configuration exist only in Forge — no amount of reading this repository tells you whether they are correct, so export them during handover. Dev commonly shares `RESOURCES_BUCKET` with live, meaning a resource import on dev uploads real PDFs into the production bucket while the rows stay on dev. `config/codeweek.php` defaults `blog_url` to the live blog, so a dev `app:sync-blogs` pulls production content unless the variable is set. `devspace.yaml` still pins a PHP 8.0 FPM image while `composer.json` requires `^8.2`; we left the tag alone because we cannot verify what exists in that private registry. [02](02-environments-and-deployment.md) + +**Half-finished and architectural.** `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET` and `AZURE_REDIRECT` are set in the deployed environment, but `LoginController` allowlists only twitter, github, google and facebook — finish it or remove the variables. Nova resources must be registered manually in `NovaServiceProvider::boot()`, because auto-discovery was missing them; add a resource and forget the array and it will not appear. `SCOUT_DRIVER` and the Algolia variables exist but nothing uses them: all search is direct MySQL, so do not assume an index exists, and expect search to be a pressure point in October. `RejectEvent` in Nova has no message field, so the `Moderation` record it writes has an empty `message` and the organiser gets no explanation, while the Blade screens at `/pending` and `/review` do capture a reason — two paths, two behaviours, and ambassadors use both. `EventPolicy::edit()` returns false once `reported_at` is set, with no explanatory message in the UI; intentional, but a steady source of support tickets. + +**Navigational debt.** Of 151 files in `app/Console/Commands/`, only about twenty are scheduled or operationally relevant; the rest are partner-specific importers and historical backfills that will never run again. `app/Console/Commands/excel/` is the obvious first candidate for a cleanup pass. Both `relocate` and `relocate:country` resolve to `app/Console/Commands/RelocateCountry.php` and one is scheduled every two minutes — confirm with `php artisan schedule:list` on the server what actually binds and whether that frequency is still wanted. Many long-lived branches are named after dates or import batches (`bulk_11_11_25`, `2-sept-imports`, `coderdojo-import-april`), so do not assume an unfamiliar branch is active. CI triggers on `master` while `docs/ops/` refers to "`main` / `master`"; confirm in Forge which branch each site deploys from. + +**Documentation.** `docs/internal/` is gitignored and holds sample payloads, partner spreadsheets and past import reports — genuinely useful reference material that must be transferred **outside git**. One inherited document, `WP5 CodeWeek IT System Merged v0.5.md`, states that geocoding uses Nominatim; it does not, the code calls the ArcGIS World GeocodeServer through `GeocodeController`. Worth knowing if that document is circulated to stakeholders. + +## What was fixed + +Done in a single pass, with the test suite green apart from the two pre-existing failures noted above. Listed so that references elsewhere make sense, and so nobody re-reports them. + +- **Turnstile bot protection never ran.** The code read `TURNSTILE_SECRET_KEY` while deployed environments set `TURNSTILE_SECRET`, so verification was wrapped in a truthiness check on an always-null variable and the CAPTCHA response was validated as `nullable`. The widget rendered, so it looked healthy. Both names now resolve through `config/codeweek.php`. **This needs a live submission test on dev** — it is the one change here that alters behaviour on a public form. +- **The contact form fell back to the outgoing team's address** when `CONTACT_FORM_RECIPIENT_EMAIL` was unset. It now falls back to `ADMIN_EMAIL`. +- **Every ambassador saw only France** in the Nova `Country` resource, which was leftover debug code. Now scoped to the ambassador's own `country_iso`. +- **`Ambassador` filtered on `model_has_roles.role_id = 4`**, which only held while the seeders had run in their original order. Now matched by role name. +- **A scheduled command that did not exist**, `app:export-search-data-to-json`, failed nightly at 02:00. Removed. +- **`certificate:preflight` defaulted to `--edition=2025`**, so a bare run silently checked the wrong year. Now defaults to the current year. +- **`.env.example` shipped a real `APP_KEY`** plus live S3 bucket names, used the ignored `QUEUE_DRIVER` name instead of `QUEUE_CONNECTION`, and omitted about forty variables the application reads. Blanked, renamed, and filled in by name. +- **Dead code deleted**: the two `nova-components/` packages that were never installed, `PromoteAmbassador`, the unattached `UserStatus` filter, the unrouted `ImporterController`, and the `/map` route whose only view include was a zero-byte file. +- **Committed debris deleted**: `cookies.txt` (a curl cookie jar with a live session cookie for meet-and-code.org), `texput.log`, `changed_files.txt`, `differences.diff`, `bom.json`, `phpunit.xml.bak`, `tailwind.js`, `server.php`, the LaTeX compile leftovers in `resources/latex/`, and the abandoned Travis configuration. `.gitignore` now covers the recurring ones. +- **CI ran only for `master`**, so pull requests into `dev` ran no tests. Both branches now trigger. +- **The bulk upload column contract is pinned** by a unit test, so changing `REQUIRED_COLUMNS` fails the suite with a reminder to update the partner-facing wiki. + +## Suggested order of work + +1. **Decide the certificate access model** and replace the hardcoded email. Until this is done you cannot operate certificates. +2. **Close the security actions.** Rotate the `forge` SSH keys, rotate `APP_KEY` where the committed one was used, and confirm `TURNSTILE_SECRET` is valid by submitting the contact form on dev. +3. **Verify the invisible parts.** Prove the scheduler runs, the workers are alive, a backup restores, and Sentry is receiving. Export the Forge configuration while you have someone to ask. +4. **Make the suite trustworthy.** Fix the two failing tests, then decide about the five inert files. A green suite is what makes everything after this cheaper. + +Then, before October: the readiness checklist in [10](10-scheduled-jobs-and-runbooks.md). diff --git a/docs/handover/13-visual-tour.md b/docs/handover/13-visual-tour.md new file mode 100644 index 000000000..f7941068f --- /dev/null +++ b/docs/handover/13-visual-tour.md @@ -0,0 +1,157 @@ +# 13 — Visual tour + +A screen-by-screen walk through the platform, so you can connect what a user sees to the code that serves it. Every caption names the route and the controller or view responsible. + +Screenshots were taken from the live site at 1920×1080. They will drift as content changes; treat the route and controller names as the durable part. + +> **Why some screens are missing.** Everything behind `/nova`, `/admin/*`, `/certificates` and `/participation` needs a privileged account. Those sections below describe the screen and point at the code instead of showing it. Once you have credentials from the [access checklist](00-access-checklist.md), walk them yourself — it is the fastest way to understand the admin surface. + +## The public site + +### Homepage + +The seasonal landing page. Much of it is editable content rather than hardcoded markup: home slides are a Nova resource, and the countdown, statistics and featured activities are pulled live. Note the subdomain parameter on the route — country subdomains are a supported entry point. + +![`GET /` — a closure in `routes/web.php` that resolves the subdomain and renders the home view](assets/01-home.jpg) + +### Activity search and map + +The busiest page on the site, and the one most worth understanding first. The Leaflet map (Mapbox and OpenStreetMap tiles) and the filter form below it share the same query layer, `app/Filters/EventFilters.php`. There is no external search index: filtering is plain MySQL, using `JSON_CONTAINS` against JSON columns for themes and audiences. Map data selects only the columns the map needs, groups by country, and is cached for 300 seconds. + +![`GET /events` — `SearchController@search`, route name `events_map`](assets/02-activities-search.jpg) + +> **There used to be a second, broken map at `/map`.** `MapController@index` rendered a view whose only include was a zero-byte file, so the page returned 200 with an empty `
` and — because `layout/simple.blade.php` has its `@vite()` calls commented out — no stylesheets either. Nothing linked to it. The route, controller and views have been deleted. The map above, on `/events`, is the real one and is untouched. + +### Online activities calendar + +Online activities are ordinary activities flagged `activity_type = 'online'`, presented on a calendar rather than a map. + +![`GET /online-activities` — `OnlineEventsController@calendar`](assets/04-online-activities.jpg) + +### Learn & Teach resource catalogue + +The teaching resource library. Resources are a separate model from activities, with their own taxonomy pivots, and are filtered by `app/Filters/ResourceFilters.php`. Resource PDFs live in the second S3 bucket. This catalogue is populated by the importer described in [06 — Bulk uploads and imports](06-bulk-uploads-and-imports.md). + +![`GET /resources/learn-and-teach` — `ResourcesController@all`, route name `resources_all`](assets/05-learn-and-teach.jpg) + +### Community + +The ambassador and community directory. `/ambassadors` redirects here, so do not be surprised when both URLs render the same page. + +![`GET /community` — `CommunityController@index`](assets/07-community.jpg) + +### Matchmaking tool + +Connects volunteers offering digital-skills help with schools and organisations asking for it. It has its own spreadsheet template download at `/matchmaking-tool/download/template`, which is a useful reminder that this feature has a bulk path as well as a form. + +![`GET /matchmaking-tool` — `MatchMakingToolController@index`](assets/10-matchmaking-tool.jpg) + +### Code Week 4 All + +Served by the generic static page controller, which resolves the route name to a Blade view. Several campaign pages work this way, so adding a new static page usually means adding a route plus a view and no controller code. + +![`GET /codeweek4all` — `StaticPageController@static`](assets/11-codeweek4all.jpg) + +### Scoreboard + +Country rankings by activities per head of population, with a year selector. The totals here are a good smoke test after an import run: if a partner feed has double-inserted, it shows up on this page first. + +![`GET /scoreboard` — `ScoreboardController@index`](assets/12-scoreboard.jpg) + +### Grassroots Grants + +![`GET /grassroots-grants` — `GrassrootsGrantsController@show`](assets/13-grassroots-grants.jpg) + +### Challenges + +Worth a look because of how it is routed: `Route::view('/challenges', '2021.challenges')`. It is a static Blade view under a year-named directory, with no controller. There is a family of these dated campaign views in `resources/views/`, and they are effectively frozen content. + +![`GET /challenges` — `Route::view()` straight to `resources/views/2021/challenges.blade.php`](assets/14-challenges.jpg) + +### Hackathons + +![`GET /hackathons` — `HackathonsController@index`](assets/15-hackathons.jpg) + +### Online courses + +![`GET /online-courses` — `OnlineCoursesController@index`](assets/16-online-courses.jpg) + +### Podcasts + +The podcast list also backs an outbound RSS feed, generated by `spatie/laravel-feed`. See [07 — Partner feeds and APIs](07-partner-feeds-and-apis.md). + +![`GET /podcasts` — `PodcastsController@index`](assets/17-podcasts.jpg) + +### About + +![`GET /about` — `StaticPageController@static`](assets/18-about.jpg) + +## Accounts and the authentication gate + +Any route behind `auth` redirects here. Four social providers are wired through Laravel Socialite alongside email and password, so a user record may have no password at all — keep that in mind when writing anything that touches credentials. + +Remember that `CheckConsent` middleware runs on every web request: a signed-in user without a recorded GDPR consent is bounced to `/consent` before they can use the site. If you are testing as a seeded user and every page redirects, this is why. + +![The shared login and register screen. `/leading-teachers/list`, `/certificates` and `/participation` all land here when signed out](assets/09-login-gate.jpg) + +## Certificates + +Four certificate types are generated as real PDFs by `pdflatex` from the templates in `resources/latex/`, then stored on S3. [08 — Certificates](08-certificates.md) covers the pipeline, the TeX Live dependencies and the annual rollover in full. + +The screens involved, none of which can be captured without a privileged account: + +| Screen | Route | Served by | Who can reach it | +|---|---|---|---| +| My certificates | `GET /certificates` | `CertificateController@list` | Any signed-in user | +| Participation certificate | `GET /participation` | `ParticipationController@show` | Signed in **and** email verified | +| Excellence report | `GET /certificates/excellence/{edition}` | `ExcellenceController@report` | Signed in | +| Super Organiser report | `GET /certificates/super-organiser/{edition}` | `SuperOrganiserController@report` | Signed in | +| Certificate admin | `GET /admin/certificates` | `AdminController@certificates` | Role-gated admin group | +| Certificate backend | `/admin/certificate-backend/*` | Excellence and Super Organiser generation and sending | **One hardcoded email address** | + +> **The certificate backend will lock you out on day one.** `EnsureSuperCertificateAdmin` permits a single hardcoded address belonging to the outgoing team. Changing it is a prerequisite for operating certificates at all, and it is the first item in [12 — Risks and known issues](12-risks-and-known-issues.md). + +## Administration + +### Nova + +The main back office is Laravel Nova 4. Access is controlled by the gate defined in `app/Providers/NovaServiceProvider.php`, and resources are registered explicitly rather than auto-discovered — so a new Nova resource that does not appear in the sidebar is usually just unregistered. [05 — Nova admin](05-nova-admin.md) has the full resource inventory. + +![`GET /nova` — the Nova login screen, the only part of the back office visible without an account](assets/21-nova-login.jpg) + +### Bespoke admin screens + +Several administrative tools predate Nova or outgrew it, and live as ordinary Blade screens under `/admin/*` behind `auth` plus `role:super admin`. These are the ones you will use most in October: + +| Screen | Route | Covered in | +|---|---|---| +| Bulk activity upload | `/admin/bulk-upload` | [06](06-bulk-uploads-and-imports.md) | +| Bulk user changes | `/admin/bulk-user-changes` | [06](06-bulk-uploads-and-imports.md) | +| Resources import | `/admin/resources-import` | [06](06-bulk-uploads-and-imports.md) | +| Certificate generation | `/admin/certificates` | [08](08-certificates.md) | + +Each follows the same three-step shape — upload, preview, import — with a queued job doing the work and a report at the end. Read the bulk upload chapter before running any of them against live data; the column contract is strict and the preview step is the only safety net. + +## The blog + +A separate WordPress installation, not part of this Laravel application. Posts are mirrored into the `blogs` table nightly by `app:sync-blogs` over the WordPress REST API, which is how blog content appears in global search on the Laravel side. Editing happens in WordPress; the Laravel copy is read-only. See [09 — The WordPress blog](09-wordpress-blog.md). + +![[codeweek.eu/blog](https://codeweek.eu/blog/) — a WordPress install behind the same domain](assets/19-wordpress-blog.jpg) + +## Environments and source + +### Dev site + +`dev.codeweek.eu` is the staging environment, deployed from the development branch through Laravel Forge. It has its own database and its own S3 configuration, and it is the only place to rehearse an import or a certificate run. [02 — Environments and deployment](02-environments-and-deployment.md) covers the deploy sequence. + +![[dev.codeweek.eu](https://dev.codeweek.eu/) — the staging environment](assets/20-dev-site.jpg) + +### Repository + +![[github.com/codeeu/codeweek](https://github.com/codeeu/codeweek) — the public repository. Note that it is public: see the secret-handling rules in the [README](README.md)](assets/24-github-repo.jpg) + +### Partner-facing import guide + +The public wiki page national partners are pointed at when they want their events to appear on codeweek.eu. Its column list must stay in step with `REQUIRED_COLUMNS` in `app/Services/BulkEventUploadValidator.php` — they have drifted apart before. [07 — Partner feeds and APIs](07-partner-feeds-and-apis.md) explains the relationship. + +![[Publish your events into Codeweek](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek) — the partner-facing wiki guide](assets/23-partner-wiki-guide.jpg) diff --git a/docs/handover/README.md b/docs/handover/README.md new file mode 100644 index 000000000..a3284a866 --- /dev/null +++ b/docs/handover/README.md @@ -0,0 +1,78 @@ +# Code Week technical handover + +Technical documentation for the [codeweek.eu](https://codeweek.eu) platform, written for a developer taking over the system. + +This set supersedes the three documents we received when we took the project over (`Codeweek - Setup Infra.docx`, `Certificates - Technical Documentation.docx`, `Certificate Generation Process Documentation`). Their content has been folded in, corrected where it had drifted from the code, and extended to cover everything built since. + +## Ground rules for this folder + +These files are **tracked in git in a public repository**. Follow the same rule as [docs/ops/README.md](../ops/README.md): + +> Do not put secrets, credentials, or large binaries here. + +Specifically: + +- Environment variables are documented by **name and purpose only**. Never paste values. +- No server IP addresses, SSH targets, bucket names, database hostnames, Nova licence credentials, or API tokens. Use `` and point at the credentials vault. +- Private working files (spreadsheets, PDFs, sample exports) stay in `docs/internal/`, which is gitignored. + +## Read in this order + +| # | Chapter | What it covers | +|---|---------|----------------| +| — | [00-access-checklist.md](00-access-checklist.md) | Everything you need access to before you can do anything. Start here. | +| 01 | [01-architecture.md](01-architecture.md) | The stack, how the pieces fit, and a tour of the repository layout. | +| 02 | [02-environments-and-deployment.md](02-environments-and-deployment.md) | Forge hosting, the dev and live environments, branch model, CI, and how to deploy. | +| 03 | [03-configuration.md](03-configuration.md) | Every environment variable and config file, grouped by concern. | +| 04 | [04-domain-model.md](04-domain-model.md) | Activities, users, ambassadors, editions, taxonomies, and the database. | +| 05 | [05-nova-admin.md](05-nova-admin.md) | The Laravel Nova back office, who can access it, and the custom packages. | +| 06 | [06-bulk-uploads-and-imports.md](06-bulk-uploads-and-imports.md) | The bulk activity uploader, bulk user changes, and the resource importer. | +| 07 | [07-partner-feeds-and-apis.md](07-partner-feeds-and-apis.md) | National partner feeds, the Germany import, and our public API. | +| 08 | [08-certificates.md](08-certificates.md) | The LaTeX certificate pipeline and the annual rollover. | +| 09 | [09-wordpress-blog.md](09-wordpress-blog.md) | The WordPress blog and how it syncs into the Laravel site. | +| 10 | [10-scheduled-jobs-and-runbooks.md](10-scheduled-jobs-and-runbooks.md) | The cron schedule, the queue, and step-by-step runbooks. | +| 11 | [11-testing-and-local-dev.md](11-testing-and-local-dev.md) | Running the test suite and getting a local environment up. | +| 12 | [12-risks-and-known-issues.md](12-risks-and-known-issues.md) | What is still open, behaviour with sharp edges, and a record of what was already fixed. | +| 13 | [13-visual-tour.md](13-visual-tour.md) | Screenshots of every major screen, each mapped to the route and controller behind it. | + +## Single-file PDF + +These chapters can be built into one printable document — roughly 120 pages, with a clickable table of contents, rendered diagrams, and the screenshot tour: + +```bash +python3 docs/handover/_pdf/build.py +``` + +It writes `docs/handover/codeweek-technical-handover.pdf`. You need `pandoc` and Google Chrome installed, plus an internet connection at build time (the Mermaid renderer is loaded from a CDN). + +The PDF is **deliberately not committed** — it is a 7 MB build artefact and this folder is meant to stay text. The markdown files are the source of truth. Regenerate the PDF when you need a copy to hand to someone. + +## If you only read three pages + +1. [00-access-checklist.md](00-access-checklist.md) — you will be blocked without this. +2. [02-environments-and-deployment.md](02-environments-and-deployment.md) — the single most misunderstood part of this repo. The Kubernetes manifests at the repo root are **local development only**; production runs on Laravel Forge. +3. [12-risks-and-known-issues.md](12-risks-and-known-issues.md) — several things are hardcoded to the outgoing team and will lock you out on day one until changed. + +## Ten-minute orientation + +Code Week is a single Laravel 11 application (PHP 8.2+) that serves the public site, the activity map, the Learn & Teach resource catalogue, user accounts, certificates, and the administrative back office. + +- **Front end** is a mix of server-rendered Blade views, Livewire 3 components, and Vue 3 islands built with Vite and Tailwind. It is not a single-page app. +- **Admin** is Laravel Nova 4 at `/nova`, plus a handful of bespoke Blade admin screens under `/admin/*` that predate or outgrew Nova. +- **Data** is MySQL 8. Redis is available for cache, session, and queue work. +- **Files** (activity images, avatars, generated certificate PDFs, resource PDFs) live in S3 across two buckets. +- **Certificates** are real PDFs produced by `pdflatex` from templates in `resources/latex/`. This is why the server needs a TeX Live installation. +- **Activities arrive three ways**: organisers register them on the site, admins bulk upload spreadsheets, and national partners feed them in over HTTP. See [06](06-bulk-uploads-and-imports.md) and [07](07-partner-feeds-and-apis.md). +- **The blog is a separate WordPress install** that is pulled into the Laravel database nightly over the WordPress REST API. See [09](09-wordpress-blog.md). + +The busiest period by far is **October**, when Code Week runs. Traffic is roughly a quarter of the annual total in that one month, activity registrations spike, and certificate generation runs hot afterwards. Plan changes around it rather than through it. + +## External references + +- Repository: [github.com/codeeu/codeweek](https://github.com/codeeu/codeweek) +- Live site: [codeweek.eu](https://codeweek.eu) +- Dev site: [dev.codeweek.eu](https://dev.codeweek.eu/) +- Blog: [codeweek.eu/blog](https://codeweek.eu/blog/) +- Partner-facing import guide (public wiki): [Publish your events into Codeweek](https://github.com/codeeu/codeweek/wiki/Publish-your-events-into-Codeweek) +- Existing operational runbooks: [docs/ops/](../ops/) +- Public "getting help" note: [docs/getting-help-public.md](../getting-help-public.md) diff --git a/docs/handover/_pdf/build.py b/docs/handover/_pdf/build.py new file mode 100644 index 000000000..6057e1903 --- /dev/null +++ b/docs/handover/_pdf/build.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +"""Build a single combined PDF from the docs/handover chapter set. + +Pipeline: markdown -> preprocessed markdown -> pandoc HTML -> headless Chrome -> PDF. + +Mermaid blocks are emitted as escaped `
` so mermaid.js can +render them in the browser before Chrome prints. Code-reference fences of the +form ```START:END:path are turned into captioned code blocks. +""" + +import html +import re +import shutil +import subprocess +import sys +from datetime import date +from pathlib import Path + +HERE = Path(__file__).resolve().parent +HANDOVER = HERE.parent +BUILD = HERE / "build" +REPO_BLOB = "https://github.com/codeeu/codeweek/blob/master" + +CHAPTERS = [ + ("readme", "README.md"), + ("00", "00-access-checklist.md"), + ("01", "01-architecture.md"), + ("02", "02-environments-and-deployment.md"), + ("03", "03-configuration.md"), + ("04", "04-domain-model.md"), + ("05", "05-nova-admin.md"), + ("06", "06-bulk-uploads-and-imports.md"), + ("07", "07-partner-feeds-and-apis.md"), + ("08", "08-certificates.md"), + ("09", "09-wordpress-blog.md"), + ("10", "10-scheduled-jobs-and-runbooks.md"), + ("11", "11-testing-and-local-dev.md"), + ("12", "12-risks-and-known-issues.md"), + ("13", "13-visual-tour.md"), +] + +SLUG_BY_FILE = {fname: f"chapter-{cid}" for cid, fname in CHAPTERS} + +EXT_LANG = { + ".php": "php", + ".yaml": "yaml", + ".yml": "yaml", + ".xml": "xml", + ".sh": "bash", + ".js": "javascript", + ".json": "json", + ".sql": "sql", + ".css": "css", +} + +CODE_REF = re.compile(r"^```(\d+):(\d+):(\S+)\s*$") +FENCE = re.compile(r"^```") + + +def convert_mermaid_and_coderefs(text: str) -> str: + """Rewrite fenced blocks that pandoc cannot handle natively.""" + out, lines, i = [], text.split("\n"), 0 + while i < len(lines): + line = lines[i] + + if line.strip() == "```mermaid": + body, i = [], i + 1 + while i < len(lines) and not FENCE.match(lines[i]): + body.append(lines[i]) + i += 1 + i += 1 # closing fence + # Emit as a pandoc raw HTML block. A bare HTML block would be + # terminated by the first blank line inside the diagram source, + # and pandoc would then parse the remainder as markdown. + out += [ + "", + "```{=html}", + '
', + html.escape("\n".join(body)), + "
", + "```", + "", + ] + continue + + m = CODE_REF.match(line) + if m: + start, end, path = m.group(1), m.group(2), m.group(3) + lang = EXT_LANG.get(Path(path).suffix, "") + body, i = [], i + 1 + while i < len(lines) and not FENCE.match(lines[i]): + body.append(lines[i]) + i += 1 + i += 1 + out += [ + "", + "::: coderef", + f"[`{path}`]({REPO_BLOB}/{path}#L{start}-L{end}){{.coderef-head}} " + f"[lines {start}–{end}]{{.coderef-lines}}", + "", + f"```{lang}", + *body, + "```", + ":::", + "", + ] + continue + + out.append(line) + i += 1 + return "\n".join(out) + + +def rewrite_links(text: str) -> str: + """Point intra-set links at anchors and repo-relative links at GitHub.""" + + def repl(m): + label, target = m.group(1), m.group(2) + anchor = "" + if "#" in target: + target, anchor = target.split("#", 1) + anchor = "#" + anchor + if target in SLUG_BY_FILE: + return f"[{label}](#{SLUG_BY_FILE[target]})" + if target.startswith("../../"): + return f"[{label}]({REPO_BLOB}/{target[6:]}{anchor})" + if target.startswith("../"): + return f"[{label}]({REPO_BLOB}/docs/{target[3:]}{anchor})" + return m.group(0) + + return re.sub(r"\[([^\]]+)\]\(((?!https?://)[^)]+)\)", repl, text) + + +def demote_headings(text: str) -> str: + """Chapter files each start at h1; push everything down one level.""" + return re.sub(r"^(#{1,5}) ", r"#\1 ", text, flags=re.MULTILINE) + + +def build_markdown() -> Path: + BUILD.mkdir(parents=True, exist_ok=True) + parts = [] + for cid, fname in CHAPTERS: + raw = (HANDOVER / fname).read_text() + # Chapter title comes from the file's own h1. + m = re.match(r"^#\s+(.+)$", raw.split("\n")[0]) + title = m.group(1) if m else fname + body = "\n".join(raw.split("\n")[1:]) + body = convert_mermaid_and_coderefs(body) + body = rewrite_links(body) + body = demote_headings(body) + parts.append(f'\n\n# {title} {{#chapter-{cid} .chapter}}\n\n{body}\n') + combined = BUILD / "combined.md" + combined.write_text("\n".join(parts)) + return combined + + +def main() -> int: + for tool in ("pandoc",): + if not shutil.which(tool): + print(f"error: {tool} not found on PATH", file=sys.stderr) + return 1 + chrome = Path("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome") + if not chrome.exists(): + print("error: Google Chrome not found", file=sys.stderr) + return 1 + + combined = build_markdown() + html_out = BUILD / "handover.html" + pdf_out = HANDOVER / "codeweek-technical-handover.pdf" + + subprocess.run( + [ + "pandoc", str(combined), + "--standalone", + "--from", + "markdown+fenced_divs+bracketed_spans+pipe_tables" + "+backtick_code_blocks+raw_attribute", + "--to", "html5", + "--toc", "--toc-depth=3", + "--highlight-style", "tango", + "--metadata", "title=EU Code Week — Technical Handover", + "--metadata", "lang=en", + "--css", str(HERE / "style.css"), + "--include-in-header", str(HERE / "head.html"), + "--include-before-body", str(HERE / "cover.html"), + "--resource-path", str(HANDOVER), + "-o", str(html_out), + ], + check=True, + ) + + # Absolute file: URLs so Chrome resolves the stylesheet and screenshots. + doc = html_out.read_text() + doc = doc.replace(f'href="{HERE / "style.css"}"', f'href="file://{HERE / "style.css"}"') + doc = doc.replace('src="assets/', f'src="file://{HANDOVER}/assets/') + doc = doc.replace("{{BUILD_DATE}}", date.today().strftime("%-d %B %Y")) + html_out.write_text(doc) + + subprocess.run( + [ + str(chrome), + "--headless=new", + "--disable-gpu", + "--no-sandbox", + "--run-all-compositor-stages-before-draw", + "--virtual-time-budget=90000", + "--no-pdf-header-footer", + f"--print-to-pdf={pdf_out}", + f"file://{html_out}", + ], + check=True, + capture_output=True, + ) + + size = pdf_out.stat().st_size / 1024 / 1024 + print(f"built {pdf_out} ({size:.1f} MB)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/docs/handover/_pdf/cover.html b/docs/handover/_pdf/cover.html new file mode 100644 index 000000000..5690dfc23 --- /dev/null +++ b/docs/handover/_pdf/cover.html @@ -0,0 +1,31 @@ +
+
Technical handover
+

EU Code Week
Platform Handover

+

Everything a developer taking over codeweek.eu needs to build, deploy, + operate and troubleshoot the platform — the Laravel application, the Nova back office, the bulk + uploaders, the partner feeds, the LaTeX certificate pipeline and the WordPress blog.

+ +
+
Repository
+
github.com/codeeu/codeweek
+
Live
+
codeweek.eu
+
Staging
+
dev.codeweek.eu
+
Blog
+
codeweek.eu/blog
+
Stack
+
Laravel 11 · PHP 8.2+ · MySQL 8 · Redis · Nova 4 · Vue 3 · Livewire 3 · S3 · LaTeX
+
Chapters
+
13, plus an access checklist
+
Generated
+
{{BUILD_DATE}}
+
+ +
+ Contains no credentials. Environment variables are documented by name and purpose + only. Server addresses, keys, tokens and bucket names are held in the credentials vault and are + deliberately absent from this document. See the Access checklist chapter for what must be handed + over separately. +
+
diff --git a/docs/handover/_pdf/head.html b/docs/handover/_pdf/head.html new file mode 100644 index 000000000..7d072b0d6 --- /dev/null +++ b/docs/handover/_pdf/head.html @@ -0,0 +1,33 @@ + + diff --git a/docs/handover/_pdf/style.css b/docs/handover/_pdf/style.css new file mode 100644 index 000000000..3f65b2b1d --- /dev/null +++ b/docs/handover/_pdf/style.css @@ -0,0 +1,401 @@ +/* Print styling for the combined Code Week technical handover PDF. */ + +@page { + size: A4; + margin: 18mm 16mm 18mm 16mm; +} + +:root { + --ink: #16202c; + --muted: #5b6b7c; + --rule: #dde4ec; + --accent: #1b4f9c; + --accent-soft: #eef3fb; + --warn-bg: #fff6e8; + --warn-edge: #e0a33a; +} + +html { + font-size: 10.5pt; +} + +body { + font-family: "Charter", "Georgia", "Iowan Old Style", serif; + color: var(--ink); + line-height: 1.5; + margin: 0; + max-width: none; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +/* ---------- cover ---------- */ + +.cover { + height: 247mm; + display: flex; + flex-direction: column; + justify-content: center; + page-break-after: always; + border-top: 10px solid var(--accent); +} + +.cover .eyebrow { + font-family: "Helvetica Neue", Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.22em; + font-size: 9pt; + color: var(--accent); + font-weight: 700; +} + +.cover h1 { + font-size: 34pt; + line-height: 1.1; + margin: 0.35em 0 0.2em; + border: 0; + padding: 0; +} + +.cover .sub { + font-size: 13pt; + color: var(--muted); + margin: 0 0 2.4em; + max-width: 30em; +} + +.cover dl { + display: grid; + grid-template-columns: 8.5em 1fr; + row-gap: 0.45em; + font-size: 10pt; + margin: 0; + border-top: 1px solid var(--rule); + padding-top: 1.2em; + max-width: 34em; +} + +.cover dt { + font-family: "Helvetica Neue", Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 8pt; + color: var(--muted); + padding-top: 0.25em; +} + +.cover dd { + margin: 0; +} + +.cover .notice { + margin-top: 2.2em; + background: var(--warn-bg); + border-left: 4px solid var(--warn-edge); + padding: 0.8em 1em; + font-size: 9.5pt; + max-width: 34em; +} + +/* ---------- table of contents ---------- */ + +#TOC { + page-break-after: always; + padding-top: 0.5em; +} + +#TOC::before { + content: "Contents"; + display: block; + font-family: "Helvetica Neue", Arial, sans-serif; + font-size: 19pt; + font-weight: 700; + margin-bottom: 0.8em; + color: var(--accent); +} + +#TOC ul { + list-style: none; + padding-left: 0; + margin: 0; +} + +#TOC > ul > li { + margin: 0.55em 0; + font-weight: 600; + font-size: 11pt; + border-bottom: 1px dotted var(--rule); + padding-bottom: 0.4em; +} + +#TOC ul ul { + margin: 0.3em 0 0.5em 1.1em; + columns: 2; + column-gap: 2em; +} + +#TOC ul ul li { + font-weight: 400; + font-size: 9pt; + color: var(--muted); + margin: 0.12em 0; + break-inside: avoid; +} + +#TOC a { + color: inherit; + text-decoration: none; +} + +/* ---------- headings ---------- */ + +h1.chapter { + page-break-before: always; + font-family: "Helvetica Neue", Arial, sans-serif; + font-size: 22pt; + color: #fff; + background: var(--accent); + padding: 0.55em 0.7em; + margin: 0 0 1.1em; + border-radius: 3px; +} + +h2, h3, h4, h5 { + font-family: "Helvetica Neue", Arial, sans-serif; + color: var(--accent); + page-break-after: avoid; + line-height: 1.25; +} + +h2 { + font-size: 15pt; + margin: 1.7em 0 0.6em; + padding-bottom: 0.25em; + border-bottom: 2px solid var(--rule); +} + +h3 { + font-size: 12pt; + margin: 1.3em 0 0.45em; +} + +h4 { + font-size: 10.5pt; + margin: 1.1em 0 0.4em; + color: var(--ink); +} + +p, li { + orphans: 2; + widows: 2; +} + +a { + color: var(--accent); + text-decoration: none; + word-break: break-word; +} + +strong { + color: #0d1a27; +} + +/* ---------- tables ---------- */ + +table { + border-collapse: collapse; + width: 100%; + margin: 1em 0 1.3em; + font-size: 9pt; + page-break-inside: avoid; +} + +caption { + caption-side: top; + text-align: left; + font-size: 9pt; + color: var(--muted); + padding-bottom: 0.35em; +} + +th { + background: var(--accent-soft); + text-align: left; + font-family: "Helvetica Neue", Arial, sans-serif; + font-size: 8.5pt; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--accent); + border-bottom: 2px solid var(--rule); +} + +th, td { + padding: 0.42em 0.6em; + vertical-align: top; + border-bottom: 1px solid var(--rule); +} + +tbody tr:nth-child(even) { + background: #fafbfd; +} + +/* ---------- code ---------- */ + +code { + font-family: "SF Mono", "Menlo", "Consolas", monospace; + font-size: 0.85em; + background: #f3f5f8; + padding: 0.1em 0.32em; + border-radius: 3px; +} + +pre { + background: #f7f9fb; + border: 1px solid var(--rule); + border-left: 3px solid var(--accent); + border-radius: 3px; + padding: 0.75em 0.9em; + overflow-x: auto; + font-size: 8.4pt; + line-height: 1.45; + page-break-inside: avoid; + white-space: pre-wrap; + word-break: break-word; +} + +pre code { + background: none; + padding: 0; + font-size: inherit; +} + +/* captioned citation of real repository code */ +.coderef { + margin: 1em 0 1.3em; + page-break-inside: avoid; +} + +.coderef > p:first-child { + font-family: "Helvetica Neue", Arial, sans-serif; + font-size: 8pt; + background: var(--accent); + color: #fff; + margin: 0; + padding: 0.35em 0.7em; + border-radius: 3px 3px 0 0; +} + +.coderef > p:first-child a, +.coderef > p:first-child code { + color: #fff; + background: none; + text-decoration: none; + padding: 0; +} + +.coderef-lines { + opacity: 0.8; + font-style: italic; +} + +.coderef pre { + margin-top: 0; + border-radius: 0 0 3px 3px; + border-left-width: 1px; +} + +/* ---------- mermaid ---------- */ + +.mermaid-wrap { + margin: 1.2em 0 1.5em; + padding: 1em; + background: #fbfcfe; + border: 1px solid var(--rule); + border-radius: 4px; + text-align: center; + page-break-inside: avoid; +} + +.mermaid svg { + max-width: 100% !important; + height: auto !important; +} + +/* ---------- screenshots ---------- */ + +figure { + margin: 1.2em 0 1.5em; + page-break-inside: avoid; + text-align: center; +} + +figure img, +p > img { + max-width: 100%; + max-height: 200mm; + border: 1px solid var(--rule); + border-radius: 3px; + box-shadow: 0 1px 4px rgba(20, 35, 55, 0.12); +} + +figcaption { + font-family: "Helvetica Neue", Arial, sans-serif; + font-size: 8.5pt; + color: var(--muted); + margin-top: 0.5em; + font-style: italic; +} + +/* placeholder for screens we could not capture without credentials */ +.shot-missing { + border: 1.5px dashed var(--warn-edge); + background: var(--warn-bg); + border-radius: 4px; + padding: 1.1em 1.2em; + margin: 1.2em 0; + font-size: 9.5pt; + page-break-inside: avoid; +} + +.shot-missing::before { + content: "Screenshot not captured"; + display: block; + font-family: "Helvetica Neue", Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 7.5pt; + font-weight: 700; + color: #8a6114; + margin-bottom: 0.35em; +} + +blockquote { + margin: 1em 0; + padding: 0.6em 1em; + border-left: 3px solid var(--warn-edge); + background: var(--warn-bg); + font-style: normal; +} + +blockquote p { + margin: 0.3em 0; +} + +hr { + border: 0; + border-top: 1px solid var(--rule); + margin: 1.8em 0; +} + +ul, ol { + padding-left: 1.3em; +} + +li { + margin: 0.22em 0; +} + +/* task-list checkboxes in the access checklist */ +input[type="checkbox"] { + margin-right: 0.4em; +} diff --git a/docs/handover/assets/01-home.jpg b/docs/handover/assets/01-home.jpg new file mode 100644 index 000000000..3f319283b Binary files /dev/null and b/docs/handover/assets/01-home.jpg differ diff --git a/docs/handover/assets/02-activities-search.jpg b/docs/handover/assets/02-activities-search.jpg new file mode 100644 index 000000000..f349482d8 Binary files /dev/null and b/docs/handover/assets/02-activities-search.jpg differ diff --git a/docs/handover/assets/04-online-activities.jpg b/docs/handover/assets/04-online-activities.jpg new file mode 100644 index 000000000..88068fed8 Binary files /dev/null and b/docs/handover/assets/04-online-activities.jpg differ diff --git a/docs/handover/assets/05-learn-and-teach.jpg b/docs/handover/assets/05-learn-and-teach.jpg new file mode 100644 index 000000000..47d231047 Binary files /dev/null and b/docs/handover/assets/05-learn-and-teach.jpg differ diff --git a/docs/handover/assets/07-community.jpg b/docs/handover/assets/07-community.jpg new file mode 100644 index 000000000..2741323bd Binary files /dev/null and b/docs/handover/assets/07-community.jpg differ diff --git a/docs/handover/assets/09-login-gate.jpg b/docs/handover/assets/09-login-gate.jpg new file mode 100644 index 000000000..e6eeeee04 Binary files /dev/null and b/docs/handover/assets/09-login-gate.jpg differ diff --git a/docs/handover/assets/10-matchmaking-tool.jpg b/docs/handover/assets/10-matchmaking-tool.jpg new file mode 100644 index 000000000..8cf5948c0 Binary files /dev/null and b/docs/handover/assets/10-matchmaking-tool.jpg differ diff --git a/docs/handover/assets/11-codeweek4all.jpg b/docs/handover/assets/11-codeweek4all.jpg new file mode 100644 index 000000000..35e6939d2 Binary files /dev/null and b/docs/handover/assets/11-codeweek4all.jpg differ diff --git a/docs/handover/assets/12-scoreboard.jpg b/docs/handover/assets/12-scoreboard.jpg new file mode 100644 index 000000000..3b5fc1a1e Binary files /dev/null and b/docs/handover/assets/12-scoreboard.jpg differ diff --git a/docs/handover/assets/13-grassroots-grants.jpg b/docs/handover/assets/13-grassroots-grants.jpg new file mode 100644 index 000000000..e3d7879b8 Binary files /dev/null and b/docs/handover/assets/13-grassroots-grants.jpg differ diff --git a/docs/handover/assets/14-challenges.jpg b/docs/handover/assets/14-challenges.jpg new file mode 100644 index 000000000..5d49b196c Binary files /dev/null and b/docs/handover/assets/14-challenges.jpg differ diff --git a/docs/handover/assets/15-hackathons.jpg b/docs/handover/assets/15-hackathons.jpg new file mode 100644 index 000000000..8ee9fbcae Binary files /dev/null and b/docs/handover/assets/15-hackathons.jpg differ diff --git a/docs/handover/assets/16-online-courses.jpg b/docs/handover/assets/16-online-courses.jpg new file mode 100644 index 000000000..8acd03e11 Binary files /dev/null and b/docs/handover/assets/16-online-courses.jpg differ diff --git a/docs/handover/assets/17-podcasts.jpg b/docs/handover/assets/17-podcasts.jpg new file mode 100644 index 000000000..90cd81c38 Binary files /dev/null and b/docs/handover/assets/17-podcasts.jpg differ diff --git a/docs/handover/assets/18-about.jpg b/docs/handover/assets/18-about.jpg new file mode 100644 index 000000000..834f2417c Binary files /dev/null and b/docs/handover/assets/18-about.jpg differ diff --git a/docs/handover/assets/19-wordpress-blog.jpg b/docs/handover/assets/19-wordpress-blog.jpg new file mode 100644 index 000000000..bca960a4c Binary files /dev/null and b/docs/handover/assets/19-wordpress-blog.jpg differ diff --git a/docs/handover/assets/20-dev-site.jpg b/docs/handover/assets/20-dev-site.jpg new file mode 100644 index 000000000..55115b379 Binary files /dev/null and b/docs/handover/assets/20-dev-site.jpg differ diff --git a/docs/handover/assets/21-nova-login.jpg b/docs/handover/assets/21-nova-login.jpg new file mode 100644 index 000000000..2bc22b361 Binary files /dev/null and b/docs/handover/assets/21-nova-login.jpg differ diff --git a/docs/handover/assets/23-partner-wiki-guide.jpg b/docs/handover/assets/23-partner-wiki-guide.jpg new file mode 100644 index 000000000..76df593d8 Binary files /dev/null and b/docs/handover/assets/23-partner-wiki-guide.jpg differ diff --git a/docs/handover/assets/24-github-repo.jpg b/docs/handover/assets/24-github-repo.jpg new file mode 100644 index 000000000..3ac9543ee Binary files /dev/null and b/docs/handover/assets/24-github-repo.jpg differ diff --git a/docs/ops/learn-and-teach-resource-import.md b/docs/ops/learn-and-teach-resource-import.md index 17043c46c..6500fa821 100644 --- a/docs/ops/learn-and-teach-resource-import.md +++ b/docs/ops/learn-and-teach-resource-import.md @@ -153,20 +153,22 @@ zip -r learn-teach-assets.zip links images ### 2. Test on DEV first -Servers (Forge): +Servers (Forge). Host addresses are in the credentials vault — do not commit them here. + +> **Security note:** earlier revisions of this file contained the real dev and live IP addresses, and they remain in this public repository's git history. Redacting the file does not undo that. The actual mitigations are rotating the `forge` SSH keys and restricting SSH ingress at the AWS security group. See [docs/handover/12-risks-and-known-issues.md](../handover/12-risks-and-known-issues.md). | Env | SSH | App dir | |-----|-----|---------| -| **DEV** | `ssh -i ~/.ssh/id_rsa forge@35.156.58.10` | `cd dev.codeweek.eu` | -| **Live** | `ssh -i ~/.ssh/id_rsa forge@3.68.107.5` | `cd codeweek.eu` | +| **DEV** | `ssh -i ~/.ssh/id_rsa forge@` | `cd dev.codeweek.eu` | +| **Live** | `ssh -i ~/.ssh/id_rsa forge@` | `cd codeweek.eu` | ```bash # From your laptop: upload package scp -i ~/.ssh/id_rsa metadata.csv learn-teach-assets.zip \ - forge@35.156.58.10:/home/forge/dev.codeweek.eu/storage/app/learn-teach-upload-2026/ + forge@:/home/forge/dev.codeweek.eu/storage/app/learn-teach-upload-2026/ # On DEV — unzip + import in the SAME session -ssh -i ~/.ssh/id_rsa forge@35.156.58.10 +ssh -i ~/.ssh/id_rsa forge@ cd /home/forge/dev.codeweek.eu cd storage/app/learn-teach-upload-2026 && unzip -o -q learn-teach-assets.zip cd /home/forge/dev.codeweek.eu diff --git a/nova-components/Analytics/.gitignore b/nova-components/Analytics/.gitignore deleted file mode 100644 index b81757726..000000000 --- a/nova-components/Analytics/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/.idea -/vendor -/node_modules -package-lock.json -composer.phar -composer.lock -phpunit.xml -.phpunit.result.cache -.DS_Store -Thumbs.db diff --git a/nova-components/Analytics/composer.json b/nova-components/Analytics/composer.json deleted file mode 100644 index 1554d3a19..000000000 --- a/nova-components/Analytics/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "acme/analytics", - "description": "A Laravel Nova asset.", - "keywords": [ - "laravel", - "nova" - ], - "license": "MIT", - "require": { - "php": "^7.3|^8.0" - }, - "autoload": { - "psr-4": { - "Acme\\Analytics\\": "src/" - } - }, - "extra": { - "laravel": { - "providers": [ - "Acme\\Analytics\\AssetServiceProvider" - ] - } - }, - "config": { - "sort-packages": true - }, - "minimum-stability": "dev", - "prefer-stable": true -} diff --git a/nova-components/Analytics/dist/css/asset.css b/nova-components/Analytics/dist/css/asset.css deleted file mode 100644 index 3e9bd089c..000000000 --- a/nova-components/Analytics/dist/css/asset.css +++ /dev/null @@ -1,2 +0,0 @@ -/* Nova Asset CSS */ - diff --git a/nova-components/Analytics/dist/js/asset.js b/nova-components/Analytics/dist/js/asset.js deleted file mode 100644 index 7ee5fb116..000000000 --- a/nova-components/Analytics/dist/js/asset.js +++ /dev/null @@ -1,173 +0,0 @@ -/******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ "./resources/js/asset.js": -/*!*******************************!*\ - !*** ./resources/js/asset.js ***! - \*******************************/ -/***/ (() => { - -// import Analytics from './components/Analytics' - -// Nova.booting(app => { -// app.component('analytics', Analytics) -// }) - -/***/ }), - -/***/ "./resources/css/asset.css": -/*!*********************************!*\ - !*** ./resources/css/asset.css ***! - \*********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = __webpack_modules__; -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/chunk loaded */ -/******/ (() => { -/******/ var deferred = []; -/******/ __webpack_require__.O = (result, chunkIds, fn, priority) => { -/******/ if(chunkIds) { -/******/ priority = priority || 0; -/******/ for(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1]; -/******/ deferred[i] = [chunkIds, fn, priority]; -/******/ return; -/******/ } -/******/ var notFulfilled = Infinity; -/******/ for (var i = 0; i < deferred.length; i++) { -/******/ var [chunkIds, fn, priority] = deferred[i]; -/******/ var fulfilled = true; -/******/ for (var j = 0; j < chunkIds.length; j++) { -/******/ if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) { -/******/ chunkIds.splice(j--, 1); -/******/ } else { -/******/ fulfilled = false; -/******/ if(priority < notFulfilled) notFulfilled = priority; -/******/ } -/******/ } -/******/ if(fulfilled) { -/******/ deferred.splice(i--, 1) -/******/ var r = fn(); -/******/ if (r !== undefined) result = r; -/******/ } -/******/ } -/******/ return result; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/jsonp chunk loading */ -/******/ (() => { -/******/ // no baseURI -/******/ -/******/ // object to store loaded and loading chunks -/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched -/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded -/******/ var installedChunks = { -/******/ "/js/asset": 0, -/******/ "css/asset": 0 -/******/ }; -/******/ -/******/ // no chunk on demand loading -/******/ -/******/ // no prefetching -/******/ -/******/ // no preloaded -/******/ -/******/ // no HMR -/******/ -/******/ // no HMR manifest -/******/ -/******/ __webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0); -/******/ -/******/ // install a JSONP callback for chunk loading -/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { -/******/ var [chunkIds, moreModules, runtime] = data; -/******/ // add "moreModules" to the modules object, -/******/ // then flag all "chunkIds" as loaded and fire callback -/******/ var moduleId, chunkId, i = 0; -/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) { -/******/ for(moduleId in moreModules) { -/******/ if(__webpack_require__.o(moreModules, moduleId)) { -/******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; -/******/ } -/******/ } -/******/ if(runtime) var result = runtime(__webpack_require__); -/******/ } -/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); -/******/ for(;i < chunkIds.length; i++) { -/******/ chunkId = chunkIds[i]; -/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { -/******/ installedChunks[chunkId][0](); -/******/ } -/******/ installedChunks[chunkId] = 0; -/******/ } -/******/ return __webpack_require__.O(result); -/******/ } -/******/ -/******/ var chunkLoadingGlobal = self["webpackChunkacme_analytics"] = self["webpackChunkacme_analytics"] || []; -/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); -/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module depends on other loaded chunks and execution need to be delayed -/******/ __webpack_require__.O(undefined, ["css/asset"], () => (__webpack_require__("./resources/js/asset.js"))) -/******/ var __webpack_exports__ = __webpack_require__.O(undefined, ["css/asset"], () => (__webpack_require__("./resources/css/asset.css"))) -/******/ __webpack_exports__ = __webpack_require__.O(__webpack_exports__); -/******/ -/******/ })() -; \ No newline at end of file diff --git a/nova-components/Analytics/dist/mix-manifest.json b/nova-components/Analytics/dist/mix-manifest.json deleted file mode 100644 index 4eedffd88..000000000 --- a/nova-components/Analytics/dist/mix-manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "/js/asset.js": "/js/asset.js", - "/css/asset.css": "/css/asset.css" -} diff --git a/nova-components/Analytics/nova.mix.js b/nova-components/Analytics/nova.mix.js deleted file mode 100644 index 3a5352f62..000000000 --- a/nova-components/Analytics/nova.mix.js +++ /dev/null @@ -1,24 +0,0 @@ -const mix = require('laravel-mix') -const webpack = require('webpack') - -class NovaExtension { - name() { - return 'nova-extension' - } - - register(name) { - this.name = name - } - - webpackConfig(webpackConfig) { - webpackConfig.externals = { - vue: 'Vue', - } - - webpackConfig.output = { - uniqueName: this.name, - } - } -} - -mix.extend('nova', new NovaExtension()) diff --git a/nova-components/Analytics/package.json b/nova-components/Analytics/package.json deleted file mode 100644 index 9552f3149..000000000 --- a/nova-components/Analytics/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "private": true, - "scripts": { - "dev": "npm run development", - "development": "mix", - "watch": "mix watch", - "watch-poll": "mix watch -- --watch-options-poll=1000", - "hot": "mix watch --hot", - "prod": "npm run production", - "production": "mix --production", - "nova:install": "npm --prefix='../../vendor/laravel/nova' ci" - }, - "devDependencies": { - "@vue/compiler-sfc": "^3.2.22", - "laravel-mix": "^6.0.41", - "postcss": "^8.3.11", - "vue-loader": "^16.8.3" - }, - "dependencies": {} -} diff --git a/nova-components/Analytics/postcss.config.js b/nova-components/Analytics/postcss.config.js deleted file mode 100644 index 4ba52ba2c..000000000 --- a/nova-components/Analytics/postcss.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {} diff --git a/nova-components/Analytics/resources/css/asset.css b/nova-components/Analytics/resources/css/asset.css deleted file mode 100644 index 81a90c785..000000000 --- a/nova-components/Analytics/resources/css/asset.css +++ /dev/null @@ -1 +0,0 @@ -/* Nova Asset CSS */ diff --git a/nova-components/Analytics/resources/js/asset.js b/nova-components/Analytics/resources/js/asset.js deleted file mode 100644 index be1e2681a..000000000 --- a/nova-components/Analytics/resources/js/asset.js +++ /dev/null @@ -1,5 +0,0 @@ -// import Analytics from './components/Analytics' - -// Nova.booting(app => { -// app.component('analytics', Analytics) -// }) diff --git a/nova-components/Analytics/src/AssetServiceProvider.php b/nova-components/Analytics/src/AssetServiceProvider.php deleted file mode 100644 index 4e6285d12..000000000 --- a/nova-components/Analytics/src/AssetServiceProvider.php +++ /dev/null @@ -1,33 +0,0 @@ -=7.1.0" - }, - "autoload": { - "psr-4": { - "Codeeu\\NovaTheme\\": "src/" - } - }, - "extra": { - "laravel": { - "providers": [ - "Codeeu\\NovaTheme\\ThemeServiceProvider" - ] - } - }, - "config": { - "sort-packages": true - }, - "minimum-stability": "dev", - "prefer-stable": true -} diff --git a/nova-components/NovaTheme/resources/css/theme.css b/nova-components/NovaTheme/resources/css/theme.css deleted file mode 100644 index b9e310d71..000000000 --- a/nova-components/NovaTheme/resources/css/theme.css +++ /dev/null @@ -1,22 +0,0 @@ -:root { - --primary: rgb(253, 137, 56); - --primary-dark: rgb(240, 126, 56); - --primary-70: rgba(226, 123, 51, 0.7); - --primary-50: rgba(205, 112, 44, 0.5); - --primary-30: rgba(187, 101, 40, 0.3); - --primary-10: rgba(155, 85, 36, 0.1); - --logo: #437cb9; - --sidebar-icon: #fbfdff; -} - -.bg-grad-sidebar { - background-image: -webkit-gradient( - linear, - left bottom, - left top, - from(rgb(254, 1, 129, 1)), - to(#3c4655) - ); - - background-image: linear-gradient(0deg, rgb(253, 137, 56),#437cb9); -} diff --git a/nova-components/NovaTheme/src/ThemeServiceProvider.php b/nova-components/NovaTheme/src/ThemeServiceProvider.php deleted file mode 100644 index fae03046a..000000000 --- a/nova-components/NovaTheme/src/ThemeServiceProvider.php +++ /dev/null @@ -1,32 +0,0 @@ - - - - - ./tests/Unit - - - ./tests/Feature - - - - - ./app - - - - - - - - - - - - - - - - - - - diff --git a/resources/latex/nH8nokEg9c-1.aux b/resources/latex/nH8nokEg9c-1.aux deleted file mode 100644 index ec554c775..000000000 --- a/resources/latex/nH8nokEg9c-1.aux +++ /dev/null @@ -1,4 +0,0 @@ -\relax -\providecommand\babel@aux[2]{} -\@nameuse{bbl@beforestart} -\catcode `"\active diff --git a/resources/latex/nH8nokEg9c-1.log b/resources/latex/nH8nokEg9c-1.log deleted file mode 100644 index cfd901746..000000000 --- a/resources/latex/nH8nokEg9c-1.log +++ /dev/null @@ -1,948 +0,0 @@ -This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024) (preloaded format=pdflatex 2024.8.13) 6 NOV 2025 13:36 -entering extended mode - restricted \write18 enabled. - %&-line parsing enabled. -**nH8nokEg9c-1.tex - -(/Users/bernardhanna/Desktop/codeweek/codeweek/resources/latex/nH8nokEg9c-1.tex -LaTeX2e <2023-11-01> patch level 1 -L3 programming layer <2024-02-20> -(/usr/local/texlive/2024/texmf-dist/tex/latex/base/article.cls -Document Class: article 2023/05/17 v1.4n Standard LaTeX document class -(/usr/local/texlive/2024/texmf-dist/tex/latex/base/size12.clo -File: size12.clo 2023/05/17 v1.4n Standard LaTeX file (size option) -) -\c@part=\count188 -\c@section=\count189 -\c@subsection=\count190 -\c@subsubsection=\count191 -\c@paragraph=\count192 -\c@subparagraph=\count193 -\c@figure=\count194 -\c@table=\count195 -\abovecaptionskip=\skip48 -\belowcaptionskip=\skip49 -\bibindent=\dimen140 -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/base/fontenc.sty -Package: fontenc 2021/04/29 v2.0v Standard LaTeX package - -(/usr/local/texlive/2024/texmf-dist/tex/latex/greek-fontenc/lgrenc.def -File: lgrenc.def 2023-09-12 2.5 LGR Greek font encoding definitions -Now handling font encoding LGR ... -... processing UTF-8 mapping file for font encoding LGR - -(/usr/local/texlive/2024/texmf-dist/tex/latex/greek-inputenc/lgrenc.dfu -File: lgrenc.dfu 2023-03-21 1.9 UTF-8 support for Greek - defining Unicode char U+00A8 (decimal 168) - defining Unicode char U+00AB (decimal 171) - defining Unicode char U+00AF (decimal 175) - defining Unicode char U+00B4 (decimal 180) - defining Unicode char U+00B7 (decimal 183) - defining Unicode char U+00BB (decimal 187) - defining Unicode char U+0259 (decimal 601) - defining Unicode char U+02D8 (decimal 728) - defining Unicode char U+0374 (decimal 884) - defining Unicode char U+0375 (decimal 885) - defining Unicode char U+037A (decimal 890) - defining Unicode char U+037E (decimal 894) - defining Unicode char U+0384 (decimal 900) - defining Unicode char U+0385 (decimal 901) - defining Unicode char U+0386 (decimal 902) - defining Unicode char U+0387 (decimal 903) - defining Unicode char U+0388 (decimal 904) - defining Unicode char U+0389 (decimal 905) - defining Unicode char U+038A (decimal 906) - defining Unicode char U+038C (decimal 908) - defining Unicode char U+038E (decimal 910) - defining Unicode char U+038F (decimal 911) - defining Unicode char U+0390 (decimal 912) - defining Unicode char U+0391 (decimal 913) - defining Unicode char U+0392 (decimal 914) - defining Unicode char U+0393 (decimal 915) - defining Unicode char U+0394 (decimal 916) - defining Unicode char U+0395 (decimal 917) - defining Unicode char U+0396 (decimal 918) - defining Unicode char U+0397 (decimal 919) - defining Unicode char U+0398 (decimal 920) - defining Unicode char U+0399 (decimal 921) - defining Unicode char U+039A (decimal 922) - defining Unicode char U+039B (decimal 923) - defining Unicode char U+039C (decimal 924) - defining Unicode char U+039D (decimal 925) - defining Unicode char U+039E (decimal 926) - defining Unicode char U+039F (decimal 927) - defining Unicode char U+03A0 (decimal 928) - defining Unicode char U+03A1 (decimal 929) - defining Unicode char U+03A3 (decimal 931) - defining Unicode char U+03A4 (decimal 932) - defining Unicode char U+03A5 (decimal 933) - defining Unicode char U+03A6 (decimal 934) - defining Unicode char U+03A7 (decimal 935) - defining Unicode char U+03A8 (decimal 936) - defining Unicode char U+03A9 (decimal 937) - defining Unicode char U+03AA (decimal 938) - defining Unicode char U+03AB (decimal 939) - defining Unicode char U+03AC (decimal 940) - defining Unicode char U+03AD (decimal 941) - defining Unicode char U+03AE (decimal 942) - defining Unicode char U+03AF (decimal 943) - defining Unicode char U+03B0 (decimal 944) - defining Unicode char U+03B1 (decimal 945) - defining Unicode char U+03B2 (decimal 946) - defining Unicode char U+03B3 (decimal 947) - defining Unicode char U+03B4 (decimal 948) - defining Unicode char U+03B5 (decimal 949) - defining Unicode char U+03B6 (decimal 950) - defining Unicode char U+03B7 (decimal 951) - defining Unicode char U+03B8 (decimal 952) - defining Unicode char U+03B9 (decimal 953) - defining Unicode char U+03BA (decimal 954) - defining Unicode char U+03BB (decimal 955) - defining Unicode char U+03BC (decimal 956) - defining Unicode char U+03BD (decimal 957) - defining Unicode char U+03BE (decimal 958) - defining Unicode char U+03BF (decimal 959) - defining Unicode char U+03C0 (decimal 960) - defining Unicode char U+03C1 (decimal 961) - defining Unicode char U+03C2 (decimal 962) - defining Unicode char U+03C3 (decimal 963) - defining Unicode char U+03C4 (decimal 964) - defining Unicode char U+03C5 (decimal 965) - defining Unicode char U+03C6 (decimal 966) - defining Unicode char U+03C7 (decimal 967) - defining Unicode char U+03C8 (decimal 968) - defining Unicode char U+03C9 (decimal 969) - defining Unicode char U+03CA (decimal 970) - defining Unicode char U+03CB (decimal 971) - defining Unicode char U+03CC (decimal 972) - defining Unicode char U+03CD (decimal 973) - defining Unicode char U+03CE (decimal 974) - defining Unicode char U+03D0 (decimal 976) - defining Unicode char U+03D1 (decimal 977) - defining Unicode char U+03D5 (decimal 981) - defining Unicode char U+03D6 (decimal 982) - defining Unicode char U+03D8 (decimal 984) - defining Unicode char U+03D9 (decimal 985) - defining Unicode char U+03DA (decimal 986) - defining Unicode char U+03DB (decimal 987) - defining Unicode char U+03DC (decimal 988) - defining Unicode char U+03DD (decimal 989) - defining Unicode char U+03DE (decimal 990) - defining Unicode char U+03DF (decimal 991) - defining Unicode char U+03E0 (decimal 992) - defining Unicode char U+03E1 (decimal 993) - defining Unicode char U+03F0 (decimal 1008) - defining Unicode char U+03F1 (decimal 1009) - defining Unicode char U+03F4 (decimal 1012) - defining Unicode char U+03F5 (decimal 1013) - defining Unicode char U+1F00 (decimal 7936) - defining Unicode char U+1F01 (decimal 7937) - defining Unicode char U+1F02 (decimal 7938) - defining Unicode char U+1F03 (decimal 7939) - defining Unicode char U+1F04 (decimal 7940) - defining Unicode char U+1F05 (decimal 7941) - defining Unicode char U+1F06 (decimal 7942) - defining Unicode char U+1F07 (decimal 7943) - defining Unicode char U+1F08 (decimal 7944) - defining Unicode char U+1F09 (decimal 7945) - defining Unicode char U+1F0A (decimal 7946) - defining Unicode char U+1F0B (decimal 7947) - defining Unicode char U+1F0C (decimal 7948) - defining Unicode char U+1F0D (decimal 7949) - defining Unicode char U+1F0E (decimal 7950) - defining Unicode char U+1F0F (decimal 7951) - defining Unicode char U+1F10 (decimal 7952) - defining Unicode char U+1F11 (decimal 7953) - defining Unicode char U+1F12 (decimal 7954) - defining Unicode char U+1F13 (decimal 7955) - defining Unicode char U+1F14 (decimal 7956) - defining Unicode char U+1F15 (decimal 7957) - defining Unicode char U+1F18 (decimal 7960) - defining Unicode char U+1F19 (decimal 7961) - defining Unicode char U+1F1A (decimal 7962) - defining Unicode char U+1F1B (decimal 7963) - defining Unicode char U+1F1C (decimal 7964) - defining Unicode char U+1F1D (decimal 7965) - defining Unicode char U+1F20 (decimal 7968) - defining Unicode char U+1F21 (decimal 7969) - defining Unicode char U+1F22 (decimal 7970) - defining Unicode char U+1F23 (decimal 7971) - defining Unicode char U+1F24 (decimal 7972) - defining Unicode char U+1F25 (decimal 7973) - defining Unicode char U+1F26 (decimal 7974) - defining Unicode char U+1F27 (decimal 7975) - defining Unicode char U+1F28 (decimal 7976) - defining Unicode char U+1F29 (decimal 7977) - defining Unicode char U+1F2A (decimal 7978) - defining Unicode char U+1F2B (decimal 7979) - defining Unicode char U+1F2C (decimal 7980) - defining Unicode char U+1F2D (decimal 7981) - defining Unicode char U+1F2E (decimal 7982) - defining Unicode char U+1F2F (decimal 7983) - defining Unicode char U+1F30 (decimal 7984) - defining Unicode char U+1F31 (decimal 7985) - defining Unicode char U+1F32 (decimal 7986) - defining Unicode char U+1F33 (decimal 7987) - defining Unicode char U+1F34 (decimal 7988) - defining Unicode char U+1F35 (decimal 7989) - defining Unicode char U+1F36 (decimal 7990) - defining Unicode char U+1F37 (decimal 7991) - defining Unicode char U+1F38 (decimal 7992) - defining Unicode char U+1F39 (decimal 7993) - defining Unicode char U+1F3A (decimal 7994) - defining Unicode char U+1F3B (decimal 7995) - defining Unicode char U+1F3C (decimal 7996) - defining Unicode char U+1F3D (decimal 7997) - defining Unicode char U+1F3E (decimal 7998) - defining Unicode char U+1F3F (decimal 7999) - defining Unicode char U+1F40 (decimal 8000) - defining Unicode char U+1F41 (decimal 8001) - defining Unicode char U+1F42 (decimal 8002) - defining Unicode char U+1F43 (decimal 8003) - defining Unicode char U+1F44 (decimal 8004) - defining Unicode char U+1F45 (decimal 8005) - defining Unicode char U+1F48 (decimal 8008) - defining Unicode char U+1F49 (decimal 8009) - defining Unicode char U+1F4A (decimal 8010) - defining Unicode char U+1F4B (decimal 8011) - defining Unicode char U+1F4C (decimal 8012) - defining Unicode char U+1F4D (decimal 8013) - defining Unicode char U+1F50 (decimal 8016) - defining Unicode char U+1F51 (decimal 8017) - defining Unicode char U+1F52 (decimal 8018) - defining Unicode char U+1F53 (decimal 8019) - defining Unicode char U+1F54 (decimal 8020) - defining Unicode char U+1F55 (decimal 8021) - defining Unicode char U+1F56 (decimal 8022) - defining Unicode char U+1F57 (decimal 8023) - defining Unicode char U+1F59 (decimal 8025) - defining Unicode char U+1F5B (decimal 8027) - defining Unicode char U+1F5D (decimal 8029) - defining Unicode char U+1F5F (decimal 8031) - defining Unicode char U+1F60 (decimal 8032) - defining Unicode char U+1F61 (decimal 8033) - defining Unicode char U+1F62 (decimal 8034) - defining Unicode char U+1F63 (decimal 8035) - defining Unicode char U+1F64 (decimal 8036) - defining Unicode char U+1F65 (decimal 8037) - defining Unicode char U+1F66 (decimal 8038) - defining Unicode char U+1F67 (decimal 8039) - defining Unicode char U+1F68 (decimal 8040) - defining Unicode char U+1F69 (decimal 8041) - defining Unicode char U+1F6A (decimal 8042) - defining Unicode char U+1F6B (decimal 8043) - defining Unicode char U+1F6C (decimal 8044) - defining Unicode char U+1F6D (decimal 8045) - defining Unicode char U+1F6E (decimal 8046) - defining Unicode char U+1F6F (decimal 8047) - defining Unicode char U+1F70 (decimal 8048) - defining Unicode char U+1F71 (decimal 8049) - defining Unicode char U+1F72 (decimal 8050) - defining Unicode char U+1F73 (decimal 8051) - defining Unicode char U+1F74 (decimal 8052) - defining Unicode char U+1F75 (decimal 8053) - defining Unicode char U+1F76 (decimal 8054) - defining Unicode char U+1F77 (decimal 8055) - defining Unicode char U+1F78 (decimal 8056) - defining Unicode char U+1F79 (decimal 8057) - defining Unicode char U+1F7A (decimal 8058) - defining Unicode char U+1F7B (decimal 8059) - defining Unicode char U+1F7C (decimal 8060) - defining Unicode char U+1F7D (decimal 8061) - defining Unicode char U+1F80 (decimal 8064) - defining Unicode char U+1F81 (decimal 8065) - defining Unicode char U+1F82 (decimal 8066) - defining Unicode char U+1F83 (decimal 8067) - defining Unicode char U+1F84 (decimal 8068) - defining Unicode char U+1F85 (decimal 8069) - defining Unicode char U+1F86 (decimal 8070) - defining Unicode char U+1F87 (decimal 8071) - defining Unicode char U+1F88 (decimal 8072) - defining Unicode char U+1F89 (decimal 8073) - defining Unicode char U+1F8A (decimal 8074) - defining Unicode char U+1F8B (decimal 8075) - defining Unicode char U+1F8C (decimal 8076) - defining Unicode char U+1F8D (decimal 8077) - defining Unicode char U+1F8E (decimal 8078) - defining Unicode char U+1F8F (decimal 8079) - defining Unicode char U+1F90 (decimal 8080) - defining Unicode char U+1F91 (decimal 8081) - defining Unicode char U+1F92 (decimal 8082) - defining Unicode char U+1F93 (decimal 8083) - defining Unicode char U+1F94 (decimal 8084) - defining Unicode char U+1F95 (decimal 8085) - defining Unicode char U+1F96 (decimal 8086) - defining Unicode char U+1F97 (decimal 8087) - defining Unicode char U+1F98 (decimal 8088) - defining Unicode char U+1F99 (decimal 8089) - defining Unicode char U+1F9A (decimal 8090) - defining Unicode char U+1F9B (decimal 8091) - defining Unicode char U+1F9C (decimal 8092) - defining Unicode char U+1F9D (decimal 8093) - defining Unicode char U+1F9E (decimal 8094) - defining Unicode char U+1F9F (decimal 8095) - defining Unicode char U+1FA0 (decimal 8096) - defining Unicode char U+1FA1 (decimal 8097) - defining Unicode char U+1FA2 (decimal 8098) - defining Unicode char U+1FA3 (decimal 8099) - defining Unicode char U+1FA4 (decimal 8100) - defining Unicode char U+1FA5 (decimal 8101) - defining Unicode char U+1FA6 (decimal 8102) - defining Unicode char U+1FA7 (decimal 8103) - defining Unicode char U+1FA8 (decimal 8104) - defining Unicode char U+1FA9 (decimal 8105) - defining Unicode char U+1FAA (decimal 8106) - defining Unicode char U+1FAB (decimal 8107) - defining Unicode char U+1FAC (decimal 8108) - defining Unicode char U+1FAD (decimal 8109) - defining Unicode char U+1FAE (decimal 8110) - defining Unicode char U+1FAF (decimal 8111) - defining Unicode char U+1FB0 (decimal 8112) - defining Unicode char U+1FB1 (decimal 8113) - defining Unicode char U+1FB2 (decimal 8114) - defining Unicode char U+1FB3 (decimal 8115) - defining Unicode char U+1FB4 (decimal 8116) - defining Unicode char U+1FB6 (decimal 8118) - defining Unicode char U+1FB7 (decimal 8119) - defining Unicode char U+1FB8 (decimal 8120) - defining Unicode char U+1FB9 (decimal 8121) - defining Unicode char U+1FBA (decimal 8122) - defining Unicode char U+1FBB (decimal 8123) - defining Unicode char U+1FBC (decimal 8124) - defining Unicode char U+1FBD (decimal 8125) - defining Unicode char U+1FBE (decimal 8126) - defining Unicode char U+1FBF (decimal 8127) - defining Unicode char U+1FC0 (decimal 8128) - defining Unicode char U+1FC1 (decimal 8129) - defining Unicode char U+1FC2 (decimal 8130) - defining Unicode char U+1FC3 (decimal 8131) - defining Unicode char U+1FC4 (decimal 8132) - defining Unicode char U+1FC6 (decimal 8134) - defining Unicode char U+1FC7 (decimal 8135) - defining Unicode char U+1FC8 (decimal 8136) - defining Unicode char U+1FC9 (decimal 8137) - defining Unicode char U+1FCA (decimal 8138) - defining Unicode char U+1FCB (decimal 8139) - defining Unicode char U+1FCC (decimal 8140) - defining Unicode char U+1FCD (decimal 8141) - defining Unicode char U+1FCE (decimal 8142) - defining Unicode char U+1FCF (decimal 8143) - defining Unicode char U+1FD0 (decimal 8144) - defining Unicode char U+1FD1 (decimal 8145) - defining Unicode char U+1FD2 (decimal 8146) - defining Unicode char U+1FD3 (decimal 8147) - defining Unicode char U+1FD6 (decimal 8150) - defining Unicode char U+1FD7 (decimal 8151) - defining Unicode char U+1FD8 (decimal 8152) - defining Unicode char U+1FD9 (decimal 8153) - defining Unicode char U+1FDA (decimal 8154) - defining Unicode char U+1FDB (decimal 8155) - defining Unicode char U+1FDD (decimal 8157) - defining Unicode char U+1FDE (decimal 8158) - defining Unicode char U+1FDF (decimal 8159) - defining Unicode char U+1FE0 (decimal 8160) - defining Unicode char U+1FE1 (decimal 8161) - defining Unicode char U+1FE2 (decimal 8162) - defining Unicode char U+1FE3 (decimal 8163) - defining Unicode char U+1FE4 (decimal 8164) - defining Unicode char U+1FE5 (decimal 8165) - defining Unicode char U+1FE6 (decimal 8166) - defining Unicode char U+1FE7 (decimal 8167) - defining Unicode char U+1FE8 (decimal 8168) - defining Unicode char U+1FE9 (decimal 8169) - defining Unicode char U+1FEA (decimal 8170) - defining Unicode char U+1FEB (decimal 8171) - defining Unicode char U+1FEC (decimal 8172) - defining Unicode char U+1FED (decimal 8173) - defining Unicode char U+1FEE (decimal 8174) - defining Unicode char U+1FEF (decimal 8175) - defining Unicode char U+1FF2 (decimal 8178) - defining Unicode char U+1FF3 (decimal 8179) - defining Unicode char U+1FF4 (decimal 8180) - defining Unicode char U+1FF6 (decimal 8182) - defining Unicode char U+1FF7 (decimal 8183) - defining Unicode char U+1FF8 (decimal 8184) - defining Unicode char U+1FF9 (decimal 8185) - defining Unicode char U+1FFA (decimal 8186) - defining Unicode char U+1FFB (decimal 8187) - defining Unicode char U+1FFC (decimal 8188) - defining Unicode char U+1FFD (decimal 8189) - defining Unicode char U+1FFE (decimal 8190) - defining Unicode char U+2013 (decimal 8211) - defining Unicode char U+2014 (decimal 8212) - defining Unicode char U+2018 (decimal 8216) - defining Unicode char U+2019 (decimal 8217) - defining Unicode char U+201A (decimal 8218) - defining Unicode char U+2030 (decimal 8240) - defining Unicode char U+2039 (decimal 8249) - defining Unicode char U+203A (decimal 8250) - defining Unicode char U+20AC (decimal 8364) - defining Unicode char U+2126 (decimal 8486) - defining Unicode char U+10144 (decimal 65860) - defining Unicode char U+10145 (decimal 65861) - defining Unicode char U+10146 (decimal 65862) - defining Unicode char U+10147 (decimal 65863) -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/greek-fontenc/greek-fontenc.def -File: greek-fontenc.def 2023-09-12 2.5 Common Greek font encoding definitions -)) (/usr/local/texlive/2024/texmf-dist/tex/latex/cyrillic/t2aenc.def -File: t2aenc.def 2022/06/11 v1.0j Cyrillic encoding definition file -Now handling font encoding T2A ... -... processing UTF-8 mapping file for font encoding T2A - -(/usr/local/texlive/2024/texmf-dist/tex/latex/base/t2aenc.dfu -File: t2aenc.dfu 2022/06/07 v1.3c UTF-8 support - defining Unicode char U+00A4 (decimal 164) - defining Unicode char U+00A7 (decimal 167) - defining Unicode char U+00AB (decimal 171) - defining Unicode char U+00BB (decimal 187) - defining Unicode char U+0131 (decimal 305) - defining Unicode char U+0237 (decimal 567) - defining Unicode char U+0400 (decimal 1024) - defining Unicode char U+0401 (decimal 1025) - defining Unicode char U+0402 (decimal 1026) - defining Unicode char U+0403 (decimal 1027) - defining Unicode char U+0404 (decimal 1028) - defining Unicode char U+0405 (decimal 1029) - defining Unicode char U+0406 (decimal 1030) - defining Unicode char U+0407 (decimal 1031) - defining Unicode char U+0408 (decimal 1032) - defining Unicode char U+0409 (decimal 1033) - defining Unicode char U+040A (decimal 1034) - defining Unicode char U+040B (decimal 1035) - defining Unicode char U+040C (decimal 1036) - defining Unicode char U+040D (decimal 1037) - defining Unicode char U+040E (decimal 1038) - defining Unicode char U+040F (decimal 1039) - defining Unicode char U+0410 (decimal 1040) - defining Unicode char U+0411 (decimal 1041) - defining Unicode char U+0412 (decimal 1042) - defining Unicode char U+0413 (decimal 1043) - defining Unicode char U+0414 (decimal 1044) - defining Unicode char U+0415 (decimal 1045) - defining Unicode char U+0416 (decimal 1046) - defining Unicode char U+0417 (decimal 1047) - defining Unicode char U+0418 (decimal 1048) - defining Unicode char U+0419 (decimal 1049) - defining Unicode char U+041A (decimal 1050) - defining Unicode char U+041B (decimal 1051) - defining Unicode char U+041C (decimal 1052) - defining Unicode char U+041D (decimal 1053) - defining Unicode char U+041E (decimal 1054) - defining Unicode char U+041F (decimal 1055) - defining Unicode char U+0420 (decimal 1056) - defining Unicode char U+0421 (decimal 1057) - defining Unicode char U+0422 (decimal 1058) - defining Unicode char U+0423 (decimal 1059) - defining Unicode char U+0424 (decimal 1060) - defining Unicode char U+0425 (decimal 1061) - defining Unicode char U+0426 (decimal 1062) - defining Unicode char U+0427 (decimal 1063) - defining Unicode char U+0428 (decimal 1064) - defining Unicode char U+0429 (decimal 1065) - defining Unicode char U+042A (decimal 1066) - defining Unicode char U+042B (decimal 1067) - defining Unicode char U+042C (decimal 1068) - defining Unicode char U+042D (decimal 1069) - defining Unicode char U+042E (decimal 1070) - defining Unicode char U+042F (decimal 1071) - defining Unicode char U+0430 (decimal 1072) - defining Unicode char U+0431 (decimal 1073) - defining Unicode char U+0432 (decimal 1074) - defining Unicode char U+0433 (decimal 1075) - defining Unicode char U+0434 (decimal 1076) - defining Unicode char U+0435 (decimal 1077) - defining Unicode char U+0436 (decimal 1078) - defining Unicode char U+0437 (decimal 1079) - defining Unicode char U+0438 (decimal 1080) - defining Unicode char U+0439 (decimal 1081) - defining Unicode char U+043A (decimal 1082) - defining Unicode char U+043B (decimal 1083) - defining Unicode char U+043C (decimal 1084) - defining Unicode char U+043D (decimal 1085) - defining Unicode char U+043E (decimal 1086) - defining Unicode char U+043F (decimal 1087) - defining Unicode char U+0440 (decimal 1088) - defining Unicode char U+0441 (decimal 1089) - defining Unicode char U+0442 (decimal 1090) - defining Unicode char U+0443 (decimal 1091) - defining Unicode char U+0444 (decimal 1092) - defining Unicode char U+0445 (decimal 1093) - defining Unicode char U+0446 (decimal 1094) - defining Unicode char U+0447 (decimal 1095) - defining Unicode char U+0448 (decimal 1096) - defining Unicode char U+0449 (decimal 1097) - defining Unicode char U+044A (decimal 1098) - defining Unicode char U+044B (decimal 1099) - defining Unicode char U+044C (decimal 1100) - defining Unicode char U+044D (decimal 1101) - defining Unicode char U+044E (decimal 1102) - defining Unicode char U+044F (decimal 1103) - defining Unicode char U+0450 (decimal 1104) - defining Unicode char U+0451 (decimal 1105) - defining Unicode char U+0452 (decimal 1106) - defining Unicode char U+0453 (decimal 1107) - defining Unicode char U+0454 (decimal 1108) - defining Unicode char U+0455 (decimal 1109) - defining Unicode char U+0456 (decimal 1110) - defining Unicode char U+0457 (decimal 1111) - defining Unicode char U+0458 (decimal 1112) - defining Unicode char U+0459 (decimal 1113) - defining Unicode char U+045A (decimal 1114) - defining Unicode char U+045B (decimal 1115) - defining Unicode char U+045C (decimal 1116) - defining Unicode char U+045D (decimal 1117) - defining Unicode char U+045E (decimal 1118) - defining Unicode char U+045F (decimal 1119) - defining Unicode char U+0490 (decimal 1168) - defining Unicode char U+0491 (decimal 1169) - defining Unicode char U+0492 (decimal 1170) - defining Unicode char U+0493 (decimal 1171) - defining Unicode char U+0496 (decimal 1174) - defining Unicode char U+0497 (decimal 1175) - defining Unicode char U+0498 (decimal 1176) - defining Unicode char U+0499 (decimal 1177) - defining Unicode char U+049A (decimal 1178) - defining Unicode char U+049B (decimal 1179) - defining Unicode char U+049C (decimal 1180) - defining Unicode char U+049D (decimal 1181) - defining Unicode char U+04A0 (decimal 1184) - defining Unicode char U+04A1 (decimal 1185) - defining Unicode char U+04A2 (decimal 1186) - defining Unicode char U+04A3 (decimal 1187) - defining Unicode char U+04A4 (decimal 1188) - defining Unicode char U+04A5 (decimal 1189) - defining Unicode char U+04AA (decimal 1194) - defining Unicode char U+04AB (decimal 1195) - defining Unicode char U+04AE (decimal 1198) - defining Unicode char U+04AF (decimal 1199) - defining Unicode char U+04B0 (decimal 1200) - defining Unicode char U+04B1 (decimal 1201) - defining Unicode char U+04B2 (decimal 1202) - defining Unicode char U+04B3 (decimal 1203) - defining Unicode char U+04B6 (decimal 1206) - defining Unicode char U+04B7 (decimal 1207) - defining Unicode char U+04B8 (decimal 1208) - defining Unicode char U+04B9 (decimal 1209) - defining Unicode char U+04BA (decimal 1210) - defining Unicode char U+04BB (decimal 1211) - defining Unicode char U+04C0 (decimal 1216) - defining Unicode char U+04C1 (decimal 1217) - defining Unicode char U+04C2 (decimal 1218) - defining Unicode char U+04D0 (decimal 1232) - defining Unicode char U+04D1 (decimal 1233) - defining Unicode char U+04D2 (decimal 1234) - defining Unicode char U+04D3 (decimal 1235) - defining Unicode char U+04D4 (decimal 1236) - defining Unicode char U+04D5 (decimal 1237) - defining Unicode char U+04D6 (decimal 1238) - defining Unicode char U+04D7 (decimal 1239) - defining Unicode char U+04D8 (decimal 1240) - defining Unicode char U+04D9 (decimal 1241) - defining Unicode char U+04DA (decimal 1242) - defining Unicode char U+04DB (decimal 1243) - defining Unicode char U+04DC (decimal 1244) - defining Unicode char U+04DD (decimal 1245) - defining Unicode char U+04DE (decimal 1246) - defining Unicode char U+04DF (decimal 1247) - defining Unicode char U+04E2 (decimal 1250) - defining Unicode char U+04E3 (decimal 1251) - defining Unicode char U+04E4 (decimal 1252) - defining Unicode char U+04E5 (decimal 1253) - defining Unicode char U+04E6 (decimal 1254) - defining Unicode char U+04E7 (decimal 1255) - defining Unicode char U+04E8 (decimal 1256) - defining Unicode char U+04E9 (decimal 1257) - defining Unicode char U+04EC (decimal 1260) - defining Unicode char U+04ED (decimal 1261) - defining Unicode char U+04EE (decimal 1262) - defining Unicode char U+04EF (decimal 1263) - defining Unicode char U+04F0 (decimal 1264) - defining Unicode char U+04F1 (decimal 1265) - defining Unicode char U+04F2 (decimal 1266) - defining Unicode char U+04F3 (decimal 1267) - defining Unicode char U+04F4 (decimal 1268) - defining Unicode char U+04F5 (decimal 1269) - defining Unicode char U+04F8 (decimal 1272) - defining Unicode char U+04F9 (decimal 1273) - defining Unicode char U+200C (decimal 8204) - defining Unicode char U+2013 (decimal 8211) - defining Unicode char U+2014 (decimal 8212) - defining Unicode char U+2018 (decimal 8216) - defining Unicode char U+2019 (decimal 8217) - defining Unicode char U+201C (decimal 8220) - defining Unicode char U+201D (decimal 8221) - defining Unicode char U+201E (decimal 8222) - defining Unicode char U+2030 (decimal 8240) - defining Unicode char U+2031 (decimal 8241) - defining Unicode char U+2116 (decimal 8470) - defining Unicode char U+2329 (decimal 9001) - defining Unicode char U+3008 (decimal 12296) - defining Unicode char U+232A (decimal 9002) - defining Unicode char U+3009 (decimal 12297) - defining Unicode char U+2423 (decimal 9251) - defining Unicode char U+27E8 (decimal 10216) - defining Unicode char U+27E9 (decimal 10217) - defining Unicode char U+FB00 (decimal 64256) - defining Unicode char U+FB01 (decimal 64257) - defining Unicode char U+FB02 (decimal 64258) - defining Unicode char U+FB03 (decimal 64259) - defining Unicode char U+FB04 (decimal 64260) - defining Unicode char U+FB05 (decimal 64261) - defining Unicode char U+FB06 (decimal 64262) -))) -(/usr/local/texlive/2024/texmf-dist/tex/latex/base/inputenc.sty -Package: inputenc 2021/02/14 v1.3d Input encoding file -\inpenc@prehook=\toks17 -\inpenc@posthook=\toks18 -) -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel/babel.sty -Package: babel 2024/02/07 v24.2 The Babel package -\babel@savecnt=\count196 -\U@D=\dimen141 -\l@unhyphenated=\language89 - -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel/txtbabel.def) -\bbl@readstream=\read2 -\bbl@dirlevel=\count197 - -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel-greek/greek.ldf -Language: greek 2023/10/13 1.15 Greek support for the babel system -\bbl@monogreek=\count198 -\bbl@polygreek=\count199 -\bbl@ancientgreek=\count266 -) -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel-russian/russianb.ldf -File: russianb.ldf 2021/01/10 1.3m Russian support for the Babel system -Language: russian 2020/09/09 1.3k Russian support for the Babel system -Package babel Info: Making " an active character on input line 124. -Package babel Info: Default for \cyrdash is provided on input line 163. -) -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel-english/english.ldf -Language: english 2017/06/06 v3.3r English support from the babel system -Package babel Info: Hyphen rules for 'canadian' set to \l@english -(babel) (\language0). Reported on input line 102. -Package babel Info: Hyphen rules for 'australian' set to \l@ukenglish -(babel) (\language23). Reported on input line 105. -Package babel Info: Hyphen rules for 'newzealand' set to \l@ukenglish -(babel) (\language23). Reported on input line 108. -)) -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel/locale/en/babel-english.t -ex -Package babel Info: Importing font and identification data for english -(babel) from babel-en.ini. Reported on input line 11. -) -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel/locale/ru/babel-russian.t -ex -Package babel Info: Importing font and identification data for russian -(babel) from babel-ru.ini. Reported on input line 11. -) -(/usr/local/texlive/2024/texmf-dist/tex/generic/babel/locale/el/babel-greek.tex -Package babel Info: Importing font and identification data for greek -(babel) from babel-el.ini. Reported on input line 11. -) (/usr/local/texlive/2024/texmf-dist/tex/latex/geometry/geometry.sty -Package: geometry 2020/01/02 v5.9 Page Geometry - -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics/keyval.sty -Package: keyval 2022/05/29 v1.15 key=value parser (DPC) -\KV@toks@=\toks19 -) -(/usr/local/texlive/2024/texmf-dist/tex/generic/iftex/ifvtex.sty -Package: ifvtex 2019/10/25 v1.7 ifvtex legacy package. Use iftex instead. - -(/usr/local/texlive/2024/texmf-dist/tex/generic/iftex/iftex.sty -Package: iftex 2022/02/03 v1.0f TeX engine tests -)) -\Gm@cnth=\count267 -\Gm@cntv=\count268 -\c@Gm@tempcnt=\count269 -\Gm@bindingoffset=\dimen142 -\Gm@wd@mp=\dimen143 -\Gm@odd@mp=\dimen144 -\Gm@even@mp=\dimen145 -\Gm@layoutwidth=\dimen146 -\Gm@layoutheight=\dimen147 -\Gm@layouthoffset=\dimen148 -\Gm@layoutvoffset=\dimen149 -\Gm@dimlist=\toks20 -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/fancybox/fancybox.sty -Package: fancybox 2010/05/15 1.4 - -Style option: `fancybox' v1.4 <2010/05/15> (tvz) -\@fancybox=\box51 -\shadowsize=\dimen150 -\@Sbox=\box52 -\do@VerbBox=\toks21 -\the@fancyput=\toks22 -\this@fancyput=\toks23 -\EndVerbatimTokens=\toks24 -\Verbatim@Outfile=\write3 -\Verbatim@Infile=\read3 -) (/usr/local/texlive/2024/texmf-dist/tex/latex/tools/calc.sty -Package: calc 2023/07/08 v4.3 Infix arithmetic (KKT,FJ) -\calc@Acount=\count270 -\calc@Bcount=\count271 -\calc@Adimen=\dimen151 -\calc@Bdimen=\dimen152 -\calc@Askip=\skip50 -\calc@Bskip=\skip51 -LaTeX Info: Redefining \setlength on input line 80. -LaTeX Info: Redefining \addtolength on input line 81. -\calc@Ccount=\count272 -\calc@Cskip=\skip52 -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/tools/multicol.sty -Package: multicol 2023/03/30 v1.9f multicolumn formatting (FMi) -\c@tracingmulticols=\count273 -\mult@box=\box53 -\multicol@leftmargin=\dimen153 -\c@unbalance=\count274 -\c@collectmore=\count275 -\doublecol@number=\count276 -\multicoltolerance=\count277 -\multicolpretolerance=\count278 -\full@width=\dimen154 -\page@free=\dimen155 -\premulticols=\dimen156 -\postmulticols=\dimen157 -\multicolsep=\skip53 -\multicolbaselineskip=\skip54 -\partial@page=\box54 -\last@line=\box55 -\mc@boxedresult=\box56 -\maxbalancingoverflow=\dimen158 -\mult@rightbox=\box57 -\mult@grightbox=\box58 -\mult@firstbox=\box59 -\mult@gfirstbox=\box60 -\@tempa=\box61 -\@tempa=\box62 -\@tempa=\box63 -\@tempa=\box64 -\@tempa=\box65 -\@tempa=\box66 -\@tempa=\box67 -\@tempa=\box68 -\@tempa=\box69 -\@tempa=\box70 -\@tempa=\box71 -\@tempa=\box72 -\@tempa=\box73 -\@tempa=\box74 -\@tempa=\box75 -\@tempa=\box76 -\@tempa=\box77 -\@tempa=\box78 -\@tempa=\box79 -\@tempa=\box80 -\@tempa=\box81 -\@tempa=\box82 -\@tempa=\box83 -\@tempa=\box84 -\@tempa=\box85 -\@tempa=\box86 -\@tempa=\box87 -\@tempa=\box88 -\@tempa=\box89 -\@tempa=\box90 -\@tempa=\box91 -\@tempa=\box92 -\@tempa=\box93 -\@tempa=\box94 -\@tempa=\box95 -\@tempa=\box96 -\c@minrows=\count279 -\c@columnbadness=\count280 -\c@finalcolumnbadness=\count281 -\last@try=\dimen159 -\multicolovershoot=\dimen160 -\multicolundershoot=\dimen161 -\mult@nat@firstbox=\box97 -\colbreak@box=\box98 -\mc@col@check@num=\count282 -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics/graphicx.sty -Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR) - -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics/graphics.sty -Package: graphics 2022/03/10 v1.4e Standard LaTeX Graphics (DPC,SPQR) - -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics/trig.sty -Package: trig 2021/08/11 v1.11 sin cos tan (DPC) -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics-cfg/graphics.cfg -File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration -) -Package graphics Info: Driver file: pdftex.def on input line 107. - -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics-def/pdftex.def -File: pdftex.def 2022/09/22 v1.2b Graphics/color driver for pdftex -)) -\Gin@req@height=\dimen162 -\Gin@req@width=\dimen163 -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/url/url.sty -\Urlmuskip=\muskip16 -Package: url 2013/09/16 ver 3.4 Verb mode for urls, etc. -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/eso-pic/eso-pic.sty -Package: eso-pic 2023/05/03 v3.0c eso-pic (RN) -\ESO@tempdima=\dimen164 -\ESO@tempdimb=\dimen165 - -(/usr/local/texlive/2024/texmf-dist/tex/latex/xcolor/xcolor.sty -Package: xcolor 2023/11/15 v3.01 LaTeX color extensions (UK) - -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics-cfg/color.cfg -File: color.cfg 2016/01/02 v1.6 sample color configuration -) -Package xcolor Info: Driver file: pdftex.def on input line 274. - -(/usr/local/texlive/2024/texmf-dist/tex/latex/graphics/mathcolor.ltx) -Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1350. -Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1354. -Package xcolor Info: Model `RGB' extended on input line 1366. -Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1368. -Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1369. -Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1370. -Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1371. -Package xcolor Info: Model `Gray' substituted by `gray' on input line 1372. -Package xcolor Info: Model `wave' substituted by `hsb' on input line 1373. -)) -(/usr/local/texlive/2024/texmf-dist/tex/latex/base/textcomp.sty -Package: textcomp 2020/02/02 v2.0n Standard LaTeX package -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/tex-gyre/tgheros.sty -Package: tgheros 2009/09/27 v1.2 TeX Gyre Heros as default sans serif family - -(/usr/local/texlive/2024/texmf-dist/tex/latex/kvoptions/kvoptions.sty -Package: kvoptions 2022-06-15 v3.15 Key value format for package options (HO) - -(/usr/local/texlive/2024/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty -Package: ltxcmds 2023-12-04 v1.26 LaTeX kernel commands for general use (HO) -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty -Package: kvsetkeys 2022-10-05 v1.19 Key value parser (HO) -))) -LaTeX Font Info: Trying to load font information for T1+qhv on input line 40 -. - -(/usr/local/texlive/2024/texmf-dist/tex/latex/tex-gyre/t1qhv.fd -File: t1qhv.fd 2009/09/25 v1.2 font definition file for T1/qhv -) -(/usr/local/texlive/2024/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -File: l3backend-pdftex.def 2024-02-20 L3 backend support: PDF output (pdfTeX) -\l__color_backend_stack_int=\count283 -\l__pdf_internal_box=\box99 -) -No file nH8nokEg9c-1.aux. -\openout1 = `nH8nokEg9c-1.aux'. - -LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 40. -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 40. -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 40. -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 40. -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 40. -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 40. -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 40. -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for LGR/cmr/m/n on input line 40. -LaTeX Font Info: Trying to load font information for LGR+cmr on input line 4 -0. -(/usr/local/texlive/2024/texmf-dist/tex/latex/cbfonts-fd/lgrcmr.fd -File: lgrcmr.fd 2017/07/29 v1.2 Greek European Computer Regular -) -LaTeX Font Info: ... okay on input line 40. -LaTeX Font Info: Checking defaults for T2A/cmr/m/n on input line 40. -LaTeX Font Info: Trying to load font information for T2A+cmr on input line 4 -0. - -(/usr/local/texlive/2024/texmf-dist/tex/latex/cyrillic/t2acmr.fd -File: t2acmr.fd 2001/08/11 v1.0a Computer Modern Cyrillic font definitions -) -LaTeX Font Info: ... okay on input line 40. -LaTeX Info: Redefining \th on input line 40. - -*geometry* driver: auto-detecting -*geometry* detected driver: pdftex -*geometry* verbose mode - [ preamble ] result: -* driver: pdftex -* paper: a4paper -* layout: -* layoutoffset:(h,v)=(0.0pt,0.0pt) -* modes: landscape -* h-part:(L,W,R)=(85.35826pt, 674.33032pt, 85.35826pt) -* v-part:(T,H,B)=(0.0pt, 597.50787pt, 0.0pt) -* \paperwidth=845.04684pt -* \paperheight=597.50787pt -* \textwidth=674.33032pt -* \textheight=597.50787pt -* \oddsidemargin=13.08827pt -* \evensidemargin=13.08827pt -* \topmargin=-109.26999pt -* \headheight=12.0pt -* \headsep=25.0pt -* \topskip=12.0pt -* \footskip=30.0pt -* \marginparwidth=44.0pt -* \marginparsep=10.0pt -* \columnsep=10.0pt -* \skip\footins=10.8pt plus 4.0pt minus 2.0pt -* \hoffset=0.0pt -* \voffset=0.0pt -* \mag=1000 -* \@twocolumnfalse -* \@twosidefalse -* \@mparswitchfalse -* \@reversemarginfalse -* (1in=72.27pt=25.4mm, 1cm=28.453pt) - -(/usr/local/texlive/2024/texmf-dist/tex/context/base/mkii/supp-pdf.mkii -[Loading MPS to PDF converter (version 2006.09.02).] -\scratchcounter=\count284 -\scratchdimen=\dimen166 -\scratchbox=\box100 -\nofMPsegments=\count285 -\nofMParguments=\count286 -\everyMPshowfont=\toks25 -\MPscratchCnt=\count287 -\MPscratchDim=\dimen167 -\MPnumerator=\count288 -\makeMPintoPDFobject=\count289 -\everyMPtoPDFconversion=\toks26 -) (/usr/local/texlive/2024/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty -Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf -Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4 -85. - -(/usr/local/texlive/2024/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg -File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv -e -)) - -! LaTeX Error: Command \textalpha unavailable in encoding T1. - -See the LaTeX manual or LaTeX Companion for explanation. -Type H for immediate help. - ... - -l.55 } - \\ -Here is how much of TeX's memory you used: - 6866 strings out of 474116 - 112742 string characters out of 5743682 - 1925187 words of memory out of 5000000 - 29151 multiletter control sequences out of 15000+600000 - 568122 words of font info for 41 fonts, out of 8000000 for 9000 - 1141 hyphenation exceptions out of 8191 - 57i,3n,65p,467b,311s stack positions out of 10000i,1000n,20000p,200000b,200000s - -! ==> Fatal error occurred, no output PDF file produced! diff --git a/resources/latex/nH8nokEg9c-1.tex b/resources/latex/nH8nokEg9c-1.tex deleted file mode 100644 index a0e77bf8e..000000000 --- a/resources/latex/nH8nokEg9c-1.tex +++ /dev/null @@ -1,89 +0,0 @@ -\documentclass[12pt]{article} - -% Encoding & languages -\usepackage[LGR,T2A,T1]{fontenc} % LGR for Greek, T2A for Cyrillic, T1 for Latin -\usepackage[utf8]{inputenc} -\usepackage[greek,russian,english]{babel} % English is the main language (last) - -% Page & layout -\usepackage[landscape,a4paper]{geometry} -\geometry{verbose,tmargin=0cm,bmargin=0cm,lmargin=3cm,rmargin=3cm} -\usepackage{fancybox} -\usepackage{calc} -\usepackage{multicol} -\usepackage{graphicx} -\usepackage{url} -\usepackage{eso-pic} -\usepackage{textcomp} -\usepackage{tgheros} % TeX Gyre Heros (Helvetica-like) -\renewcommand{\familydefault}{\sfdefault} - -% Colors -\usepackage{xcolor} -\definecolor{myorange}{RGB}{226, 83, 48} -\definecolor{Slate600}{HTML}{20262C} -\definecolor{GrayLine}{HTML}{858B91} - -% Be a bit more permissive with line-breaking to avoid overfull boxes -\setlength{\emergencystretch}{2em} - -% Background image -\newcommand\BackgroundPic{% - \put(0,0){% - \parbox[b][\paperheight]{\paperwidth}{% - \vfill - \centering - \includegraphics[width=\paperwidth,height=\paperheight,keepaspectratio]{images/participation-certificate-2025.png}% - \vfill -}}} - -\begin{document} -\AddToShipoutPicture{\BackgroundPic} - -~ -\vspace{3.5cm} -~ - -\begin{center} - \vspace{1cm} - \textcolor{Slate600}{\fontsize{13}{13}\selectfont This certificate recognizes that} \\ - \vspace{0.5cm} - - % Certificate Holder Name (auto-wrap; language wrapping, if needed, should be applied in PHP before injection) - \noindent\parbox{0.9\textwidth}{\centering - \textcolor{myorange}{\fontsize{28}{32}\selectfont α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σ τ υ φ χ ψ ω} - }\\ - - \vspace{0.5cm} - \textcolor{Slate600}{\fontsize{13}{13}\selectfont Has developed essential computational thinking skills} \\ - \textcolor{Slate600}{\fontsize{13}{13}\selectfont and contributed to the success of} \\ - - \vspace{0.8cm} - \textcolor{myorange}{\fontsize{36}{42}\selectfont EU CodeWeek 2025} \\ - - \vspace{0.8cm} - \textcolor{Slate600}{\fontsize{13}{13}\selectfont by actively participating in the coding event} \\ - \vspace{0.5cm} - - % Event Name (auto-wrap; no forced Greek — inject Greek wrapper from PHP only when text is Greek) - \noindent\parbox{0.9\textwidth}{\centering - \textcolor{myorange}{\fontsize{28}{32}\selectfont α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σ τ υ φ χ ψ ω} - }\\ - - \vspace{0.5cm} - \textcolor{Slate600}{\fontsize{13}{13}\selectfont Which took place on} \\ - \vspace{0.5cm} - - % Event Date (auto-wrap if long; typically short) - \noindent\parbox{0.9\textwidth}{\centering - \textcolor{Slate600}{\fontsize{24}{28}\selectfont 2025-11-20 13:36} - }\\ - - % Horizontal divider - \vspace{0.3cm} - \centering - \textcolor{GrayLine}{\rule{183pt}{2pt}} - -\end{center} - -\end{document} diff --git a/resources/views/include/map.blade.php b/resources/views/include/map.blade.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/resources/views/map.blade.php b/resources/views/map.blade.php deleted file mode 100644 index 131f6b750..000000000 --- a/resources/views/map.blade.php +++ /dev/null @@ -1,7 +0,0 @@ -@extends('layout.simple') - -@section('content') - - @include('include.map') - -@endsection \ No newline at end of file diff --git a/resources/views/static/contact-us.blade.php b/resources/views/static/contact-us.blade.php index 486a054b6..722140f1d 100644 --- a/resources/views/static/contact-us.blade.php +++ b/resources/views/static/contact-us.blade.php @@ -228,7 +228,7 @@ function validateForm() { // Turnstile will call this once rendered window.turnstileCallback = function () { turnstile.render('.cf-turnstile', { - sitekey: '{{ env('TURNSTILE_SITEKEY') }}', + sitekey: '{{ config('codeweek.turnstile_sitekey') }}', callback: function (token) { captchaInput.value = token; validateForm(); // ensure button re-validates once CAPTCHA succeeds diff --git a/routes/console.php b/routes/console.php index 45c86a8cd..124f079b1 100644 --- a/routes/console.php +++ b/routes/console.php @@ -40,7 +40,6 @@ //Schedule::command('certificates:fix')->everyFiveMinutes(); Schedule::command('app:sync-blogs')->dailyAt('1:00'); -Schedule::command('app:export-search-data-to-json')->dailyAt('2:00'); Schedule::command('events:generate-recurring')->dailyAt('01:00'); diff --git a/routes/web.php b/routes/web.php index 1c7453b5e..67abc885d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -44,7 +44,6 @@ use App\Http\Controllers\LeadingTeachersSignup; use App\Http\Controllers\LocationController; use App\Http\Controllers\MailTemplateController; -use App\Http\Controllers\MapController; use App\Http\Controllers\MatchMakingToolController; use App\Http\Controllers\OnlineCoursesController; use App\Http\Controllers\OnlineEventsController; @@ -445,7 +444,6 @@ )->name('codingathome-turning-code-into-pictures'); Route::get('/add', [EventController::class, 'create'])->name('create_event')->middleware('auth'); -Route::get('/map', [MapController::class, 'index'])->name('map'); //Route::get('/resources', 'ResourcesPageController@index')->name('resources'); Route::get('/resources', [ResourcesController::class, 'all'])->name('resources'); Route::get('/resources/learn-and-teach', [ResourcesController::class, 'all'])->name('resources_all'); diff --git a/server.php b/server.php deleted file mode 100644 index b07fa44b3..000000000 --- a/server.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -$uri = urldecode( - parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) -); - -// This file allows us to emulate Apache's "mod_rewrite" functionality from the -// built-in PHP web server. This provides a convenient way to test a Laravel -// application without having installed a "real" web server software here. -if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { - return false; -} - -require_once __DIR__.'/public/index.php'; diff --git a/tailwind.js b/tailwind.js deleted file mode 100644 index 00a770ed5..000000000 --- a/tailwind.js +++ /dev/null @@ -1,925 +0,0 @@ -/* - -Tailwind - The Utility-First CSS Framework - -A project by Adam Wathan (@adamwathan), Jonathan Reinink (@reinink), -David Hemphill (@davidhemphill) and Steve Schoger (@steveschoger). - -Welcome to the Tailwind config file. This is where you can customize -Tailwind specifically for your project. Don't be intimidated by the -length of this file. It's really just a big JavaScript object and -we've done our very best to explain each section. - -View the full documentation at https://tailwindcss.com. - - -|------------------------------------------------------------------------------- -| The default config -|------------------------------------------------------------------------------- -| -| This variable contains the default Tailwind config. You don't have -| to use it, but it can sometimes be helpful to have available. For -| example, you may choose to merge your custom configuration -| values with some of the Tailwind defaults. -| -*/ - -// let defaultConfig = require('tailwindcss/defaultConfig')() - - -/* -|------------------------------------------------------------------------------- -| Colors https://tailwindcss.com/docs/colors -|------------------------------------------------------------------------------- -| -| Here you can specify the colors used in your project. To get you started, -| we've provided a generous palette of great looking colors that are perfect -| for prototyping, but don't hesitate to change them for your project. You -| own these colors, nothing will break if you change everything about them. -| -| We've used literal color names ("red", "blue", etc.) for the default -| palette, but if you'd rather use functional names like "primary" and -| "secondary", or even a numeric scale like "100" and "200", go for it. -| -*/ - -let colors = { - 'transparent': 'transparent', - - 'black': '#22292f', - 'grey-darkest': '#3d4852', - 'grey-darker': '#606f7b', - 'grey-dark': '#8795a1', - 'grey': '#b8c2cc', - 'grey-light': '#dae1e7', - 'grey-lighter': '#f1f5f8', - 'grey-lightest': '#f8fafc', - 'white': '#ffffff', - - 'red-darkest': '#3b0d0c', - 'red-darker': '#621b18', - 'red-dark': '#cc1f1a', - 'red': '#e3342f', - 'red-light': '#ef5753', - 'red-lighter': '#f9acaa', - 'red-lightest': '#fcebea', - - 'orange-darkest': '#462a16', - 'orange-darker': '#613b1f', - 'orange-dark': '#de751f', - 'orange': '#f6993f', - 'orange-light': '#faad63', - 'orange-lighter': '#fcd9b6', - 'orange-lightest': '#fff5eb', - - 'yellow-darkest': '#453411', - 'yellow-darker': '#684f1d', - 'yellow-dark': '#f2d024', - 'yellow': '#ffed4a', - 'yellow-light': '#fff382', - 'yellow-lighter': '#fff9c2', - 'yellow-lightest': '#fcfbeb', - - 'green-darkest': '#0f2f21', - 'green-darker': '#1a4731', - 'green-dark': '#1f9d55', - 'green': '#38c172', - 'green-light': '#51d88a', - 'green-lighter': '#a2f5bf', - 'green-lightest': '#e3fcec', - - 'teal-darkest': '#0d3331', - 'teal-darker': '#20504f', - 'teal-dark': '#38a89d', - 'teal': '#4dc0b5', - 'teal-light': '#64d5ca', - 'teal-lighter': '#a0f0ed', - 'teal-lightest': '#e8fffe', - - 'blue-darkest': '#12283a', - 'blue-darker': '#1c3d5a', - 'blue-dark': '#2779bd', - 'blue': '#3490dc', - 'blue-light': '#07A0C4', - 'blue-lighter': '#bcdefa', - 'blue-lightest': '#eff8ff', - - 'indigo-darkest': '#191e38', - 'indigo-darker': '#2f365f', - 'indigo-dark': '#5661b3', - 'indigo': '#6574cd', - 'indigo-light': '#7886d7', - 'indigo-lighter': '#b2b7ff', - 'indigo-lightest': '#e6e8ff', - - 'purple-darkest': '#21183c', - 'purple-darker': '#382b5f', - 'purple-dark': '#794acf', - 'purple': '#9561e2', - 'purple-light': '#a779e9', - 'purple-lighter': '#d6bbfc', - 'purple-lightest': '#f3ebff', - - 'pink-darkest': '#451225', - 'pink-darker': '#6f213f', - 'pink-dark': '#eb5286', - 'pink': '#f66d9b', - 'pink-light': '#fa7ea8', - 'pink-lighter': '#ffbbca', - 'pink-lightest': '#ffebef', -} - -module.exports = { - - /* - |----------------------------------------------------------------------------- - | Colors https://tailwindcss.com/docs/colors - |----------------------------------------------------------------------------- - | - | The color palette defined above is also assigned to the "colors" key of - | your Tailwind config. This makes it easy to access them in your CSS - | using Tailwind's config helper. For example: - | - | .error { color: config('colors.red') } - | - */ - - colors: colors, - - - /* - |----------------------------------------------------------------------------- - | Screens https://tailwindcss.com/docs/responsive-design - |----------------------------------------------------------------------------- - | - | Screens in Tailwind are translated to CSS media queries. They define the - | responsive breakpoints for your project. By default Tailwind takes a - | "mobile first" approach, where each screen size represents a minimum - | viewport width. Feel free to have as few or as many screens as you - | want, naming them in whatever way you'd prefer for your project. - | - | Tailwind also allows for more complex screen definitions, which can be - | useful in certain situations. Be sure to see the full responsive - | documentation for a complete list of options. - | - | Class name: .{screen}:{utility} - | - */ - - screens: { - 'sm': '576px', - 'md': '768px', - 'lg': '992px', - 'xl': '1200px', - }, - - - /* - |----------------------------------------------------------------------------- - | Fonts https://tailwindcss.com/docs/fonts - |----------------------------------------------------------------------------- - | - | Here is where you define your project's font stack, or font families. - | Keep in mind that Tailwind doesn't actually load any fonts for you. - | If you're using custom fonts you'll need to import them prior to - | defining them here. - | - | By default we provide a native font stack that works remarkably well on - | any device or OS you're using, since it just uses the default fonts - | provided by the platform. - | - | Class name: .font-{name} - | - */ - - fonts: { - 'sans': [ - 'system-ui', - 'BlinkMacSystemFont', - '-apple-system', - 'Segoe UI', - 'Roboto', - 'Oxygen', - 'Ubuntu', - 'Cantarell', - 'Fira Sans', - 'Droid Sans', - 'Helvetica Neue', - 'sans-serif', - ], - 'serif': [ - 'Constantia', - 'Lucida Bright', - 'Lucidabright', - 'Lucida Serif', - 'Lucida', - 'DejaVu Serif', - 'Bitstream Vera Serif', - 'Liberation Serif', - 'Georgia', - 'serif', - ], - 'mono': [ - 'Menlo', - 'Monaco', - 'Consolas', - 'Liberation Mono', - 'Courier New', - 'monospace', - ] - }, - - - /* - |----------------------------------------------------------------------------- - | Text sizes https://tailwindcss.com/docs/text-sizing - |----------------------------------------------------------------------------- - | - | Here is where you define your text sizes. Name these in whatever way - | makes the most sense to you. We use size names by default, but - | you're welcome to use a numeric scale or even something else - | entirely. - | - | By default Tailwind uses the "rem" unit type for most measurements. - | This allows you to set a root font size which all other sizes are - | then based on. That said, you are free to use whatever units you - | prefer, be it rems, ems, pixels or other. - | - | Class name: .text-{size} - | - */ - - textSizes: { - 'xs': '.75rem', // 12px - 'sm': '.875rem', // 14px - 'base': '1rem', // 16px - 'lg': '1.125rem', // 18px - 'xl': '1.25rem', // 20px - '2xl': '1.5rem', // 24px - '3xl': '1.875rem', // 30px - '4xl': '2.25rem', // 36px - '5xl': '3rem', // 48px - }, - - - /* - |----------------------------------------------------------------------------- - | Font weights https://tailwindcss.com/docs/font-weight - |----------------------------------------------------------------------------- - | - | Here is where you define your font weights. We've provided a list of - | common font weight names with their respective numeric scale values - | to get you started. It's unlikely that your project will require - | all of these, so we recommend removing those you don't need. - | - | Class name: .font-{weight} - | - */ - - fontWeights: { - 'hairline': 100, - 'thin': 200, - 'light': 300, - 'normal': 400, - 'medium': 500, - 'semibold': 600, - 'bold': 700, - 'extrabold': 800, - 'black': 900, - }, - - - /* - |----------------------------------------------------------------------------- - | Leading (line height) https://tailwindcss.com/docs/line-height - |----------------------------------------------------------------------------- - | - | Here is where you define your line height values, or as we call - | them in Tailwind, leadings. - | - | Class name: .leading-{size} - | - */ - - leading: { - 'none': 1, - 'tight': 1.25, - 'normal': 1.5, - 'loose': 2, - }, - - - /* - |----------------------------------------------------------------------------- - | Tracking (letter spacing) https://tailwindcss.com/docs/letter-spacing - |----------------------------------------------------------------------------- - | - | Here is where you define your letter spacing values, or as we call - | them in Tailwind, tracking. - | - | Class name: .tracking-{size} - | - */ - - tracking: { - 'tight': '-0.05em', - 'normal': '0', - 'wide': '0.05em', - }, - - - /* - |----------------------------------------------------------------------------- - | Text colors https://tailwindcss.com/docs/text-color - |----------------------------------------------------------------------------- - | - | Here is where you define your text colors. By default these use the - | color palette we defined above, however you're welcome to set these - | independently if that makes sense for your project. - | - | Class name: .text-{color} - | - */ - - textColors: colors, - - - /* - |----------------------------------------------------------------------------- - | Background colors https://tailwindcss.com/docs/background-color - |----------------------------------------------------------------------------- - | - | Here is where you define your background colors. By default these use - | the color palette we defined above, however you're welcome to set - | these independently if that makes sense for your project. - | - | Class name: .bg-{color} - | - */ - - backgroundColors: colors, - - - /* - |----------------------------------------------------------------------------- - | Background sizes https://tailwindcss.com/docs/background-size - |----------------------------------------------------------------------------- - | - | Here is where you define your background sizes. We provide some common - | values that are useful in most projects, but feel free to add other sizes - | that are specific to your project here as well. - | - | Class name: .bg-{size} - | - */ - - backgroundSize: { - 'auto': 'auto', - 'cover': 'cover', - 'contain': 'contain', - }, - - - /* - |----------------------------------------------------------------------------- - | Border widths https://tailwindcss.com/docs/border-width - |----------------------------------------------------------------------------- - | - | Here is where you define your border widths. Take note that border - | widths require a special "default" value set as well. This is the - | width that will be used when you do not specify a border width. - | - | Class name: .border{-side?}{-width?} - | - */ - - borderWidths: { - default: '1px', - '0': '0', - '2': '2px', - '4': '4px', - '8': '8px', - }, - - - /* - |----------------------------------------------------------------------------- - | Border colors https://tailwindcss.com/docs/border-color - |----------------------------------------------------------------------------- - | - | Here is where you define your border colors. By default these use the - | color palette we defined above, however you're welcome to set these - | independently if that makes sense for your project. - | - | Take note that border colors require a special "default" value set - | as well. This is the color that will be used when you do not - | specify a border color. - | - | Class name: .border-{color} - | - */ - - borderColors: global.Object.assign({ default: colors['grey-light'] }, colors), - - - /* - |----------------------------------------------------------------------------- - | Border radius https://tailwindcss.com/docs/border-radius - |----------------------------------------------------------------------------- - | - | Here is where you define your border radius values. If a `default` radius - | is provided, it will be made available as the non-suffixed `.rounded` - | utility. - | - | If your scale includes a `0` value to reset already rounded corners, it's - | a good idea to put it first so other values are able to override it. - | - | Class name: .rounded{-side?}{-size?} - | - */ - - borderRadius: { - 'none': '0', - 'sm': '.125rem', - default: '.25rem', - 'lg': '.5rem', - 'full': '9999px', - }, - - - /* - |----------------------------------------------------------------------------- - | Width https://tailwindcss.com/docs/width - |----------------------------------------------------------------------------- - | - | Here is where you define your width utility sizes. These can be - | percentage based, pixels, rems, or any other units. By default - | we provide a sensible rem based numeric scale, a percentage - | based fraction scale, plus some other common use-cases. You - | can, of course, modify these values as needed. - | - | - | It's also worth mentioning that Tailwind automatically escapes - | invalid CSS class name characters, which allows you to have - | awesome classes like .w-2/3. - | - | Class name: .w-{size} - | - */ - - width: { - 'auto': 'auto', - 'px': '1px', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '24': '6rem', - '32': '8rem', - '48': '12rem', - '64': '16rem', - '1/2': '50%', - '1/3': '33.33333%', - '2/3': '66.66667%', - '1/4': '25%', - '3/4': '75%', - '1/5': '20%', - '2/5': '40%', - '3/5': '60%', - '4/5': '80%', - '1/6': '16.66667%', - '5/6': '83.33333%', - 'full': '100%', - 'screen': '100vw' - }, - - - /* - |----------------------------------------------------------------------------- - | Height https://tailwindcss.com/docs/height - |----------------------------------------------------------------------------- - | - | Here is where you define your height utility sizes. These can be - | percentage based, pixels, rems, or any other units. By default - | we provide a sensible rem based numeric scale plus some other - | common use-cases. You can, of course, modify these values as - | needed. - | - | Class name: .h-{size} - | - */ - - height: { - 'auto': 'auto', - 'px': '1px', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '24': '6rem', - '32': '8rem', - '48': '12rem', - '64': '16rem', - 'full': '100%', - 'screen': '100vh' - }, - - - /* - |----------------------------------------------------------------------------- - | Minimum width https://tailwindcss.com/docs/min-width - |----------------------------------------------------------------------------- - | - | Here is where you define your minimum width utility sizes. These can - | be percentage based, pixels, rems, or any other units. We provide a - | couple common use-cases by default. You can, of course, modify - | these values as needed. - | - | Class name: .min-w-{size} - | - */ - - minWidth: { - '0': '0', - 'full': '100%', - }, - - - /* - |----------------------------------------------------------------------------- - | Minimum height https://tailwindcss.com/docs/min-height - |----------------------------------------------------------------------------- - | - | Here is where you define your minimum height utility sizes. These can - | be percentage based, pixels, rems, or any other units. We provide a - | few common use-cases by default. You can, of course, modify these - | values as needed. - | - | Class name: .min-h-{size} - | - */ - - minHeight: { - '0': '0', - 'full': '100%', - 'screen': '100vh' - }, - - - /* - |----------------------------------------------------------------------------- - | Maximum width https://tailwindcss.com/docs/max-width - |----------------------------------------------------------------------------- - | - | Here is where you define your maximum width utility sizes. These can - | be percentage based, pixels, rems, or any other units. By default - | we provide a sensible rem based scale and a "full width" size, - | which is basically a reset utility. You can, of course, - | modify these values as needed. - | - | Class name: .max-w-{size} - | - */ - - maxWidth: { - 'xs': '20rem', - 'sm': '30rem', - 'md': '40rem', - 'lg': '50rem', - 'xl': '60rem', - '2xl': '70rem', - '3xl': '80rem', - '4xl': '90rem', - '5xl': '100rem', - 'full': '100%', - }, - - - /* - |----------------------------------------------------------------------------- - | Maximum height https://tailwindcss.com/docs/max-height - |----------------------------------------------------------------------------- - | - | Here is where you define your maximum height utility sizes. These can - | be percentage based, pixels, rems, or any other units. We provide a - | couple common use-cases by default. You can, of course, modify - | these values as needed. - | - | Class name: .max-h-{size} - | - */ - - maxHeight: { - 'full': '100%', - 'screen': '100vh', - }, - - - /* - |----------------------------------------------------------------------------- - | Padding https://tailwindcss.com/docs/padding - |----------------------------------------------------------------------------- - | - | Here is where you define your padding utility sizes. These can be - | percentage based, pixels, rems, or any other units. By default we - | provide a sensible rem based numeric scale plus a couple other - | common use-cases like "1px". You can, of course, modify these - | values as needed. - | - | Class name: .p{side?}-{size} - | - */ - - padding: { - 'px': '1px', - '0': '0', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '6': '1.5rem', - '8': '2rem', - }, - - - /* - |----------------------------------------------------------------------------- - | Margin https://tailwindcss.com/docs/margin - |----------------------------------------------------------------------------- - | - | Here is where you define your margin utility sizes. These can be - | percentage based, pixels, rems, or any other units. By default we - | provide a sensible rem based numeric scale plus a couple other - | common use-cases like "1px". You can, of course, modify these - | values as needed. - | - | Class name: .m{side?}-{size} - | - */ - - margin: { - 'auto': 'auto', - 'px': '1px', - '0': '0', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '6': '1.5rem', - '8': '2rem', - }, - - - /* - |----------------------------------------------------------------------------- - | Negative margin https://tailwindcss.com/docs/negative-margin - |----------------------------------------------------------------------------- - | - | Here is where you define your negative margin utility sizes. These can - | be percentage based, pixels, rems, or any other units. By default we - | provide matching values to the padding scale since these utilities - | generally get used together. You can, of course, modify these - | values as needed. - | - | Class name: .-m{side?}-{size} - | - */ - - negativeMargin: { - 'px': '1px', - '0': '0', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '6': '1.5rem', - '8': '2rem', - }, - - - /* - |----------------------------------------------------------------------------- - | Shadows https://tailwindcss.com/docs/shadows - |----------------------------------------------------------------------------- - | - | Here is where you define your shadow utilities. As you can see from - | the defaults we provide, it's possible to apply multiple shadows - | per utility using comma separation. - | - | If a `default` shadow is provided, it will be made available as the non- - | suffixed `.shadow` utility. - | - | Class name: .shadow-{size?} - | - */ - - shadows: { - default: '0 2px 4px 0 rgba(0,0,0,0.10)', - 'md': '0 4px 8px 0 rgba(0,0,0,0.12), 0 2px 4px 0 rgba(0,0,0,0.08)', - 'lg': '0 15px 30px 0 rgba(0,0,0,0.11), 0 5px 15px 0 rgba(0,0,0,0.08)', - 'inner': 'inset 0 2px 4px 0 rgba(0,0,0,0.06)', - 'none': 'none', - }, - - - /* - |----------------------------------------------------------------------------- - | Z-index https://tailwindcss.com/docs/z-index - |----------------------------------------------------------------------------- - | - | Here is where you define your z-index utility values. By default we - | provide a sensible numeric scale. You can, of course, modify these - | values as needed. - | - | Class name: .z-{index} - | - */ - - zIndex: { - 'auto': 'auto', - '0': 0, - '10': 10, - '20': 20, - '30': 30, - '40': 40, - '50': 50, - }, - - - /* - |----------------------------------------------------------------------------- - | Opacity https://tailwindcss.com/docs/opacity - |----------------------------------------------------------------------------- - | - | Here is where you define your opacity utility values. By default we - | provide a sensible numeric scale. You can, of course, modify these - | values as needed. - | - | Class name: .opacity-{name} - | - */ - - opacity: { - '0': '0', - '25': '.25', - '50': '.5', - '75': '.75', - '100': '1', - }, - - - /* - |----------------------------------------------------------------------------- - | SVG fill https://tailwindcss.com/docs/svg - |----------------------------------------------------------------------------- - | - | Here is where you define your SVG fill colors. By default we just provide - | `fill-current` which sets the fill to the current text color. This lets you - | specify a fill color using existing text color utilities and helps keep the - | generated CSS file size down. - | - | Class name: .fill-{name} - | - */ - - svgFill: { - 'current': 'currentColor', - }, - - - /* - |----------------------------------------------------------------------------- - | SVG stroke https://tailwindcss.com/docs/svg - |----------------------------------------------------------------------------- - | - | Here is where you define your SVG stroke colors. By default we just provide - | `stroke-current` which sets the stroke to the current text color. This lets - | you specify a stroke color using existing text color utilities and helps - | keep the generated CSS file size down. - | - | Class name: .stroke-{name} - | - */ - - svgStroke: { - 'current': 'currentColor', - }, - - - /* - |----------------------------------------------------------------------------- - | Modules https://tailwindcss.com/docs/configuration#modules - |----------------------------------------------------------------------------- - | - | Here is where you control which modules are generated and what variants are - | generated for each of those modules. - | - | Currently supported variants: - | - responsive - | - hover - | - focus - | - active - | - group-hover - | - | To disable a module completely, use `false` instead of an array. - | - */ - - modules: { - appearance: ['responsive'], - backgroundAttachment: ['responsive'], - backgroundColors: ['responsive', 'hover'], - backgroundPosition: ['responsive'], - backgroundRepeat: ['responsive'], - backgroundSize: ['responsive'], - borderColors: ['responsive', 'hover'], - borderRadius: ['responsive'], - borderStyle: ['responsive'], - borderWidths: ['responsive'], - cursor: ['responsive'], - display: ['responsive'], - flexbox: ['responsive'], - float: ['responsive'], - fonts: ['responsive'], - fontWeights: ['responsive', 'hover'], - height: ['responsive'], - leading: ['responsive'], - lists: ['responsive'], - margin: ['responsive'], - maxHeight: ['responsive'], - maxWidth: ['responsive'], - minHeight: ['responsive'], - minWidth: ['responsive'], - negativeMargin: ['responsive'], - opacity: ['responsive'], - overflow: ['responsive'], - padding: ['responsive'], - pointerEvents: ['responsive'], - position: ['responsive'], - resize: ['responsive'], - shadows: ['responsive'], - svgFill: [], - svgStroke: [], - textAlign: ['responsive'], - textColors: ['responsive', 'hover'], - textSizes: ['responsive'], - textStyle: ['responsive', 'hover'], - tracking: ['responsive'], - userSelect: ['responsive'], - verticalAlign: ['responsive'], - visibility: ['responsive'], - whitespace: ['responsive'], - width: ['responsive'], - zIndex: ['responsive'], - }, - - - /* - |----------------------------------------------------------------------------- - | Plugins https://tailwindcss.com/docs/plugins - |----------------------------------------------------------------------------- - | - | Here is where you can register any plugins you'd like to use in your - | project. Tailwind's built-in `container` plugin is enabled by default to - | give you a Bootstrap-style responsive container component out of the box. - | - | Be sure to view the complete plugin documentation to learn more about how - | the plugin system works. - | - */ - - plugins: [ - require('tailwindcss/plugins/container')({ - // center: true, - // padding: '1rem', - }), - ], - - - /* - |----------------------------------------------------------------------------- - | Advanced Options https://tailwindcss.com/docs/configuration#options - |----------------------------------------------------------------------------- - | - | Here is where you can tweak advanced configuration options. We recommend - | leaving these options alone unless you absolutely need to change them. - | - */ - - options: { - prefix: '', - important: false, - separator: ':', - }, - -} diff --git a/tests/Unit/BulkEventUploadColumnContractTest.php b/tests/Unit/BulkEventUploadColumnContractTest.php new file mode 100644 index 000000000..c52f89ec5 --- /dev/null +++ b/tests/Unit/BulkEventUploadColumnContractTest.php @@ -0,0 +1,49 @@ +assertSame( + self::PUBLISHED_COLUMNS, + BulkEventUploadValidator::REQUIRED_COLUMNS, + 'The bulk upload column contract changed. Update the public wiki page ' + .'and this test together, and tell national partners before deploying.' + ); + } +} diff --git a/texput.log b/texput.log deleted file mode 100644 index f34b22330..000000000 --- a/texput.log +++ /dev/null @@ -1,19 +0,0 @@ -This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024) (preloaded format=pdflatex 2024.8.13) 5 NOV 2025 16:42 -entering extended mode - restricted \write18 enabled. - %&-line parsing enabled. -**ua-min.tex - -! Emergency stop. -<*> ua-min.tex - -Here is how much of TeX's memory you used: - 3 strings out of 474116 - 102 string characters out of 5743682 - 1925187 words of memory out of 5000000 - 22465 multiletter control sequences out of 15000+600000 - 558069 words of font info for 36 fonts, out of 8000000 for 9000 - 1141 hyphenation exceptions out of 8191 - 0i,0n,0p,1b,6s stack positions out of 10000i,1000n,20000p,200000b,200000s - -! ==> Fatal error occurred, no output PDF file produced!