TYPO3

From flux page templates to backend layouts and PAGEVIEW

7 min read

Goodbye flux, hello PAGEVIEW

Hi folks,

For many years my own site hoja.in ran on TYPO3 7.6 with flux, fluidpages and vhs. It was a one-page portfolio and flux was the hero of that time: you wrote one Fluid file, and the same file described the backend grid, the form fields and the frontend markup. When I moved the site to a fresh TYPO3 14.3 Composer project this year, I decided not to carry flux, fluidpages and vhs along. The core has everything a page template needs now: backend layouts, site sets and the PAGEVIEW content object.

This post is about that one piece of the move: how each flux page template became a backend layout plus a PAGEVIEW template, and what I did with the values already stored on the pages.

Problems I faced

A flux page template is a normal Fluid file with a special Configuration section. Here is a shortened version of the old Main.html from my 2015 provider extension:

<f:layout name="Page" />

<f:section name="Configuration">
    <flux:form id="main" />
    <flux:grid>
        <flux:grid.row>
            <flux:grid.column colPos="0" colspan="3" name="main" />
        </flux:grid.row>
    </flux:grid>
</f:section>

<f:section name="Main">
    <v:page.menu levels="1" as="sections">
        <f:for each="{sections}" as="section">
            <section id="...">
                <v:content.render pageUid="{section.uid}" />
            </section>
        </f:for>
    </v:page.menu>
</f:section>

Three things are hidden in there, and all three need a new home:

  1. The backend grid (flux:grid) is what the page module showed to editors.
  2. The template selection was not stored in backend_layout the normal way. Flux wrote fluidpages__fluidpages into pages.backend_layout and the real choice into its own fields tx_fed_page_controller_action and tx_fed_page_controller_action_sub, with values like Harvisoft.Templates->main.
  3. The rendering used vhs ViewHelpers for menus and content.

If you just remove flux, the page module shows an empty layout, the frontend has no template and, worse, the next database compare offers to drop the tx_fed_* columns. After that the information which page used which template is gone.

Solution

Step 1: Make an inventory before you remove anything

I booted the old 7.6 site in a separate DDEV project with the live dump, so I could query it freely. One query shows which template each page really uses:

SELECT uid, pid, title, backend_layout, backend_layout_next_level,
       tx_fed_page_controller_action, tx_fed_page_controller_action_sub
FROM pages
WHERE deleted = 0 AND tx_fed_page_controller_action <> ''
ORDER BY uid;

On hoja.in the answer was short. The root page and the “About” page used Harvisoft.Templates->main, two hidden helper pages had ->main or ->inner, and the Inner template had an empty Main section anyway. So two flux templates became one backend layout. On a bigger site you will likely get a longer list; write down one target backend layout per template that is really used, and drop templates nobody selects.

Check the content too. Flux stored content of nested grids with its own colPos value 18181 and the tx_flux_parent and tx_flux_column fields. On my site only two records lived there: a carousel that never showed up on the live site (my smoke test baseline proved it) and one text element. I did not import them. On your site this may be the biggest part of the job, so look before you plan.

Step 2: One backend layout per template, in the site set

Since TYPO3 13.1 a site set can ship its own page.tsconfig, so the backend layout lives next to the TypoScript of the same set. This is the real definition from the new sitepackage (Configuration/Sets/Sitepackage/page.tsconfig), slightly shortened:

mod.web_layout.BackendLayouts {
  Default {
    title = One-page section
    icon = EXT:sitepackage/Resources/Public/Icons/Extension.svg
    config {
      backend_layout {
        colCount = 1
        rowCount = 1
        rows {
          1 {
            columns {
              1 {
                name = Main content
                colPos = 0
                identifier = main
              }
            }
          }
        }
      }
    }
  }
}

The identifier of the column is important for v14. It becomes the name of the content area in the template, so give it a short and stable name like main, sidebar or stage. Keep the colPos numbers of the old flux grid where you can, then existing content lands in the right column without any update.

Step 3: Let PAGEVIEW find the template

PAGEVIEW arrived in TYPO3 13.1. It resolves the template from the backend layout of the current page: a page with the layout pagets__Default gets Pages/Default.html from the configured path. Layouts and partials are looked up in Layouts/ and Partials/ under the same path. The TypoScript of the set is small:

page = PAGE
page {
  10 = PAGEVIEW
  10 {
    paths.10 = EXT:sitepackage/Resources/Private/PageView/

    dataProcessing {
      10 = menu
      10 {
        entryLevel = 0
        levels = 1
        as = sections
      }
    }
  }
}

# Content of one section page, rendered with that page record as data
lib.sectionContent = CONTENT
lib.sectionContent {
  table = tt_content
  select {
    pidInList.field = uid
    where = {#colPos} = 0
    orderBy = sorting
  }
}

The menu processor replaced v:page.menu, and the small CONTENT object replaced v:content.render pageUid="…", because a one-page site renders the content of its subpages. For the content of the current page, TYPO3 14.2 added the {content} variable (one object per column identifier) and the f:render.contentArea ViewHelper. The new Pages/Default.html looks like this:

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:layout name="Page" />

<f:section name="Main">
    <f:render.contentArea contentArea="{content.main}" />

    <f:for each="{sections}" as="section">
        <section id="{section.title -> f:replace(search: ' ', replace: '')}">
            <f:cObject typoscriptObjectPath="lib.sectionContent" data="{section.data}" />
        </section>
    </f:for>
</f:section>
</html>

If you are still on v13, {content} and f:render.contentArea are not there yet; render the column with a CONTENT object like above. Also note that Fluid 5 in v14 prefers Default.fluid.html if both files exist, and falls back to Default.html. Pick one style per folder, or you will edit the wrong file one day. Ask me how I know 🙂

Step 4: Migrate the stored values

Now the pages must point to the new layout. For an in-place upgrade, run this before you uninstall flux and before any database compare removes the tx_fed_* columns. It maps the flux selection to the new identifiers, for the page itself and for its subpages:

UPDATE pages
SET backend_layout = CASE tx_fed_page_controller_action
        WHEN 'Harvisoft.Templates->main' THEN 'pagets__Default'
        WHEN 'Harvisoft.Templates->inner' THEN 'pagets__Default'
        ELSE backend_layout
    END,
    backend_layout_next_level = CASE tx_fed_page_controller_action_sub
        WHEN 'Harvisoft.Templates->main' THEN 'pagets__Default'
        WHEN 'Harvisoft.Templates->inner' THEN 'pagets__Default'
        ELSE backend_layout_next_level
    END
WHERE backend_layout = 'fluidpages__fluidpages'
   OR backend_layout_next_level = 'fluidpages__fluidpages';

Take a database dump first, and compare the counts before and after. Pages that still carry fluidpages__fluidpages afterwards need a decision by hand.

hoja.in did not get an in-place upgrade. I imported the content into a fresh v14 database with a script, so the mapping is one line in that script: the root page gets pagets__Default in both fields and every subpage inherits it through backend_layout_next_level. Same idea, fewer moving parts.

'backend_layout' => (int)$p['uid'] === ROOT_PAGE ? 'pagets__Default' : '',
'backend_layout_next_level' => (int)$p['uid'] === ROOT_PAGE ? 'pagets__Default' : '',

Step 5: Check it with real pages

Open the page module for a few pages of each old template and see if the columns and content are where editors expect them. Then compare the frontend with the old site. For hoja.in I compared status codes, titles and sizes of all section pages against a baseline captured from the 7.6 copy; that is a separate post. The custom field tx_templates_hide_from_scrollmenu from the old ext_tables.sql also moved, now as tx_sitepackage_hide_from_scrollmenu in a TCA override, without any SQL file, because v13 creates columns from TCA.

What I would tell my 2015 self

Flux was a smart idea when backend layouts were clumsy. Today the core split is cleaner: the backend layout says which columns exist, PAGEVIEW picks the template by the layout name, and the template only renders. The migration itself is not hard. The real work is the inventory in step 1 and the nested flux content. Do those first and the rest is a one-evening job.

If this post helped you to get rid of your last flux page, I will be really happy. Cheers!

Further reading

Leave a comment

Your email address will not be published.