Wrangler Pages: The Core of Textstem Content

In Textstem, a Wrangler Page is the primary unit of content delivery. Unlike traditional static pages, a Wrangler Page is a dynamic, highly configurable entity that serves as a container for modular components and metadata.

Key Concepts

  • Modular Architecture: A Wrangler Page doesn't have a single fixed "body" field. Instead, it is composed of multiple Wrangler Components (e.g., Hero blocks, Prose sections, Image galleries) assigned to specific Slots (like hero, main, or aside).
  • Template-Driven: Each page is associated with a Template. The template defines the available slots and the overall layout. Textstem looks for corresponding Blade views in your application (resources/views/wrangler/pages/{template}.blade.php) or falls back to package defaults.
  • URL Management: Wrangler Pages feature a flexible URL system. They can have a fixed URL, a slug-based URL, or even "greedy" matching for nested content patterns.
  • AI-Enhanced: Pages are tightly integrated with OpenAI. You can generate SEO metadata (titles, descriptions, keywords) and JSON-LD structured data directly from the page's component content.

Anatomy of a Wrangler Page

When a developer interacts with a WranglerPage model, they are working with:

  1. Metadata: Standard SEO fields (Title, Slug, Description, Keywords) plus a flexible meta JSON field for custom attributes.
  2. Components: A collection of WranglerComponent instances. Each component has its own type, data, and position within a slot.
  3. Hierarchy: Support for parent_id allows for nested page structures and breadcrumb generation.
  4. Snapshots: Textstem automatically creates "Snapshots" (versions) of pages whenever they are updated, ensuring you can track changes over time.

Rendering Pipeline

Rendering a Wrangler Page involves the PageRenderer service, which:

  1. Identifies the correct Template.
  2. Loads and instantiates all enabled Components for that page.
  3. Organizes components into their respective Slots.
  4. Processes Shortcodes and Link Parsers within the component content.
  5. Injects the resulting HTML into the final Blade view.

Developer Usage

For headless or hybrid setups, Wrangler Pages are accessible via the API:

GET /api/v1/public/pages/by-url?url=/about&include_components=true

This returns a structured JSON representation of the page, its SEO metadata, and all attached components, allowing for easy integration with frontend frameworks like Vue, React, or Inertia.js.

Page Access

When creating pages, administrators can specify access to the pages. Public pages can be accessed by anyone. Private pages can only be accessed by authenticated users. Protected pages can be accessed by anyone with the page password.

Protected Pages

When attempting to access a protected page, the PageLoader class checks the request for a password. If there is no password, then the session is checked to see if access has been granted for this page.

To access a protected page, either attach the page password to the url with the parameter "password", or of you wish to prompt users to enter a password, then create a route named 'page_login' and users will be redirected there. For example, you could create routes like this:

Route::get('/page_login/{page}', 'PageAccess@show')->name('page_login');
Route::post('/page_login', 'PageAccess@process')->name('check_access');

And the corresponding controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\View\View;
use Textstem\Models\WranglerPage;
use Illuminate\Validation\ValidationException;

class PageAccess extends Controller
{
    /**
     * Show the request access form
     *   $request
     *   $page
     *  
     */
    public function show(Request $request, WranglerPage $page): View
    {
        return view('requestaccess', ['page' => $page]);
    }

    /**
     * Process the request access form
     *   $request
     * @return \Illuminate\Http\RedirectResponse
     *  
     */
    public function process(Request $request)
    {
        $id = $request->get('id', '');
        $password = $request->get('password', '');
        $page = WranglerPage::where('id', $id)->where('password', $password)->first();
        if ($page) {
            $key = 'page_access_' . $page->id;
            $request->session()->put($key, 1);
            return redirect($page->url);
        } else {
            throw ValidationException::withMessages(['url' => 'This passcode is incorrect']);
        }
    }
}
esc