Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Sulu Content Extra Bundle Laravel Package

alengo/sulu-content-extra-bundle

Extends Sulu CMS 3 Pages, Articles and Snippets with a configurable Additional Data tab, built-in entities, and field mapping to localized or unlocalized content. Also adds navigation sourceLink/sourceUuid markers and sortable template groups, with zero-config entity registration.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Installation:

    composer require alengo/sulu-content-extra-bundle
    

    Register the bundle in config/bundles.php:

    Alengo\SuluContentExtraBundle\AlengoContentExtraBundle::class => ['all' => true],
    
  2. Define a Basic Form: Create config/forms/page_additional_data.xml:

    <form xmlns="http://schemas.sulu.io/template/template">
        <key>page_additional_data</key>
        <properties>
            <property name="template_theme" type="select" mandatory="false">
                <meta>
                    <title lang="en">Theme</title>
                </meta>
                <params>
                    <param name="values" type="collection">
                        <param name="default" type="collection">
                            <param name="title" value="Default"/>
                            <param name="name" value="default"/>
                        </param>
                    </param>
                </params>
            </property>
        </properties>
    </form>
    
  3. Configure Field Mapping (optional, uses defaults if omitted):

    # config/packages/alengo_content_extra.yaml
    alengo_content_extra:
        page:
            form_key: page_additional_data
            unlocalized_keys: [template_theme]
            localized_keys: [notes]  # Add this if you create a 'notes' field later
    
  4. Restart Sulu:

    php bin/console cache:clear
    
  5. Access the New Tab: Edit a Page or Article in the Sulu admin panel. A new "Additional Data" tab will appear with your form fields.

Version note (1.7.1): the Article and Snippet Options tab now respects Sulu's publish permission — editors without publish rights see the tab in a read-only/restricted state rather than being able to write into it. Make sure your role setup grants publish permission to whoever is expected to maintain this data.


First Use Case: Adding a Global Theme Selector

  1. Create a Form Field: Add a select field for template_theme (as shown above) with options like default, dark, light.

  2. Configure Localization: In alengo_content_extra.yaml, mark template_theme as unlocalized (shared across all languages):

    alengo_content_extra:
        page:
            unlocalized_keys: [template_theme]
    
  3. Use the Data in Templates: Access the value in Twig via the additionalData object:

    {% if additionalData.template_theme == 'dark' %}
        <body class="dark-theme">
    {% endif %}
    

Implementation Patterns

Core Workflows

1. Adding Localized vs. Unlocalized Fields

  • Unlocalized Fields (e.g., template_theme): Stored in the base Page/Article entity (shared across all languages). Configure via unlocalized_keys in YAML.

  • Localized Fields (e.g., notes): Stored in the dimension-specific PageDimensionContent/ArticleDimensionContent. Configure via localized_keys in YAML.

Example Workflow:

  1. Add a notes field to your form:
    <property name="notes" type="textarea" mandatory="false"/>
    
  2. Configure it as localized:
    alengo_content_extra:
        page:
            localized_keys: [notes]
    
  3. Access in Twig:
    {{ additionalData.notes|default('No notes') }}
    

2. Extending Navigation Links

Use the sourceLink and sourceUuid markers for link-type pages:

  1. Template Usage:
    {% if navlink.sourceLink %}
        <a href="{{ navlink.sourceUuid }}" class="external-link">View Original</a>
    {% endif %}
    
  2. Dynamic Logic:
    {% if navlink.sourceUuid == 'some-uuid' %}
        <div class="promo-banner">Special Offer!</div>
    {% endif %}
    

3. Custom Entity Integration

Override the default entities in alengo_content_extra.yaml:

alengo_content_extra:
    page:
        page_class: App\Entity\CustomPage
        entity_class: App\Entity\CustomPageDimensionContent

Ensure your custom entities extend the bundle’s base classes:

// src/Entity/CustomPage.php
namespace App\Entity;

use Alengo\SuluContentExtraBundle\Entity\Page as BasePage;

class CustomPage extends BasePage
{
    // Add custom fields/methods
}

4. Sorting Admin Tabs

Leverage translation order to reorder admin tabs:

  1. Define sulu_admin.template_group.* keys in translations:
    # translations/admin+intl-icu.en.yaml
    sulu_admin:
        template_group:
            page_additional_data: 50  # Higher = appears later
            seo: 10
    
  2. The SortedGroupProvider will order tabs by these values.

5. Working With the Restricted Options Tab (Articles & Snippets)

Since 1.7.1 the extra-data tab on Articles and Snippets is permission-aware:

  • Editors with publish permission on the context (sulu.article.publish, sulu.snippet.publish) can edit and save the additional fields as before.
  • Editors without publish permission get a restricted tab: treat the stored extra data as read-only metadata for them.

Practical consequences in your workflow:

  • Content review stage: a reviewer role can inspect the extra metadata (e.g. sourceLink, template_theme) without being able to alter it — good for audit-style fields.
  • Automation/seeder scripts: anything writing these fields through the admin UI impersonating a non-publishing user will silently not persist. Use a user with publish rights, or write via Doctrine/API.
  • Field design: if a field is genuinely editorial (SEO-ish copy, notes) put it in the regular content tab; keep in the extra tab only values that should follow publish rights.
# config/packages/sulu_security.yaml (illustrative)
security:
    role_hierarchy:
        ROLE_SULU_EDITOR: [ROLE_SULU_AUTHOR]

Integration Tips

Forms Integration

  • Reuse Existing Forms: The bundle doesn’t provide default forms—define your own in config/forms/ and reference them via form_key in YAML.
  • Field Validation: Use Sulu’s built-in validation (e.g., mandatory="true") or add custom validation via Symfony’s constraints in your form XML:
    <property name="campaign_id" type="text" mandatory="true">
        <meta>
            <constraints>
                <constraint name="NotBlank" />
                <constraint name="Regex" params="{{ pattern: '/^[A-Z0-9_-]+$/' }}" />
            </constraints>
        </meta>
    </property>
    

Template Access

  • Pages/Articles:
    {{ additionalData.template_theme }}
    {{ additionalData.notes|default }}
    
  • Navigation Links:
    {% if navlink.sourceLink %}
        {{ navlink.sourceUuid }}
    {% endif %}
    

API/Data Export

Access additional data via the Sulu API or Doctrine:

// Get additional data for a Page
$page = $pageRepository->find($uuid);
$dimensionContent = $page->getDimensionContent($locale);
$additionalData = $dimensionContent->getAdditionalData(); // Returns array

Gotchas and Tips

Pitfalls

  1. Publish Permission Hides Your Fields (1.7.1+):

    • Issue: On Articles and Snippets, a user without publish permission can no longer edit the extra-data tab. After upgrading, "the tab stopped working" reports are almost always a role/permission regression, not a bundle bug.
    • Fix: Verify the role actually grants publish on the relevant context:
      php bin/console sulu:security:role:downgrade <role>   # or inspect roles in the admin UI
      
      Grant sulu.article.publish / sulu.snippet.publish (or the equivalent role) to the editors who must maintain this data.
  2. Proxy Generation in Development:

    • Issue: The bundle replaces Sulu’s entity classes at container build time, which can cause proxy generation issues in development.
    • Fix: Ensure auto_generate_proxy_classes: true in dev environments and false in prod (as recommended in the docs). Run cache:warmup after changes:
      php bin/console cache:warmup --env=prod
      
  3. Form Key Conflicts:

    • Issue: If page_additional_data or article_additional_data already exists in your project, the bundle’s auto-registration will conflict.
    • Fix: Rename your existing form key or override the bundle’s form_key in YAML:
      alengo_content_extra:
          page:
              form_key: custom_page_extra_data
      
  4. Localization Mismatches:

    • Issue: Fields marked as unlocalized in YAML but accessed as localized (or vice versa) will return null.
    • Fix: Double-check your YAML configuration and test with:
      {{ dump(additionalData) }}
      
  5. Navigation Link Markers Not Appearing:

    • Issue: sourceLink/sourceUuid may not show up if the page isn’t of "link" type.
    • Fix: Ensure the page’s type is set to link in Sulu’s admin panel. Verify the NavigationLinkEnhancer is active (it’s auto-registered by default).
  6. Doctrine Schema Updates:

    • Issue: The bundle adds a additionalData JSON column to pa_page_dimension_contents and ar_article_dimension_contents. If your database is already customized, migrations may fail.
    • Fix: Backup your database before installing. If needed, manually add the column:
      ALTER TABLE pa_page_dimension_contents ADD COLUMN additionalData JSON;
      

Debugging Tips

  1. Check Entity Overrides: Verify the bundle’s entities are being used:

    php bin/console debug:container Alengo\SuluContentExtraBundle\Entity\Page
    

    Should return the bundle’s Page class, not Sulu’s.

  2. Form Registration: Ensure your form is registered:

    php bin/console debug:form page_additional_data
    

    If not found, check for XML syntax errors or missing config/forms/ directory.

  3. Additional Data Storage: Inspect the database directly to verify data is stored correctly:

    SELECT additionalData FROM pa_page_dimension_contents WHERE uuid = 'your-page-uuid';
    
  4. Permission-Sensitive Saving (Articles/Snippets): When data "doesn't save" only for some users, check the security context of the save request before digging into the bundle:

    php bin/console debug:container --parameter=sulu_security.system
    

    Then confirm the acting user's role grants publish for that system; a 200 response with unchanged data is the typical symptom.

  5. Template Debugging: Dump the additionalData object in Twig to inspect its structure:

    {{ dump(additionalData) }}
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource