Skip to content

Instantly share code, notes, and snippets.

@nuxodin
Created February 26, 2026 12:36
Show Gist options
  • Select an option

  • Save nuxodin/62c6f4c916e6f3e47879cf0735b7b7bf to your computer and use it in GitHub Desktop.

Select an option

Save nuxodin/62c6f4c916e6f3e47879cf0735b7b7bf to your computer and use it in GitHub Desktop.
vanilla-cms-skill.md
name description
cms
Wichtigste funktionen für das arbeiten mit dem vanilla-cms

name: qg-cms description: > Understanding and working with the qg CMS (PHP-based). Use this skill whenever you're editing, creating, or debugging template/module files for this CMS — including layout files, content modules (Cont), form modules, gallery modules, or any file using $Cont, html::, G(), Page(), or qg:: calls.

qg CMS – Developer Reference

Globals (auto-injected)

Variable Description
$Cont Current Page instance being rendered (type=c for content blocks, type=p for pages)
G() Global config/state singleton
Page($id) Returns a Page instance by ID; Page() = current page
D() Database access
L() Localisation helper

$Cont and Page() are instances of the same Page class — all methods below apply to each.


Core Patterns

Assets — always before any HTML output

html::addJsFile(sysURL.'core/js/c1.js');
html::addJSM(G()->u2Root.'u2/auto.js');          // ESM module
html::addCssFile($Cont->modUrl.'pub/style.css');
html::$head .= '';
html::$link['https://cdn.example.com'] = ['rel'=>'preconnect'];

URL constants: sysURL (CMS core), appURL (app root), $Cont->modUrl (this module).

hee($str) — Alias für htmlspecialchars(), für HTML-Attribute verwenden.

qg::need('module.name') — lädt ein Modul lazy (z.B. qg::need('mail1')).


Content Blocks (Page / $Cont)

$Cont is a Page instance. Type c = content block, type p = page. Cast to string yields its integer ID. All Page methods apply.

$Cont->Cont('main')                              // get/create named child Cont
$Cont->Cont('fields', 'cms.cont.form1.fields2')  // typed child
$Cont->Cont('nav', 'cms.cont.nav3')->get()       // render to HTML
$Cont->get()                                     // render this Cont to HTML

Text Fields

TextPro ist die Text-Klasse. Cast zu String gibt den Text der aktuellen Sprache zurück — oder falls leer, den Text einer anderen verfügbaren Sprache (Fallback).

$T = $Cont->Text('field_name');   // TextPro object
$T = $Cont->Title();              // TextPro object für den Titel

echo $T;                          // aktuell Sprache, mit Fallback auf andere Sprachen
$T->get($lang)->get()            // Text einer bestimmten Sprache (kein Fallback)
$T->getTranslated($lang)->get()  // Text mit Fallback-Logik
$T->set($value)                  // Text der aktuellen Sprache setzen
$T->set($value, $lang)           // Text einer bestimmten Sprache setzen

// Multilingual defaults beim Init:
$Cont->Text('main', 'de', 'Standardtext');
$Cont->Text('main', 'en', 'Default text');

cms_text($Cont, $name, $options) — preferred render helper, handles edit mode automatically:

<?=cms_text($Cont, 'main')?>
<?=cms_text($Cont, 'title')?>   // special: uses $Cont->Title() instead of Text()

// Options:
<?=cms_text($Cont, 'main', [
    'tag'     => 'h2',           // wrapper tag (default: 'div')
    'style'   => 'color:red',
    'initial' => 'Default text', // set if empty (array per lang or TextPro also accepted)
    'if'      => true,           // suppress output when empty (non-edit mode only)
])?>

// input / textarea variants:
<?=cms_text($Cont, 'main', ['tag'=>'input'])?>     // 
<?=cms_text($Cont, 'main', ['tag'=>'textarea'])?>  // ...

Settings (SET)

SET ist ein settingArray — ein lazy, hierarchischer Key-Value-Store, persistiert in DB. Zugriff auf einen nicht-existierenden Key erstellt ihn automatisch.

$Cont->SET['key']->v                      // raw value (string)
$Cont->SET['key']->get()                  // typed value (bool/int/float/string)
$Cont->SET['key'] = 'value';              // setzt Default-Wert (persistiert)
$Cont->SET->has('key')                    // existence check (ohne auto-create)
$Cont->SET->make('key', 'default')        // get-or-create mit Default

// Typen
$Cont->SET['flag']->setType('bool')->v    // 'string' | 'bool' | 'float' | 'int'
(int)$Cont->SET['cols']->v               // manueller cast

// Editor-UI
$Cont->SET['type']->setHandler('select')->setOptions('opt1', 'opt2')
// getHandler() gibt bei bool→'checkbox', int/float→'number' zurück

// User-spezifischer Wert (überschreibt Default pro User/Session)
$Cont->SET['key']->custom(true)          // als user-overridable markieren
$Cont->SET['key']->setUser('value')      // User-Wert setzen

// Serialisierung
$Cont->SET->getJson()                    // ganzer Baum als JSON
$Cont->SET->get()                        // ganzer Baum als PHP-Array

// Nested
$Cont->SET['inputs']['input1']           // auto-create verschachtelte Keys
foreach ($Cont->SET as $key => $S) { }  // iterierbar

$Cont->SET = Settings des aktuellen Conts. G()->SET = globaler Settings-Root. G()->SET['cms']['pages'][$id] = Settings einer bestimmten Page.


Edit Mode

if ($Cont->edit) {
    html::addJsFile($Cont->modUrl.'pub/edit.js');
}

Use $Cont->edit to load editor JS or show admin warnings. cms_text() handles contenteditable automatically.


Pages & URLs

Page()                           // current rendered page
Page(2)                          // by ID — returns 404-page with is()===false if not found
Page()->is()                     // false if ID didn't exist in DB
Page()->url()                    // SEO URL (current lang)
Page()->url($lang)               // SEO URL in specific language
Page()->Title()                  // TextPro object (cast to string = title)
Page()->Title($lang)             // title string for a language
Page()->Parent()                 // direct parent (false if root)
Page()->Parent($level)           // ancestor at path index $level (0 = root)
Page()->Children('readable')     // child pages, lazy-loaded
Page()->Children(['type'=>'c'])  // filter: 'p'=pages, 'c'=conts, '*'=all
Page()->Conts()                  // content-type children only
Page()->in($otherPage)           // true if $otherPage is this page or an ancestor of it
Page()->isOnline()               // checks online_start/online_end timestamps
Page()->isReadable()             // access > 0 AND isOnline() (or edit mode)
Page()->access()                 // 0=none 1=read 2=edit 3=admin
Page()->firstchild()             // first child
Page()->nextSibling()            // next sibling
Page()->previousSibling()        // previous sibling
Page()->Bough($filter)           // flat array of this page + all descendants

$Page->Page                      // nearest type='p' ancestor (own page for a Cont)
$Page->id                        // integer ID — same as (int)$Page / (string)$Page
$Page->vs['module']              // module string, e.g. 'cms.cont.text'
$Page->vs['type']                // 'p' = page, 'c' = content block
$Page->vs['visible']             // visibility flag
$Page->edit                      // true if current user is in edit mode

$Cont->url()                     // URL posting back to this Cont
cms_url($id)                     // page ID → URL string
cms_link($Page)                  // renders  with cmsActive/cmsInside classes

$Page->Page walks up to the nearest type=p ancestor — use when you have a Cont and need its owning page. Page() = currently rendered page (can differ in sub-renders).


Files / Media

$F = $Cont->File('logo');
$F->url().'/h-200/w-300/max/'.urlencode($F->name())  // resized: fit
$F->url().'/h-200/w-300/'.urlencode($F->name())      // resized: crop
$F->exists()                                          // check if file is actually uploaded
$Cont->FileHas('logo')                                // returns File or false (no auto-create)

// Inherit file from ancestor pages
cms_parentFile('logo')              // walks up tree until file found
cms_parentFile('logo', $Cont)       // start from specific Cont

// Language-specific file (looks for 'name de', 'name en', etc.)
cms_file_lang('banner')             // returns best match for current language
cms_file_lang('banner', $Cont, $lang)

Global CMS Helper Functions

// Link helpers
cms_link($Cont)                  // full  with title + active classes
cms_link_attributes($Cont)       // just the attributes string (href, class, aria-current)

// URL resolver — handles page IDs, external URLs, bare domains
cms_url(42)                      // page ID → URL; returns false if page missing
cms_url('example.com')           // → 'http://example.com'
cms_url('https://example.com')   // → 'https://example.com'
// $return ref gets ['target'=>'_self'/'_blank', 'Page'=>$Page]

Forms

Required hidden fields:

         // honeypot
 // CSRF
       // routing

Localisation (L)

L()                        // current language code ('de', 'en', …)
L('Translate me')          // UI-String übersetzen (auto-insert in DB wenn nicht vorhanden)
L('Hello ###1###', $name)  // mit Platzhaltern

L::$all   // ['de'=>'de', 'en'=>'en', …] — alle aktiven Sprachen
L::all()  // gleich wie L::$all (Methode)

Sprache wird bestimmt aus: URL-Prefix (/de/…), ?changeLanguage=de, User-Session, oder Browser-Accept-Language als Fallback.

Language-switcher Pattern: iterate L::all(), build $Page->url($l) per lang, preserve $_GET params (skip changeLanguage, cmspid), set hreflang + aria-current=page.


Skeletons

Layout file

[PHP: namespace + assets]
[PHP: logic]
<div id=container>
  <header>  <?=Page(2)->Cont('nav', 'cms.cont.nav3')->get()?>  </header>
  <main>    <?=$Cont->Cont('main')->get()?>                    </main>
  <footer>  <?=Page(2)->Cont('foot')->get()?>                  </footer>
</div>

Page(1) = root (invisible), Page(2) = start page.

Content module

edit) { html::addJsFile($Cont->modUrl.'pub/edit.js'); }
$Cont->SET->make('my_setting', 'default');
$value = $Cont->SET['my_setting']->v;
?>

    <?=cms_text($Cont, 'main')?>

Common Module Types

Module string Purpose
cms.cont.nav3 Navigation tree
cms.cont.lang.choose2 Language switcher
cms.cont.text Rich-text block
cms.cont.section3 Section container
cms.cont.form1.fields2 Form field collection
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment