Skip to the content
Documentation

Help center

Help for Developers

Build on it: extensions, templates, settings, the APIs, the command line and upgrades.

5997 pages

Start here

Read these pages in this order.

  1. Develop with Git: grow from user to developer The recommended way to build websites with Exponential. Install the kernel and every se7enxweb extension as...
  2. Extension development: the book (start here) A complete book on growing from user to builder, extension developer and theme developer with Exponential:...
  3. Add a database table A migration that creates a table of the site's own on every engine from a share/db_schema.dba definition, w...
  4. AI, agents and agentic: start here The landing page for people who use AI with Exponential and for people who build agents and agentic workflo...
  5. Ecosystem: start here The landing page for the Exponential Ecosystem - the greater Exponential world within the PHP stack, its us...
  6. Integrators: start here The landing page for system integrators and integration developers who connect Exponential with CRM, ERP, P...

Good to know

  • Migrations

    The start of everything about migrations in Exponential 6.0.15 - schema and content changes written once as files under version control and applied to every installation of a site in the same order, with a caption…

  • Migrations: a first PHP migration, step by step

    In this tutorial you write two PHP migrations for a site: one creates a table exp_news_note with a first row, the other publishes a folder "News" below the content root and notes it in the table. You check them, look at…

  • Add a database table

    A migration that creates a table of the site's own on every engine from a share/db_schema.dba definition, writes a first row, and drops it again in down() - with the dry run, the run, the checks and the rollback.

  • Testing migrations: check, simulate, diff and CI

    Four layers of tests for migrations - the lint without a database, the dry run, the simulation with a diff on PostgreSQL or SQLite, and a PHPUnit test on a throwaway SQLite database - and how to run them in CI.

  • Deploying with migrations: CI/CD and GitHub Actions

    Run migrations as part of every deploy - the order of the steps, a complete GitHub Actions workflow that checks pull requests and deploys over SSH, the exit codes a pipeline acts on, the installation lock, and runs…

  • Team workflow with git: branches, conflicts and order

    How several developers write migrations on branches without stepping on each other - timestamps and order, requires for real dependencies, two migrations with the same timestamp, rebasing, renaming only before a merge…

Search by topic

Each button lists the pages of one topic for Developers.

What changed in the source code most recently: the main branch of se7enxweb/exponential, read from its public feed by this server.

Show merge commits too

  1. Updated: The trash list wraps long text and scrolls instead of cuttin… (opens in a new window) ·
    Message
    Updated: The trash list wraps long text and scrolls instead of cutting off its Restore column
    
    A long unbroken name in the Date trashed column (a script name such as
    cli hmeb_trash_help_ebooks_of_untranslated_manual_editions.php) made the
    table wider than its card, which cut off the last column with the Restore
    buttons. Cells now break such text anywhere and the list scrolls sideways
    if it still does not fit, in both admin designs.
    821ea9bd2b
  2. Updated: URL map files read .ods, keep temporary redirects and read A… (opens in a new window) ·
    Message
    Updated: URL map files read .ods, keep temporary redirects and read Apache Redirect as a prefix
    
    Follow-ups of the URL map files, as decided after their first part.
    
    - Temporary redirects keep their status: the kinds redirect-temporary and
      wildcard-forward-temporary (302), and an optional seventh column status for
      308, 303 or 307 of the same kind; a file without the column reads as before.
      The Apache and nginx readers read 302, 303 and 307 as temporary kinds with
      their status instead of 301 with a note, and the writers write each row's
      status
    - Apache Redirect /old /new is read as Apache answers it: the path as a
      redirect plus the wildcard /old/* -> /new/{1} for everything below it
    - .ods is read too (expSpreadsheetReader::readOdsFile(), expUrlMapOdsReader):
      repeated rows and columns, spaces, tabs and line breaks in cells; a ZIP file
      is recognized as .ods by its mimetype
    - The spreadsheet classes put their temporary files in the installation's
      cache directory (var//cache/spreadsheet, else var/tmp), never the
      system's temporary directory, so they work under open_basedir
    - Specification (English and German), changelog, tests: an .ods saved by
      LibreOffice, .ods cells of every kind, every redirect status through every
      format and back
    6e7d7bb5ca
  3. Added: Readers and writers for URL map files of the URL translator an… (opens in a new window) ·
    Message
    Added: Readers and writers for URL map files of the URL translator and wildcards
    
    A URL map lists old addresses and where each goes (old_url, new_url, kind,
    siteaccess, language, note), as the first step of importing and exporting
    global URL aliases and URL wildcards. This adds the file part only; nothing
    here touches the database.
    
    - expUrlMapRow: one mapping, with the kinds redirect, alias,
      wildcard-forward, wildcard-direct (or empty for automatic), the
      normalization of old and new addresses (scheme and host, slashes, dot
      segments, trailing slash, percent-encoding, node references) and the
      problems that keep a row from being imported, with its line
    - Readers for CSV (delimiter sniffing, byte order mark, UTF-16 and
      Windows-1252 input, quoted multi-line fields, English and German column
      names), JSON (exp-url-map/1), .xlsx, Apache (Redirect, RedirectMatch,
      RewriteRule with host and query conditions) and nginx (rewrite, return
      in exact and regular expression locations, map); simple regular
      expressions become wildcards, other rules rows that are not importable
    - Writers for CSV, JSON, .xlsx, .ods, Apache and nginx; internal kinds and
      node targets go into #exp: comments that the readers read back
    - expSpreadsheetReader and expSpreadsheetWriter: the table part of .xlsx
      (read and write) and .ods (write) with ZipArchive, no new dependency
    - expUrlMapFormats chooses reader and writer by name, extension or content
    - The specification of the file formats in English and German, and the
      6.0.15 changelog entry
    - DB-less tests with fixtures for every format, round trips through every
      writer, 10,000 rows, and a LibreOffice conversion where it is installed
    fea55e693c
  4. Updated: A character reference shown as text in a code block is no lo… (opens in a new window) ·
    Message
    Updated: A character reference shown as text in a code block is no longer decoded twice
    
    The online editor's literal handler decoded the named references first and the numeric ones after, so
    "Ӓ", the text "Ӓ" in a code block about character references, became "Ӓ" and then
    the character itself. The numeric references are decoded first now; an escaped one stays text.
    f33289c6c6
  5. Updated: A code block keeps its line breaks, tabs and indentation whe… (opens in a new window) ·
    Message
    Updated: A code block keeps its line breaks, tabs and indentation when the online editor saves it
    
    eZXMLInputParser::process() removes every line break of HTML input (they are no content in HTML) and makes
    every tab a space, before it parses. Inside , which becomes , the white space is the content:
    every code block was stored as one long line, its indentation left as runs of spaces in the middle of it.
    This hit every block an editor saved in the online editor and every page an import wrote through it.
    
    preformattedWhitespace() now writes the line breaks of a  block (LF, CRLF, CR) as , which the
    literal handler stores as line breaks, and its tabs as 	, which stay tabs. The line break right after the
    opening tag is no content, as in HTML. Input without  is unchanged, and text outside code blocks loses
    its line breaks as before.
    
    Tests without a database: lines and indentation, empty lines, tabs, leading spaces, CRLF and CR,  from
    the editor, entities, the block's class, text outside code blocks, and a block opened in the editor and
    saved again unchanged.
    6bb303b456
  6. Updated: cond gives its last parameter as given without template comp… (opens in a new window) ·
    Message
    Updated: cond gives its last parameter as given without template compilation, also an empty array, hash, 0, '' or false
    
    The processing interpreter took the value for "no condition holds" only when it was true, so cond( ..., array() )
    gave no value with TemplateCompile=disabled, while a compiled template gave the empty list. The missing value
    contains everything, which put every link of the admin4 left menu and the Setup overview into the first group
    without GroupLinks_[] (pull request #357). The same form is used in about 45 places of the admin, admin4
    and standard designs; they now get the empty value in both modes.
    
    - The parity test renders cond with an empty array, empty hash, 0, false, an empty string and later clauses,
      processed and compiled, with literal and variable input (12 of its new cases failed processed before).
    - The template language page (English and German) describes cond's last parameter; the 6.0.15 changelog names
      the menu fix and the engine change.
    f845c2ca02
  7. Updated: The translation wave pages count the German example descript… (opens in a new window) ·
    Message
    Updated: The translation wave pages count the German example descriptions added today
    
    The German description catalogue of the generated examples grew by 131
    entries, so the help tables now hold 117233 words instead of 112725; the
    English and German translation wave pages are regenerated with
    ./console exp:i18n waves, as their test asks.
    7666a06bd7
  8. Renamed: The help pages' script lives in design/standard, so the doc … (opens in a new window) ·
    Message
    Renamed: The help pages' script lives in design/standard, so the doc site's filter and search work again
    
    The contents page's filter box, the dashboard's instant search, the lazy
    folders of the viewer's tree, the copy and print buttons and the reading
    position all come from exphelp.js, which the help templates load with
    ezscript_require. The script existed only in design/admin and
    design/admin4, so on a front-end siteaccess such as the doc site
    (designs doc/doc2025, then standard) the packer found nothing and left
    it out without a word: the filter stayed hidden and typing in a search
    field did nothing. Signed in to the admin, where the file was found,
    everything worked.
    
    The two identical copies become one, design/standard/javascript/exphelp.js,
    which every design finds through the standard fallback. The tests read
    that copy and now fail if a design or an extension gets its own again;
    the test map and the file manifest follow the move.
    2a7ee17b6e
  9. Updated: German design pattern pages link to the German area pages (opens in a new window) ·
    Message
    Updated: German design pattern pages link to the German area pages
    
    The German pattern catalogue linked 104 times to area-*.md, chain.md and
    fetch-function.md, the English file names, which do not exist below
    doc/ger-de/standards/muster/ (the German pages are bereich-*.md, kette.md
    and fetch-funktion.md). Each link now names the German file the path map
    gives for that page, so the German link check no longer reports them.
    de911a1fd3
  10. Updated: The German generated examples translate every source descrip… (opens in a new window) ·
    Message
    Updated: The German generated examples translate every source description again
    
    The German example pages quoted 131 descriptions from the sources in English:
    newer INI comments (the SQLite busy wait and PRAGMA settings, the search
    suggestions, Smarty security, preview links, the review hold, migrations,
    static publishing, Verify it's you groups), new command options and the
    code_file migration step. They now have German translations in the
    description catalogue; 23 older translations whose English source had
    changed are updated in place, and the pages are regenerated with
    exp:help:examples --language=ger-de (no text left in English).
    
    The German directory name map is written sorted, one entry per line, as
    exp:help maps expects (content unchanged).
    d0a4987476

Showing 21–30 of 40 commits

Recently updated

Free documentation: use it to help yourself and others

This help is free documentation, licensed under the GNU Free Documentation License 1.3 or later. You may use it, share it and improve it:

  • Study it Read any page as often as you like, for any purpose.
  • Copy it and share it Give pages to other users, on paper or as files. Keep the copyright notice, the license notice and a copy of the license with every copy.
  • Translate, adapt and improve it Share your version under the same license, with the original notices and a note of what you changed.
  • Cite it Refer to a page by its title and its address in the help, or its file in the doc directory.
  • Contribute fixes back Use "Edit on GitHub" on any page. Changes are accepted under the same license, so they stay free for everyone.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x