Search by

chatixy / contao-chatixy

JanchaLV

Chatixy AI support agent for Contao - paste your widget key, no code.

Package info

github.com/Devoflex/contao-chatixy

Homepage

Issues

Type:contao-bundle

pkg:composer/chatixy/contao-chatixy

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.2 2026-08-19 20:15 UTC

This package is auto-updated.

Last update: 2026-08-19 20:16:35 UTC


README

Adds the Chatixy AI support agent to every front end page of a Contao site. Install it, paste your widget key, tick one checkbox. No template edit, no layout change, no insert tag to place - and it works the same on a classic PHP layout and on a Contao 5 Twig-slot layout.

Requirements

Contao 4.13 or 5.x (contao/core-bundle: ^4.13 || ^5.0)
PHP 8.1+
Symfony 5.4, 6.x or 7.x
Licence MIT

The package is a contao-bundle and ships a Contao Manager Plugin, so the Managed Edition registers the bundle for you - there is no kernel file to edit.

Install

composer require chatixy/contao-chatixy
vendor/bin/contao-console cache:clear

Or install it from the Contao Manager, which clears the cache for you.

Then go to Back end -> System -> Settings -> Chatixy AI Support Agent. Tick Show support agent, paste your Widget key, and save. The checkbox is off until you tick it, so installing the bundle never changes a live site on its own.

The key field accepts any of the three shapes the dashboard hands out:

  • the bare 64-character key,
  • <key>.js,
  • the whole <script src="https://chatixy.com/source/<key>.js" async></script> snippet.

The key is extracted from whatever you paste - the first 64-character hex run wins - and only the bare key is stored. Leave it empty and the bundle does nothing at all.

Both settings are site-wide: they extend Contao's own tl_settings (the DC_File container that persists to system/config/localconfig.php), rather than adding a table and a back end module for two scalars. Contao caches DCA definitions, so clear the cache after installing.

No Chatixy account yet? Start at https://chatixy.com/register.

What it does

On every front end page it splices one tag in before the closing </body>:

<script src="https://chatixy.com/source/<key>.js?platform=contao" async
        data-chatixy-source="contao"></script>

That is the whole payload. Nothing is added to <head>, no CSS, no extra requests from the server side.

The hook it uses, and why

The listener runs on modifyFrontendPage, registered with the #[AsHook('modifyFrontendPage')] attribute. That hook receives the finished HTML of the page as a string, after insert tags have been replaced, whatever produced it - so the bundle is independent of your theme, your templates and your layout type.

The obvious alternative, generatePage with $GLOBALS['TL_BODY'], is deliberately not used: it only fires from PageRegular, the classic PHP-template page controller. Contao 5's Twig layout with slots does not render through that controller, so on such a site the support agent would silently never appear - a failure mode that is invisible while you develop against a classic layout and only shows up on the customer's modern theme.

Where it does not inject

  • Anything with no </body>. An XML feed, a JSON response rendered through a front end template, or a partial is returned byte-for-byte untouched.
  • A page that already carries the loader. The data-chatixy-source attribute doubles as the marker the listener greps for, so a page assembled from more than one front end template still gets exactly one tag rather than a double-booted support agent.
  • Anything, when the stored key is not a real 64-character hex key. The key is re-sanitised on read rather than trusted, so a value written straight into localconfig.php by a CLI script cannot produce a loader tag either.

The tag goes before the last </body> in the buffer, because a page may legitimately contain that literal string earlier - a code example, an escaped snippet in a news article - and the real document end is the last one.

Security: the origin is pinned

The widget key is public; the host is not configurable. There is deliberately no "advanced host" field, no container parameter and no environment variable, because a stored host would build a first-party <script src> on every front end page - i.e. site-wide stored XSS if it could ever be influenced. A back end form can be CSRF'd and localconfig.php can be written by other means, so stored settings are not treated as a trust boundary here.

Chatixy\ContaoBundle\Service\ChatixyKey::sanitizeHost() accepts only an https origin whose host is chatixy.com or a subdomain of it, and returns the canonical https://chatixy.com for anything else. The pattern is anchored at both ends and only ever grows the host to the left of a literal dot, so evilchatixy.com, chatixy.com.evil.example, chatixy.com@evil.example and http://chatixy.com are all rejected. The emitted src is HTML-escaped at the point of output as well, even though every component of it is already constrained to hex characters and a pinned origin.

Support

MIT licensed. See LICENSE.