[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.yml]\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n*.css linguist-vendored\n*.scss linguist-vendored\n*.js linguist-vendored\nCHANGELOG.md export-ignore\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: statamic\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n/node_modules\n/public/hot\n/public/storage\n/public/vendor/statamic\n/public/build/*\n/users/*\n/public/js\n/public/css/scratch.css\n/storage/*.key\n/storage/statamic/users\n/storage/debugbar/*\n/storage/glide/*\n/storage/antlers-language-server/*\n/storage/stillat/*\n/vendor\n.env\n.env.backup\n.phpunit.result.cache\nHomestead.json\nHomestead.yaml\nnpm-debug.log\nyarn-error.log\n\n# When in dev...\n/public/**/.meta\n.antlers.json\n"
  },
  {
    "path": ".nvmrc",
    "content": "22"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright © Statamic, LLC.\n\nPermission is hereby granted to any person obtaining a copy of this software (the “Software”) to use, copy, modify, merge, publish and/or distribute copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n1. **Do not plagiarize.** The above copyright notice and this license shall be included in all copies or substantial portions of the Software.\n\n2. **Do not use the same license on more than one project.** Each licensed copy of the Software shall be actively installed in no more than one production environment at a time.\n\n3. **Do not alter the licensing features.** Software features related to licensing shall not be altered or circumvented in any way, including (but not limited to) license validation, feature or edition restrictions, and update eligibility.\n\n4. **Follow the law.** All use of the Software shall not violate any applicable law or regulation, nor infringe the rights of any other person or entity.\n\nFailure to comply with the foregoing conditions will automatically and immediately result in termination of the permission granted hereby. This license does not include any right to receive updates to the Software or technical support. Licensees bear all risk related to the quality and\nperformance of the Software and any modifications made or obtained to it, including liability for actual and consequential harm, such as loss or corruption of data, and any necessary service, repair, or correction.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, INCLUDING SPECIAL, INCIDENTAL AND CONSEQUENTIAL DAMAGES, WHETHER IN\nAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://statamic.com/assets/branding/Statamic-Logo+Wordmark-Rad.svg\" width=\"400\" alt=\"Statamic Logo\" /></p>\n\n## Statamic Documentation\n\nThis is the source of the official [Statamic docs][docs].\n\n\n## Local Development\n\nIf you want to work on this project on your local machine, you may follow the instructions below. These instructions assume you are serving the site using [Laravel Valet](https://laravel.com/docs/valet) out of your `~/Sites` directory:\n\n1. Fork this repository\n2. Open your terminal and cd to your `~/Sites` folder\n3. Clone your fork into the `~/Sites/docs` folder, by running the following command with your username placed into the {username} slot:\n    ```\n    git clone git@github.com:{username}/docs statamic-docs\n    ```\n4. CD into the new directory you just created.\n5. Run the following commands:\n  ```\n  composer install\n  npm install\n  npm run dev\n  cp .env.example .env\n  php artisan key:generate\n  ```\n\n## Providing Feedback\n\nWe love it when people provide thoughtful feedback! Feel free to open issues on for any content you find confusing or incomplete. We are happy to consider anything you feel will make the docs and CMS better.\n\n\n## Contributing\n\nThank you for considering contributing to Statamic! Every page in the [docs site](https://statamic.dev) has a link at the bottom that will take you right to the exact content file that renders the page. Click the edit button and submit those PRs!\n\n**We simply ask that you please review the [contribution guide][contribution] before you send pull requests.**\n\n\n## Code of Conduct\n\nIn order to ensure that the Statamic community is welcoming to all and generally a rad place to belong, please review and abide by the [Code of Conduct](https://github.com/statamic/cms/wiki/Code-of-Conduct).\n\n\n## Important Links\n\n- [Statamic Main Site](https://statamic.com)\n- [Statamic Documentation][docs]\n- [Statamic CMS Repo][cms-repo] (that we maintain)\n- [Statamic Application Repo][app-repo] (that you clone)\n- [Statamic Migrator](https://github.com/statamic/migrator)\n- [Statamic Discord][discord]\n\n[docs]: https://statamic.dev/\n[discord]: https://statamic.com/discord\n[contribution]: https://github.com/statamic/cms/blob/master/CONTRIBUTING.md\n[app-repo]: https://github.com/statamic/statamic\n[cms-repo]: https://github.com/statamic/cms\n"
  },
  {
    "path": "app/Http/Controllers/Controller.php",
    "content": "<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Foundation\\Auth\\Access\\AuthorizesRequests;\nuse Illuminate\\Foundation\\Bus\\DispatchesJobs;\nuse Illuminate\\Foundation\\Validation\\ValidatesRequests;\nuse Illuminate\\Routing\\Controller as BaseController;\n\nclass Controller extends BaseController\n{\n    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;\n}\n"
  },
  {
    "path": "app/Http/Controllers/DocsMarkdownController.php",
    "content": "<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Support\\Facades\\Cache;\nuse Statamic\\Exceptions\\NotFoundHttpException;\nuse Statamic\\Facades\\Data;\n\nclass DocsMarkdownController extends Controller\n{\n    public function __invoke(string $uri)\n    {\n        $markdown = Cache::rememberForever(\"markdown.$uri\", function () use ($uri) {\n            $entry = Data::findByUri('/'.$uri);\n\n            throw_unless($entry, new NotFoundHttpException);\n\n            return collect([\n                '# '.$entry->value('title'),\n                $entry->value('intro'),\n                $entry->value('content'),\n            ])->filter()->implode(\"\\n\\n\");\n        });\n\n        $markdown = $this->appendMdExtensionToInternalLinks($markdown);\n\n        return response($markdown, 200, [\n            'Content-Type' => 'text/markdown; charset=UTF-8',\n        ]);\n    }\n\n    private function appendMdExtensionToInternalLinks(string $markdown): string\n    {\n        return preg_replace_callback(\n            '/(?<!!)\\[([^\\]]+)\\]\\(([^)]+)\\)/',\n            function ($matches) {\n                $text = $matches[1];\n                $url = $matches[2];\n\n                if ($this->shouldAppendMdExtension($url)) {\n                    $url .= '.md';\n                }\n\n                return \"[$text]($url)\";\n            },\n            $markdown\n        );\n    }\n\n    private function shouldAppendMdExtension(string $url): bool\n    {\n        if (preg_match('/^https?:\\/\\//', $url)) {\n            return false;\n        }\n\n        if (preg_match('/\\.[a-z0-9]{2,4}$/i', $url)) {\n            return false;\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/Http/Controllers/LlmsTxtController.php",
    "content": "<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Support\\Facades\\Cache;\nuse Statamic\\Facades\\Collection;\nuse Statamic\\Facades\\Entry;\n\nclass LlmsTxtController extends Controller\n{\n    public function __invoke()\n    {\n        $lines = Cache::rememberForever(\"llms.txt\", function () {\n            $tree = Collection::find('pages')->structure()->trees()->first()->tree();\n            $lines = ['# Statamic Documentation', ''];\n\n            foreach ($tree as $section) {\n                $children = $section['children'] ?? [];\n\n                if (! $children) {\n                    continue;\n                }\n\n                $sectionEntry = Entry::find($section['entry']);\n                $lines[] = '## '.$sectionEntry->value('title');\n\n                $firstChild = Entry::find($children[0]['entry']);\n                if ($firstChild && str_contains($firstChild->slug(), 'overview')) {\n                    if ($intro = $firstChild->value('intro')) {\n                        $lines[] = '> '.str_replace(\"\\n\", ' ', $intro);\n                    }\n                }\n\n                $lines[] = '';\n\n                foreach ($children as $child) {\n                    $entry = Entry::find($child['entry']);\n                    if (! $entry) {\n                        continue;\n                    }\n\n                    $url = $entry->url();\n                    if (! $url) {\n                        continue;\n                    }\n\n                    $title = $entry->value('title');\n                    $isExternal = str_starts_with($url, 'http');\n                    $href = $isExternal ? $url : url($url).'.md';\n                    $line = '- ['.$title.']('.$href.')';\n\n                    if ($intro = $entry->value('intro')) {\n                        $line .= ': '.str_replace(\"\\n\", ' ', $intro);\n                    }\n\n                    $lines[] = $line;\n                }\n\n                $lines[] = '';\n            }\n\n            return $lines;\n        });\n\n        return response(implode(\"\\n\", $lines), 200, [\n            'Content-Type' => 'text/plain; charset=UTF-8',\n        ]);\n    }\n}\n"
  },
  {
    "path": "app/Http/Middleware/CheckForMaintenanceMode.php",
    "content": "<?php\n\nnamespace App\\Http\\Middleware;\n\nuse Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode as Middleware;\n\nclass CheckForMaintenanceMode extends Middleware\n{\n    /**\n     * The URIs that should be reachable while maintenance mode is enabled.\n     *\n     * @var array\n     */\n    protected $except = [\n        //\n    ];\n}\n"
  },
  {
    "path": "app/Markdown/Hint/Hint.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Hint;\n\nuse League\\CommonMark\\Node\\Block\\AbstractBlock;\nuse League\\CommonMark\\Node\\StringContainerInterface;\n\nclass Hint extends AbstractBlock implements StringContainerInterface\n{\n    private ?string $header = 'Hot Tip!';\n\n    protected string $literal;\n\n    public function getTitle(): ?string\n    {\n        $words = $this->getHeaderWords();\n\n        if (count($words) > 1) {\n\n            array_shift($words);\n\n            return implode(' ', $words);\n        }\n\n        if ($words[0] === 'tip') {\n            return 'Hot Tip!';\n        }\n\n        if ($words[0] === 'hint') {\n            return 'Hinty Hint!';\n        }\n\n        if ($words[0] === 'warning') {\n            return 'Warning!';\n        }\n\n        if ($words[0] === 'best-practice') {\n            return 'Best Practice';\n        }\n\n        return null;\n    }\n\n    public function getType(): ?string\n    {\n        $words = $this->getHeaderWords();\n\n        if (count($words) > 0) {\n            return $words[0];\n        }\n\n        return null;\n    }\n\n    public function getHeaderWords(): array\n    {\n        return \\preg_split('/\\s+/', $this->header ?? '') ?: [];\n    }\n\n    public function setHeader($header)\n    {\n        $this->header = $header;\n    }\n\n    public function setLiteral(string $literal): void\n    {\n        $this->literal = $literal;\n    }\n\n    public function getLiteral(): string\n    {\n        return $this->literal;\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Hint/HintExtension.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Hint;\n\nuse League\\CommonMark\\Environment\\EnvironmentBuilderInterface;\nuse League\\CommonMark\\Extension\\ExtensionInterface;\n\nclass HintExtension implements ExtensionInterface\n{\n    public function register(EnvironmentBuilderInterface $environment): void\n    {\n        $environment->addBlockStartParser(new HintStartParser);\n        $environment->addRenderer(Hint::class, new HintRenderer);\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Hint/HintParser.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Hint;\n\nuse League\\CommonMark\\Node\\Block\\AbstractBlock;\nuse League\\CommonMark\\Parser\\Block\\AbstractBlockContinueParser;\nuse League\\CommonMark\\Parser\\Block\\BlockContinue;\nuse League\\CommonMark\\Parser\\Block\\BlockContinueParserInterface;\nuse League\\CommonMark\\Parser\\Cursor;\n\nclass HintParser extends AbstractBlockContinueParser implements BlockContinueParserInterface\n{\n    private Hint $block;\n\n    public function __construct(?string $headerText)\n    {\n        $this->block = new Hint;\n        $this->block->setHeader($headerText);\n    }\n\n    public function getBlock(): Hint\n    {\n        return $this->block;\n    }\n\n    public function isContainer(): bool\n    {\n        return true;\n    }\n\n    public function canContain(AbstractBlock $childBlock): bool\n    {\n        return true;\n    }\n\n    public function canHaveLazyContinuationLines(): bool\n    {\n        return false;\n    }\n\n    public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue\n    {\n        if ($cursor->getLine() === ':::') {\n            return BlockContinue::finished();\n        }\n\n        return BlockContinue::at($cursor);\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Hint/HintRenderer.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Hint;\n\nuse League\\CommonMark\\Node\\Node;\nuse League\\CommonMark\\Renderer\\ChildNodeRendererInterface;\nuse League\\CommonMark\\Renderer\\NodeRendererInterface;\nuse League\\CommonMark\\Util\\HtmlElement;\n\nfinal class HintRenderer implements NodeRendererInterface\n{\n    /**\n     * @param  Hint  $node\n     *\n     * {@inheritDoc}\n     *\n     * @psalm-suppress MoreSpecificImplementedParamType\n     */\n    public function render(Node $node, ChildNodeRendererInterface $childRenderer): \\Stringable\n    {\n        Hint::assertInstanceOf($node);\n\n        $attrs = $node->data->get('attributes');\n        isset($attrs['class']) ? $attrs['class'] .= ' hint' : $attrs['class'] = 'c-tip';\n\n        if ($type = $node->getType()) {\n            $attrs['class'] = isset($attrs['class']) ? $attrs['class'].' ' : '';\n            $attrs['class'] .= $type.' c-tip--'.$type;\n        }\n\n        if ($type === 'watch') {\n            return $this->renderWatch($node, $childRenderer, $attrs);\n        }\n\n        $title = $node->getTitle();\n        $title = $title\n            ? new HtmlElement(\n                'span',\n                ['class' => 'hint-title'],\n                $title,\n            )\n            : '';\n\n        $content = $childRenderer->renderNodes($node->children());\n\n        // Add mascot image for tips and best practices\n        $mascot = in_array($type, ['tip', 'hint', 'best-practice', 'warning'])\n            ? '<img src=\"/img/tip-troll.webp\" class=\"c-tip__mascot\" alt=\"A troll pointing a teaching stick\" width=\"242\" height=\"293\" />'\n            : '';\n\n        return new HtmlElement(\n            'div',\n            $attrs,\n            \"\\n\".\n            $title.\"\\n\".\n            $content.\n            $mascot.\n            \"\\n\"\n        );\n    }\n\n    private function renderWatch(Hint $node, ChildNodeRendererInterface $childRenderer, array $attrs)\n    {\n        // Strip all HTML tags except for essential formatting elements (links, code, bold, italic)\n        // This ensures the caption is rendered as plain text without unwanted p tags or other wrappers\n        $caption = strip_tags($childRenderer->renderNodes($node->children()), '<a><code><strong><em>');\n\n        return new HtmlElement(\n            'div',\n            $attrs,\n            '<figure class=\"c-video\">'.\n                '<iframe src=\"'.$node->getTitle().'\"></iframe>'.\n                '<figcaption>'.$caption.'</figcaption>'.\n            '</figure>'\n        );\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Hint/HintStartParser.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Hint;\n\nuse League\\CommonMark\\Parser\\Block\\BlockStart;\nuse League\\CommonMark\\Parser\\Block\\BlockStartParserInterface;\nuse League\\CommonMark\\Parser\\Cursor;\nuse League\\CommonMark\\Parser\\MarkdownParserStateInterface;\n\nclass HintStartParser implements BlockStartParserInterface\n{\n    public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart\n    {\n        if ($cursor->isIndented()) {\n            return BlockStart::none();\n        }\n\n        $fence = $cursor->match('/^(?:\\:{3,}\\s?(?!.*`))/');\n\n        if ($fence === null) {\n            return BlockStart::none();\n        }\n\n        $headerText = $cursor->getRemainder();\n\n        $cursor->advanceToEnd();\n\n        return BlockStart::of(new HintParser($headerText))->at($cursor);\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Tabs/TabbedCodeBlock.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Tabs;\n\nuse League\\CommonMark\\Node\\Block\\AbstractBlock;\n\nclass TabbedCodeBlock extends AbstractBlock\n{\n    // You can store code snippets for each tab here\n    protected $codeSamples = [];\n\n    public function addCodeSample($language, $code)\n    {\n        $this->codeSamples[$language] = $code;\n    }\n\n    public function getCodeSamples()\n    {\n        return $this->codeSamples;\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Tabs/TabbedCodeBlockExtension.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Tabs;\n\nuse League\\CommonMark\\Environment\\EnvironmentBuilderInterface;\nuse League\\CommonMark\\Extension\\ExtensionInterface;\n\nclass TabbedCodeBlockExtension implements ExtensionInterface\n{\n    public function register(EnvironmentBuilderInterface $environment): void\n    {\n        $environment->addBlockStartParser(new TabbedCodeStartParser);\n        $environment->addRenderer(TabbedCodeBlock::class, new TabsRenderer);\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Tabs/TabbedCodeStartParser.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Tabs;\n\nuse League\\CommonMark\\Parser\\Block\\BlockStart;\nuse League\\CommonMark\\Parser\\Block\\BlockStartParserInterface;\nuse League\\CommonMark\\Parser\\Cursor;\nuse League\\CommonMark\\Parser\\MarkdownParserStateInterface;\n\nclass TabbedCodeStartParser implements BlockStartParserInterface\n{\n    public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart\n    {\n        if ($cursor->isIndented()) {\n            return BlockStart::none();\n        }\n\n        $fence = $cursor->match('/^(?:\\:{2,}tabs)/');\n\n        if ($fence === null) {\n            return BlockStart::none();\n        }\n\n        $headerText = $cursor->getRemainder();\n\n        $cursor->advanceToEnd();\n\n        return BlockStart::of(new TabsParser)->at($cursor);\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Tabs/TabsParser.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Tabs;\n\nuse League\\CommonMark\\Node\\Block\\AbstractBlock;\nuse League\\CommonMark\\Parser\\Block\\AbstractBlockContinueParser;\nuse League\\CommonMark\\Parser\\Block\\BlockContinue;\nuse League\\CommonMark\\Parser\\Block\\BlockContinueParserInterface;\nuse League\\CommonMark\\Parser\\Cursor;\n\nclass TabsParser extends AbstractBlockContinueParser implements BlockContinueParserInterface\n{\n    protected TabbedCodeBlock $tabs;\n\n    public function __construct()\n    {\n        $this->tabs = new TabbedCodeBlock;\n    }\n\n    public function isContainer(): bool\n    {\n        return true;\n    }\n\n    public function canContain(AbstractBlock $childBlock): bool\n    {\n        return true;\n    }\n\n    public function getBlock(): AbstractBlock\n    {\n        return $this->tabs;\n    }\n\n    public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue\n    {\n        if ($cursor->getLine() === '::') {\n            return BlockContinue::finished();\n        }\n\n        return BlockContinue::at($cursor);\n    }\n}\n"
  },
  {
    "path": "app/Markdown/Tabs/TabsRenderer.php",
    "content": "<?php\n\nnamespace App\\Markdown\\Tabs;\n\nuse Illuminate\\Support\\Str;\nuse League\\CommonMark\\Extension\\CommonMark\\Node\\Block\\FencedCode;\nuse League\\CommonMark\\Node\\Block\\Paragraph;\nuse League\\CommonMark\\Node\\Inline\\Text;\nuse League\\CommonMark\\Node\\Node;\nuse League\\CommonMark\\Renderer\\ChildNodeRendererInterface;\nuse League\\CommonMark\\Renderer\\NodeRendererInterface;\nuse League\\CommonMark\\Util\\HtmlElement;\n\nclass TabsRenderer implements NodeRendererInterface\n{\n    protected array $languageNames = [\n        'blade' => 'Blade',\n        'antlers' => 'Antlers',\n        'php' => 'PHP',\n    ];\n\n    public function render(Node $node, ChildNodeRendererInterface $childRenderer)\n    {\n        TabbedCodeBlock::assertInstanceOf($node);\n\n        $attrs = $node->data->get('attributes');\n\n        $attrs['class'] = 'c-doc-tabs';\n\n        $tabs = [];\n\n        $currentTab = [];\n        $tabNameSetManually = false;\n        $lastTabName = '';\n\n        foreach ($node->children() as $child) {\n\n            if ($child instanceof FencedCode && ! $tabNameSetManually) {\n                $lastTabName = mb_strtoupper($child->getInfo());\n            }\n\n            if ($child instanceof Paragraph && $child->firstChild() instanceof Text) {\n                /** @var Text $text */\n                $text = $child->firstChild();\n\n                if (Str::startsWith($text->getLiteral(), '::tab')) {\n                    $renderedContent = $childRenderer->renderNodes($currentTab);\n                    $currentTab = [];\n\n                    $extra = trim(mb_substr($text->getLiteral(), 5));\n\n                    $currentTabName = $lastTabName;\n\n                    if (mb_strlen($extra) > 0) {\n                        $lastTabName = $extra;\n                        $tabNameSetManually = true;\n                    } else {\n                        $tabNameSetManually = false;\n                    }\n\n                    if (mb_strlen(strip_tags($renderedContent)) == 0) {\n                        continue;\n                    }\n\n                    $tabs[$currentTabName] = $renderedContent;\n\n                    continue;\n                }\n            }\n\n            $currentTab[] = $child;\n        }\n\n        if (count($currentTab)) {\n            $tabs[$lastTabName] = $childRenderer->renderNodes($currentTab);\n            $currentTab = [];\n        }\n\n        $sampleNames = [];\n\n        foreach ($tabs as $language => $sample) {\n            if (array_key_exists($language, $this->languageNames)) {\n                $sampleNames[$language] = $this->languageNames[$language];\n\n                continue;\n            }\n\n            $sampleNames[$language] = mb_strtoupper($language);\n        }\n\n        return new HtmlElement(\n            'div',\n            $attrs,\n            view('tabs', ['tabs' => $sampleNames, 'samples' => $tabs, 'active' => array_keys($tabs)[0]])->render()\n        );\n    }\n}\n"
  },
  {
    "path": "app/Models/User.php",
    "content": "<?php\n\nnamespace App\\Models;\n\n// use Illuminate\\Contracts\\Auth\\MustVerifyEmail;\nuse Database\\Factories\\UserFactory;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Fillable;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Hidden;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Illuminate\\Notifications\\Notifiable;\n\n#[Fillable(['name', 'email', 'password'])]\n#[Hidden(['password', 'remember_token'])]\nclass User extends Authenticatable\n{\n    /** @use HasFactory<UserFactory> */\n    use HasFactory, Notifiable;\n\n    /**\n     * Get the attributes that should be cast.\n     *\n     * @return array<string, string>\n     */\n    protected function casts(): array\n    {\n        return [\n            'email_verified_at' => 'datetime',\n            'password' => 'hashed',\n        ];\n    }\n}\n"
  },
  {
    "path": "app/Modifiers/Split.php",
    "content": "<?php\n\nnamespace App\\Modifiers;\n\nuse Statamic\\Modifiers\\Modifier;\nuse Statamic\\Support\\Arr;\n\nclass Split extends Modifier\n{\n    private $context;\n\n    /**\n     * Break an array into a given number of groups.\n     *\n     * @return array\n     */\n    public function index($value, $params)\n    {\n        $size = Arr::get($params, 0, 1);\n\n        return collect($value)\n            ->split($size)\n            ->map(function ($collection) {\n                return [\n                    'items' => $collection->all(),\n                ];\n            })->all();\n    }\n}\n"
  },
  {
    "path": "app/Modifiers/Toc.php",
    "content": "<?php\n\nnamespace App\\Modifiers;\n\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Str;\nuse Statamic\\Modifiers\\Modifier;\nuse Statamic\\Statamic;\n\nclass Toc extends Modifier\n{\n    private $context;\n\n    /**\n     * Modify a value\n     *\n     * @param  mixed  $value  The value to be modified\n     * @param  array  $params  Any parameters used in the modifier\n     * @param  array  $context  Contextual values\n     * @return mixed\n     */\n    public function index($value, $params, $context)\n    {\n        $this->context = $context;\n\n        $creatingIds = Arr::get($params, 0) == 'ids';\n\n        // Here maxHeadingLevels is set to either 5 (when creating IDs) or 3 (for TOC)\n        [$toc, $content] = $this->create($value, $creatingIds ? 5 : 3);\n\n        return $creatingIds ? $content : $toc;\n    }\n\n    // Good golly this thing is ugly.\n    private function create($content, $maxHeadingLevels)\n    {\n        // First try with h2-hN headings\n        preg_match_all('/<h([2-'.$maxHeadingLevels.'])([^>]*)>(.*)<\\/h[2-'.$maxHeadingLevels.']>/i', $content, $matches, PREG_SET_ORDER);\n\n        // If we don't have enough entries, include h1 headings as well\n        if (count($matches) < 3) {\n            preg_match_all('/<h([1-'.$maxHeadingLevels.'])([^>]*)>(.*)<\\/h[1-'.$maxHeadingLevels.']>/i', $content, $matches, PREG_SET_ORDER);\n        }\n\n        if (! $matches) {\n            return [null, $content];\n        }\n\n        // Track unique anchor IDs across the document\n        global $anchors;\n        $anchors = [];\n\n        // Initialize TOC with an unordered list\n        $toc = '<ul class=\"o-scroll-spy-timeline__toc js__scroll-spy-toc\">'.\"\\n\";\n        $i = 0;\n\n        foreach ($matches as $heading) {\n            // Track the starting heading level for proper list nesting\n            if ($i == 0) {\n                $startlvl = ($heading[1] == '1') ? '2' : $heading[1];\n            }\n\n            // Normalize h1 to same level as h2\n            $lvl = ($heading[1] == '1') ? '2' : $heading[1];\n\n            // Get the ID attribute generated by Commonmark\n            $anchor = Str::of($heading[0])->after('id=\"')->before('\"');\n\n            // Extract title from title attribute or use heading text\n            $ret = preg_match('/title=[\\'|\"](.*)?[\\'|\"]/i', stripslashes($heading[2]), $title);\n\n            if ($ret && $title[1] != '') {\n                $title = stripslashes($title[1]);\n            } else {\n                $title = $heading[3];\n            }\n\n            $title = trim(strip_tags($title));\n\n            // Remove the \"#\" suffix from titles.\n            $title = str($title)->replaceEnd('#', '')->__toString();\n\n            // Handle nested list structure based on heading levels\n            if ($i > 0) {\n                if ($prevlvl < $lvl) {\n                    // Start a new nested list wrapped in li, don't increment counter for parent li\n                    $toc .= \"\\n\".'<li><ul>'.\"\\n\";\n                } elseif ($prevlvl > $lvl) {\n                    // Close current item and any nested lists\n                    $toc .= '</li>'.\"\\n\";\n                    while ($prevlvl > $lvl) {\n                        $toc .= '</ul></li>'.\"\\n\".'</li>'.\"\\n\";\n                        $prevlvl--;\n                    }\n                } else {\n                    // Close current item at same level\n                    $toc .= '</li>'.\"\\n\";\n                }\n            }\n\n            // Add TOC entry (only for leaf nodes)\n            $toc .= '<li><a href=\"#'.$anchor.'\">'.$title.'</a>';\n\n            $prevlvl = $lvl;\n\n            $i++;\n        }\n\n        unset($anchors);\n\n        while ($lvl > $startlvl) {\n            $toc .= \"\\n</ul>\";\n            $lvl--;\n        }\n\n        $toc .= '</li>'.\"\\n\";\n        $toc .= '</ul>'.\"\\n\";\n\n        return [$toc, $content];\n    }\n\n    /**\n     * Safely extracts value from Statamic Value objects\n     */\n    private function valueGet($value)\n    {\n        if ($value instanceof \\Statamic\\Fields\\Value) {\n            return $value->value();\n        }\n\n        return $value;\n    }\n\n    private function slugify($text)\n    {\n        $slugified = Statamic::modify($text)->replace('&amp;', '')->slugify()->stripTags();\n        // Remove 'code-code' from the slugified text e.g. Otherwise \"the `@` ignore symbol\" gets converted to `the-code-code-ignore-symbol`\n        $slugified = str_replace('code-code-', '', $slugified);\n        // Remove HTML entity remnants that might be left after processing\n        // Only remove these if they appear as standalone words or at word boundaries\n        $slugified = preg_replace('/\\b(codelt|rt|gtcode)\\b/', '', $slugified);\n\n        return $slugified;\n    }\n}\n"
  },
  {
    "path": "app/Providers/AppServiceProvider.php",
    "content": "<?php\n\nnamespace App\\Providers;\n\nuse App\\Markdown\\Hint\\HintExtension;\nuse App\\Markdown\\Tabs\\TabbedCodeBlockExtension;\nuse App\\Search\\Listeners\\SearchEntriesCreatedListener;\nuse App\\Search\\Storybook\\StorybookSearchProvider;\nuse Illuminate\\Support\\Facades\\Event;\nuse Illuminate\\Support\\ServiceProvider;\nuse League\\CommonMark\\Extension\\Attributes\\AttributesExtension;\nuse League\\CommonMark\\Extension\\DescriptionList\\DescriptionListExtension;\nuse League\\CommonMark\\Extension\\HeadingPermalink\\HeadingPermalinkExtension;\nuse Statamic\\Facades\\Markdown;\nuse Stillat\\DocumentationSearch\\Events\\SearchEntriesCreated;\nuse Torchlight\\Engine\\CommonMark\\Extension as TorchlightExtension;\nuse Torchlight\\Engine\\Options as TorchlightOptions;\n\nclass AppServiceProvider extends ServiceProvider\n{\n    /**\n     * Register any application services.\n     */\n    public function register(): void\n    {\n        //\n    }\n\n    /**\n     * Bootstrap any application services.\n     */\n    public function boot(): void\n    {\n        Markdown::addExtensions(function () {\n            return [new DescriptionListExtension, new HintExtension, new TabbedCodeBlockExtension, new AttributesExtension, new HeadingPermalinkExtension];\n        });\n\n        if (! app()->runningConsoleCommand('search:update')) {\n            TorchlightOptions::setDefaultOptionsBuilder(fn () => TorchlightOptions::fromArray(config('torchlight.options')));\n\n            $extension = new TorchlightExtension(config('torchlight.theme'));\n            $extension\n                ->renderer()\n                ->setDefaultGrammar(config('torchlight.options.defaultLanguage'));\n\n            Markdown::addExtension(fn () => $extension);\n        }\n\n        Event::listen(SearchEntriesCreated::class, SearchEntriesCreatedListener::class);\n\n        StorybookSearchProvider::register();\n    }\n}\n"
  },
  {
    "path": "app/Search/DocTransformer.php",
    "content": "<?php\n\nnamespace App\\Search;\n\nuse Illuminate\\Support\\Str;\nuse Stillat\\DocumentationSearch\\Contracts\\DocumentTransformer;\nuse Stillat\\DocumentationSearch\\Document\\DocumentFragment;\n\nclass DocTransformer implements DocumentTransformer\n{\n    private function normalizeConent($value)\n    {\n        return str($value)\n            ->lower()\n            ->explode(' ')\n            ->map(fn ($word) => Str::singular($word))\n            ->join(' ');\n    }\n\n    public function handle(DocumentFragment $fragment, $entry): void\n    {\n        $fragment->additionalContextData[] = $this->normalizeConent($entry->title);\n\n        // Add some extra details to \"additional_context\"\n        if (Str::containsAll($fragment->content, ['clear', 'cache'])) {\n            $fragment->additionalContextData[] = 'delete cache';\n        }\n\n        if (Str::contains($fragment->content, 'JS Drivers')) {\n            $fragment->additionalContextData[] = 'javascript drivers';\n        }\n    }\n}\n"
  },
  {
    "path": "app/Search/Listeners/SearchEntriesCreatedListener.php",
    "content": "<?php\n\nnamespace App\\Search\\Listeners;\n\nuse Stillat\\DocumentationSearch\\Events\\SearchEntriesCreated;\n\nclass SearchEntriesCreatedListener\n{\n    protected array $escapeProperties = [\n        'description',\n        'intro',\n        'content',\n        'search_content',\n    ];\n\n    protected function getParentHeadings($headers, $target)\n    {\n        $hierarchy = [];\n        $levels = [];\n\n        $currentLevel = $target->level;\n\n        for ($i = array_search($target, $headers) - 1; $i >= 0; $i--) {\n            $header = $headers[$i];\n\n            if ($header->level < $currentLevel) {\n                $hierarchy[] = $header;\n                $currentLevel = $header->level;\n            }\n        }\n\n        foreach (array_reverse($hierarchy) as $level) {\n            $levels[$level->level] = str($level->text)->replaceEnd('#', '')->__toString();\n        }\n\n        return $levels;\n    }\n\n    /**\n     * Handle the event.\n     */\n    public function handle(SearchEntriesCreated $event): void\n    {\n        $collection = $event->entry->collection()->title;\n\n        $headers = [];\n\n        foreach ($event->sections as $section) {\n            if ($section->fragment->headerDetails == null) {\n                continue;\n            }\n\n            $headers[] = $section->fragment->headerDetails;\n        }\n\n        foreach ($event->sections as $section) {\n            $data = $section->searchEntry->data();\n            $data['search_title'] = str($data['search_title'] ?? '')->replaceEnd('#', '')->__toString();\n\n            $category = match (true) {\n                $collection === 'Pages' => ($event->entry->parent() ? $event->entry->parent()?->title.' » ' : null).$data['origin_title'],\n                default => $collection.' » '.$data['origin_title'],\n            };\n\n            $parentHeadings = null;\n\n            if (\n                $section->fragment->headerDetails != null &&\n                $section->fragment->headerDetails->level >= 3\n            ) {\n                $header = $section->fragment->headerDetails;\n\n                $parentHeadings = $this->getParentHeadings(\n                    $headers,\n                    $header\n                );\n                $parentHeadings[$header->level] = $header->text;\n            }\n\n            if ($parentHeadings === null) {\n                $parentHeadings = [];\n\n                if ($data['search_title'] != null && $data['origin_title'] != $data['search_title']) {\n                    $parentHeadings[1] = $data['search_title'];\n                }\n            }\n\n            if (count($parentHeadings) > 2) {\n                array_shift($parentHeadings);\n            }\n\n            $data['hierarchy_lvl0'] = $category;\n            $data['hierarchy_lvl1'] = str(implode(' » ', $parentHeadings))->replaceEnd('#', '')->__toString();\n\n            if ($data['is_root']) {\n                $data['content'] = strip_tags($event->entry->intro ?? $event->entry->description ?? $data['search_content']);\n            } else {\n                $data['content'] = $data['search_content'] ?? '';\n            }\n\n            $data['url'] = $data['search_url'];\n\n            foreach ($this->escapeProperties as $property) {\n                if (! $data->has($property)) {\n                    continue;\n                }\n\n                $data[$property] = e($data[$property]);\n            }\n\n            // Clear this out to prevent \"too much\" from a specific page dominating the results.\n            if (! $data['is_root']) {\n                $data['origin_title'] = null;\n            }\n\n            $section->searchEntry->data($data);\n        }\n    }\n}\n"
  },
  {
    "path": "app/Search/RequestContentRetriever.php",
    "content": "<?php\n\nnamespace App\\Search;\n\nuse DOMDocument;\nuse Illuminate\\Http\\Request;\nuse Statamic\\Contracts\\Entries\\Entry;\nuse Statamic\\Facades\\Cascade;\nuse Stillat\\DocumentationSearch\\Contracts\\ContentRetriever;\n\nclass RequestContentRetriever implements ContentRetriever\n{\n    public function getContent(Entry $entry): string\n    {\n        $originalRequest = app('request');\n        $request = tap(Request::capture(), function ($request) {\n            app()->instance('request', $request);\n            Cascade::withRequest($request);\n        });\n\n        $content = '';\n\n        try {\n            $content = $entry->toResponse($request)->getContent();\n        } finally {\n            app()->instance('request', $originalRequest);\n        }\n\n        return $this->extractArticleContent($content);\n    }\n\n    protected function extractArticleContent(string $content): string\n    {\n        $dom = new DOMDocument;\n\n        libxml_use_internal_errors(true);\n        $dom->loadHTML($content);\n        libxml_clear_errors();\n\n        $articles = $dom->getElementsByTagName('article');\n\n        $result = '';\n\n        foreach ($articles as $article) {\n            $result .= $dom->saveHTML($article);\n        }\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "app/Search/Storybook/StorybookSearchProvider.php",
    "content": "<?php\n\nnamespace App\\Search\\Storybook;\n\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Http;\nuse Illuminate\\Support\\LazyCollection;\nuse Illuminate\\Support\\Str;\nuse Statamic\\Search\\Searchables\\Provider;\n\nclass StorybookSearchProvider extends Provider\n{\n    protected static $handle = 'storybook';\n\n    protected static $referencePrefix = 'storybook';\n\n    public function provide(): Collection|LazyCollection\n    {\n        return Http::get('https://ui.statamic.dev/index.json')\n            ->collect('entries')\n            ->filter(fn (array $story) => Str::endsWith($story['id'], ['--docs', '--default']))\n            ->unique('title')\n            ->map(fn (array $story) => StorybookSearchable::from($story))\n            ->map->reference();\n    }\n\n    public function contains($searchable): bool\n    {\n        // TODO: Implement contains() method.\n    }\n\n    public function find(array $keys): Collection\n    {\n        $stories = Http::get('https://ui.statamic.dev/index.json')->collect('entries');\n\n        return collect($keys)->map(fn (string $key) => StorybookSearchable::from($stories->get($key)));\n    }\n}"
  },
  {
    "path": "app/Search/Storybook/StorybookSearchable.php",
    "content": "<?php\n\nnamespace App\\Search\\Storybook;\n\nuse Illuminate\\Support\\Str;\nuse Statamic\\Contracts\\Search\\Searchable as SearchableContract;\nuse Statamic\\Search\\Searchable;\n\nclass StorybookSearchable implements SearchableContract\n{\n    use Searchable;\n\n    protected string $id;\n    protected string $title;\n\n    public static function from(array $component)\n    {\n        $instance = new static();\n\n        $instance->id = $component['id'];\n        $instance->title = Str::after($component['title'], '/');\n\n        return $instance;\n    }\n\n    public function id(): string\n    {\n        return $this->id;\n    }\n\n    public function title(): string\n    {\n        return $this->title;\n    }\n\n    public function getSearchValue(string $field)\n    {\n        if ($field === 'title' || $field === 'origin_title' || $field === 'search_title') {\n            return $this->title();\n        }\n\n        if ($field === 'hierarchy_lvl0') {\n            return \"UI Components » {$this->title()}\";\n        }\n\n        if ($field === 'url') {\n            return $this->url();\n        }\n\n        return null;\n    }\n\n    public function url(): string\n    {\n        return \"https://ui.statamic.dev/?path=/docs/{$this->id}\";\n    }\n\n    public function reference(): string\n    {\n        return \"storybook::{$this->id()}\";\n    }\n}"
  },
  {
    "path": "app/Tags/GithubCommitsUrl.php",
    "content": "<?php\n\nnamespace App\\Tags;\n\nuse Illuminate\\Support\\Str;\nuse Statamic\\Facades\\Data;\n\nclass GithubCommitsUrl extends \\Statamic\\Tags\\Tags\n{\n    public function index()\n    {\n        if (! $id = $this->context->get('id')) {\n            return false;\n        }\n\n        $content = Data::find($id);\n\n        if ($content instanceof \\Statamic\\Taxonomies\\LocalizedTerm) {\n            return;\n        }\n\n        $path = Str::after($content->path(), 'content/');\n\n        return \"https://github.com/statamic/docs/commits/{$this->getCurrentBranch()}/content/{$path}\";\n    }\n\n    private function getCurrentBranch(): string\n    {\n        return collect(config('docs.versions'))\n            ->where('version', config('docs.version'))\n            ->first()['branch'] ?? '5.x';\n    }\n}\n"
  },
  {
    "path": "app/Tags/GithubEditUrl.php",
    "content": "<?php\n\nnamespace App\\Tags;\n\nuse Illuminate\\Support\\Str;\nuse Statamic\\Facades\\Data;\n\nclass GithubEditUrl extends \\Statamic\\Tags\\Tags\n{\n    public function index()\n    {\n        if (! $id = $this->context->get('id')) {\n            return false;\n        }\n\n        $content = Data::find($id);\n\n        if ($content instanceof \\Statamic\\Taxonomies\\LocalizedTerm) {\n            return;\n        }\n\n        $path = Str::after($path = $content->path(), 'content/');\n\n        return \"https://github.com/statamic/docs/blob/{$this->getCurrentBranch()}/content/{$path}\";\n    }\n\n    private function getCurrentBranch(): string\n    {\n        return collect(config('docs.versions'))\n            ->where('version', config('docs.version'))\n            ->first()['branch'] ?? '5.x';\n    }\n}\n"
  },
  {
    "path": "app/Tags/HeroSponsors.php",
    "content": "<?php\n\nnamespace App\\Tags;\n\nuse Illuminate\\Support\\Facades\\Cache;\nuse Illuminate\\Support\\Facades\\Http;\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\LazyCollection;\nuse Statamic\\Support\\Arr;\nuse Statamic\\Tags\\Tags;\n\nclass HeroSponsors extends Tags\n{\n    public function index()\n    {\n        return Cache::remember('hero-sponsors', now()->addHour(), function () {\n            try {\n                return $this->sponsors()->collect()->where('price', '>=', 25);\n            } catch (\\Exception $e) {\n                Log::error($e);\n\n                return collect(['error' => true]);\n            }\n        });\n    }\n\n    private function sponsors()\n    {\n        return LazyCollection::make(function () {\n            $cursor = null;\n\n            do {\n                $data = $this->request($cursor);\n                $sponsorships = Arr::get($data, 'data.viewer.organization.sponsorshipsAsMaintainer');\n                $cursor = $sponsorships['pageInfo']['endCursor'];\n                $hasNextPage = $sponsorships['pageInfo']['hasNextPage'];\n\n                yield from collect($sponsorships['nodes'])->map(fn ($sponsorship) => array_merge(\n                    $sponsorship['sponsorEntity'],\n                    ['price' => $sponsorship['tier']['monthlyPriceInDollars']]\n                ));\n            } while ($hasNextPage && $cursor);\n        });\n    }\n\n    private function request($cursor)\n    {\n        return Http::baseUrl('https://api.github.com')\n            ->withToken(config('services.github.token'))\n            ->asJson()\n            ->accept('application/vnd.github.v4+json')\n            ->withUserAgent('statamic/docs')\n            ->post('/graphql', [\n                'query' => $this->query(),\n                'variables' => ['cursor' => $cursor],\n            ])\n            ->json();\n    }\n\n    private function query()\n    {\n        return <<<'GQL'\nquery($cursor: String) {\n    viewer {\n        organization(login: \"statamic\") {\n            sponsorshipsAsMaintainer(first: 100, after: $cursor, orderBy: {direction: ASC, field: CREATED_AT}) {\n                pageInfo {\n                    hasNextPage\n                    endCursor\n                }\n                nodes {\n                    sponsorEntity {\n                        ... on User {\n                            name\n                            url\n                            avatarUrl(size: 80)\n                        }\n                        ... on Organization {\n                            name\n                            url\n                            avatarUrl(size: 80)\n                        }\n                    }\n                    tier {\n                        name\n                        monthlyPriceInDollars\n                    }\n                }\n            }\n        }\n    }\n}\nGQL;\n    }\n}\n"
  },
  {
    "path": "app/ViewModels/Fieldtypes.php",
    "content": "<?php\n\nnamespace App\\ViewModels;\n\nuse Statamic\\View\\ViewModel;\n\nclass Fieldtypes extends ViewModel\n{\n    public function data(): array\n    {\n        return ['title' => ucwords($this->cascade->get('title')).' Fieldtype'];\n    }\n}\n"
  },
  {
    "path": "app/ViewModels/Modifiers.php",
    "content": "<?php\n\nnamespace App\\ViewModels;\n\nuse Statamic\\View\\ViewModel;\n\nclass Modifiers extends ViewModel\n{\n    public function data(): array\n    {\n        return ['title' => ucwords($this->cascade->get('title')).' Modifier'];\n    }\n}\n"
  },
  {
    "path": "app/ViewModels/Tags.php",
    "content": "<?php\n\nnamespace App\\ViewModels;\n\nuse Statamic\\View\\ViewModel;\n\nclass Tags extends ViewModel\n{\n    public function data(): array\n    {\n        return ['title' => ucwords($this->cascade->get('slug')).' Tag'];\n    }\n}\n"
  },
  {
    "path": "app/ViewModels/Variables.php",
    "content": "<?php\n\nnamespace App\\ViewModels;\n\nuse Statamic\\View\\ViewModel;\n\nclass Variables extends ViewModel\n{\n    public function data(): array\n    {\n        return ['title' => strtolower($this->cascade->get('slug'))];\n    }\n}\n"
  },
  {
    "path": "artisan",
    "content": "#!/usr/bin/env php\n<?php\n\nuse Symfony\\Component\\Console\\Input\\ArgvInput;\n\ndefine('LARAVEL_START', microtime(true));\n\n// Register the Composer autoloader...\nrequire __DIR__.'/vendor/autoload.php';\n\n// Bootstrap Laravel and handle the command...\n$status = (require_once __DIR__.'/bootstrap/app.php')\n    ->handleCommand(new ArgvInput);\n\nexit($status);\n"
  },
  {
    "path": "bootstrap/app.php",
    "content": "<?php\n\nuse Illuminate\\Foundation\\Application;\nuse Illuminate\\Foundation\\Configuration\\Exceptions;\nuse Illuminate\\Foundation\\Configuration\\Middleware;\n\nreturn Application::configure(basePath: dirname(__DIR__))\n    ->withRouting(\n        web: __DIR__.'/../routes/web.php',\n        commands: __DIR__.'/../routes/console.php',\n        health: '/up',\n    )\n    ->withMiddleware(function (Middleware $middleware): void {\n        //\n    })\n    ->withExceptions(function (Exceptions $exceptions): void {\n        //\n    })->create();\n"
  },
  {
    "path": "bootstrap/cache/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "bootstrap/providers.php",
    "content": "<?php\n\nuse App\\Providers\\AppServiceProvider;\n\nreturn [\n    AppServiceProvider::class,\n];\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"laravel/laravel\",\n    \"type\": \"project\",\n    \"description\": \"The Laravel Framework.\",\n    \"keywords\": [\n        \"framework\",\n        \"laravel\"\n    ],\n    \"license\": \"MIT\",\n    \"require\": {\n        \"php\": \"^8.2\",\n        \"laravel/framework\": \"^13.0\",\n        \"laravel/tinker\": \"^3.0\",\n        \"statamic-rad-pack/meilisearch\": \"^4.1\",\n        \"statamic/cms\": \"^6.0\",\n        \"torchlight/engine\": \"^0.1.0\",\n        \"stillat/documentation-search\": \"^2.0\"\n    },\n    \"require-dev\": {\n        \"fruitcake/laravel-debugbar\": \"^4.0\",\n        \"fakerphp/faker\": \"^1.4\",\n        \"mockery/mockery\": \"^1.0\",\n        \"nunomaduro/collision\": \"^8.0\",\n        \"phpunit/phpunit\": \"^12.0\",\n        \"spatie/laravel-ignition\": \"^2.0\",\n        \"spatie/laravel-ray\": \"^1.37\"\n    },\n    \"config\": {\n        \"optimize-autoloader\": true,\n        \"preferred-install\": \"dist\",\n        \"sort-packages\": true,\n        \"allow-plugins\": {\n            \"composer/package-versions-deprecated\": true,\n            \"pixelfear/composer-dist-plugin\": true,\n            \"php-http/discovery\": true\n        }\n    },\n    \"extra\": {\n        \"laravel\": {\n            \"dont-discover\": []\n        }\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"App\\\\\": \"app/\",\n            \"Database\\\\Factories\\\\\": \"database/factories/\",\n            \"Database\\\\Seeders\\\\\": \"database/seeders/\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Tests\\\\\": \"tests/\"\n        }\n    },\n    \"prefer-stable\": true,\n    \"minimum-stability\": \"dev\",\n    \"scripts\": {\n        \"pre-update-cmd\": [\n            \"Statamic\\\\Console\\\\Composer\\\\Scripts::preUpdateCmd\"\n        ],\n        \"post-autoload-dump\": [\n            \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n            \"@php artisan package:discover --ansi\",\n            \"@php artisan statamic:install --ansi\"\n        ],\n        \"post-root-package-install\": [\n            \"@php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n        ],\n        \"post-create-project-cmd\": [\n            \"@php artisan key:generate --ansi\"\n        ]\n    }\n}\n"
  },
  {
    "path": "config/app.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Name\n    |--------------------------------------------------------------------------\n    |\n    | This value is the name of your application, which will be used when the\n    | framework needs to place the application's name in a notification or\n    | other UI elements where an application name needs to be displayed.\n    |\n    */\n\n    'name' => env('APP_NAME', 'Statamic'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Environment\n    |--------------------------------------------------------------------------\n    |\n    | This value determines the \"environment\" your application is currently\n    | running in. This may determine how you prefer to configure various\n    | services the application utilizes. Set this in your \".env\" file.\n    |\n    */\n\n    'env' => env('APP_ENV', 'production'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Debug Mode\n    |--------------------------------------------------------------------------\n    |\n    | When your application is in debug mode, detailed error messages with\n    | stack traces will be shown on every error that occurs within your\n    | application. If disabled, a simple generic error page is shown.\n    |\n    */\n\n    'debug' => (bool) env('APP_DEBUG', false),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application URL\n    |--------------------------------------------------------------------------\n    |\n    | This URL is used by the console to properly generate URLs when using\n    | the Artisan command line tool. You should set this to the root of\n    | the application so that it's available within Artisan commands.\n    |\n    */\n\n    'url' => env('APP_URL', 'http://localhost'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Timezone\n    |--------------------------------------------------------------------------\n    |\n    | Here you may specify the default timezone for your application, which\n    | will be used by the PHP date and date-time functions. The timezone\n    | is set to \"UTC\" by default as it is suitable for most use cases.\n    |\n    */\n\n    'timezone' => 'UTC',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Locale Configuration\n    |--------------------------------------------------------------------------\n    |\n    | The application locale determines the default locale that will be used\n    | by Laravel's translation / localization methods. This option can be\n    | set to any locale for which you plan to have translation strings.\n    |\n    */\n\n    'locale' => env('APP_LOCALE', 'en'),\n\n    'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),\n\n    'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Encryption Key\n    |--------------------------------------------------------------------------\n    |\n    | This key is utilized by Laravel's encryption services and should be set\n    | to a random, 32 character string to ensure that all encrypted values\n    | are secure. You should do this prior to deploying the application.\n    |\n    */\n\n    'cipher' => 'AES-256-CBC',\n\n    'key' => env('APP_KEY'),\n\n    'previous_keys' => [\n        ...array_filter(\n            explode(',', (string) env('APP_PREVIOUS_KEYS', ''))\n        ),\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Maintenance Mode Driver\n    |--------------------------------------------------------------------------\n    |\n    | These configuration options determine the driver used to determine and\n    | manage Laravel's \"maintenance mode\" status. The \"cache\" driver will\n    | allow maintenance mode to be controlled across multiple machines.\n    |\n    | Supported drivers: \"file\", \"cache\"\n    |\n    */\n\n    'maintenance' => [\n        'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),\n        'store' => env('APP_MAINTENANCE_STORE', 'file'),\n    ],\n\n];\n"
  },
  {
    "path": "config/auth.php",
    "content": "<?php\n\nuse App\\Models\\User;\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Authentication Defaults\n    |--------------------------------------------------------------------------\n    |\n    | This option defines the default authentication \"guard\" and password\n    | reset \"broker\" for your application. You may change these values\n    | as required, but they're a perfect start for most applications.\n    |\n    */\n\n    'defaults' => [\n        'guard' => env('AUTH_GUARD', 'web'),\n        'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Authentication Guards\n    |--------------------------------------------------------------------------\n    |\n    | Next, you may define every authentication guard for your application.\n    | Of course, a great default configuration has been defined for you\n    | which utilizes session storage plus the Eloquent user provider.\n    |\n    | All authentication guards have a user provider, which defines how the\n    | users are actually retrieved out of your database or other storage\n    | system used by the application. Typically, Eloquent is utilized.\n    |\n    | Supported: \"session\"\n    |\n    */\n\n    'guards' => [\n        'web' => [\n            'driver' => 'session',\n            'provider' => 'users',\n        ],\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | User Providers\n    |--------------------------------------------------------------------------\n    |\n    | All authentication guards have a user provider, which defines how the\n    | users are actually retrieved out of your database or other storage\n    | system used by the application. Typically, Eloquent is utilized.\n    |\n    | If you have multiple user tables or models you may configure multiple\n    | providers to represent the model / table. These providers may then\n    | be assigned to any extra authentication guards you have defined.\n    |\n    | Supported: \"statamic\", \"database\", \"eloquent\"\n    |\n    */\n\n    'providers' => [\n        'users' => [\n            'driver' => 'statamic',\n        ],\n\n        // 'users' => [\n        //     'driver' => 'eloquent',\n        //     'model' => env('AUTH_MODEL', User::class),\n        // ],\n\n        // 'users' => [\n        //     'driver' => 'database',\n        //     'table' => 'users',\n        // ],\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Resetting Passwords\n    |--------------------------------------------------------------------------\n    |\n    | These configuration options specify the behavior of Laravel's password\n    | reset functionality, including the table utilized for token storage\n    | and the user provider that is invoked to actually retrieve users.\n    |\n    | The expiry time is the number of minutes that each reset token will be\n    | considered valid. This security feature keeps tokens short-lived so\n    | they have less time to be guessed. You may change this as needed.\n    |\n    | The throttle setting is the number of seconds a user must wait before\n    | generating more password reset tokens. This prevents the user from\n    | quickly generating a very large amount of password reset tokens.\n    |\n    */\n\n    'passwords' => [\n        'users' => [\n            'provider' => 'users',\n            'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),\n            'expire' => 60,\n            'throttle' => 60,\n        ],\n\n        'activations' => [\n            'provider' => 'users',\n            'table' => env('AUTH_ACTIVATION_TOKEN_TABLE', 'password_activation_tokens'),\n            'expire' => 4320,\n            'throttle' => 60,\n        ],\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Password Confirmation Timeout\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define the number of seconds before a password confirmation\n    | window expires and users are asked to re-enter their password via the\n    | confirmation screen. By default, the timeout lasts for three hours.\n    |\n    */\n\n    'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),\n\n];\n"
  },
  {
    "path": "config/cache.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Str;\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Cache Store\n    |--------------------------------------------------------------------------\n    |\n    | This option controls the default cache store that will be used by the\n    | framework. This connection is utilized if another isn't explicitly\n    | specified when running a cache operation inside the application.\n    |\n    */\n\n    'default' => env('CACHE_STORE', 'file'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Cache Stores\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define all of the cache \"stores\" for your application as\n    | well as their drivers. You may even define multiple stores for the\n    | same cache driver to group types of items stored in your caches.\n    |\n    | Supported drivers: \"array\", \"database\", \"file\", \"memcached\",\n    |                    \"redis\", \"dynamodb\", \"octane\",\n    |                    \"failover\", \"null\"\n    |\n    */\n\n    'stores' => [\n\n        'array' => [\n            'driver' => 'array',\n            'serialize' => false,\n        ],\n\n        'database' => [\n            'driver' => 'database',\n            'connection' => env('DB_CACHE_CONNECTION', null),\n            'table' => env('DB_CACHE_TABLE', 'cache'),\n            'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),\n            'lock_table' => env('DB_CACHE_LOCK_TABLE'),\n        ],\n\n        'file' => [\n            'driver' => 'file',\n            'path' => storage_path('framework/cache/data'),\n            'lock_path' => storage_path('framework/cache/data'),\n        ],\n\n        'memcached' => [\n            'driver' => 'memcached',\n            'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),\n            'sasl' => [\n                env('MEMCACHED_USERNAME'),\n                env('MEMCACHED_PASSWORD'),\n            ],\n            'options' => [\n                // Memcached::OPT_CONNECT_TIMEOUT => 2000,\n            ],\n            'servers' => [\n                [\n                    'host' => env('MEMCACHED_HOST', '127.0.0.1'),\n                    'port' => env('MEMCACHED_PORT', 11211),\n                    'weight' => 100,\n                ],\n            ],\n        ],\n\n        'redis' => [\n            'driver' => 'redis',\n            'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),\n            'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),\n        ],\n\n        'dynamodb' => [\n            'driver' => 'dynamodb',\n            'key' => env('AWS_ACCESS_KEY_ID'),\n            'secret' => env('AWS_SECRET_ACCESS_KEY'),\n            'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),\n            'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),\n            'endpoint' => env('DYNAMODB_ENDPOINT'),\n        ],\n\n        'octane' => [\n            'driver' => 'octane',\n        ],\n\n        'failover' => [\n            'driver' => 'failover',\n            'stores' => [\n                'database',\n                'array',\n            ],\n        ],\n\n        'static_cache' => [\n            'driver' => 'file',\n            'path' => storage_path('statamic/static-urls-cache'),\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Cache Key Prefix\n    |--------------------------------------------------------------------------\n    |\n    | When utilizing the APC, database, memcached, Redis, and DynamoDB cache\n    | stores, there might be other applications using the same cache. For\n    | that reason, you may prefix every cache key to avoid collisions.\n    |\n    */\n\n    'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Serializable Classes\n    |--------------------------------------------------------------------------\n    |\n    | This value determines the classes that can be unserialized from cache\n    | storage. By default, no PHP classes will be unserialized from your\n    | cache to prevent gadget chain attacks if your APP_KEY is leaked.\n    |\n    */\n\n    'serializable_classes' => false,\n\n];\n"
  },
  {
    "path": "config/database.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Str;\nuse Pdo\\Mysql;\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Database Connection Name\n    |--------------------------------------------------------------------------\n    |\n    | Here you may specify which of the database connections below you wish\n    | to use as your default connection for database operations. This is\n    | the connection which will be utilized unless another connection\n    | is explicitly specified when you execute a query / statement.\n    |\n    */\n\n    'default' => env('DB_CONNECTION', 'sqlite'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Database Connections\n    |--------------------------------------------------------------------------\n    |\n    | Below are all of the database connections defined for your application.\n    | An example configuration is provided for each database system which\n    | is supported by Laravel. You're free to add / remove connections.\n    |\n    */\n\n    'connections' => [\n\n        'sqlite' => [\n            'driver' => 'sqlite',\n            'url' => env('DB_URL'),\n            'database' => env('DB_DATABASE', database_path('database.sqlite')),\n            'prefix' => '',\n            'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),\n            'busy_timeout' => null,\n            'journal_mode' => null,\n            'synchronous' => null,\n            'transaction_mode' => 'DEFERRED',\n        ],\n\n        'mysql' => [\n            'driver' => 'mysql',\n            'url' => env('DB_URL'),\n            'host' => env('DB_HOST', '127.0.0.1'),\n            'port' => env('DB_PORT', '3306'),\n            'database' => env('DB_DATABASE', 'laravel'),\n            'username' => env('DB_USERNAME', 'root'),\n            'password' => env('DB_PASSWORD', ''),\n            'unix_socket' => env('DB_SOCKET', ''),\n            'charset' => env('DB_CHARSET', 'utf8mb4'),\n            'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),\n            'prefix' => '',\n            'prefix_indexes' => true,\n            'strict' => true,\n            'engine' => null,\n            'options' => extension_loaded('pdo_mysql') ? array_filter([\n                (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),\n            ]) : [],\n        ],\n\n        'mariadb' => [\n            'driver' => 'mariadb',\n            'url' => env('DB_URL'),\n            'host' => env('DB_HOST', '127.0.0.1'),\n            'port' => env('DB_PORT', '3306'),\n            'database' => env('DB_DATABASE', 'laravel'),\n            'username' => env('DB_USERNAME', 'root'),\n            'password' => env('DB_PASSWORD', ''),\n            'unix_socket' => env('DB_SOCKET', ''),\n            'charset' => env('DB_CHARSET', 'utf8mb4'),\n            'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),\n            'prefix' => '',\n            'prefix_indexes' => true,\n            'strict' => true,\n            'engine' => null,\n            'options' => extension_loaded('pdo_mysql') ? array_filter([\n                (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),\n            ]) : [],\n        ],\n\n        'pgsql' => [\n            'driver' => 'pgsql',\n            'url' => env('DB_URL'),\n            'host' => env('DB_HOST', '127.0.0.1'),\n            'port' => env('DB_PORT', '5432'),\n            'database' => env('DB_DATABASE', 'laravel'),\n            'username' => env('DB_USERNAME', 'root'),\n            'password' => env('DB_PASSWORD', ''),\n            'charset' => env('DB_CHARSET', 'utf8'),\n            'prefix' => '',\n            'prefix_indexes' => true,\n            'search_path' => 'public',\n            'sslmode' => env('DB_SSLMODE', 'prefer'),\n        ],\n\n        'sqlsrv' => [\n            'driver' => 'sqlsrv',\n            'url' => env('DB_URL'),\n            'host' => env('DB_HOST', 'localhost'),\n            'port' => env('DB_PORT', '1433'),\n            'database' => env('DB_DATABASE', 'laravel'),\n            'username' => env('DB_USERNAME', 'root'),\n            'password' => env('DB_PASSWORD', ''),\n            'charset' => env('DB_CHARSET', 'utf8'),\n            'prefix' => '',\n            'prefix_indexes' => true,\n            // 'encrypt' => env('DB_ENCRYPT', 'yes'),\n            // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Migration Repository Table\n    |--------------------------------------------------------------------------\n    |\n    | This table keeps track of all the migrations that have already run for\n    | your application. Using this information, we can determine which of\n    | the migrations on disk haven't actually been run on the database.\n    |\n    */\n\n    'migrations' => [\n        'table' => 'migrations',\n        'update_date_on_publish' => true,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Redis Databases\n    |--------------------------------------------------------------------------\n    |\n    | Redis is an open source, fast, and advanced key-value store that also\n    | provides a richer body of commands than a typical key-value system\n    | such as Memcached. You may define your connection settings here.\n    |\n    */\n\n    'redis' => [\n\n        'client' => env('REDIS_CLIENT', 'phpredis'),\n\n        'options' => [\n            'cluster' => env('REDIS_CLUSTER', 'redis'),\n            'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),\n            'persistent' => env('REDIS_PERSISTENT', false),\n        ],\n\n        'default' => [\n            'url' => env('REDIS_URL'),\n            'host' => env('REDIS_HOST', '127.0.0.1'),\n            'username' => env('REDIS_USERNAME'),\n            'password' => env('REDIS_PASSWORD'),\n            'port' => env('REDIS_PORT', '6379'),\n            'database' => env('REDIS_DB', '0'),\n            'max_retries' => env('REDIS_MAX_RETRIES', 3),\n            'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),\n            'backoff_base' => env('REDIS_BACKOFF_BASE', 100),\n            'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),\n        ],\n\n        'cache' => [\n            'url' => env('REDIS_URL'),\n            'host' => env('REDIS_HOST', '127.0.0.1'),\n            'username' => env('REDIS_USERNAME'),\n            'password' => env('REDIS_PASSWORD'),\n            'port' => env('REDIS_PORT', '6379'),\n            'database' => env('REDIS_CACHE_DB', '1'),\n            'max_retries' => env('REDIS_MAX_RETRIES', 3),\n            'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),\n            'backoff_base' => env('REDIS_BACKOFF_BASE', 100),\n            'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),\n        ],\n\n    ],\n\n];\n"
  },
  {
    "path": "config/docs.php",
    "content": "<?php\n\nreturn [\n\n    'version' => env('STATAMIC_DOCS_VERSION', '6'),\n\n    'versions' => [\n        [\n            'version' => '6',\n            'branch' => '6.x',\n            'url' => 'https://statamic.dev',\n        ],\n        [\n            'version' => '5',\n            'branch' => '5.x',\n            'url' => 'https://v5.statamic.dev',\n        ],\n    ],\n\n];\n"
  },
  {
    "path": "config/filesystems.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Filesystem Disk\n    |--------------------------------------------------------------------------\n    |\n    | Here you may specify the default filesystem disk that should be used\n    | by the framework. The \"local\" disk, as well as a variety of cloud\n    | based disks are available to your application for file storage.\n    |\n    */\n\n    'default' => env('FILESYSTEM_DISK', 'local'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Filesystem Disks\n    |--------------------------------------------------------------------------\n    |\n    | Below you may configure as many filesystem disks as necessary, and you\n    | may even configure multiple disks for the same driver. Examples for\n    | most supported storage drivers are configured here for reference.\n    |\n    | Supported drivers: \"local\", \"ftp\", \"sftp\", \"s3\"\n    |\n    */\n\n    'disks' => [\n\n        'local' => [\n            'driver' => 'local',\n            'root' => storage_path('app/private'),\n            'serve' => true,\n            'throw' => false,\n            'report' => false,\n        ],\n\n        'public' => [\n            'driver' => 'local',\n            'root' => storage_path('app/public'),\n            'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage',\n            'visibility' => 'public',\n            'throw' => false,\n            'report' => false,\n        ],\n\n        's3' => [\n            'driver' => 's3',\n            'key' => env('AWS_ACCESS_KEY_ID'),\n            'secret' => env('AWS_SECRET_ACCESS_KEY'),\n            'region' => env('AWS_DEFAULT_REGION'),\n            'bucket' => env('AWS_BUCKET'),\n            'url' => env('AWS_URL'),\n            'endpoint' => env('AWS_ENDPOINT'),\n            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),\n            'throw' => false,\n            'report' => false,\n            // 'visibility' => 'public', // https://statamic.dev/assets#container-visibility\n        ],\n\n        'assets' => [\n            'driver' => 'local',\n            'root' => public_path('img'),\n            'url' => '/img',\n            'visibility' => 'public',\n            'throw' => false,\n            'report' => false,\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Symbolic Links\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the symbolic links that will be created when the\n    | `storage:link` Artisan command is executed. The array keys should be\n    | the locations of the links and the values should be their targets.\n    |\n    */\n\n    'links' => [\n        public_path('storage') => storage_path('app/public'),\n    ],\n\n];\n"
  },
  {
    "path": "config/logging.php",
    "content": "<?php\n\nuse Monolog\\Handler\\NullHandler;\nuse Monolog\\Handler\\StreamHandler;\nuse Monolog\\Handler\\SyslogUdpHandler;\nuse Monolog\\Processor\\PsrLogMessageProcessor;\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Log Channel\n    |--------------------------------------------------------------------------\n    |\n    | This option defines the default log channel that is utilized to write\n    | messages to your logs. The value provided here should match one of\n    | the channels present in the list of \"channels\" configured below.\n    |\n    */\n\n    'default' => env('LOG_CHANNEL', 'stack'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Deprecations Log Channel\n    |--------------------------------------------------------------------------\n    |\n    | This option controls the log channel that should be used to log warnings\n    | regarding deprecated PHP and library features. This allows you to get\n    | your application ready for upcoming major versions of dependencies.\n    |\n    */\n\n    'deprecations' => [\n        'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),\n        'trace' => env('LOG_DEPRECATIONS_TRACE', false),\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Log Channels\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the log channels for your application. Laravel\n    | utilizes the Monolog PHP logging library, which includes a variety\n    | of powerful log handlers and formatters that you're free to use.\n    |\n    | Available drivers: \"single\", \"daily\", \"slack\", \"syslog\",\n    |                    \"errorlog\", \"monolog\", \"custom\", \"stack\"\n    |\n    */\n\n    'channels' => [\n\n        'stack' => [\n            'driver' => 'stack',\n            'channels' => explode(',', (string) env('LOG_STACK', 'single')),\n            'ignore_exceptions' => false,\n        ],\n\n        'single' => [\n            'driver' => 'single',\n            'path' => storage_path('logs/laravel.log'),\n            'level' => env('LOG_LEVEL', 'debug'),\n            'replace_placeholders' => true,\n        ],\n\n        'daily' => [\n            'driver' => 'daily',\n            'path' => storage_path('logs/laravel.log'),\n            'level' => env('LOG_LEVEL', 'debug'),\n            'days' => env('LOG_DAILY_DAYS', 14),\n            'replace_placeholders' => true,\n        ],\n\n        'slack' => [\n            'driver' => 'slack',\n            'url' => env('LOG_SLACK_WEBHOOK_URL'),\n            'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Laravel')),\n            'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),\n            'level' => env('LOG_LEVEL', 'critical'),\n            'replace_placeholders' => true,\n        ],\n\n        'papertrail' => [\n            'driver' => 'monolog',\n            'level' => env('LOG_LEVEL', 'debug'),\n            'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),\n            'handler_with' => [\n                'host' => env('PAPERTRAIL_URL'),\n                'port' => env('PAPERTRAIL_PORT'),\n                'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),\n            ],\n            'processors' => [PsrLogMessageProcessor::class],\n        ],\n\n        'stderr' => [\n            'driver' => 'monolog',\n            'level' => env('LOG_LEVEL', 'debug'),\n            'handler' => StreamHandler::class,\n            'handler_with' => [\n                'stream' => 'php://stderr',\n            ],\n            'formatter' => env('LOG_STDERR_FORMATTER'),\n            'processors' => [PsrLogMessageProcessor::class],\n        ],\n\n        'syslog' => [\n            'driver' => 'syslog',\n            'level' => env('LOG_LEVEL', 'debug'),\n            'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),\n            'replace_placeholders' => true,\n        ],\n\n        'errorlog' => [\n            'driver' => 'errorlog',\n            'level' => env('LOG_LEVEL', 'debug'),\n            'replace_placeholders' => true,\n        ],\n\n        'null' => [\n            'driver' => 'monolog',\n            'handler' => NullHandler::class,\n        ],\n\n        'emergency' => [\n            'path' => storage_path('logs/laravel.log'),\n        ],\n\n    ],\n\n];\n"
  },
  {
    "path": "config/mail.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Mailer\n    |--------------------------------------------------------------------------\n    |\n    | This option controls the default mailer that is used to send all email\n    | messages unless another mailer is explicitly specified when sending\n    | the message. All additional mailers can be configured within the\n    | \"mailers\" array. Examples of each type of mailer are provided.\n    |\n    */\n\n    'default' => env('MAIL_MAILER', 'log'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Mailer Configurations\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure all of the mailers used by your application plus\n    | their respective settings. Several examples have been configured for\n    | you and you are free to add your own as your application requires.\n    |\n    | Laravel supports a variety of mail \"transport\" drivers that can be used\n    | when delivering an email. You may specify which one you're using for\n    | your mailers below. You may also add additional mailers if needed.\n    |\n    | Supported: \"smtp\", \"sendmail\", \"mailgun\", \"ses\", \"ses-v2\",\n    |            \"postmark\", \"resend\", \"log\", \"array\",\n    |            \"failover\", \"roundrobin\"\n    |\n    */\n\n    'mailers' => [\n\n        'smtp' => [\n            'transport' => 'smtp',\n            'scheme' => env('MAIL_SCHEME'),\n            'url' => env('MAIL_URL'),\n            'host' => env('MAIL_HOST', '127.0.0.1'),\n            'port' => env('MAIL_PORT', 2525),\n            'username' => env('MAIL_USERNAME'),\n            'password' => env('MAIL_PASSWORD'),\n            'timeout' => null,\n            'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),\n        ],\n\n        'ses' => [\n            'transport' => 'ses',\n        ],\n\n        'postmark' => [\n            'transport' => 'postmark',\n            // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),\n            // 'client' => [\n            //     'timeout' => 5,\n            // ],\n        ],\n\n        'resend' => [\n            'transport' => 'resend',\n        ],\n\n        'sendmail' => [\n            'transport' => 'sendmail',\n            'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),\n        ],\n\n        'log' => [\n            'transport' => 'log',\n            'channel' => env('MAIL_LOG_CHANNEL'),\n        ],\n\n        'array' => [\n            'transport' => 'array',\n        ],\n\n        'failover' => [\n            'transport' => 'failover',\n            'mailers' => [\n                'smtp',\n                'log',\n            ],\n            'retry_after' => 60,\n        ],\n\n        'roundrobin' => [\n            'transport' => 'roundrobin',\n            'mailers' => [\n                'ses',\n                'postmark',\n            ],\n            'retry_after' => 60,\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Global \"From\" Address\n    |--------------------------------------------------------------------------\n    |\n    | You may wish for all emails sent by your application to be sent from\n    | the same address. Here you may specify a name and address that is\n    | used globally for all emails that are sent by your application.\n    |\n    */\n\n    'from' => [\n        'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),\n        'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')),\n    ],\n\n];\n"
  },
  {
    "path": "config/queue.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Queue Connection Name\n    |--------------------------------------------------------------------------\n    |\n    | Laravel's queue supports a variety of backends via a single, unified\n    | API, giving you convenient access to each backend using identical\n    | syntax for each. The default queue connection is defined below.\n    |\n    */\n\n    'default' => env('QUEUE_CONNECTION', 'sync'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Queue Connections\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the connection options for every queue backend\n    | used by your application. An example configuration is provided for\n    | each backend supported by Laravel. You're also free to add more.\n    |\n    | Drivers: \"sync\", \"database\", \"beanstalkd\", \"sqs\", \"redis\",\n    |          \"deferred\", \"background\", \"failover\", \"null\"\n    |\n    */\n\n    'connections' => [\n\n        'sync' => [\n            'driver' => 'sync',\n        ],\n\n        'database' => [\n            'driver' => 'database',\n            'connection' => env('DB_QUEUE_CONNECTION'),\n            'table' => env('DB_QUEUE_TABLE', 'jobs'),\n            'queue' => env('DB_QUEUE', 'default'),\n            'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),\n            'after_commit' => false,\n        ],\n\n        'beanstalkd' => [\n            'driver' => 'beanstalkd',\n            'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),\n            'queue' => env('BEANSTALKD_QUEUE', 'default'),\n            'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),\n            'block_for' => 0,\n            'after_commit' => false,\n        ],\n\n        'sqs' => [\n            'driver' => 'sqs',\n            'key' => env('AWS_ACCESS_KEY_ID'),\n            'secret' => env('AWS_SECRET_ACCESS_KEY'),\n            'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),\n            'queue' => env('SQS_QUEUE', 'default'),\n            'suffix' => env('SQS_SUFFIX'),\n            'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),\n            'after_commit' => false,\n        ],\n\n        'redis' => [\n            'driver' => 'redis',\n            'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),\n            'queue' => env('REDIS_QUEUE', 'default'),\n            'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),\n            'block_for' => null,\n            'after_commit' => false,\n        ],\n\n        'deferred' => [\n            'driver' => 'deferred',\n        ],\n\n        'background' => [\n            'driver' => 'background',\n        ],\n\n        'failover' => [\n            'driver' => 'failover',\n            'connections' => [\n                'database',\n                'deferred',\n            ],\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Job Batching\n    |--------------------------------------------------------------------------\n    |\n    | The following options configure the database and table that store job\n    | batching information. These options can be updated to any database\n    | connection and table which has been defined by your application.\n    |\n    */\n\n    'batching' => [\n        'database' => env('DB_CONNECTION', 'sqlite'),\n        'table' => 'job_batches',\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Failed Queue Jobs\n    |--------------------------------------------------------------------------\n    |\n    | These options configure the behavior of failed queue job logging so you\n    | can control how and where failed jobs are stored. Laravel ships with\n    | support for storing failed jobs in a simple file or in a database.\n    |\n    | Supported drivers: \"database-uuids\", \"dynamodb\", \"file\", \"null\"\n    |\n    */\n\n    'failed' => [\n        'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),\n        'database' => env('DB_CONNECTION', 'sqlite'),\n        'table' => 'failed_jobs',\n    ],\n\n];\n"
  },
  {
    "path": "config/services.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Third Party Services\n    |--------------------------------------------------------------------------\n    |\n    | This file is for storing the credentials for third party services such\n    | as Mailgun, Postmark, AWS and more. This file provides the de facto\n    | location for this type of information, allowing packages to have\n    | a conventional file to locate the various service credentials.\n    |\n    */\n\n    'postmark' => [\n        'key' => env('POSTMARK_API_KEY'),\n    ],\n\n    'resend' => [\n        'key' => env('RESEND_API_KEY'),\n    ],\n\n    'ses' => [\n        'key' => env('AWS_ACCESS_KEY_ID'),\n        'secret' => env('AWS_SECRET_ACCESS_KEY'),\n        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),\n    ],\n\n    'slack' => [\n        'notifications' => [\n            'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),\n            'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),\n        ],\n    ],\n\n    'github' => [\n        'token' => env('GITHUB_TOKEN'),\n    ],\n\n];\n"
  },
  {
    "path": "config/session.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Str;\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Session Driver\n    |--------------------------------------------------------------------------\n    |\n    | This option determines the default session driver that is utilized for\n    | incoming requests. Laravel supports a variety of storage options to\n    | persist session data. Database storage is a great default choice.\n    |\n    | Supported: \"file\", \"cookie\", \"database\", \"memcached\",\n    |            \"redis\", \"dynamodb\", \"array\"\n    |\n    */\n\n    'driver' => env('SESSION_DRIVER', 'file'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Lifetime\n    |--------------------------------------------------------------------------\n    |\n    | Here you may specify the number of minutes that you wish the session\n    | to be allowed to remain idle before it expires. If you want them\n    | to expire immediately when the browser is closed then you may\n    | indicate that via the expire_on_close configuration option.\n    |\n    */\n\n    'lifetime' => (int) env('SESSION_LIFETIME', 120),\n\n    'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Encryption\n    |--------------------------------------------------------------------------\n    |\n    | This option allows you to easily specify that all of your session data\n    | should be encrypted before it's stored. All encryption is performed\n    | automatically by Laravel and you may use the session like normal.\n    |\n    */\n\n    'encrypt' => env('SESSION_ENCRYPT', false),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session File Location\n    |--------------------------------------------------------------------------\n    |\n    | When utilizing the \"file\" session driver, the session files are placed\n    | on disk. The default storage location is defined here; however, you\n    | are free to provide another location where they should be stored.\n    |\n    */\n\n    'files' => storage_path('framework/sessions'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Database Connection\n    |--------------------------------------------------------------------------\n    |\n    | When using the \"database\" or \"redis\" session drivers, you may specify a\n    | connection that should be used to manage these sessions. This should\n    | correspond to a connection in your database configuration options.\n    |\n    */\n\n    'connection' => env('SESSION_CONNECTION'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Database Table\n    |--------------------------------------------------------------------------\n    |\n    | When using the \"database\" session driver, you may specify the table to\n    | be used to store sessions. Of course, a sensible default is defined\n    | for you; however, you're welcome to change this to another table.\n    |\n    */\n\n    'table' => env('SESSION_TABLE', 'sessions'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Cache Store\n    |--------------------------------------------------------------------------\n    |\n    | When using one of the framework's cache driven session backends, you may\n    | define the cache store which should be used to store the session data\n    | between requests. This must match one of your defined cache stores.\n    |\n    | Affects: \"dynamodb\", \"memcached\", \"redis\"\n    |\n    */\n\n    'store' => env('SESSION_STORE'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Sweeping Lottery\n    |--------------------------------------------------------------------------\n    |\n    | Some session drivers must manually sweep their storage location to get\n    | rid of old sessions from storage. Here are the chances that it will\n    | happen on a given request. By default, the odds are 2 out of 100.\n    |\n    */\n\n    'lottery' => [2, 100],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Cookie Name\n    |--------------------------------------------------------------------------\n    |\n    | Here you may change the name of the session cookie that is created by\n    | the framework. Typically, you should not need to change this value\n    | since doing so does not grant a meaningful security improvement.\n    |\n    */\n\n    'cookie' => env(\n        'SESSION_COOKIE',\n        Str::slug((string) env('APP_NAME', 'laravel')).'-session'\n    ),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Cookie Path\n    |--------------------------------------------------------------------------\n    |\n    | The session cookie path determines the path for which the cookie will\n    | be regarded as available. Typically, this will be the root path of\n    | your application, but you're free to change this when necessary.\n    |\n    */\n\n    'path' => env('SESSION_PATH', '/'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Cookie Domain\n    |--------------------------------------------------------------------------\n    |\n    | This value determines the domain and subdomains the session cookie is\n    | available to. By default, the cookie will be available to the root\n    | domain without subdomains. Typically, this shouldn't be changed.\n    |\n    */\n\n    'domain' => env('SESSION_DOMAIN'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | HTTPS Only Cookies\n    |--------------------------------------------------------------------------\n    |\n    | By setting this option to true, session cookies will only be sent back\n    | to the server if the browser has a HTTPS connection. This will keep\n    | the cookie from being sent to you when it can't be done securely.\n    |\n    */\n\n    'secure' => env('SESSION_SECURE_COOKIE'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | HTTP Access Only\n    |--------------------------------------------------------------------------\n    |\n    | Setting this value to true will prevent JavaScript from accessing the\n    | value of the cookie and the cookie will only be accessible through\n    | the HTTP protocol. It's unlikely you should disable this option.\n    |\n    */\n\n    'http_only' => env('SESSION_HTTP_ONLY', true),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Same-Site Cookies\n    |--------------------------------------------------------------------------\n    |\n    | This option determines how your cookies behave when cross-site requests\n    | take place, and can be used to mitigate CSRF attacks. By default, we\n    | will set this value to \"lax\" to permit secure cross-site requests.\n    |\n    | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value\n    |\n    | Supported: \"lax\", \"strict\", \"none\", null\n    |\n    */\n\n    'same_site' => env('SESSION_SAME_SITE', 'lax'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Partitioned Cookies\n    |--------------------------------------------------------------------------\n    |\n    | Setting this value to true will tie the cookie to the top-level site for\n    | a cross-site context. Partitioned cookies are accepted by the browser\n    | when flagged \"secure\" and the Same-Site attribute is set to \"none\".\n    |\n    */\n\n    'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Session Serialization\n    |--------------------------------------------------------------------------\n    |\n    | This value controls the serialization strategy for session data, which\n    | is JSON by default. Setting this to \"php\" allows the storage of PHP\n    | objects in the session but can make an application vulnerable to\n    | \"gadget chain\" serialization attacks if the APP_KEY is leaked.\n    |\n    | Supported: \"json\", \"php\"\n    |\n    */\n\n    'serialization' => 'json',\n\n];\n"
  },
  {
    "path": "config/statamic/antlers.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Debugbar\n    |--------------------------------------------------------------------------\n    |\n    | Here you may specify whether the Antlers profiler should be added\n    | to the Laravel Debugbar. This is incredibly useful for finding\n    | performance impacts within any of your Antlers templates.\n    |\n    */\n\n    'debugbar' => env('STATAMIC_ANTLERS_DEBUGBAR', true),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Guarded Variables\n    |--------------------------------------------------------------------------\n    |\n    | Any variable pattern that appears in this list will not be allowed\n    | in any Antlers template, including any user-supplied values.\n    |\n    */\n\n    'guardedVariables' => [\n        'config.app.key',\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Guarded Tags\n    |--------------------------------------------------------------------------\n    |\n    | Any tag pattern that appears in this list will not be allowed\n    | in any Antlers template, including any user-supplied values.\n    |\n    */\n\n    'guardedTags' => [\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Guarded Modifiers\n    |--------------------------------------------------------------------------\n    |\n    | Any modifier pattern that appears in this list will not be allowed\n    | in any Antlers template, including any user-supplied values.\n    |\n    */\n\n    'guardedModifiers' => [\n\n    ],\n\n];\n"
  },
  {
    "path": "config/statamic/api.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | API\n    |--------------------------------------------------------------------------\n    |\n    | Whether the API should be enabled, and through what route. You\n    | can enable or disable the whole API, and expose individual\n    | resources per environment, depending on your site needs.\n    |\n    | https://statamic.dev/content-api#enable-the-api\n    |\n    */\n\n    'enabled' => env('STATAMIC_API_ENABLED', false),\n\n    'resources' => [\n        'collections' => false,\n        'navs' => false,\n        'taxonomies' => false,\n        'assets' => false,\n        'globals' => false,\n        'forms' => false,\n        'users' => false,\n    ],\n\n    'route' => env('STATAMIC_API_ROUTE', 'api'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Authentication\n    |--------------------------------------------------------------------------\n    |\n    | By default, the API will be publicly accessible. However, you may define\n    | an API token here which will be used to authenticate requests.\n    |\n    */\n\n    'auth_token' => env('STATAMIC_API_AUTH_TOKEN'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Middleware\n    |--------------------------------------------------------------------------\n    |\n    | Define the middleware / middleware group that will be applied to the\n    | API route group. If you want to externally expose this API, here\n    | you can configure a middleware-based authentication layer.\n    |\n    */\n\n    'middleware' => env('STATAMIC_API_MIDDLEWARE', 'api'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Pagination\n    |--------------------------------------------------------------------------\n    |\n    | The numbers of items to show on each paginated page.\n    |\n    */\n\n    'pagination_size' => 50,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Caching\n    |--------------------------------------------------------------------------\n    |\n    | By default, Statamic will cache each endpoint until the specified\n    | expiry, or until content is changed. See the documentation for\n    | more details on how to customize your cache implementation.\n    |\n    | https://statamic.dev/content-api#caching\n    |\n    */\n\n    'cache' => [\n        'expiry' => 60,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Exclude Keys\n    |--------------------------------------------------------------------------\n    |\n    | Here you may provide an array of keys to be excluded from API responses.\n    | For example, you may want to hide things like edit_url, api_url, etc.\n    |\n    */\n\n    'excluded_keys' => [\n        //\n    ],\n\n];"
  },
  {
    "path": "config/statamic/assets.php",
    "content": "<?php\n\nreturn [\n\n    'image_manipulation' => [\n\n        /*\n        |--------------------------------------------------------------------------\n        | Route Prefix\n        |--------------------------------------------------------------------------\n        |\n        | The route prefix for serving HTTP based manipulated images through Glide.\n        | If using the cached option, this should be the URL of the cached path.\n        |\n        */\n\n        'route' => 'img',\n\n        /*\n        |--------------------------------------------------------------------------\n        | Require Glide security token\n        |--------------------------------------------------------------------------\n        |\n        | With this option enabled, you are protecting your website from mass image\n        | resize attacks. You will need to generate tokens using the Glide tag\n        | but may want to disable this while in development to tinker.\n        |\n        */\n\n        'secure' => true,\n\n        /*\n        |--------------------------------------------------------------------------\n        | Image Manipulation Driver\n        |--------------------------------------------------------------------------\n        |\n        | The driver that will be used under the hood for image manipulation.\n        | Supported: \"gd\", \"imagick\" or a class name of a custom driver.\n        |\n        */\n\n        'driver' => 'gd',\n\n        /*\n        |--------------------------------------------------------------------------\n        | Save Cached Images\n        |--------------------------------------------------------------------------\n        |\n        | Enabling this will make Glide save publicly accessible images. It will\n        | increase performance at the cost of the dynamic nature of HTTP based\n        | image manipulation. You will need to invalidate images manually.\n        |\n        */\n\n        'cache' => false,\n        'cache_path' => public_path('img'),\n\n        /*\n        |--------------------------------------------------------------------------\n        | Image Manipulation Defaults\n        |--------------------------------------------------------------------------\n        |\n        | You may define global defaults for all manipulation parameters, such as\n        | quality, format, and sharpness. These can and will be overwritten\n        | on the tag parameter level as well as the preset level.\n        |\n        */\n\n        'defaults' => [\n            // 'quality' => 50,\n        ],\n\n        /*\n        |--------------------------------------------------------------------------\n        | Image Manipulation Presets\n        |--------------------------------------------------------------------------\n        |\n        | Rather than specifying your manipulation params in your templates with\n        | the glide tag, you may define them here and reference their handles.\n        | They may also be automatically generated when you upload assets.\n        | Containers can be configured to warm these caches on upload.\n        |\n        */\n\n        'presets' => [\n            // 'small' => ['w' => 200, 'h' => 200, 'q' => 75, 'fit' => 'crop'],\n        ],\n\n        /*\n        |--------------------------------------------------------------------------\n        | Generate Image Manipulation Presets on Upload\n        |--------------------------------------------------------------------------\n        |\n        | By default, presets will be automatically generated on upload, ensuring\n        | the cached images are available when they are first used. You may opt\n        | out of this behavior here and have the presets generated on demand.\n        |\n        */\n\n        'generate_presets_on_upload' => true,\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Auto-Crop Assets\n    |--------------------------------------------------------------------------\n    |\n    | Enabling this will make Glide automatically crop assets at their focal\n    | point (which is the center if no focal point is defined). Otherwise,\n    | you will need to manually add any crop related parameters.\n    |\n    */\n\n    'auto_crop' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Control Panel Thumbnail Restrictions\n    |--------------------------------------------------------------------------\n    |\n    | Thumbnails will not be generated for any assets any larger (in either\n    | axis) than the values listed below. This helps prevent memory usage\n    | issues out of the box. You may increase or decrease as necessary.\n    |\n    */\n\n    'thumbnails' => [\n        'max_width' => 10000,\n        'max_height' => 10000,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Control Panel Video Thumbnails\n    |--------------------------------------------------------------------------\n    |\n    | When enabled, Statamic will generate thumbnails for videos.\n    | Generated thumbnails are displayed in the Control Panel.\n    |\n    */\n\n    'video_thumbnails' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | File Previews with Google Docs\n    |--------------------------------------------------------------------------\n    |\n    | Filetypes that cannot be rendered with HTML5 can opt into the Google Docs\n    | Viewer. Google will get temporary access to these files so keep that in\n    | mind for any privacy implications: https://policies.google.com/privacy\n    |\n    */\n\n    'google_docs_viewer' => false,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Cache Metadata\n    |--------------------------------------------------------------------------\n    |\n    | Asset metadata (filesize, dimensions, custom data, etc) will get cached\n    | to optimize performance, so that it will not need to be constantly\n    | re-evaluated from disk. You may disable this option if you are\n    | planning to continually modify the same asset repeatedly.\n    |\n    */\n\n    'cache_meta' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Focal Point Editor\n    |--------------------------------------------------------------------------\n    |\n    | When editing images in the Control Panel, there is an option to choose\n    | a focal point. When working with third-party image providers such as\n    | Cloudinary it can be useful to disable Statamic's built-in editor.\n    |\n    */\n\n    'focal_point_editor' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enforce Lowercase Filenames\n    |--------------------------------------------------------------------------\n    |\n    | Control whether asset filenames will be converted to lowercase when\n    | uploading and renaming. This can help you avoid file conflicts\n    | when working in case-insensitive filesystem environments.\n    |\n    */\n\n    'lowercase' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Additional Uploadable Extensions\n    |--------------------------------------------------------------------------\n    |\n    | Statamic will only allow uploads of certain approved file extensions.\n    | If you need to allow more file extensions, you may add them here.\n    |\n    */\n\n    'additional_uploadable_extensions' => [],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Additional Filename Character Replacements\n    |--------------------------------------------------------------------------\n    |\n    | When uploading files, certain characters in filenames will be replaced\n    | to ensure a safe filename. You may configure additional replacements.\n    | These are in addition to the native ones. They are not overridable.\n    |\n    */\n\n    'additional_filename_replacements' => [],\n\n    /*\n    |--------------------------------------------------------------------------\n    | SVG Sanitization\n    |--------------------------------------------------------------------------\n    |\n    | Statamic will automatically sanitize SVG files when uploaded to avoid\n    | potential security issues. However, if you have a valid reason for\n    | disabling this, and you trust your users, you may do so here.\n    |\n    */\n\n    'svg_sanitization_on_upload' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | FFmpeg\n    |--------------------------------------------------------------------------\n    |\n    | Statamic uses FFmpeg to extract thumbnails from videos to be shown in the\n    | Control Panel. You may adjust the binary location and cache path here.\n    |\n    */\n\n    'ffmpeg' => [\n        'binary' => null,\n        'cache_path' => storage_path('statamic/glide/ffmpeg'),\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Replicator and Bard Set Preview Images\n    |--------------------------------------------------------------------------\n    |\n    | Replicator and Bard sets may have preview images to give users a visual\n    | representation of the content within. Here you may specify the asset\n    | container and folder where these preview images are to be stored.\n    |\n    */\n\n    'set_preview_images' => [\n        'container' => 'assets',\n        'folder' => 'set-previews',\n    ],\n\n];"
  },
  {
    "path": "config/statamic/autosave.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enable autosave\n    |--------------------------------------------------------------------------\n    |\n    | THIS IS A EXPERIMENTAL FEATURE. Things may go wrong.\n    |\n    | Set to true to enable autosave. You must also enable autosave\n    | manually in every collection in order for it to work.\n    |\n    | For example, inside `content/collections/pages.yaml`, add\n    | `autosave: 5000` for a 5s interval or `autosave: true`\n    | to use the default interval as defined below.\n    |\n    */\n\n    'enabled' => false,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default autosave interval\n    |--------------------------------------------------------------------------\n    |\n    | The default value may be set here and will apply to all collections.\n    | However, it is also possible to manually adjust the value in the\n    | each collection's config file. By default, this is set to 5s.\n    |\n    */\n\n    'interval' => 5000,\n\n];\n"
  },
  {
    "path": "config/statamic/cp.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Control Panel\n    |--------------------------------------------------------------------------\n    |\n    | Whether the Control Panel should be enabled, and through what route.\n    |\n    */\n\n    'enabled' => env('CP_ENABLED', true),\n\n    'route' => env('CP_ROUTE', 'cp'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Authentication\n    |--------------------------------------------------------------------------\n    |\n    | Whether the Control Panel's authentication pages should be enabled,\n    | and where users should be redirected in order to authenticate.\n    |\n    */\n\n    'auth' => [\n        'enabled' => true,\n        'redirect_to' => null,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Start Page\n    |--------------------------------------------------------------------------\n    |\n    | When a user logs into the Control Panel, they will be taken here.\n    | For example: \"dashboard\", \"collections/pages\", etc.\n    |\n    */\n\n    'start_page' => 'dashboard',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Dashboard Widgets\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define any number of dashboard widgets. You're free to\n    | use the same widget multiple times in different configurations.\n    |\n    */\n\n    'widgets' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Pagination\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define the default pagination size as well as the options\n    | the user can select on any paginated listing in the Control Panel.\n    |\n    */\n\n    'pagination_size' => 50,\n\n    'pagination_size_options' => [10, 25, 50, 100, 500],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Links to Documentation\n    |--------------------------------------------------------------------------\n    |\n    | Show contextual links to documentation throughout the Control Panel.\n    |\n    */\n\n    'link_to_docs' => env('STATAMIC_LINK_TO_DOCS', true),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Support Link\n    |--------------------------------------------------------------------------\n    |\n    | Set the location of the support link in the header.\n    |\n    */\n\n    'support_url' => env('STATAMIC_SUPPORT_URL', 'https://statamic.com/support'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | White Labeling\n    |--------------------------------------------------------------------------\n    |\n    | When in Pro Mode you may replace the Statamic name, logo, favicon,\n    | and add your own CSS to the control panel to match your\n    | company or client's brand.\n    |\n    */\n\n    'custom_cms_name' => env('STATAMIC_CUSTOM_CMS_NAME', 'Statamic'),\n\n    'custom_logo_url' => env('STATAMIC_CUSTOM_LOGO_URL', null),\n\n    'custom_dark_logo_url' => env('STATAMIC_CUSTOM_DARK_LOGO_URL', null),\n\n    'custom_logo_text' => env('STATAMIC_CUSTOM_LOGO_TEXT', null),\n\n    'custom_favicon_url' => env('STATAMIC_CUSTOM_FAVICON_URL', null),\n\n    'custom_css_url' => env('STATAMIC_CUSTOM_CSS_URL', null),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Thumbnails\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define additional CP asset thumbnail presets.\n    |\n    */\n\n    'thumbnail_presets' => [\n        // 'medium' => 800,\n    ],\n\n];"
  },
  {
    "path": "config/statamic/editions.php",
    "content": "<?php\n\nreturn [\n\n    'pro' => env('STATAMIC_PRO_ENABLED', false),\n\n    'addons' => [\n        //\n    ],\n\n];\n"
  },
  {
    "path": "config/statamic/forms.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Forms Path\n    |--------------------------------------------------------------------------\n    |\n    | Where your form YAML files are stored.\n    |\n    */\n\n    'forms' => resource_path('forms'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Email View Folder\n    |--------------------------------------------------------------------------\n    |\n    | The folder under resources/views where your email templates are found.\n    |\n    */\n\n    'email_view_folder' => null,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Send Email Job\n    |--------------------------------------------------------------------------\n    |\n    | The class name of the job that will be used to send an email.\n    |\n    */\n\n    'send_email_job' => \\Statamic\\Forms\\SendEmail::class,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Exporters\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define all the available form submission exporters.\n    | You may customize the options within each exporter's array.\n    |\n    */\n\n    'exporters' => [\n        'csv' => [\n            'class' => Statamic\\Forms\\Exporters\\CsvExporter::class,\n        ],\n        'json' => [\n            'class' => Statamic\\Forms\\Exporters\\JsonExporter::class,\n        ],\n    ],\n\n];\n"
  },
  {
    "path": "config/statamic/git.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Git Integration\n    |--------------------------------------------------------------------------\n    |\n    | Whether Statamic's git integration should be enabled. This feature\n    | assumes that git is already installed and accessible by your\n    | PHP process' server user. For more info, see the docs at:\n    |\n    | https://statamic.dev/git-automation\n    |\n    */\n\n    'enabled' => env('STATAMIC_GIT_ENABLED', false),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Automatically Run\n    |--------------------------------------------------------------------------\n    |\n    | By default, commits are automatically queued when `Saved` or `Deleted`\n    | events are fired. If you prefer users to manually trigger commits\n    | using the `Git` utility interface, you may set this to `false`.\n    |\n    | https://statamic.dev/git-automation#committing-changes\n    |\n    */\n\n    'automatic' => env('STATAMIC_GIT_AUTOMATIC', true),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Queue Connection\n    |--------------------------------------------------------------------------\n    |\n    | You may choose which queue connection should be used when dispatching\n    | commit jobs. Unless specified, the default connection will be used.\n    |\n    | https://statamic.dev/git-automation#queueing-commits\n    |\n    */\n\n    'queue_connection' => env('STATAMIC_GIT_QUEUE_CONNECTION'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Dispatch Delay\n    |--------------------------------------------------------------------------\n    |\n    | When `Saved` and `Deleted` events queue up commits, you may wish to\n    | set a delay time in minutes for each queued job. This can allow\n    | for more consolidated commits when you have multiple users\n    | making simultaneous content changes to your repository.\n    |\n    | Note: Not supported by default `sync` queue driver.\n    |\n    */\n\n    'dispatch_delay' => env('STATAMIC_GIT_DISPATCH_DELAY', 0),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Git User\n    |--------------------------------------------------------------------------\n    |\n    | The git user that will be used when committing changes. By default, it\n    | will attempt to commit with the authenticated user's name and email\n    | when possible, falling back to the below user when not available.\n    |\n    | https://statamic.dev/git-automation#git-user\n    |\n    */\n\n    'use_authenticated' => true,\n\n    'user' => [\n        'name' => env('STATAMIC_GIT_USER_NAME', 'Spock'),\n        'email' => env('STATAMIC_GIT_USER_EMAIL', 'spock@example.com'),\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Tracked Paths\n    |--------------------------------------------------------------------------\n    |\n    | Define the tracked paths to be considered when staging changes. Default\n    | stache and file locations are already set up for you, but feel free\n    | to modify these paths to suit your storage config. Referencing\n    | absolute paths to external repos is also completely valid.\n    |\n    */\n\n    'paths' => [\n        base_path('content'),\n        base_path('users'),\n        resource_path('addons'),\n        resource_path('blueprints'),\n        resource_path('fieldsets'),\n        resource_path('forms'),\n        resource_path('users'),\n        resource_path('preferences.yaml'),\n        resource_path('sites.yaml'),\n        storage_path('forms'),\n        public_path('assets'),\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Git Binary\n    |--------------------------------------------------------------------------\n    |\n    | By default, Statamic will try to use the \"git\" command, but you can set\n    | an absolute path to the git binary if necessary for your environment.\n    |\n    */\n\n    'binary' => env('STATAMIC_GIT_BINARY', 'git'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Commands\n    |--------------------------------------------------------------------------\n    |\n    | Define a list commands to be run when Statamic is ready to `git add`\n    | and `git commit` your changes. These commands will be run once\n    | per repo, attempting to consolidate commits where possible.\n    |\n    | https://statamic.dev/git-automation#customizing-commits\n    |\n    */\n\n    'commands' => [\n        '{{ git }} add {{ paths }}',\n        '{{ git }} -c \"user.name={{ name }}\" -c \"user.email={{ email }}\" commit -m \"{{ message }}\"',\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Push\n    |--------------------------------------------------------------------------\n    |\n    | Determine whether `git push` should be run after the commands above\n    | have finished. This is disabled by default, but can be enabled\n    | globally, or per environment using the provided variable.\n    |\n    | https://statamic.dev/git-automation#pushing-changes\n    |\n    */\n\n    'push' => env('STATAMIC_GIT_PUSH', false),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Ignored Events\n    |--------------------------------------------------------------------------\n    |\n    | Statamic will listen on all `Saved` and `Deleted` events, as well\n    | as any events registered by installed addons. If you wish to\n    | ignore any specific events, you may reference them here.\n    |\n    */\n\n    'ignored_events' => [\n        // \\Statamic\\Events\\UserSaved::class,\n        // \\Statamic\\Events\\UserDeleted::class,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Locale\n    |--------------------------------------------------------------------------\n    |\n    | The locale to be used when translating commit messages, etc. By\n    | default, the authenticated user's locale will be used, but\n    | feel free to override this using the provided variable.\n    |\n    */\n\n    'locale' => env('STATAMIC_GIT_LOCALE', null),\n\n];\n"
  },
  {
    "path": "config/statamic/graphql.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | GraphQL\n    |--------------------------------------------------------------------------\n    |\n    | Here you may enable the GraphQL API, and select which resources\n    | are available to be queried, depending on your site's needs.\n    |\n    | https://statamic.dev/graphql\n    |\n    */\n\n    'enabled' => env('STATAMIC_GRAPHQL_ENABLED', false),\n\n    'resources' => [\n        'collections' => false,\n        'navs' => false,\n        'taxonomies' => false,\n        'assets' => false,\n        'globals' => false,\n        'forms' => false,\n        'sites' => false,\n        'users' => false,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Queries\n    |--------------------------------------------------------------------------\n    |\n    | Here you may list queries to be added to the Statamic schema.\n    |\n    | https://statamic.dev/graphql#custom-queries\n    |\n    */\n\n    'queries' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Mutations\n    |--------------------------------------------------------------------------\n    |\n    | Here you may list mutations to be added to the Statamic schema.\n    |\n    | https://statamic.dev/graphql#custom-mutations\n    */\n\n    'mutations' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Middleware\n    |--------------------------------------------------------------------------\n    |\n    | Here you may list middleware to be added to the Statamic schema.\n    |\n    | https://statamic.dev/graphql#custom-middleware\n    |\n    */\n\n    'middleware' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Caching\n    |--------------------------------------------------------------------------\n    |\n    | By default, Statamic will cache each request until the specified\n    | expiry, or until content is changed. See the documentation for\n    | more details on how to customize your cache implementation.\n    |\n    | https://statamic.dev/graphql#caching\n    |\n    */\n\n    'cache' => [\n        'expiry' => 60,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Introspection\n    |--------------------------------------------------------------------------\n    |\n    | Introspection queries allow a user to see the schema and will power\n    | development tools. This is \"auto\" by default, which will enable\n    | it locally and keep it disabled everywhere else for security.\n    |\n    */\n\n    'introspection' => env('STATAMIC_GRAPHQL_INTROSPECTION_ENABLED', 'auto'),\n\n];\n"
  },
  {
    "path": "config/statamic/live_preview.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Devices\n    |--------------------------------------------------------------------------\n    |\n    | Live Preview displays a device selector for you to preview the page\n    | in predefined sizes. You are free to add or edit these presets.\n    |\n    */\n\n    'devices' => [\n        'Laptop' => ['width' => 1440, 'height' => 900],\n        'Tablet' => ['width' => 1024, 'height' => 786],\n        'Mobile' => ['width' => 375, 'height' => 812],\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Additional Inputs\n    |--------------------------------------------------------------------------\n    |\n    | Additional fields may be added to the Live Preview header bar. You\n    | may define a list of Vue components to be injected. Their values\n    | will be added to the cascade on the front-end for you to use.\n    |\n    */\n\n    'inputs' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Force Reload Javascript Modules\n    |--------------------------------------------------------------------------\n    |\n    | To force a reload, Live Preview appends a timestamp to the URL on\n    | script tags of type 'module'. You may disable this behavior here.\n    |\n    */\n\n    'force_reload_js_modules' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Hot Reload Contents\n    |--------------------------------------------------------------------------\n    |\n    | Should the Live Preview embed be hot-reloaded when the content changes?\n    | Only applies when \"Refresh\" is disabled on the live preview target.\n    |\n    */\n\n    'hot_reload_contents' => true,\n\n];"
  },
  {
    "path": "config/statamic/markdown.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Markdown Parser Configurations\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define the configuration arrays for each markdown parser.\n    | You may use the base CommonMark options as well as any extensions'\n    | options here. The available options are in the CommonMark docs.\n    |\n    | https://statamic.dev/extending/markdown#configuration\n    |\n    */\n\n    'configs' => [\n\n        'default' => [\n             'heading_permalink' => [\n                 'id_prefix' => '',\n                 'fragment_prefix' => '',\n                 'apply_id_to_heading' => true,\n                 'html_class' => 'c-anchor',\n                 'aria_hidden' => false,\n                 'insert' => 'after',\n                 'symbol' => '#',\n             ],\n        ],\n\n    ],\n\n];\n"
  },
  {
    "path": "config/statamic/oauth.php",
    "content": "<?php\n\nreturn [\n\n    'enabled' => env('STATAMIC_OAUTH_ENABLED', false),\n\n    'email_login_enabled' => true,\n\n    'providers' => [\n        // 'github',\n    ],\n\n    'routes' => [\n        'login' => 'oauth/{provider}',\n        'callback' => 'oauth/{provider}/callback',\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Create User\n    |--------------------------------------------------------------------------\n    |\n    | Whether or not a user account should be created upon authentication\n    | with an OAuth provider. If disabled, a user account will be need\n    | to be explicitly created ahead of time.\n    |\n    */\n\n    'create_user' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Merge User Data\n    |--------------------------------------------------------------------------\n    |\n    | When authenticating with an OAuth provider, the user data returned\n    | such as their name will be merged with the existing user account.\n    |\n    */\n\n    'merge_user_data' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Unauthorized Redirect\n    |--------------------------------------------------------------------------\n    |\n    | This controls where the user is taken after authenticating with\n    | an OAuth provider but their account is unauthorized. This may\n    | happen when the create_user option has been set to false.\n    |\n    */\n\n    'unauthorized_redirect' => null,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Remember Me\n    |--------------------------------------------------------------------------\n    |\n    | Whether or not the \"remember me\" functionality should be used when\n    | authenticating using OAuth. When enabled, the user will remain\n    | logged in indefinitely, or until they manually log out.\n    |\n    */\n\n    'remember_me' => true,\n\n];\n"
  },
  {
    "path": "config/statamic/protect.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default (or site-wide) Scheme\n    |--------------------------------------------------------------------------\n    |\n    | The default scheme will be applied to every page of the site.\n    | By default, you probably won't want to protect anything\n    | at all, but you are free to select one if necessary.\n    |\n    */\n\n    'default' => null,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Protection Schemes\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define all of the protection schemes for your application\n    | as well as their drivers. You may even define multiple schemes for\n    | the same driver to easily protect different types of pages.\n    |\n    | Supported drivers: \"ip_address\", \"auth\", \"password\"\n    |\n    */\n\n    'schemes' => [\n\n        'ip_address' => [\n            'driver' => 'ip_address',\n            'allowed' => ['127.0.0.1'],\n        ],\n\n        'logged_in' => [\n            'driver' => 'auth',\n            'login_url' => '/login',\n            'append_redirect' => true,\n        ],\n\n        'password' => [\n            'driver' => 'password',\n            'allowed' => ['secret'],\n            'field' => null,\n            'form_url' => null,\n        ],\n\n    ],\n\n];\n"
  },
  {
    "path": "config/statamic/revisions.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Revisions\n    |--------------------------------------------------------------------------\n    |\n    | Revisions must be enabled per-collection by adding `revisions: true` to\n    | the collection's yaml file. Here you may disable revisions completely\n    | in one go. This is useful for disabling revisions per environment.\n    |\n    */\n\n    'enabled' => env('STATAMIC_REVISIONS_ENABLED', false),\n\n];\n"
  },
  {
    "path": "config/statamic/routes.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enable Routes\n    |--------------------------------------------------------------------------\n    |\n    | Statamic adds its own routes to the front-end of your site. You are\n    | free to disable this behavior.\n    |\n    | More info: https://statamic.dev/routing\n    |\n    */\n\n    'enabled' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enable Route Bindings\n    |--------------------------------------------------------------------------\n    |\n    | Whether route bindings for Statamic repositories (entry, taxonomy,\n    | collections, etc) are enabled for front end routes. This may be\n    | useful if you want to make your own custom routes with them.\n    |\n    */\n\n    'bindings' => false,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Action Route Prefix\n    |--------------------------------------------------------------------------\n    |\n    | Some extensions may provide routes that go through the frontend of your\n    | website. These URLs begin with the following prefix. We've chosen an\n    | unobtrusive default but you are free to select whatever you want.\n    |\n    */\n\n    'action' => '!',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Middleware\n    |--------------------------------------------------------------------------\n    |\n    | Define the middleware that will be applied to the web route group.\n    |\n    */\n\n    'middleware' => 'web',\n\n];\n"
  },
  {
    "path": "config/statamic/search.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default search index\n    |--------------------------------------------------------------------------\n    |\n    | This option controls the search index that gets queried when performing\n    | search functions without explicitly selecting another index.\n    |\n    */\n\n    'default' => env('STATAMIC_DEFAULT_SEARCH_INDEX', 'docs-'.config('docs.version')),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Search Indexes\n    |--------------------------------------------------------------------------\n    |\n    | Here you can define all of the available search indexes.\n    |\n    */\n\n    'indexes' => [\n\n        'docs-'.config('docs.version') => [\n            'driver' => env('SEARCH_DRIVER', 'meilisearch'),\n            'searchables' => ['docs:*', 'storybook:*'],\n            'fields' => [\n                'title',\n                'search_title',\n                'content',\n                'origin_title',\n                'search_content',\n                'additional_context',\n                'hierarchy_lvl0',\n                'hierarchy_lvl1',\n                'url',\n            ],\n            'settings' => [\n                'rankingRules' => [\n                    'words',\n                    'typo',\n                    'proximity',\n                    'attribute',\n                    'exactness',\n                    'origin_title:desc',\n                    'hierarchy_lvl0:asc',\n                ],\n                'searchableAttributes' => [\n                    'additional_context',\n                    'hierarchy_lvl0',\n                    'title',\n                    'origin_title',\n                    'search_title',\n                    'search_content',\n                    'hierarchy_lvl1',\n                    'url',\n                ],\n            ],\n            'content_retriever' => App\\Search\\RequestContentRetriever::class,\n            'document_transformers' => [\n                App\\Search\\DocTransformer::class,\n            ],\n        ],\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Driver Defaults\n    |--------------------------------------------------------------------------\n    |\n    | Here you can specify default configuration to be applied to all indexes\n    | that use the corresponding driver. For instance, if you have two\n    | indexes that use the \"local\" driver, both of them can have the\n    | same base configuration. You may override for each index.\n    |\n    */\n\n    'drivers' => [\n\n        'local' => [\n            'path' => storage_path('statamic/search'),\n        ],\n\n        'algolia' => [\n            'credentials' => [\n                'id' => env('ALGOLIA_APP_ID', ''),\n                'secret' => env('ALGOLIA_SECRET', ''),\n            ],\n        ],\n\n        'meilisearch' => [\n            'credentials' => [\n                'url' => env('MEILISEARCH_HOST', 'http://localhost:7700'),\n                'secret' => env('MEILISEARCH_KEY', ''),\n            ],\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Search Defaults\n    |--------------------------------------------------------------------------\n    |\n    | Here you can specify default configuration to be applied to all indexes\n    | regardless of the driver. You can override these per driver or per index.\n    |\n    */\n\n    'defaults' => [\n        'fields' => ['title'],\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Indexing Queue\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the queue name and connection used when indexing\n    | documents.\n    |\n    */\n\n    'queue' => env('STATAMIC_SEARCH_QUEUE'),\n\n    'queue_connection' => env('STATAMIC_SEARCH_QUEUE_CONNECTION'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Chunk Size\n    |--------------------------------------------------------------------------\n    |\n    | Here you can configure the chunk size used when indexing documents.\n    | The higher you make it, the more memory it will use, but the quicker\n    | the indexing process will be.\n    |\n    */\n\n    'chunk_size' => 100,\n\n];\n"
  },
  {
    "path": "config/statamic/stache.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | File Watcher\n    |--------------------------------------------------------------------------\n    |\n    | File changes will be noticed and data will be updated accordingly.\n    | This can be disabled to reduce overhead, but you will need to\n    | either update the cache manually or use the Control Panel.\n    |\n    */\n\n    'watcher' => env('STATAMIC_STACHE_WATCHER', 'auto'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Cache Store\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure which Cache Store the Stache uses.\n    |\n    */\n\n    'cache_store' => null,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Stores\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the stores that are used inside the Stache.\n    |\n    | https://statamic.dev/stache#stores\n    |\n    */\n\n    'stores' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Indexes\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define any additional indexes that will be inherited\n    | by each store in the Stache. You may also define indexes on a\n    | per-store level by adding an \"indexes\" key to its config.\n    |\n    */\n\n    'indexes' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Locking\n    |--------------------------------------------------------------------------\n    |\n    | In order to prevent concurrent requests from updating the Stache at the\n    | same time and wasting resources, it will be locked so that subsequent\n    | requests will have to wait until the first one has been completed.\n    |\n    | https://statamic.dev/stache#locks\n    |\n    */\n\n    'lock' => [\n        'enabled' => true,\n        'timeout' => 30,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Warming Optimization\n    |--------------------------------------------------------------------------\n    |\n    | These options control performance optimizations during Stache warming.\n    |\n    */\n\n    'warming' => [\n        // Enable parallel store processing for faster warming on multi-core systems\n        'parallel_processing' => env('STATAMIC_STACHE_PARALLEL_WARMING', false),\n\n        // Maximum number of parallel processes (0 = auto-detect CPU cores)\n        'max_processes' => env('STATAMIC_STACHE_MAX_PROCESSES', 0),\n\n        // Minimum number of stores required to enable parallel processing\n        'min_stores_for_parallel' => env('STATAMIC_STACHE_MIN_STORES_PARALLEL', 3),\n\n        // Concurrency driver: 'process', 'fork', or 'sync'\n        'concurrency_driver' => env('STATAMIC_STACHE_CONCURRENCY_DRIVER', 'process'),\n    ],\n\n];\n"
  },
  {
    "path": "config/statamic/static_caching.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Active Static Caching Strategy\n    |--------------------------------------------------------------------------\n    |\n    | To enable Static Caching, you should choose a strategy from the ones\n    | you have defined below. Leave this null to disable static caching.\n    |\n    */\n\n    'strategy' => env('STATAMIC_STATIC_CACHING_STRATEGY', null),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Caching Strategies\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define all of the static caching strategies for your\n    | application as well as their drivers.\n    |\n    | Supported drivers: \"application\", \"file\"\n    |\n    */\n\n    'strategies' => [\n\n        'half' => [\n            'driver' => 'application',\n            'expiry' => null,\n        ],\n\n        'full' => [\n            'driver' => 'file',\n            'path' => public_path('static'),\n            'lock_hold_length' => 0,\n            'permissions' => [\n                'directory' => 0755,\n                'file' => 0644,\n            ],\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Exclusions\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define a list of URLs to be excluded from static\n    | caching. You may want to exclude URLs containing dynamic\n    | elements like contact forms, or shopping carts.\n    |\n    */\n\n    'exclude' => [\n\n        'class' => null,\n\n        'urls' => [\n            '/sitemap.xml',\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Invalidation Rules\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define the rules that trigger when and how content would be\n    | flushed from the static cache. See the documentation for more details.\n    | If a custom class is not defined, the default invalidator is used.\n    |\n    | https://statamic.dev/static-caching\n    |\n    */\n\n    'invalidation' => [\n\n        'class' => null,\n\n        'rules' => [\n            //\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Ignoring Query Strings\n    |--------------------------------------------------------------------------\n    |\n    | Statamic will cache pages of the same URL but with different query\n    | parameters separately. This is useful for pages with pagination.\n    | If you'd like to ignore the query strings, you may do so.\n    |\n    */\n\n    'ignore_query_strings' => false,\n\n    'allowed_query_strings' => [\n        //\n    ],\n\n    'disallowed_query_strings' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Nocache\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define where the nocache data is stored.\n    |\n    | https://statamic.dev/tags/nocache#database\n    |\n    | Supported drivers: \"cache\", \"database\"\n    |\n    */\n\n    'nocache' => 'cache',\n\n    'nocache_db_connection' => env('STATAMIC_NOCACHE_DB_CONNECTION'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Replacers\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define replacers that dynamically replace content within\n    | the response. Each replacer must implement the Replacer interface.\n    |\n    */\n\n    'replacers' => [\n        \\Statamic\\StaticCaching\\Replacers\\CsrfTokenReplacer::class,\n        \\Statamic\\StaticCaching\\Replacers\\NoCacheReplacer::class,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Warm Queue\n    |--------------------------------------------------------------------------\n    |\n    | Here you may define the queue name and connection\n    | that will be used when warming the static cache and\n    | optionally set the \"--insecure\" flag by default.\n    |\n    */\n\n    'warm_queue' => env('STATAMIC_STATIC_WARM_QUEUE'),\n\n    'warm_queue_connection' => env('STATAMIC_STATIC_WARM_QUEUE_CONNECTION'),\n\n    'warm_insecure' => env('STATAMIC_STATIC_WARM_INSECURE', false),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Background Re-cache\n    |--------------------------------------------------------------------------\n    |\n    | When this is enabled, Statamic will re-cache URLs in the background,\n    | overwriting the existing cache, without removing it first.\n    |\n    */\n\n    'background_recache' => env('STATAMIC_BACKGROUND_RECACHE', false),\n\n    'recache_token' => env('STATAMIC_RECACHE_TOKEN'),\n\n    'recache_token_parameter' => '__recache',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Shared Error Pages\n    |--------------------------------------------------------------------------\n    |\n    | You may choose to share the same statically generated error page across\n    | all errors. For example, the first time a 404 is encountered it will\n    | be generated and cached, and then served for all subsequent 404s.\n    |\n    | This is only supported for half measure.\n    |\n    */\n\n    'share_errors' => false,\n\n];\n"
  },
  {
    "path": "config/statamic/system.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | License Key\n    |--------------------------------------------------------------------------\n    |\n    | The license key for the corresponding domain from your Statamic account.\n    | Without a key entered, your app will be considered to be in Trial Mode.\n    |\n    | https://statamic.dev/licensing#trial-mode\n    |\n    */\n\n    'license_key' => env('STATAMIC_LICENSE_KEY'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enable Multi-site\n    |--------------------------------------------------------------------------\n    |\n    | Whether Statamic's multi-site functionality should be enabled. It is\n    | assumed Statamic Pro is also enabled. To get started, you can run\n    | the `php please multisite` command to update your content file\n    | structure, after which you can manage your sites in the CP.\n    |\n    | https://statamic.dev/multi-site\n    |\n    */\n\n    'multisite' => false,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Addons Paths\n    |--------------------------------------------------------------------------\n    |\n    | When generating addons via `php please make:addon`, this path will be\n    | used by default. You can still specify custom repository paths in\n    | your composer.json, but this is the path used by the generator.\n    |\n    */\n\n    'addons_path' => base_path('addons'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Blueprints Path\n    |--------------------------------------------------------------------------\n    |\n    | Where your blueprint YAML files are stored.\n    |\n    */\n\n    'blueprints_path' => resource_path('blueprints'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Fieldsets Path\n    |--------------------------------------------------------------------------\n    |\n    | Where your fieldset YAML files are stored.\n    |\n    */\n\n    'fieldsets_path' => resource_path('fieldsets'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Send the Powered-By Header\n    |--------------------------------------------------------------------------\n    |\n    | Websites like builtwith.com use the X-Powered-By header to determine\n    | what technologies are used on a particular site. By default, we'll\n    | send this header, but you are absolutely allowed to disable it.\n    |\n    */\n\n    'send_powered_by_header' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Date Format\n    |--------------------------------------------------------------------------\n    |\n    | This format will be used whenever a Carbon date is cast to a string on\n    | front-end routes. It doesn't affect how dates are formatted in the CP.\n    | You can customize this format using PHP's date string constants.\n    | Setting this value to null will use Carbon's default format.\n    |\n    | https://www.php.net/manual/en/function.date.php\n    |\n    */\n\n    'date_format' => 'F jS, Y',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Timezone\n    |--------------------------------------------------------------------------\n    |\n    | Statamic will use this timezone when displaying dates on the front-end.\n    | You can use any timezone supported by PHP. When set to null it will\n    | fall back to the timezone defined in your `app.php` config file.\n    |\n    | https://www.php.net/manual/en/timezones.php\n    |\n    */\n\n    'display_timezone' => null,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Localize Dates in Modifiers\n    |--------------------------------------------------------------------------\n    |\n    | When using date-related modifiers, Carbon instances will be in UTC.\n    | Enabling this setting will ensure that dates get localized into\n    | the timezone defined in `display_timezone`. Otherwise you'll\n    | need to manually localize dates in all of your templates.\n    |\n    */\n\n    'localize_dates_in_modifiers' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Character Set\n    |--------------------------------------------------------------------------\n    |\n    | Statamic will use this character set when performing specific string\n    | encoding and decoding operations; This does not apply everywhere.\n    |\n    */\n\n    'charset' => 'UTF-8',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Track Last Update\n    |--------------------------------------------------------------------------\n    |\n    | Statamic will automatically set an `updated_at` timestamp (along with\n    | `updated_by`, where applicable) when specific content is updated.\n    | In some situations, you may wish disable this functionality.\n    |\n    */\n\n    'track_last_update' => false,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enable Cache Tags\n    |--------------------------------------------------------------------------\n    |\n    | Sometimes you'll want to be able to disable the {{ cache }} tags in\n    | Antlers, so here is where you can do that. Otherwise, it will be\n    | enabled all the time.\n    |\n    */\n\n    'cache_tags_enabled' => env('STATAMIC_CACHE_TAGS_ENABLED', true),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Intensive Operations\n    |--------------------------------------------------------------------------\n    |\n    | Sometimes Statamic requires extra resources to complete intensive\n    | operations. Here you may configure system resource limits for\n    | those rare times when we need to turn things up to eleven!\n    |\n    */\n\n    'php_memory_limit' => '-1',\n    'php_max_execution_time' => '-1',\n    'ajax_timeout' => '600000',\n    'pcre_backtrack_limit' => '-1',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Debugbar Integration\n    |--------------------------------------------------------------------------\n    |\n    | Statamic integrates with Laravel Debugbar to bring more detail to your\n    | debugging experience. Here you may adjust various default options.\n    |\n    */\n\n    'debugbar' => [\n        'pretty_print_variables' => true,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | ASCII\n    |--------------------------------------------------------------------------\n    |\n    | During various string manipulations (e.g. slugification), Statamic will\n    | need to make ASCII character conversions. Here you may define whether\n    | or not extra characters get converted. e.g. \"%\" becomes \"percent\".\n    |\n    */\n\n    'ascii_replace_extra_symbols' => false,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Update References on Change\n    |--------------------------------------------------------------------------\n    |\n    | With this enabled, Statamic will attempt to update references to assets\n    | and terms when moving, renaming, replacing, deleting, etc. This will\n    | be queued, but it can disabled as needed for performance reasons.\n    |\n    */\n\n    'update_references' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Always Augment to Query\n    |--------------------------------------------------------------------------\n    |\n    | By default, Statamic will augment relationship fields with max_items: 1\n    | to the result of a query, for example an Entry instance. Setting this\n    | to true will augment to the query builder instead of the result.\n    |\n    */\n\n    'always_augment_to_query' => false,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Row ID handle\n    |--------------------------------------------------------------------------\n    |\n    | Rows in Grid, Replicator, and Bard fields will be given a unique ID using\n    | the \"id\" field. You may need your own field named \"id\", in which case\n    | you may customize the handle of the field that Statamic will use.\n    |\n    */\n\n    'row_id_handle' => 'id',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Fake SQL Queries\n    |--------------------------------------------------------------------------\n    |\n    | Enable while using the flat-file Stache driver to show fake \"SQL\" query\n    | approximations in your database debugging tools — including Debugbar,\n    | Laravel Telescope, and Ray with the ray()->showQueries() helper.\n    |\n    */\n\n    'fake_sql_queries' => config('app.debug'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Layout\n    |--------------------------------------------------------------------------\n    |\n    | Define the default layout that will be used by views.\n    |\n    */\n\n    'layout' => env('STATAMIC_LAYOUT', 'layout'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | View Config Allowlist\n    |--------------------------------------------------------------------------\n    |\n    | Config keys that are allowed to be accessed in Antlers templates. Use\n    | '@default' to include Statamic's default list. Add 'docs' so the docs\n    | version switcher can access config.docs.version and config.docs.versions.\n    |\n    */\n\n    'view_config_allowlist' => ['@default', 'docs'],\n\n];\n"
  },
  {
    "path": "config/statamic/templates.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Template Language\n    |--------------------------------------------------------------------------\n    |\n    | The preferred templated language to use when scaffolding templates.\n    |\n    | Acceptable values are 'blade' and 'antlers'\n    */\n\n    'language' => 'antlers',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Code Style\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the code generator's output style.\n    |\n    */\n\n    'style' => [\n        'line_ending' => 'auto',\n        'indent_type' => 'space',\n        'indent_size' => 4,\n        'final_newline' => false,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Antlers Settings\n    |--------------------------------------------------------------------------\n    |\n    | Antlers specific template generation settings.\n    |\n    */\n\n    'antlers' => [\n        'use_components' => false,\n    ],\n];"
  },
  {
    "path": "config/statamic/users.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | User Repository\n    |--------------------------------------------------------------------------\n    |\n    | Statamic uses a repository to get users, roles, groups, and their\n    | relationships from specified storage locations. The file driver\n    | gets it from disk, while the eloquent driver gets from a DB.\n    |\n    | Supported: \"file\", \"eloquent\"\n    |\n    */\n\n    'repository' => 'file',\n\n    'repositories' => [\n\n        'file' => [\n            'driver' => 'file',\n            'paths' => [\n                'roles' => resource_path('users/roles.yaml'),\n                'groups' => resource_path('users/groups.yaml'),\n            ],\n        ],\n\n        'eloquent' => [\n            'driver' => 'eloquent',\n        ],\n\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Avatars\n    |--------------------------------------------------------------------------\n    |\n    | User avatars are initials by default, with custom options for services\n    | like Gravatar.com.\n    |\n    | Supported: \"initials\", \"gravatar\", or a custom class name.\n    |\n    */\n\n    'avatars' => 'initials',\n\n    /*\n    |--------------------------------------------------------------------------\n    | New User Roles\n    |--------------------------------------------------------------------------\n    |\n    | When registering new users through the user:register_form tag, these\n    | roles will automatically be applied to your newly created users.\n    |\n    */\n\n    'new_user_roles' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | New User Groups\n    |--------------------------------------------------------------------------\n    |\n    | When registering new users through the user:register_form tag, these\n    | groups will automatically be applied to your newly created users.\n    |\n    */\n\n    'new_user_groups' => [\n        //\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Registration form honeypot field\n    |--------------------------------------------------------------------------\n    |\n    | When registering new users through the user:register_form tag,\n    | specify the field to act as a honeypot for bots\n    |\n    */\n\n    'registration_form_honeypot_field' => null,\n\n    /*\n    |--------------------------------------------------------------------------\n    | User Wizard Invitation Email\n    |--------------------------------------------------------------------------\n    |\n    | When creating new users through the wizard in the control panel,\n    | you may choose whether to be able to send an invitation email.\n    | Setting to true will give the user the option. But setting\n    | it to false will disable the invitation option entirely.\n    |\n    */\n\n    'wizard_invitation' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Password Brokers\n    |--------------------------------------------------------------------------\n    |\n    | When resetting passwords, Statamic uses an appropriate password broker.\n    | Here you may define which broker should be used for each situation.\n    | You may want a longer expiry for user activations, for example.\n    |\n    */\n\n    'passwords' => [\n        'resets' => 'users',\n        'activations' => 'activations',\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Database\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the database connection and its table names.\n    |\n    */\n\n    'database' => config('database.default'),\n\n    'tables' => [\n        'users' => 'users',\n        'role_user' => 'role_user',\n        'roles' => false,\n        'group_user' => 'group_user',\n        'groups' => false,\n        'webauthn' => 'webauthn',\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Authentication Guards\n    |--------------------------------------------------------------------------\n    |\n    | By default, Statamic will use the `web` authentication guard. However,\n    | if you want to run Statamic alongside the default Laravel auth\n    | guard, you can configure that for your cp and/or frontend.\n    |\n    */\n\n    'guards' => [\n        'cp' => 'web',\n        'web' => 'web',\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Impersonation\n    |--------------------------------------------------------------------------\n    |\n    | Here you can configure if impersonation is available, and what URL to\n    | redirect to after impersonation begins.\n    |\n    */\n\n    'impersonate' => [\n        'enabled' => env('STATAMIC_IMPERSONATE_ENABLED', true),\n        'redirect' => env('STATAMIC_IMPERSONATE_REDIRECT', null),\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Elevated Sessions\n    |--------------------------------------------------------------------------\n    |\n    | Users may be required to reauthorize before performing certain\n    | sensitive actions. This is called an elevated session. Here\n    | you may configure the duration of the session in minutes.\n    |\n    */\n\n    'elevated_session_duration' => 15,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enforce Two-Factor Authentication\n    |--------------------------------------------------------------------------\n    |\n    | Specify which user roles should be required to enable two-factor\n    | authentication. Use \"*\" to enforce 2FA for all users, or \"super_users\"\n    | to enforce it for super users.\n    |\n    */\n\n    'two_factor_enforced_roles' => [],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Sorting\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure the default sort behavior for user listings.\n    |\n    */\n\n    'sort_field' => 'email',\n    'sort_direction' => 'asc',\n\n];\n"
  },
  {
    "path": "config/statamic/webauthn.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Allow password logins to be used when user has a passkey\n    |--------------------------------------------------------------------------\n    |\n    | Whether or not the password field should be shown to users that\n    | have set up a passkey, or whether it should be hidden.\n    |\n    */\n\n    'allow_password_login_with_passkey' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Remember Me\n    |--------------------------------------------------------------------------\n    |\n    | Whether or not the \"remember me\" functionality should be used when\n    | authenticating using WebAuthn. When enabled, the user will remain\n    | logged in indefinitely, or until they manually log out.\n    |\n    */\n\n    'remember_me' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Model\n    |--------------------------------------------------------------------------\n    |\n    | When using eloquent passkeys you can specify the model you want to use\n    |\n    */\n\n    'model' => \\Statamic\\Auth\\Eloquent\\WebAuthnModel::class,\n\n];"
  },
  {
    "path": "config/torchlight.php",
    "content": "<?php\n\nreturn [\n    // The Torchlight client caches highlighted code blocks. Here\n    // you can define which cache driver you'd like to use. If\n    // leave this blank your default app cache will be used.\n    'cache' => env('TORCHLIGHT_CACHE_DRIVER'),\n\n    // Which theme you want to use. You can find all of the themes at\n    // https://torchlight.dev/docs/themes.\n    'theme' => env('TORCHLIGHT_THEME', 'synthwave-84'),\n\n    // Your API token from torchlight.dev.\n    'token' => env('TORCHLIGHT_TOKEN'),\n\n    // If you want to register the blade directives, set this to true.\n    'blade_components' => false,\n\n    // The Host of the API.\n    'host' => env('TORCHLIGHT_HOST', 'https://api.torchlight.dev'),\n\n    // We replace tabs in your code blocks with spaces in HTML. Set\n    // the number of spaces you'd like to use per tab. Set to\n    // `false` to leave literal tabs in the HTML.\n    'tab_width' => 4,\n\n    // If you pass a filename to the code component or in a markdown\n    // block, Torchlight will look for code snippets in the\n    // following directories.\n    'snippet_directories' => [\n        resource_path(),\n    ],\n\n    // Global options to control blocks-level settings.\n    // https://torchlight.dev/docs/options\n    'options' => [\n        // Turn line numbers on or off globally.\n        'lineNumbers' => false,\n\n        'defaultLanguage' => 'antlers',\n\n        // Control the `style` attribute applied to line numbers.\n        // 'lineNumbersStyle' => '',\n\n        'fileStyle' => 'html',\n\n        // Turn on +/- diff indicators.\n        // 'diffIndicators' => true,\n\n        // If there are any diff indicators for a line, put them\n        // in place of the line number to save horizontal space.\n        // 'diffIndicatorsInPlaceOfLineNumbers' => true,\n\n        // When lines are collapsed, this is the text that will\n        // be shown to indicate that they can be expanded.\n        // 'summaryCollapsedIndicator' => '...',\n    ],\n];\n"
  },
  {
    "path": "content/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "content/assets/main.yaml",
    "content": "title: Main\ndisk: assets\n"
  },
  {
    "path": "content/collections/.gitkeep",
    "content": ""
  },
  {
    "path": "content/collections/fieldtypes/array.md",
    "content": "---\ntitle: Array\nmeta_title: Array Fieldtype\nintro: Manage data in a `key:value` array format.\noverview: |\n  The array fieldtype is used to manage `key: value` array data. It's similar to the [table](/fieldtypes/table) fieldtype but with a more strict data structure and compact user interface.\nscreenshot: fieldtypes/screenshots/v6/array.webp\nscreenshot_dark: fieldtypes/screenshots/v6/array-dark.webp\noptions:\n  -\n    name: keys\n    type: array\n    description: >\n      Define keys when using [keyed mode](#keyed-mode).\n      Default: `null`.\n  -\n    name: mode\n    type: string\n    description: \"Determine which [mode](#modes) to use. Default: `dynamic`.\"\n  -\n    name: value_header\n    type: string\n    description: >\n      **Value** column heading displayed when using [dynamic mode](#dynamic-mode)\n      Default: `Value`.\n  -\n    name: key_header\n    type: string\n    description: >\n      **Key** column heading displayed when using [dynamic mode](#dynamic-mode)\n      Default: `Key`.\n  -\n    name: add_button\n    type: string\n    description: >\n      Add button text customization.\n      Default: `Add Row`.\n  -\n    name: expand\n    type: boolean\n    description: >\n      Save the field as an ordered list of `key` / `value` objects instead of a flat YAML mapping.\n      Enable when you need numeric keys, stable option order with database-backed content stores, or to avoid YAML limitations with certain key types.\n      Default: `false`.\nid: 457f17eb-c0ee-4345-bf90-88322abc212d\n---\n## Overview\n\nThis fieldtype is used to manage `key: value` array data in the right situation. It's used for situations when there is data you would like to stay grouped together because there's only _one_ set and you don't want to use loops.\n\nIf you'd like to have _lists_ of this type of data, you might want to use a [grid](/fieldtypes/grid) or [replicator](/fieldtypes/replicator) field.\n\n## Modes\n\nThe screenshot above depicts the three modes you can choose from. Two for when you know there is a fixed set of keys (keyed/single), and one for when you don't (dynamic).\n\n### Keyed Mode\n\nThe first field contains pre-defined keys. This will give the user a stricter input. They can only enter the values for the specified keys, and they cannot be reordered.\n\n```yaml\naddress:\n  type: array\n  keys:\n    street: Street\n    city: City\n    country: Country\n```\n\nThe keys can be specified with or without labels. The snippet above (and what's shown in the screenshot) uses labels.\n\nThe following is an example of just keys. When using this syntax, the key and the label will be identical.\n\n```\nkeys:\n  - street\n  - city\n  - country\n```\n\n### Single Mode\n\nExactly the same restrictions and setup as keyed mode, except the user can only manage an array one item at a time, using a select box to switch between keys.\n\n### Dynamic Mode\n\nThe second field contains no pre-defined keys. This will allow the user to define them on the fly and re-arrange them.\n\n```yaml\naddress:\n  type: array\n```\nColumn headings can be set with `value_header` & `key_header`.\n```\nvalue_header: Type of Bacon\nkey_header: Why is it awesome?\n```\n\n### Expanded storage (`expand`) {#expanded-storage}\n\nBy default, array data is stored as a YAML mapping (`key: value`). Set `expand: true` to store it as an ordered list of objects instead:\n\n```yaml\n# expand: false (default)\nsizes:\n  \"42\": Medium\n  \"7\": Small\n\n# expand: true\nsizes:\n  -\n    key: \"42\"\n    value: Medium\n  -\n    key: \"7\"\n    value: Small\n```\n\nUse expanded storage when keys need to be **numbers** (YAML mappings treat numeric keys oddly), when you rely on **explicit row order** (for example with the Eloquent Driver and MySQL, where key order on associative arrays is not preserved), or when you edit raw YAML and want unambiguous structure.\n\n```yaml\ninventory:\n  type: array\n  expand: true\n  keys:\n    sku: SKU\n    qty: Quantity\n```\n\n[Augmentation](/augmentation) still exposes the field as a normal key/value structure for templates and Antlers, so `{{ inventory:sku }}` and nested variable syntax behave the same whether `expand` is on or off.\n\n\n## Data Structure\n\nIn the example above, the keyed mode and dynamic mode would save the exact same data (unless [expanded storage](#expanded-storage) is enabled—in that case the same logical keys are stored as a list of `key` / `value` objects).\n\n```yaml\naddress:\n  street: 221B Baker Street\n  city: London\n  country: England\n```\n\nSingle mode will only save data if it has been entered by the user.\n\n```yaml\naddress:\n  England: '221 Baker Street, London'\n```\n\n\n## Templating\n\nThis fieldtype _is not_ [augmented](/augmentation).\n\n\n::tabs\n\nYou can use basic array access, nested variables, or the [foreach tag](/tags/foreach) to loop through all of the keys. All three of the following methods are equivalent.\n\n```antlers\n// Array access\n{{ address }}\n    {{ street }} {{ city }} {{ country }}\n{{ /address }}\n\n// Nested variables\n{{ address:street }} {{ address:city }} {{ address:country }}\n\n// Foreach tag:\n{{ foreach:address }}\n    {{ value }}\n{{ /foreach:address }}\n```\n\n::tab\n\nYou can use basic array access or the `@foreach` directive to loop through all of the keys.\n\n```blade\n// Nested variables\n{{ $address['street'] }}\n{{ $address['city'] }}\n{{ $address['country'] }}\n\n// Using foreach\n@foreach ($address as $key => $value)\n\t{{ $key }}: {{ $value }}\n@endforeach\n```\n::"
  },
  {
    "path": "content/collections/fieldtypes/assets.md",
    "content": "---\ntitle: Assets\nmeta_title: Assets Fieldtype\nintro: Any time you want to list, display, or work with assets (external files with enhanced abilities), this is the way to do it. Upload, browse, reorder, delete, and even manage field data on individual assets.\ndescription: Upload files and use the Asset Browser to pick from existing files in your Asset Containers.\nscreenshot: fieldtypes/screenshots/v6/assets-list.webp\nscreenshot_dark: fieldtypes/screenshots/v6/assets-list-dark.webp\noptions:\n  -\n    name: allow_uploads\n    type: bool\n    description: |\n      Enable to allow uploading new files into the container. Default: `true`.\n  -\n    name: container\n    type: string\n    description: |\n      The name of the desired [asset container](/assets#containers) to use for browsing, uploading, and managing assets. _Required when the site has more than one container._\n  -\n    name: folder\n    type: string\n    description: >\n        The folder (relative to the container) to begin browsing. Default: the root folder of the container.\n  -\n    name: dynamic\n    type: string\n    description:\n        Assets will be placed in a subfolder based on the value of this field. See [dynamic folders](#dynamic-folders). You may use `id`, `slug`, or `author`.\n  -\n    name: min_files\n    type: int\n    description: >\n      The minimum number of allowed files. Leave empty for no minimum.\n  -\n    name: max_files\n    type: int\n    description: >\n      The maximum number of allowed files. Set to `null` for unlimited. If set to `1`, will be saved as a string instead of an array. Default: `null`.\n  -\n    name: mode\n    type: string\n    description: >\n      Set to `list` to use the table layout mode, and `grid` to use the grid mode with larger thumbnails. Default: `grid`.\n  -\n    name: query_scopes\n    type: string\n    description: >\n      Allows you to specify a [query scope](/extending/query-scopes-and-filters#scopes) which should be applied when retrieving selectable assets. You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\n  -\n    name: restrict\n    type: bool\n    description: >\n      If `true`, navigation within the asset browser will be disabled. Your users will be restricted to a specified container and folder. Default: `false`.\n\nid: d0c65546-74f1-4a15-89d5-1562a95ee2c6\n---\n## Overview\n\nThe assets fieldtype is used to manage and relate files with your entries. From the fieldtype you can manage custom fields on the assets themselves (learn more about that in the [assets guide](/assets)), preview full size images or rich media files, and even set focal points for cropping.\n\nFiles are rearrangeable via drag-and-drop.\n\n## UI Modes\n\nThe list mode is shown in the previous screenshot, while the grid mode is shown below. There are no functional differences, only visual ones. List mode is more compact – useful if you're not primarily managing images.\n\n<figure>\n  <img src=\"/img/fieldtypes/screenshots/v6/assets-grid.webp\" alt=\"Assets List mode\" class=\"u-hide-in-dark-mode\">\n  <img src=\"/img/fieldtypes/screenshots/v6/assets-grid-dark.webp\" alt=\"Assets List mode\" class=\"u-hide-in-light-mode\">\n  <figcaption>List mode reveals a fanny pack in all its glory. And if you’re British—go on, have a little chuckle.</figcaption>\n</figure>\n\n## Data Structure\n\nData is stored as an array of image paths _relative to the asset container_. Each asset's full URL is generated dynamically on the frontend based on the image path and its container. This allows containers to be a bit more portable by avoiding fully hardcoded file paths.\n\nIf `max_files` is set to `1`, a string will be saved instead of an array.\n\n``` yaml\n# Default YAML\ngallery_images:\n  - fresh-prince.jpg\n  - dj-jazzy-jeff.jpg\n  - uncle-phil.jpg\n\n# With max_files: 1\nhero_image: surf-boards.jpg\n```\n\n## Templating\n\nThe Asset fieldtype uses [augmentation](/augmentation) to automatically relate the files with their Asset records, pull in custom and meta data, and resolve all image paths based on the container.\n\n::tabs\n\nBy using a tag pair syntax, you'll be able to output variables for each asset:\n\n```antlers\n{{ gallery_images }}\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /gallery_images }}\n\n{{ hero_image }}\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /hero_image }}\n```\n::tab\n\nYou can use the `@foreach` directive to loop over each asset and output its variables:\n\n```blade\n@foreach ($gallery_images as $asset)\n    <img src=\"{{ $asset->url }}\" alt=\"{{ $asset->alt }}\" />\n@endforeach\n\n{{-- Assuming $hero_image is max_files: 1 --}}\n<img src=\"{{ $hero_image->url }}\" alt=\"{{ $hero_image->alt }}\" />\n```\n\n::\n\n\n```html\n<img src=\"/assets/fresh-prince.jpg\" alt=\"Will Smith as the Fresh Prince\" />\n<img src=\"/assets/dj-jazzy-jeff.jpg\" alt=\"Jeffrey Allen Townes as DJ Jazzy Jeff\" />\n<img src=\"/assets/uncle-phil.jpg\" alt=\"James Avery as Uncle Phil\" />\n\n<img src=\"/assets/surf-boards.jpg\" alt=\"3 colorful surf boards\" />\n```\n\nThe same tag pair syntax can be used regardless of your `max_files` setting.\n\n::tabs\n\nIf you have `max_files: 1`, you can also use a single tag syntax to directly use a variable inside the asset. Without a second tag part, the URL will be used.\n\n```antlers\n{{ hero_image }}\n{{ hero_image:url }}\n{{ hero_image:alt }}\n```\n\n::tab\n\nIf you have `max_files: 1`, you can use property access to output variables within the asset. When output as a string, the URL will be used.\n\n```blade\n{{ $hero_image }}\n{{ $hero_image->url }}\n{{ $hero_image->alt }}\n```\n\n::\n\n```html\n/assets/surf-boards.jpg\n/assets/surf-boards.jpg\n3 colorful surf boards\n```\n\n### Variables\n\nInside an asset variable's tag pair you'll have access to the following variables.\n\n| Variable | Description |\n|----------|-------------|\n| `url` | Web-friendly URL to the file. |\n| `path` |  Path to the file relative to asset container |\n| `permalink` |  Absolute URL to the file including your site URL. |\n| `basename` | Name of the file with file extension |\n| `blueprint` | Which blueprint is managing the asset container |\n| `container` | Which asset container the file is in |\n| `edit_url` | URL to edit asset in the Control Panel |\n| `extension` | File extension |\n| `filename` | Name of the file without file extension |\n| `folder` | Which folder the file is in |\n| `is_audio` | `true` when file is one of `aac`, `flac`, `m4a`, `mp3`, `ogg`, or `wav`. |\n| `is_image` | `true` when file is one of `jpg`, `jpeg`, `png`, `gif`, or `webp`. |\n| `is_svg` | `true` when file is an `svg`. |\n| `is_video` | `true` when file is one of `h264`, `mp4`, `m4v`, `ogv`, or `webm`. |\n| `last_modified` | Formatted date string of the last modified time |\n| `last_modified_instance` | [Carbon][carbon] instance of the last modified time |\n| `last_modified_timestamp` | Unix timestamp of the last modified time |\n| `size` | Pre-formatted file size (`3.48 MB`) |\n| `size_b` | File size in bytes |\n| `size_kb` | File size in kilobytes |\n| `size_mb` | File size in megabytes |\n| `size_gb` | File size in gigabytes |\n\n### Image Assets\n\n| Variable | Description |\n|----------|-------------|\n| `height` | height of an image, in pixels |\n| `width` | width of an image, in pixels |\n| `focus_css` | CSS `background-image` property for a focal point\n| `orientation` | Is one of `portrait`, `landscape`, `square`, or `null`. |\n| `ratio` |  An image's ratio (`1.77`) |\n\n:::tip\nYou can use [Glide](/tags/glide) to crop, flip, sharpen, pixelate, and perform other sweet image manipulations.\n:::\n\n### Asset Field Data\n\n::tabs\nAll custom data set on the assets will also be available inside the asset tag loop.\n\n```antlers\n{{ gallery_images }}\n  <figure>\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\">\n    <figcaption>{{ caption }}</figcaption>\n  </figure>\n{{ /gallery_images }}\n```\n\n::tab\n\nAll custom data set on the assets will also be available on the asset instance.\n\n```blade\n@foreach ($gallery_images as $image)\n    <img src=\"{{ $image->url }}\" alt=\"{{ $image->alt }}\" />\n\n    @if ($caption = $image->caption)\n    <figcaption>{{ $caption }}</figcaption>\n    @endif\n@endforeach\n```\n\n::\n\n## Dynamic Folders\n\nIn addition to selecting which container and folder assets will be uploaded into, you may configure the field to have a dedicated subfolder based on an entry's value.\n\nFor example, you may want to place all the images for a blog post in its own directory.\n\n```yaml\ntype: assets\ncontainer: images\nfolder: blog\ndynamic: slug\n```\n\nThis would result in `image.jpg` being uploaded to `path/to/images/blog/my-entry-slug/image.jpg`.\n\nYou may target the entry's `id`, `slug`, or `author` values.\n\n:::warning\nThe values are *not* kept in sync. For example, if you change the entry's slug, the asset folder will not be renamed. You may rename the folder manually via a control on the field, or within the asset browser.\n:::\n\n\n[carbon]: https://carbon.nesbot.com/docs/\n"
  },
  {
    "path": "content/collections/fieldtypes/bard.md",
    "content": "---\ntitle: Bard\ndescription: \"Rich article writing and block-based layouts made easy.\"\nintro: |\n  Bard is more than just a content editor, and more flexible than a block-based editor. **It is designed to provide a delightful and powerful writing experience** with unparalleled flexibility on your front-end.\nscreenshot: fieldtypes/screenshots/v6/bard-with-sets.webp\nscreenshot_dark: fieldtypes/screenshots/v6/bard-with-sets-dark.webp\noptions:\n  -\n    name: allow_source\n    type: boolean\n    description: |\n      Controls whether the \"show source code\" button is available to your editors. Default: `true`.\n  -\n    name: sets\n    type: array\n    description: An array containing sets of fields. If you don't provide any sets, Bard will act like a basic text/WYSIWYG editor and you won't see the \"Add Set\" button.\n  -\n    name: buttons\n    type: array\n    description: |\n      An array of buttons you want available in the toolbar.\n      You can choose from `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `bold`, `italic`, `small`, `underline`, `strikethrough`, `unorderedlist`, `orderedlist`, `removeformat`, `quote`, `anchor`, `image`, `table`, `code` (inline), `codeblock`, and `horizontalrule`.\n\n      These are the defaults:\n      ![Bard Buttons](/img/fieldtypes/screenshots/bard-buttons.png) {.mt-4}\n\n      You can override the default buttons using the `Bard::setDefaultButtons()` method:\n      ```php\n      \\Statamic\\Fieldtypes\\Bard::setDefaultButtons(['h2', 'h3', 'bold', 'italic']);\n      ```\n\n      When you have the `image` button toggled, make sure to define an Asset Container in the Bard field's settings, otherwise the button won't show.\n  -\n    name: target_blank\n    type: boolean\n    description: |\n      Automatically add `target=\"_blank\"` on links by default. You'll be able to override this per-link. Default: `false`.\n  -\n    name: link_noopener\n    type: boolean\n    description: |\n      Set `rel=\"noopener\"` on all created links. Default: `false`.\n  -\n    name: link_noreferrer\n    type: boolean\n    description: |\n      Set `rel=\"noreferrer\"` on all created links. Default: `false`.\n  -\n    name: fullscreen\n    type: boolean\n    description: |\n      Enable the option to toggle into fullscreen mode. Default: `true`.\n  -\n    name: collapse\n    type: boolean\n    description: >\n      Expand (`true`) or collapse (`false`) all sets by default. Default: `false`.\n  -\n    name: container\n    type: string\n    description: >\n      An asset container ID. When specified, the fieldtype will allow the user to add a link to an asset from the specified container.\n  -\n    name: inline\n    type: boolean\n    description: |\n      Switch the field to inline mode. Block elements such as sets, headings and images are not supported in inline mode and should not be enabled.\n  -\n    name: inline_hard_breaks\n    type: boolean\n    description: |\n      Enable support for hard breaks in inline mode. Only works when `inline` is set to `true`. Default: `false`.\n  -\n    name: toolbar_mode\n    type: string\n    description: >\n      Choose which style of toolbar you prefer, `fixed` or `floating`. Default: `fixed`.\n  -\n    name: reading_time\n    type: boolean\n    description: >\n      Show estimated reading time at the bottom of the field. Default: `false`.\n  -\n    name: word_count\n    type: boolean\n    description: >\n      Show the word count at the bottom of the field. Default: `false`.\n  -\n    name: save_html\n    type: boolean\n    description: >\n      Save as HTML instead of structured data. This simplifies templates so you don't need to loop through the structured nodes. Only works while no sets are defined. Default: `false`.\n  -\n    name: always_show_set_button\n    type: boolean\n    description: >\n      Always show the \"Add Set\" button. Default: `false`.\n  -\n    name: remove_empty_nodes\n    type: string\n    description: >\n      Choose how to deal with empty nodes. Options: `false`, `true`, `trim`. Default: `false`.\n\nid: f4bf58d3-cbce-4957-b883-d92fd4791e89\n---\n## Overview\n\nBard is our recommended fieldtype for creating long form content from the control panel. It's highly configurable, intuitive, user-friendly, and writes impeccable HTML (thanks to [ProseMirror][prosemirror]).\n\nBard also has the ability to manage \"sets\" of fields inline with your text. These sets can contain any number of other fields of any [fieldtype](/fieldtypes), and can be collapsed and neatly rearranged in your content.\n\n## Working With Sets {#sets}\n\nYou can use any fieldtypes inside your Bard sets. Make sure to compare the experience with the other meta-fields: [Grid](/fieldtypes/grid) and [Replicator](/fieldtypes/replicator). You can even use Grids and Replicators inside your Bard sets. Just remember that because you can doesn't mean you should. Your UI experience can vary greatly.\n\n### Set Previews\n\nNew to Statamic v6, you can add an image preview of your set, _as well as_ an icon. Previews make it easy to identify sets by showing a screenshot of what the rendered set might look like on the front-end. Clients can now say “ah, that one” without pretending to know the names you carefully gave them.\n\n#### Configuring Set Previews\n\nTo add a set preview, click the little \"pencil\" icon next to the set name.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-set-edit-preview.webp\" alt=\"Bard Set Edit Preview\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-set-edit-preview-dark.webp\" alt=\"Bard Set Edit Preview\" class=\"u-hide-in-light-mode\">\n    <figcaption>Let's give the set a preview.</figcaption>\n</figure>\n\nOnce you're in the set editor, you can add a preview image and icon. Here we're showing a lovely screenshot of what the newsletter signup form might look like on the front-end. We can even add some instructions to explain how the set is used.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-set-previews.webp\" alt=\"Bard Set Previews\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-set-previews-dark.webp\" alt=\"Bard Set Previews\" class=\"u-hide-in-light-mode\">\n    <figcaption>Behold a <em>Preview Image</em>, for the love of all clients.</figcaption>\n</figure>\n\n#### Set Previews in Action\n\nOnce you've set a preview image, users adding a bard set can hover over the set to preview what it might look like on the frontend.\n\nYou can view previews in two different UI modes: in a list of set names, or in a grid of sets with their preview images.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-hover-preview-list.webp\" alt=\"Bard Set Previews in the CP\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-hover-preview-list-dark.webp\" alt=\"Bard Set Previews in the CP\" class=\"u-hide-in-light-mode\">\n    <figcaption>A preview in a list of sets.</figcaption>\n</figure>\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-hover-preview-grid.webp\" alt=\"Bard Set Previews in the CP\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-hover-preview-grid-dark.webp\" alt=\"Bard Set Previews in the CP\" class=\"u-hide-in-light-mode\">\n    <figcaption>A preview in a grid of sets. Previews fall back to the set icon if no preview image is set.</figcaption>\n</figure>\n\n### Custom Set Icons\n\nYou can change the icons available in the set picker by configuring an icon set in a service provider.\n\nFor example, you can drop this into your `AppServiceProvider`'s `boot` method:\n\n```php\nuse Statamic\\Fieldtypes\\Sets;\n\npublic function boot()\n{\n    Sets::useIcons('heroicons', resource_path('svg/heroicons'));\n}\n```\n\n## Data Structure\n\nBard stores your data as a [ProseMirror document](https://prosemirror.net/docs/ref/#model.Document_Structure). You should never need to interact with this data directly, thanks to [augmentation](/augmentation).\n\n## Templating\n\n### Without Sets\n\nIf you are using Bard just as a rich text editor and have no need for sets you would use a single tag to render the content.\n\n::tabs\n\n```antlers\n{{ bard_field }}\n```\n\n::tab\n\n```blade\n{!! $bard_field !!}\n```\n\n::\n\n### With Sets\n\nWhen working with sets, you should use the tag pair syntax and `if/else` conditions on the `type` variable to style each set accordingly. **Note**: any content that is entered _not_ in a set (i.e. your normal rich-text content) needs to be rendered using the \"text\" type. See the first condition using \"text.\"\n\n::tabs\n\n```antlers\n{{ bard_field }}\n\n  {{ if type == \"text\" }}\n    <div class=\"text\">\n      {{ text }}\n    </div>\n\n  {{ elseif type == \"poll\" }}\n    <figure class=\"poll\">\n      <figcaption>{{ question }}</figcaption>\n      <ul>\n        {{ options }}\n          <li>{{ text }}</li>\n        {{ /options }}\n      </ul>\n    </figure>\n\n  {{ elseif type == \"hero_image\" }}\n    <div class=\"heroimage\">\n      <img src=\"{{ hero_image }}\" alt=\"{{ caption }}\" />\n    </div>\n  {{ /if }}\n\n{{ /bard_field }}\n```\n\n::tab\n\n```blade\n@foreach ($bard_field_with_sets as $set)\n\t@if ($set->type === 'text')\n\t<div class=\"text\">\n\t\t{!! $set->text !!}\n\t</div>\n\t@elseif ($set->type === 'poll')\n\t<figure class=\"poll\">\n\t\t<figcaption>{{ $set->question }}</figcaption>\n\t\t<ul>\n\t\t\t@foreach ($set->options as $option)\n\t\t\t\t<li>{{ $option->text }}</li>\n\t\t\t@endforeach\n\t\t</ul>\n\t</figure>\n\t@elseif ($set->type === 'hero_image' && $hero_image = $set->hero_image)\n\t\t<div class=\"heroimage\">\n\t\t\t<img src=\"{{ $hero_image }}\" alt=\"{{ $hero_image->caption }}\" />\n\t\t</div>\n\t@endif\n@endforeach\n```\n\n::\n\nAn alternative approach (for those who like DRY or small templates) is to create multiple \"set\" partials and pass the data to them dynamically, moving the markup into corresponding partials bearing the set's name.\n\n::tabs\n\n::tab antlers\n\n```antlers\n{{ bard_field }}\n  {{ partial src=\"sets/{type}\" }}\n{{ /bard_field }}\n```\n\n``` files theme:serendipity-light\nresources/views/partials/sets/\n  gallery.antlers.html\n  hero_image.antlers.html\n  poll.antlers.html\n  text.antlers.html\n  video.antlers.html\n```\n\n::tab blade\n\n:::tip\nBy using `[...$set]`, you can access the set variables within the set's Blade file without having to reference `$set` for each variable.\n\nFor example, `{!! $set->text !!}` becomes `{!! $text !!}`.\n:::\n\n```blade\n@foreach ($bard_field_with_sets as $set)\n\t@include('fieldtypes.bard.sets.'.$set->type, [...$set])\n@endforeach\n```\n\n``` files theme:serendipity-light\nresources/views/partials/sets/\n  gallery.blade.php\n  hero_image.blade.php\n  poll.blade.php\n  text.blade.php\n  video.blade.php\n```\n\n::\n\n## Extending Bard\n\nBard uses [TipTap](https://tiptap.dev/) (which in turn is built on top of [ProseMirror][prosemirror]) as the foundation for our quintessential block-based editor.\n\n[prosemirror]: https://prosemirror.net/\n\n### Required Reading\n\nBefore you attempt to create any Bard extensions, it is wise to learn how to write a Tiptap extension first. Otherwise you'd be trying to learn how to ride a motorcycle before you can even ride a bike. Or a unicycle before you can juggle. To have a better understanding of how to write a Tiptap extension, you'd in turn benefit greatly on reading about how ProseMirror works.\n\n:::tip\nWriting custom extensions for Bard is pretty complicated, but can be rewarding and provide powerful results.\n:::\n\nIn short, here's a quick-start of the things you should probably start with:\n\n- [The ProseMirror guide](https://prosemirror.net/docs/guide/) — Yes, it's really long, but you should at least pretend to read it\n- Checking out the [The Tiptap documentation](https://tiptap.dev/docs/editor/getting-started/overview) and [code samples for the core Tiptap extensions](https://github.com/ueberdosis/tiptap/tree/develop/packages), so you can understand how Tiptap relates to ProseMirror\n- If you don't know [how to extend the control panel](/control-panel/css-javascript) yet, go ahead and read up on that first. The code snippets later will be part of your extension to the control panel. Alternatively, you may also [extend the control panel through the creation of an addon](/addons/building-an-addon).\n- Come back here again and keep on going.\n\n### Adding New Extensions\n\nYou may add your own Tiptap extensions to Bard using the `addExtension` method. The callback may return a single extension, or an array of them.\n\n``` js\nconst { Node, Mark, Extension } = Statamic.$bard.tiptap.core;\n\nStatamic.$bard.addExtension(() => Node.create({...}));\n```\n\n``` js\nStatamic.$bard.addExtension(() => {\n    return [\n        Node.create({...}),\n        Mark.create({...}),\n        Extension.create({...}),\n    ]\n});\n```\n\nCheck out [Tiptap's custom extension documentation](https://tiptap.dev/docs/editor/extensions/custom-extensions) and [code samples for the core Tiptap extensions](https://github.com/ueberdosis/tiptap/tree/develop/packages) to find out how to write an extension.\n\nIf you're providing a new mark or node and intend to use this Bard field on the front-end, you will also need to create a Mark or Node class to be used by the PHP [renderer](#tiptap-php-rendering).\n\n:::tip\nIf you need any other Tiptap helpers or utilities you can use our [Tiptap API](#tiptap-api).\n:::\n\n### Replacing Existing Extensions\n\nIf you'd like to replace a [native extension](https://github.com/ueberdosis/tiptap/tree/develop/packages) (e.g. headings or paragraphs) you can use the `replaceExtension` method. It takes the `name` of the extension, and a callback that returns a single extension instance.\n\n```js\nconst { Node } = Statamic.$bard.tiptap.core;\n\nStatamic.$bard.replaceExtension('heading', ({ extension, bard }) => {\n    return Node.create({\n        name: 'heading',\n        ...\n    });\n});\n```\n\nThe callback will provide you with the existing extension instance, so if you are doing simple tweaks to an extension (e.g. customizing an input rule) you can simply extend the existing instance. Then you don't need to author an entire extension:\n\n```js\nconst { nodeInputRule } = Statamic.$bard.tiptap.core;\n\nStatamic.$bard.replaceExtension('heading', ({ extension, bard }) => {\n    return extension.extend({\n        addInputRules() {\n            return [\n                nodeInputRule({...}),\n            ];\n        },\n    });\n});\n```\n\nYou can also reconfigure extensions (e.g. to add Tailwind classes to headings or disable specific \"smart typography\" rules):\n\n```js\nStatamic.$bard.replaceExtension('heading', ({ extension, bard }) => {\n    return extension.configure({\n        HTMLAttributes: {\n            class: 'font-bold',\n        },\n    });\n});\n```\n```js\nStatamic.$bard.replaceExtension('typography', ({ extension, bard }) => {\n    return extension.configure({\n        oneHalf: false,\n        oneQuarter: false,\n        threeQuarters: false,\n    });\n});\n```\n\n### Buttons\n\nTo add a button to the toolbar, provide a callback to the `buttons` method.\n\nThe callback will receive two arguments:\n- `buttons` - an array of the existing buttons in the toolbar (more about that in a moment)\n- `button` - a function that wraps your button objects\n\nThe callback may return a `button` object, or an array of them.\n\n``` js\nStatamic.$bard.buttons((buttons, button) => {\n    return button({\n        name: 'custom_bold',\n        text: __('Custom Bold'), // Tooltip text\n        svg: 'bold', // Name of an SVG icon\n        html: '<svg>...</svg>', // Custom icon HTML\n        args: { class: 'font-bold' }, // The command arguments\n        command: (editor, args) => editor.chain().focus().setCustomBold(args).run(), // The command to run\n        activeName: 'customBold', // The active node/mark type that will activate this button (falls back to name)\n        active: (editor, args) => editor.isActive('bold'), // Active check callback (overrides activeName)\n        visibleWhenActive: 'example', // The active node/mark type that will show this button (always visible if not set)\n        visible: (editor, args) => editor.isActive('example'), // Visible check callback (overrides visibleWhenActive)\n    });\n});\n```\n\n``` js\nStatamic.$bard.buttons((buttons, button) => [\n    button({...}),\n    button({...}),\n]);\n```\n\nReturning values to the `buttons` method will push them onto the end. If you need more control, you can manipulate the supplied `buttons` argument, and then return nothing. For example, we'll add a button after wherever the existing bold button happens to be:\n\n``` js\nStatamic.$bard.buttons((buttons, button) => {\n    const indexOfBold = _.findIndex(buttons, { name: 'bold' });\n\n    buttons.splice(indexOfBold + 1, 0, button({...}));\n});\n```\n\n:::tip\nUsing the `button()` method will make the button only appear if the Bard field has been configured to show your button.\n\nIf you'd like your button to appear on all Bard fields, regardless of whether it's been configured to use that button, you can just return an object. Don't wrap with `button()`.\n:::\n\n### Tiptap API\n\nIn your extensions, you may need to use functions from the `tiptap` library. Rather than importing the library yourself and bloating your JS files, you may use methods through our API.\n\n``` js\nStatamic.$bard.tiptap.core; // `tiptap` (core, commands, utilities and helpers)\nStatamic.$bard.tiptap.pm.state; // `prosemirror-state`\nStatamic.$bard.tiptap.pm.model; // `prosemirror-model`\nStatamic.$bard.tiptap.pm.view; // `prosemirror-view`\n```\n\nYou could shorten things up by using destructuring. For example:\n\n``` js\nconst { InputRule, insertText, getAttributes } = Statamic.$bard.tiptap.core;\nnew InputRule(...);\ninsertText(...);\ngetAttributes(...);\n```\n\n### Tiptap PHP Rendering\n\nIf you have created an extension on the JS side to be used inside the Bard fieldtype, you will need to be able to render it on the PHP side (in your views).\n\nThe Bard `Augmentor` class is responsible for converting the ProseMirror structure to HTML.\n\nYou can use the `addExtension` or `replaceExtension` methods to bind an extension class into the renderer. Your AppServiceProvider's `boot` method is a good place to do this.\n\n``` php\nuse Statamic\\Fieldtypes\\Bard\\Augmentor;\n\npublic function boot()\n{\n    // Pass an object\n    Augmentor::addExtension('myExtension', new MyExtension);\n\n    // or a closure. You will be passed the bard fieldtype and an array of options as arguments.\n    Augmentor::addExtension('myExtension', function ($bard, $options) {\n        return new MyExtension(['foo' => $bard->config('should_foo')];\n    });\n\n    // Same for replacing extensions.\n    Augmentor::replaceExtension('paragraph', new MyCustomParagraph);\n\n    // Closures too. There will be an additional argument at the front which is the existing extension.\n    Augmentor::replaceExtension('paragraph', function ($existing, $bard, $options) {\n        return new CustomParagraph;\n    });\n}\n```\n\nCheck out [code samples for the core Tiptap extensions](https://github.com/ueberdosis/tiptap-php/tree/main/src) to find out how to write PHP extensions."
  },
  {
    "path": "content/collections/fieldtypes/button_group.md",
    "content": "---\ntitle: 'Button Group'\ndescription: 'Buttons you click. You can only choose one.'\nintro: |\n  Buttons. Create some options and let your users select one and only one. May they choose wisely.\nscreenshot: fieldtypes/screenshots/v6/button-group.webp\nscreenshot_dark: fieldtypes/screenshots/v6/button-group-dark.webp\noptions:\n  -\n    name: clearable\n    type: boolean\n    description: |\n      Allow deselecting all options, making `null` a possible value. Default: `false`.\n  -\n    name: options\n    type: array\n    description: 'Sets of key/value pairs define the values and labels of the buttons.'\nid: 26751221-fdc8-47c6-97f0-bf4997319482\n---\n## Overview\n\nThe button group fieldtype is a multiple choice input where you only get one choice. It saves the chosen option from a preset list.\n\n## Configuring\n\nUse the `options` setting to define a list of values and labels.\n\n``` yaml\nseat_choice:\n  type: button_group\n  instructions: Choose your airline seat. Choose wisely.\n  options:\n    left: Left\n    middle: Middle\n    right: Right\n```\n\nYou may omit the labels and just specify keys. If you use this syntax, the value and label will be identical.\n\n``` yaml\n  options:\n    - Left\n    - Middle\n    - Right\n```\n\n### Options in blueprint YAML\n\nLike the [select](/fieldtypes/select) fieldtype, `options` in the blueprint are saved in **expanded** form (ordered `key` / `value` rows) so option order is preserved in all storage backends. Hand-written blueprints should follow that structure if you are not using the visual editor.\n\n```yaml\noptions:\n  - key: left\n    value: Left\n  - key: middle\n    value: Middle\n```\n\n## Data Structure\n\nThe chosen option is stored as a string. If you only specified values for the `options` array, then the label will be saved.\n\n``` yaml\nseat_choice: middle\n```\n\n\n\n## Templating\n\nIt's a string, so you can just use that value.\n\n::tabs\n\n::tab antlers\n\n```\n<p>I love sitting in the {{ seat_choice }} seat. A lot.</p>\n```\n\n::tab blade\n\n```blade\n<p>I love sitting in the {{ $seat_choice }} seat. A lot.</p>\n```\n\n::\n\n```html\n<p>I love sitting in the middle seat. A lot.</p>\n```\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/checkboxes.md",
    "content": "---\ntitle: Checkboxes\ndescription: Boxes you check. You can check 'em all.\nintro: >\n  Checkboxes! Make some checkboxes, click the checkboxes, and store a record of which boxes of which ones you clicked. They're boxes you check.\nscreenshot: fieldtypes/screenshots/v6/checkboxes.webp\nscreenshot_dark: fieldtypes/screenshots/v6/checkboxes-dark.webp\noptions:\n  -\n    name: inline\n    type: bool\n    description: >\n      Show the checkboxes next to each other in a row instead of stacked vertically. Default: `false`\n  -\n    name: options\n    type: array\n    description: >\n      Sets of key/value pairs define the values and labels of the checkbox options.\nid: f922cb9b-6fc9-4249-adf4-59aa46285c13\n---\n## Overview\n\nThe checkboxes fieldtype is a multiple choice input. It saves one or more options chosen from a preset list. In other words, they're boxes you check.\n\n## Configuring\n\nUse the `options` setting to define a list of values and labels.\n\n``` yaml\nfavorites:\n  type: checkboxes\n  instructions: Choose up to 3 favorite foods.\n  options:\n    donuts: Donuts\n    icecream: Ice Cream\n    brownies: Brownies\n```\n\nYou may omit the labels and just specify keys. If you use this syntax, the value and label will be identical.\n\n``` yaml\n  options:\n    - Donuts\n    - Ice Cream\n    - Brownies\n```\n\n### Options in blueprint YAML\n\nSee [Select · Options in blueprint YAML](/fieldtypes/select#options-in-blueprint-yaml)—checkbox options in the blueprint use the same expanded `key` / `value` rows so order is preserved everywhere.\n\n## Data Structure\n\nThe values are stored as a YAML array. If you only specified values for the `options` array, then the labels will be saved.\n\n``` yaml\nfavorites:\n  - donuts\n  - icecream\n```\n\n\n\n## Templating\n\nYou can loop through the checked items and access the value and label of each item inside the loop.\n\n::tabs\n\n::tab antlers\n\n```\n<ul>\n{{ favorites }}\n    <li>{{ value }}</li>\n{{ /favorites }}\n</ul>\n```\n\n::tab blade\n\n```blade\n<ul>\n\t@foreach($favorites as $favorite)\n\t{{-- You can also access $favorite['key'] and $favorite['label'] --}}\n\t<li>{{ $favorite['value'] }}</li>\n\t@endforeach\n</ul>\n```\n\n::\n\n```html\n<ul>\n    <li>donuts</li>\n    <li>icecream</li>\n</ul>\n```\n\n::tabs\n\n::tab antlers\n\nTo conditionally check if a value has been selected, you can combine the [pluck](/modifiers/pluck) and [contains](/modifiers/contains) modifiers:\n\n```antlers\n{{ if favorites | pluck('value') | contains('donuts') }}\n   <span>Contains donuts!</span>    \n{{ /if }}\n```\n\n::tab blade\n\nTo conditionally check if a value has been selected, you can combine the pluck and contains collection methods:\n\n```blade\n@if (collect($favorites)->pluck('value')->contains('donuts'))\n\t<span>Contains donuts!</span>\n@endif\n```\n::\n\n### Variables\n\nInside an asset variable's tag pair you'll have access to the following variables.\n\n| Variable | Description |\n|----------|-------------|\n| `key` | The zero-index count of the current item |\n| `value` | The stored value of the checkbox |\n| `label` | The label of the checkbox item from the field config |\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/code.md",
    "content": "---\ntitle: Code\ndescription: 'Write code and see it highlight. But will you choose spaces or tabs?'\nintro: What are you doing writing code in a browser?! Just kidding, it's fine. We made it easy, flexible, and pretty too. We use this fieldtype a lot.\n\nscreenshot: fieldtypes/screenshots/v6/code.webp\nscreenshot_dark: fieldtypes/screenshots/v6/code-dark.webp\noptions:\n  -\n    name: theme\n    type: string\n    description: |\n      Choose between `light` and `material` (dark) themes. Default: `light`.\n  -\n    name: mode\n    type: string\n    description: |\n      Set a default language for syntax highlighting. Your choices include:\n\n      - `clike`\n      - `css`\n      - `diff`\n      - `go`\n      - `haml`\n      - `handlebars`\n      - `htmlmixed`\n      - `less`\n      - `markdown`\n      - `gfm`\n      - `nginx`\n      - `text/x-java`\n      - `javascript`\n      - `jsx`\n      - `text/x-objectivec`\n      - `php`\n      - `python`\n      - `ruby`\n      - `scss`\n      - `shell`\n      - `sql`\n      - `twig`\n      - `vue`\n      - `xml`\n      - `yaml-frontmatter`\n  -\n    name: mode_selectable\n    type: boolean\n    description: |\n      Whether the `mode` can be selected by the user in the publish form. Enabling this will change the GraphQL\n      type from a string to a Code type.\n  -\n    name: indent_type\n    type: string\n    description: |\n      Choose between `tabs` and `spaces`. Choose wisely. Default: `tabs`.\n  -\n    name: indent_size\n    type: integer\n    description: |\n      Set your preferred indentation size (in spaces). Default: `4`.\n  -\n    name: line_numbers\n    type: boolean\n    description: |\n      Show line numbers.\n  -\n    name: line_wrapping\n    type: boolean\n    description: |\n      Enable to wrap long lines of code instead of showing a horizontal scroll. Default: `true`.\n  -\n    name: key_map\n    type: string\n    description: |\n      Pick your preferred set of keyboard shortcuts. Choose between `default`, `sublime`, and `vim`. We'll let you guess which one is default.\n  -\n    name: rulers\n    type: array\n    description: |\n      You can set the columns and the line style (choose between `dashed` or `solid`) of any rulers you wish to use.\nid: 3ca28569-5b86-49a1-b620-ea3364561cde\n---\n## Overview\n\nIf your content involves code snippets, this is the fieldtype for you. It's a [CodeMirror](https://codemirror.net) field with 25 of the most common languages ready for highlighting, handles tabs and spaces, has a dark mode, and best of all — for you super nerds out there — a vim key binding.\n\n## Data Structure\n\nThe code fieldtype stores a string. Do whatever you'd like with it.\n\n``` yaml\ncode_snippet: |\n  <?php\n\n  public function engage()\n  {\n    return \"Make it so.\";\n  }\n```\n\nIf you've enabled the `mode_selectable` option, an array will be saved with `code` and `mode` in it.\n\n```yaml\ncode_snippet:\n  mode: php\n  code: |\n    <?php\n\n    public function engage()\n    {\n        return \"Make it so.\";\n    }\n```\n\n## Templating\n\nYou can output that string just as it is. If it is indeed code (and why wouldn't it be?), you'll probably want to to wrap it in pre and code tags to display it prettier. If you want code highlighting on your front-end, we recommend hooking up [prism.js](https://prismjs.com).\n\n::tabs\n\n::tab antlers\n\n```antlers\n<pre><code>{{ code_snippet }}</code></pre>\n```\n\n::tab blade\n\n```blade\n<pre><code>{!! $code_snippet !!}</code></pre>\n```\n\n::\n\nYou're also able to use it as an array if you want to output the mode.\n\n::tabs\n\n::tab antlers\n\n```\n{{ code_snippet }}\n<pre class=\"language-{{ mode }}\"><code>{{ code }}</code></pre>\n{{ /code_snippet }}\n```\n\n::tab blade\n\n```blade\n<pre class=\"language-{{ $code_snippet['mode'] }}\"><code>{!! $code_snippet['code'] !!}</code></pre>\n```\n\n::\n\n### Variables\n\nInside an code fieldtype's tag pair you'll have access to the following variables.\n\n| Variable | Description |\n|----------|-------------|\n| `code` | The contents of the field. |\n| `mode` | The selected language mode. |\n"
  },
  {
    "path": "content/collections/fieldtypes/collections.md",
    "content": "---\ntitle: Collections\nextends: 9dd58c40-6e33-49c8-83fa-61a69f6371be\ndescription: Choose from one or more collections.\noverview: Allows you to choose one or more collections.\noptions:\n  -\n    name: max_items\n    type: integer\n    description: >\n      The maximum number of items that may be selected. Setting this to `1` will change the UI to a dropdown.\nscreenshot: /fieldtypes/screenshots/collections.png\nid: 44c3da60-ef47-408e-afc4-a33026c86f5d\n---\n## Usage\n\nThis fieldtype is used to view and select from a list of Collections.\n\n```yaml\nfields:\n  my_collections_field:\n    type: collections\n```\n\n## Data Structure\n\nThe Collections fieldtype is a [Relationship fieldtype](/relationships#fieldtypes), and will save the collections as their handles (the folder name).\n\n```yaml\nlistings:\n  - blog\n  - things\n```\n\n## Templating\n\nYou're more than likely using this field as a way to dynamically display a collection.\n\n::tabs\n\n::tab antlers\n\nSince the collection tag accepts a pipe-delimited list of collection names, you can join them together like this:\n\n```antlers\n<ul>\n  {{ collection from=\"{listings|piped}\" }}\n    <li>{{ title }}</li>\n  {{ /collection }}\n</ul>\n```\n\n::tab blade\n\nYou can pass the values from the collections fieldtype to the collection tag like so:\n\n```blade\n<ul>\n\t<statamic:collection\n\t\t:from=\"$listings ?? []\"\n\t>\n\t\t<li>{{ $title }}</li>\n\t</statamic:collection>\n</ul>\n\n```\n\n::\n\n```html\n<ul>\n  <li>A blog entry</li>\n  <li>A thing entry</li>\n  <li>etc</li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/color.md",
    "content": "---\ntitle: Color\ndescription: 'Manage colors by hex code with swatches and text inputs.'\nintro: 'A simple color picker with support for pre-defined swatches as well as entering a color by hex code.'\nscreenshot: fieldtypes/screenshots/v6/color.webp\nscreenshot_dark: fieldtypes/screenshots/v6/color-dark.webp\noptions:\n  -\n    name: swatches\n    type: array\n    description: |\n      Pre-define colors that can be selected from a list. Supports all color mode formats.\n  -\n    name: allow_any\n    type: boolean\n    description: |\n      Allow entering any color value via picker or hex code.\nid: 09b4af2d-265a-49ee-ac74-3e27041c180b\n---\n## Overview\n\nIf you want work with colors, this is the way to do it. You could combine it with [Bard](/fieldtypes/bard) or [Replicator](/fieldtypes/replicator) to create page \"page builders\", use it to choose background colors for headers or hero blocks, or even image overlays with `mix-blend-mode: multiply`. Go get creative!\n\n## Data Structure\n\nThe color fieldtype stores the color values as a hex string.\n\n``` yaml\nheader_color: \"#FF269E\"\n```\n\n## Templating\n\nThe color is output as a simple string. Most often you'll use this in an inline `style` tag to style elements of your front-end site.\n\n::tabs\n\n::tab antlers\n\n```antlers\n<div class=\"hero\" style=\"background-color: {{ header_color }}\">\n  <h1>Bay Side High's Sweetheart Dance</h1>\n  <h2>This Friday Night!</h2>\n</div>\n```\n\n::tab blade\n\n```blade\n<div class=\"hero\" style=\"background-color: {{ $header_color }}\">\n\t<h1>Bay Side High's Sweetheart Dance</h1>\n\t<h2>This Friday Night!</h2>\n</div>\n```\n::"
  },
  {
    "path": "content/collections/fieldtypes/date.md",
    "content": "---\ntitle: Date\ndescription: Helps you pick a date, but not get one.\nintro: >\n  Work with dates, times, and ranges with a variety of user interface options that make you really enjoy basically just picking numbers from a table.\nscreenshot: fieldtypes/screenshots/v6/date.webp\nscreenshot_dark: fieldtypes/screenshots/v6/date-dark.webp\noptions:\n  -\n    name: columns\n    type: integer\n    description: |\n     Show multiple months at one time, in columns and rows. Default: `1`.\n  -\n    name: earliest_date\n    type: string\n    description: |\n      Set the earliest selectable date in `YYYY-MM-DD` format. Default: `1900-01-01`.\n  -\n    name: format\n    type: string\n    description: |\n      How the date should be stored, using the [PHP date format](https://www.php.net/manual/en/datetime.format.php). We recommend choosing a format which stores date & time.\n  -\n    name: full_width\n    type: boolean\n    description: |\n      Enable to stretch the calendar out like Stretch Armstrong, using the maximum amount of available horizontal space. Default: `false`.\n  -\n    name: inline\n    type: boolean\n    description: |\n      Always show the calendar instead of the text input and dropdown UI. Default: `false`.\n  -\n    name: mode\n    type: string\n    description: |\n      Choose between `single` or `range`. Range mode disables the time picker. Default: `single`.\n  -\n    name: rows\n    type: integer\n    description: |\n     Show multiple months at one time, in columns and rows. Default: `1`.\n  -\n    name: time_enabled\n    type: boolean\n    description: |\n      Enable/disable the timepicker. Default: `false`.\n      <figure>\n        <img src=\"/img/fieldtypes/screenshots/v6/date-and-time.webp\" alt=\"Date fieldtype with time enabled\" class=\"u-hide-in-dark-mode\">\n        <img src=\"/img/fieldtypes/screenshots/v6/date-and-time-dark.webp\" alt=\"Date fieldtype with time enabled\" class=\"u-hide-in-light-mode\">\n        <figcaption>Now you can pick a time, too!</figcaption>\n      </figure>\n  -\n    name: time_required\n    type: boolean\n    description: |\n      Makes the time field visible and non-dismissible. Default: `false`.\n  -\n    name: timezone\n    type: string\n    description: |\n      The timezone dates will be displayed and entered in within the Control Panel. Accepts any [IANA timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) (e.g. `America/New_York`). Defaults to the site-wide [`default_timezone`](#control-panel-timezone) configured in `config/statamic/cp.php`, which itself defaults to `auto` (the browser's local timezone).\nid: 7dfba904-8a74-40e1-b507-51cd2b5f6123\n---\n\n## Overview\n\nDate fields have highly configurable user interfaces. They can be as simple as a single date and/or time, or as fancy as a multi-month calendar with multi-day range picking. Be sure to experiment with the various config [options](#options) to create the best experience for your content authors.\n\n## Data Structure\n\nSingle dates are stored as a date/timestring. Ranges are stored as an array with a `start` and `end` key.\n\n``` yaml\ndate: 1983-10-01 12:00:00\ndate_range:\n  start: 2019-11-18 00:00\n  end: 2019-11-22 00:00\n```\n\nDates are stored in your application's timezone. \n\nThe time will be when `time_enabled` is `true`, or depending on the timezone of the user who selected the date. e.g. On date fields where there is no time configured, it will assume midnight for the person who selected it.\n\n## Templating\n\nDate fields are [augmented](/augmentation) to return a [Carbon instance][carbon]. When used as a string they will return a pre-formatting output that uses your `config.date` format setting. By default that'll look like `January 1, 2020`.\n\n### Date Ranges\n\nRanges have nested `start` and `end` variables, so you can access them like this:\n\n::tabs\n\n::tab antlers\n```antlers\n// Nested variable\nEvent: {{ date:start }} through {{ date:end }}\n\n// Tag pair\n{{ date }}\nEvent: {{ start }} through {{ end }}\n{{ /date }}\n```\n\n::tab blade\n\n```blade\nEvent: {{ $date_range['start'] }} through {{ $date_range['end'] }}\n```\n\n::\n\n<figure>\n  <img src=\"/img/fieldtypes/screenshots/v6/date-range.webp\" alt=\"Date fieldtype in range mode\" class=\"u-hide-in-dark-mode\"s>\n  <img src=\"/img/fieldtypes/screenshots/v6/date-range-dark.webp\" alt=\"Date fieldtype in range mode\" class=\"u-hide-in-light-mode\">\n  <figcaption>Ranges are much simpler than two date fields.</figcaption>\n</figure>\n\n### Formatting Dates\n\nYou can format the output of your date fields with the [format modifier](/modifiers/format) and PHP's [date formatting options](https://www.php.net/manual/en/function.date.php).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date format=\"Y\" }} // 2019\n{{ date format=\"Y-m-d\" }} // 2019-10-10\n{{ date format=\"l, F jS\" }} // Sunday, January 21st\n```\n\n::tab blade\n\nWhen using Blade, you may also call the `->format` method on Carbon instances.\n\n```blade\n{{-- Using Modifiers --}}\n{{ Statamic::modify($date)->format('Y') }} // 2019\n{{ Statamic::modify($date)->format('Y-m-d') }} // 2019-10-10\n{{ Statamic::modify($date)->format('l, F jS') }} // Sunday, January 21st\n\n{{-- Using Carbon methods --}}\n{{ $date->format('Y') }} // 2019\n{{ $date->format('Y-m-d') }} // 2019-10-10\n{{ $date->format('l, F jS') }} // Sunday, January 21st\n```\n\n::\n\n### Formatting localized Dates\n\nYou can format localized dates with the [iso modifier](/modifiers/iso_format) and [ISO formatting options](https://carbon.nesbot.com/docs/#api-localization). This use Carbon's inner translations rather than language packages you need to install on every machine where you deploy your application.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date iso_format=\"YYYY\" }} // 2019\n{{ date iso_format=\"YYYY-MM-DD\" }} // 2019-10-10\n{{ date iso_format=\"dddd, MMMM Do\" }} // Sunday, January 21st\n```\n\n::tab blade\n\nWhen using Blade, you may also call the `->isoFormat` method on Carbon instances.\n\n```blade\n{{-- Using Modifiers --}}\n{{ Statamic::modify($date)->isoFormat('YYYY') }} // 2019\n{{ Statamic::modify($date)->isoFormat('YYYY-MM-DD') }} // 2019-10-10\n{{ Statamic::modify($date)->isoFormat('dddd, MMMM Do') }} // Sunday, January 21st\n\n{{-- Using Carbon methods --}}\n{{ $date->isoFormat('YYYY') }} // 2019\n{{ $date->isoFormat('YYYY-MM-DD') }} // 2019-10-10\n{{ $date->isoFormat('dddd, MMMM Do') }} // Sunday, January 21st\n\n```\n\n::\n\n## Timezones\n\nDates are stored in your application timezone, then converted before being displayed to users.\n\nFor more information on how Statamic handles timezones, please review our [Timezones](/tips/timezones) guide.\n\n### Control Panel Timezone\n\nBy default, dates in the Control Panel are displayed and entered in the browser's local timezone. This means a user in New York and a user in London editing the same entry would each see the date in their own timezone.\n\nIf you'd prefer all users to see and enter dates in a specific timezone, you can configure it site-wide in `config/statamic/cp.php`:\n\n```php\n'default_timezone' => env('STATAMIC_CP_DEFAULT_TIMEZONE', 'auto'),\n```\n\nSet this to any [IANA timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) (e.g. `America/New_York`) to pin all date fields to that timezone. The default value of `auto` uses each user's browser timezone.\n\nYou can also override this on a per-field basis with the [`timezone`](#options) field config option.\n\n\n\n[carbon]: https://carbon.nesbot.com/docs/\n"
  },
  {
    "path": "content/collections/fieldtypes/dictionary.md",
    "content": "---\ntitle: Dictionary\ndescription: Choose from options provided by dictionaries.\nintro: Give your users a list of options to choose from. Similar to the Select field, but allows you to read options from YAML or JSON files, or even hit external APIs.\nscreenshot: fieldtypes/screenshots/v6/dictionary.webp\nscreenshot_dark: fieldtypes/screenshots/v6/dictionary-dark.webp\noptions:\n  -\n    name: dictionary\n    type: array\n    description: |\n      Configure the dictionary to be used. You may also define any config values which should be passed along to the dictionary. The `dictionary` option accepts both string & array values:\n\n      ```yaml\n      # When it's a dictionary without any config fields...\n      dictionary: countries\n\n      # When it's a dictionary with config fields...\n      dictionary:\n        type: countries\n        region: Europe\n      ```\n  -\n    name: placeholder\n    type: string\n    description: |\n      Set the non-selectable placeholder text. Default: none.\n  -\n    name: default\n    type: string\n    description: |\n      Set the default option key. Default: none.\n  -\n    name: max_items\n    type: integer\n    description: >\n      Cap the number of selections. Setting this to 1 will change the UI. Default: null (unlimited).\nid: 9b14b5b8-6a7a-4db2-8533-9c78faa0e054\n---\n## Overview\nAt a glance, the Dictionary fieldtype is similar to the [Select fieldtype](/fieldtypes/select). However, with the Dictionary fieldtype, options aren't manually defined in a field's config, but rather returned from a PHP class (called a \"dictionary\").\n\nThis can prove to be pretty powerful, since it means you can read options from YAML or JSON files, or even hit an external API. It also makes it easier to share common select options between projects.\n\n## Data Storage\nDictionary fields will store the \"key\" of the chosen option or options.\n\nFor example, a dictionary might have items such as:\n\n```php\n'jan' => 'January',\n'feb' => 'February',\n'mar' => 'March',\n```\n\nYour saved data will be:\n\n``` yaml\nselect: jan\n```\n\n## Templating\nDictionary fields will return the \"option data\" returned by the dictionary's `get` method. The shape of this data differs between dictionaries and is outlined below.\n\nFor example, using the built-in Countries dictionary, your template might look like this:\n\n```yaml\npast_vacations:\n  - USA\n  - AUS\n  - CAN\n  - DEU\n  - GBR\n```\n\n::tabs\n\n::tab antlers\n\n```antlers\n<ul>\n    {{ past_vacations }}\n        <li>{{ emoji }} {{ name }}</li>\n    {{ /past_vacations }}\n</ul>\n```\n\n::tab blade\n\n```blade\n<ul>\n\t@foreach ($past_vacations as $vacation)\n\t<li>{{ $vacation['emoji'] }} {{ $vacation['name'] }}</li>\n\t@endforeach\n</ul>\n```\n\n::\n\n```html\n<ul>\n    <li>🇺🇸 United States</li>\n    <li>🇦🇺 Australia</li>\n    <li>🇨🇦 Canada</li>\n    <li>🇩🇪 Germany</li>\n    <li>🇬🇧 United Kingdom</li>\n</ul>\n```\n\n## Available Dictionaries\nStatamic includes a few dictionaries straight out of the box.\n\n### File\nThis allows you point to a file located in your `resources/dictionaries` directory to populate the options. The file can be `json`, `yaml`, or `csv`.\n\nEach option array should have `label` and `value` keys at the minimum. Any additional keys will be available when templating.\n\nYou may redefine which keys are used for the labels and values by providing them to your fieldtype config. In the following example, `name` is the label and `id` is the value. \n\n```json\n[\n    {\"name\": \"Apple\", \"id\": \"apple\", \"emoji\": \"🍎\"},\n    {\"name\": \"Banana\", \"id\": \"banana\", \"emoji\": \"🍌\"},\n    {\"name\": \"Cherry\", \"id\": \"cherry\", \"emoji\": \"🍒\"},\n    ...\n]\n```\n\n```yaml\n-\n  handle: fruit\n  field:\n    type: dictionary\n    dictionary:\n      type: file\n      filename: fruit.json\n      label: name  # optional, defaults to \"label\"\n      value: id    # optional, defaults to \"value\"\n```\n\nYou may provide enhanced labels using basic Antlers syntax. For example, to include the emoji before the fruit name, you can do this:\n\n```yaml\nlabel: '{{ emoji }} {{ name }}'\n```\n\n### Countries\nThis provides a list of countries with their ISO codes, region, subregion, and flag emoji.\n```yaml\n-\n  handle: countries\n  field:\n    type: dictionary\n    dictionary:\n      type: countries\n      region: 'oceania' # Optionally filter the countries by a region.\n                        # Supported options are: africa, americas, asia, europe, oceania, polar\n      emojis: true      # Whether flag emojis are in the labels. They're on by default.\n```\n```yaml\ncountries:\n  - USA\n  - AUS\n```\n\n::tabs\n\n::tab antlers\n\n```antlers\n{{ countries }}\n  {{ emoji }} {{ name }}, {{ iso2 }}, {{ iso3 }}, {{ region }}, {{ subregion }}\n{{ /countries }}\n```\n\n::tab blade\n\n```blade\n@foreach ($countries as $country)\n\t{{ $country['emoji'] }} {{ $country['name'] }}, {{ $country['iso2'] }}, {{ $country['iso3'] }}, {{ $country['region'] }}, {{ $country['subregion'] }}\n@endforeach\n```\n\n::\n\n```\n🇺🇸 United States, US, USA, Americas, Northern America\n🇦🇺 Australia, AU, AUS, Oceania, Australia and New Zealand\n```\n\n### Timezones\nThis provides a list of timezones and their UTC offsets.\n\n```yaml\n-\n  handle: timezones\n  field:\n    type: dictionary\n    dictionary:\n      type: timezones\n```\n```yaml\ntimezones:\n  - America/New_York\n  - Australia/Sydney\n```\n\n::tabs\n\n::tab antlers\n\n```antlers\n{{ timezones }}\n  {{ name }} {{ offset }}\n{{ /timezones }}\n```\n\n::tab blade\n\n```blade\n@foreach ($timezones as $timezone)\n\t{{ $timezone['name'] }} {{ $timezone['offset'] }}\n@endforeach\n```\n\n::\n\n```\nAmerica/New_York -04:00\nAustralia/Sydney +10:00\n```\n\n### Currencies\nThis provides a list of currencies, with their codes, symbols, and decimals.\n\n```yaml\n-\n  handle: currencies\n  field:\n    type: dictionary\n    dictionary:\n      type: currencies\n```\n```yaml\ncurrencies:\n  - USD\n  - HUF \n```\n\n::tabs\n\n::tab antlers\n\n```antlers\n{{ currencies }}\n  {{ name }}, {{ code }}, {{ symbol }}, {{ decimals }}\n{{ /currencies }}\n```\n\n::tab blade\n\n```blade\n@foreach ($currencies as $currency)\n{{ $currency['name'] }}, {{ $currency['code'] }}, {{ $currency['symbol'] }}, {{ $currency['decimals'] }}\n@endforeach\n```\n\n::\n\n```\nUS Dollar, USD, $, 2\nHungarian Forint, HUF, Ft, 0\n```\n\n## Custom Dictionaries\n\nIn many cases, using the native [File](#file) dictionary can be all you need for something custom. However, it's possible to create an entirely custom dictionary that could read from files, APIs, or whatever you can think of.\n\n[Find out how to create a custom dictionary](/extending/dictionaries)\n"
  },
  {
    "path": "content/collections/fieldtypes/entries.md",
    "content": "---\ntitle: Entries\nmeta_title: 'Entries Fieldtype'\ndescription: 'Create relationships with other entries.'\nintro: |\n  Create relationships with other entries in one or more collections. It's not very much like online dating because you can create and link the entries on the fly without leaving the page.\nscreenshot: fieldtypes/screenshots/v6/entries.webp\nscreenshot_dark: fieldtypes/screenshots/v6/entries-dark.webp\noptions:\n  -\n    name: collections\n    type: array\n    description: |\n      Configure which collections you want to allow relationships with.\n  -\n    name: create\n    type: boolean\n    description: |\n      By default you may create new entries. Set to `false` to only allow selecting from existing entries. Only available in the `default` mode.\n  -\n    name: max_items\n    type: integer\n    description: >\n      The maximum number of items that may be selected. Setting this to `1` will change the UI to a dropdown.\n  -\n    name: mode\n    type: string\n    description: |\n        Set the UI style for this field. Can be one of 'default' (Stack Selector), 'select' (Select Dropdown) or 'typeahead' (Typeahead Field).\n  -\n    name: query_scopes\n    type: string\n    description: >\n      Allows you to specify a [query scope](/extending/query-scopes-and-filters#scopes) which should be applied when retrieving selectable entries. You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\n  -\n    name: search_index\n    type: string\n    description: >\n        Allows you to specify a [search index](/search#indexes) to be used when searching for entries.\n  -\n    name: select_across_sites\n    type: boolean\n    description: |\n      When enabled, entries from all sites will be displayed. \nid: acee879a-c832-449d-a714-c57ea5862717\n---\n## Overview\n\nUse this fieldtype to create a one-way relationship with entries of any collection in your site. It's delightfully simple.\n\n:::watch https://youtube.com/embed/WWbsM5u9afc\nWatch how to build a \"Related Articles\" feature using the Entries Fieldtype\n:::\n\n## Data Structure\n\nThis fieldtype will store an array of ids to the selected entries. They will be augmented in your Antlers templates to give you access to each entry's data.\n\n``` yaml\nrelated_entries:\n  - 12f9be1f-a12e-4680-b769-639d2d1f1d14\n  - ea48926d-bf67-4d45-9420-9627a31c37fb\n  ```\n\n## Templating\n\nLoop through the entries and do anything you want with the data.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n  {{ related_entries }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n  {{ /related_entries }}\n</ul>\n```\n\n::tab blade\n```blade\n<ul>\n  @foreach ($entries as $entry)\n    <li><a href=\"{{ $entry->url }}\">{{ $entry->title }}</a></li>\n  @endforeach\n</ul>\n```\n\n::\n\n```html\n<ul>\n  <li><a href=\"/look-at-this\">Look at This!</a></li>\n  <li><a href=\"/look-at-that\">Wait, Look at That!</a></li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/float.md",
    "content": "---\ntitle: Float\ndescription: 'For when all you want is decimal numbers.'\nintro: 'The float fieldtype is a text-style input that only accepts floats (numbers) and has increment and decrement controls.'\nscreenshot: fieldtypes/screenshots/v6/float.webp\nscreenshot_dark: fieldtypes/screenshots/v6/float-dark.webp\nid: 3dcf9495-9a02-4044-b6bb-428b7ff23807\noptions:\n  -\n    name: append\n    type: string\n    description: >\n      Add text after (to the right of) the float input.\n  -\n    name: prepend\n    type: string\n    description: >\n      Add text to the beginning (to the left of) the float input.\n  -\n    name: placeholder\n    type: int\n    description: >\n      Set a default placeholder value.\n  -\n    name: min\n    type: int\n    description: >\n      Set the minimum allowed value.\n  -\n    name: max\n    type: int\n    description: >\n      Set the maximum allowed value.\n  -\n    name: step\n    type: int\n    description: >\n      Set the interval between valid numbers.\n---\n## Overview\n\nThe float fieldtype is essentially an HTML5 input with `type=\"number\"`. Very similar to the [Integer fieldtype](/fieldtypes/integer), but it allows decimal numbers.\n\n## Data Storage\n\nStores a float – a decimal number.\n"
  },
  {
    "path": "content/collections/fieldtypes/form.md",
    "content": "---\nid: d630ea15-d94f-4404-84d2-0926a898e672\nblueprint: fieldtype\ntitle: Form\nscreenshot: fieldtypes/screenshots/v6/form.webp\nscreenshot_dark: fieldtypes/screenshots/v6/form-dark.webp\ndescription: 'Pick a form, any form.'\noverview: |\n  Use this fieldtype to create a relationship with one of your site's [forms](/forms).\noptions:\n  -\n    name: max_items\n    type: integer\n    required: false\n    description: 'The maximum number of forms that may be selected.'\n  -\n    name: placeholder\n    type: string\n    description: |\n      Set the non-selectable placeholder text. Default: none.\n  -\n    name: query_scopes\n    type: string\n    description: >\n      Allows you to specify a [query scope](/extending/query-scopes-and-filters#scopes) which should be applied when retrieving selectable assets. You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\nrelated_entries:\n  - fdb45b84-3568-437d-84f7-e3c93b6da3e6\n  - aa96fcf1-510c-404b-9b63-cea8942e1bf8\n---\n## Overview\n\nThe Form fieldtype is gives your users a way to pick a form to include along with the current entry. How that form is implemented or shows up on the page is up to you.\n\n## Data Storage\n\nThe Form fieldtype stores the `handle` of a single form as a string, or an array of handles if `max_items` is greater than 1.\n\n## Templating\n\nThe Form fieldtype provides a few useful variables:\n\n* `handle`\n* `title`\n* `fields`\n* `api_url`\n* `honeypot`\n\nYou can use the [`form:create`](/tags/form-create) tag to render a `<form>` on your page.\n"
  },
  {
    "path": "content/collections/fieldtypes/grid.md",
    "content": "---\ntitle: Grid\ndescription: Manage columns of dynamic rows of data that can contain any other fieldtypes.\noverview: >\n  The grid fieldtype is a _meta_ fieldtype, a fieldtype that serves as a container for more fieldtypes. Any fieldtypes. Think of Grid as a spreadsheet, where each column contains any fieldtype, _including another Grid_. We lovingly refer to these as Inception Grids.\n\n  Let's go deeper.\n\nscreenshot: fieldtypes/screenshots/v6/grid.webp\nscreenshot_dark: fieldtypes/screenshots/v6/grid-dark.webp\noptions:\n  -\n    name: min_rows\n    type: 'integer *0*'\n    description: The minimum number of required rows.\n  -\n    name: max_rows\n    type: integer\n    description: >\n      The maximum number of rows allowed. Once\n      reached the `Add Row`\n      button will disappear.\n  -\n    name: fields\n    type: array\n    description: >\n      A list of fields, each of which create their own column.\n  -\n    name: mode\n    type: string *table*\n    description: >\n      The Grid is displayed as a table by default. If you have a large number of columns it can get pretty crowded. Choose `stacked` mode to group rows similar to [Replicator](/fieldtypes/replicator). When [Sneak Peek]() is enabled, Grids automatically toggle into stacked mode.\n  -\n    name: add_row\n    type: string\n    description: \"The `Add Row` button's label.\"\n  -\n    name: reorderable\n    type: string\n    description: \"Enable row reordering. Default: `true`.\"\nid: fa6d2032-0e42-4ea5-b20c-4226941bf0da\n---\n## Fieldtypes\n\nYou can use any fieldtypes inside a Grid. Just remember that because you can doesn't mean you should. Your UI experience will vary greatly. Make sure to compare the experience with the other meta-fields: [Replicator](/fieldtypes/replicator) and [Bard](/fieldtypes/bard).\n\n## Data Structure\n\nThe Grid field creates a YAML collection (associative array).\n\n## Templating\n\nThe example below would have the following data which can be looped through as a tag pair with access to the column data as variables.\n\n```yaml\ncast:\n  -\n    actor: Mark Hamill\n    character: Luke Skywalker\n  -\n    actor: Harrison Ford\n    character: Han Solo\n```\n\n::tabs\n\n::tab antlers\n\n```antlers\n<h3>Star Wars Cast</h3>\n<ul>\n    {{ cast }}\n        <li>{{ character }} played by {{ actor }}</li>\n    {{ /cast }}\n</ul>\n```\n\n::tab blade\n\n```blade\n<h3>Star Wars Cast</h3>\n<ul>\n\t@foreach ($cast as $role)\n\t<li>{{ $role->character }} played by {{ $role->actor }}</li>\n\t@endforeach\n</ul>\n```\n\n::\n\n```html\n<h3>Star Wars Cast</h3>\n<ul>\n    <li>Luke Skywalker played by Mark Hamill</li>\n    <li>Han Solo played by Harrison Ford</li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/group.md",
    "content": "---\nid: 9780cee8-0732-40b5-bc71-ed846d0c290d\nblueprint: fieldtype\ntitle: Group\ndescription: 'Group fields visually and scoped their own key in the data.'\noverview: |\n  Organize the data by visually grouping related fields and assigning a distinct key to each group for clearer data structuring.\nscreenshot: fieldtypes/screenshots/v6/group.webp\nscreenshot_dark: fieldtypes/screenshots/v6/group-dark.webp\n---\n## Overview\n\nA group fieldtype is a simple container that holds additional fields you would like grouped visually as well as under a parent key.\n\n## Data Storage\n\nIn the screenshot above, the data structure for these fields will be as follows:\n\n```yaml\nlocation:\n  address: 123 Main Street\n  city: Schenectady\n  zip: 12345\n```\n\n\n## Templating\n\nAll fields inside a Group will be scoped under their parent key like so:\n\n::tabs\n\n::tab antlers\n\n```antlers\n{{ location:address }}, {{ location:city }}, {{ location:zip }}\n```\n\n::tab blade\n```blade\n{{ $location['address'] }}, {{ $location['city'] }}, {{ $location['zip'] }}\n```\n::\n\nwill output\n\n```html\n123 Main Street, Schenectady, 1234\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/hidden.md",
    "content": "---\ntitle: Hidden\ndescription: Set default data when creating new entries.\noverview: \"The hidden field is perfect for setting default data when creating new entries. Set anything as the `default` field value and you're good to go.\"\n\nid: 791c3fb3-0d3c-4e17-bd97-3ab9529a8691\n---\n## Overview\n\nThis is as simple as you get. Set a default value and it it'll be stored when creating new entries. This is useful if you want to set specific data but don't want it editable in the Control Panel.\n\n``` yaml\nhandle: snappy_comeback\nfield:\n  type: hidden\n  default: Eat my shorts\n```\n\nThat's pretty much it. Nothing else to see here.\n"
  },
  {
    "path": "content/collections/fieldtypes/html.md",
    "content": "---\ntitle: HTML\nmeta_title: 'HTML Fieldtype'\ndescription: 'Add a little presentation-only HTML to your blueprint.'\nintro: |\n  If you've ever wanted to add a little HTML to your blueprint, this is the way to do it. Longer instructions, images, embedded help videos — if you can write it, you can...write it.\n\nscreenshot: fieldtypes/screenshots/v6/html.webp\nscreenshot_dark: fieldtypes/screenshots/v6/html-dark.webp\noptions:\n  -\n    name: html\n    type: string\n    description: \"Store whatever HTML you want — it's up to you.\"\nid: 55e0bd1d-4880-42ee-9a09-c4ece62f6483\n---\n## Data Structure\n\nThis fieldtype is presentation-only and stores no data.\n\n## Templating\n\nThis fieldtype is presentation-only and has no function or purpose on the frontend. Kind of like neckties.\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/icon.md",
    "content": "---\nid: 50ed54f8-18b3-4b46-b0d7-6fedc07ad81f\nblueprint: fieldtype\ntitle: Icon\ndescription: 'Simple UI to select SVG icons from a dropdown.'\nintro: 'Give your users a list of icons to choose from. This field supports search and keyboard commands, and can be configured to use your own icons or ones managed by Statamic.'\nscreenshot: fieldtypes/screenshots/v6/icon.webp\nscreenshot_dark: fieldtypes/screenshots/v6/icon-dark.webp\noptions:\n  -\n    id: nKVDUK7I\n    name: set\n    type: string\n    description: \"Name of a custom icon set. Uses Statamic's icon set by default.\"\n    required: false\n  -\n    id: u8yCCuXb\n    name: default\n    type: string\n    description: 'Set the default option key. Default: none.'\n    required: false\n---\n## Overview\n\nThe Icon field allows you to easily select icons from the Control Panel's default list, or define the path to the directory and/or folder of your own that contains SVG icons.\n\nIt has a very minimal UI which will help streamline your Blueprints and help authors build pages faster with less clicks.\n\n\n## Templating\n\nIcon fields return inline string of the selected SVG icon.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ icon }}\n```\n\n::tab blade\n```blade\n{!! $icon !!}\n```\n::\n\n```html\n<svg viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M11.983 0a12.206 12.206 0 0 0-8.51 3.653A11.8 11.8 0 0 0 0 12.207C-.008 18.712 5.26 23.992 11.765 24h.249c6.678-.069 12.04-5.531 11.986-12.209C24.015 5.293 18.76.013 12.262-.003L11.983 0zM10.5 16.542a1.475 1.475 0 0 1 1.421-1.529l.028-.001h.027c.82.002 1.492.651 1.523 1.47a1.475 1.475 0 0 1-1.419 1.529l-.03.001h-.027a1.53 1.53 0 0 1-1.523-1.47zM11 12.5v-6a1 1 0 0 1 2 0v6a1 1 0 0 1-2 0z\"></path></svg>\n```\n\n## Icon Sets\n\nBy default, the Icon fieldtype uses Statamic's built-in icons. However, you can register a custom icon set in your `AppServiceProvider` and specify it in the field's config.\n\n```php\n// AppServiceProvider.php\nuse Statamic\\Facades\\Icon; // [tl! ++]\nIcon::register('heroicons', base_path('resources/heroicons')); // [tl! ++]\n```\n\n```yaml\n-\n  handle: favourite_icon\n  field:\n    type: icon\n    set: heroicons # [tl! ++]\n```"
  },
  {
    "path": "content/collections/fieldtypes/integer.md",
    "content": "---\ntitle: Integer\ndescription: 'For when all you want is numbers.'\nintro: 'The integer fieldtype is a text-style input that only accepts integers (numbers) and has increment and decrement controls.'\nscreenshot: fieldtypes/screenshots/v6/integer.webp\nscreenshot_dark: fieldtypes/screenshots/v6/integer-dark.webp\nid: 4038c2ac-8c3a-4f4c-8530-8c5f9c8242a6\noptions:\n  -\n    name: append\n    type: string\n    description: >\n      Add text after (to the right of) the integer input.\n  -\n    name: prepend\n    type: string\n    description: >\n      Add text to the beginning (to the left of) the integer input.\n  -\n    name: placeholder\n    type: int\n    description: >\n      Set a default placeholder value.\n  -\n    name: min\n    type: int\n    description: >\n      Set the minimum allowed value.\n  -\n    name: max\n    type: int\n    description: >\n      Set the maximum allowed value.\n  -\n    name: step\n    type: int\n    description: >\n      Set the interval between valid numbers.\n---\n## Overview\n\nThe integer fieldtype is essentially an HTML5 input with `type=\"number\"`. Using the `up` and `down` keyboard keys will increment and decrement the value by `1`.\n\n## Data Storage\n\nStores an integer – a whole number that is not a fraction.\n"
  },
  {
    "path": "content/collections/fieldtypes/link.md",
    "content": "---\ntitle: Link\ndescription: 'Create links to URLs, entries, or child entries.'\nintro: |\n  A select box gives you the option to choose what type of link you'd like to create. When set to URL it gives you a text box to enter the hyperlink. When set to Entry it opens a stack with all your entries to choose from. And when set to First Child will redirect a visitor to the first child page in a structure.\nscreenshot: fieldtypes/screenshots/v6/link.webp\nscreenshot_dark: fieldtypes/screenshots/v6/link-dark.webp\nid: 69975d6f-760e-4ce4-a92b-d98e122744a8\noptions:\n  -\n    name: collections\n    type: array\n    description: |\n      Configure which collections you want to allow relationships with.\n  -\n    name: container\n    type: string\n    description: >\n      An asset container ID. When specified, the fieldtype will allow the user to add a link to an asset from the specified container.\n---\n## Overview\n\nFor when you want to create a link to a URL or entry, this fieldtype is here for you. We often see it used in [Grids](/fieldtypes/grid) and [Replicators](/fieldtypes/replicator).\n\n## Data Storage\n\n``` yaml\n---\nurl_link: 'https://statamic.com'\nentry_link: 'entry::9d682ce3-a353-4fdd-af5e-c1d21b7a87f7'\nfirst_child_link: '@child'\n```\n\n## First Child\n\nCreating a \"first child\" link will dynamically return the URL to first entry nested below in a [Structure](/structures) or [Navigation](/navigation).\n\nFor example, if you set a First Child link on the Getting Started entry below, it will return the URL to the \"Requirements\" entry.\n\n<figure>\n    <img src=\"/img/structure.webp\" alt=\"A Statamic 6 structure tree\" width=\"535\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/structure-dark.webp\" alt=\"A Statamic 6 structure tree\" width=\"535\" class=\"u-hide-in-light-mode\">\n</figure>\n\nThis option will only be provided when the field is in a collection. Globals and terms, by their nature, don't have children.\n\n## Templating\n\nLink fields will render a URL string you can use however you choose.\n\n::tabs\n\n::tab antlers\n```antlers\nCheck out <a href=\"{{ url_link }}\">Statamic</a>!\n```\n\n::tab blade\n```blade\nCheck out <a href=\"{{ $url_link }}\">Statamic</a>!\n```\n::\n\n```output\nCheck out <a href=\"https://statamic.com\">Statamic</a>!\n```\n\nYou can access other data of the link field by using it like an array. This could be the title of an entry you link to, for example.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ link_field:title }}\n```\n\n::tab blade\n```blade\n{{ $link_field['title'] }}\n```\n::\n"
  },
  {
    "path": "content/collections/fieldtypes/list.md",
    "content": "---\ntitle: List\ndescription: Manage simple lists with the help of a keyboard-friendly interface.\nintro: >\n  Create YAML lists with a robust user interface. It has full keyboard controls\n  so you can use `up` to go up, `down` to go down, drag and drop to rearrange the order, and click an item to select it and begin editing.\nscreenshot: fieldtypes/screenshots/v6/list.webp\nscreenshot_dark: fieldtypes/screenshots/v6/list-dark.webp\nid: bd079cba-c5d2-475d-ae82-57874818858e\n---\n## Overview\n\nFor when you want to manage a simple YAML list, this fieldtype is here for you. Being able to reorder the list items is nice, as is the ability to delete them.\n\n## Data Storage\n\n``` yaml\nproduct_ideas:\n  - 'Knife-Wrench (for kids!)'\n  - 'Kite-Fork'\n  - 'Apple-Cranberry hybrid (calling it Appleberry™)'\n```\n\n## Templating\n\nLoop through the array items to display each item's `value`.\n\n::tabs\n\n::tab antlers\n```antlers\n<h1>Product Ideas</h1>\n<ul>\n  {{ product_ideas }}\n    <li>{{ value }}</li>\n  {{ /product_ideas }}\n</ul>\n```\n\n::tab blade\n```blade\n<h1>Product Ideas</h1>\n<ul>\n\t@foreach ($product_ideas as $idea)\n\t<li>{{ $idea }}</li>\n\t@endforeach\n</ul>\n```\n::\n\n```html\n<h1>Product Ideas</h1>\n<ul>\n  <li>Knife-Wrench (for kids!)</li>\n  <li>Kite-Fork</li>\n  <li>Apple-Cranberry hybrid (calling it Appleberry™)</li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/markdown.md",
    "content": "---\ntitle: Markdown\ndescription: Our beautiful Markdown editor with preview, assets integration, and more.\nintro: Write Markdown with the help of formatting buttons, assets integration, fullscreen mode, a Markdown cheatsheet, and HTML preview mode. What more do you need?\nscreenshot: fieldtypes/screenshots/v6/markdown.webp\nscreenshot_dark: fieldtypes/screenshots/v6/markdown-dark.webp\nid: 607cfe62-7239-461b-8f55-8e7a312c2d5d\nrelated_entries:\n  - be292d2b-dc0e-48dc-bce4-0058df27ccc6\noptions:\n  -\n    name: antlers\n    type: string\n    description: >\n      Enable Antlers parsing in this field's content.\n  -\n    name: automatic_line_breaks\n    type: boolean\n    description: >\n      Automatically convert line breaks to `&lt;br&gt;` tags. Default: `true`.\n  -\n    name: automatic_links\n    type: boolean\n    description: >\n      Automatically links any URLs in the text. Default: `false`.\n  -\n    name: container\n    type: string\n    description: |\n      Set the name of an [asset container](/assets#containers) to enable browsing, uploading, and inserting assets.\n  -\n    name: escape_markup\n    type: boolean\n    description: >\n      Escapes inline HTML markup. For example, `&lt;div&gt;` will be replaced with `&amp;lt;div&amp;gt;`. Default: `false`.\n  -\n    name: folder\n    type: string\n    description: |\n      The folder (relative to the container) to begin browsing. Default: the root folder of the container.\n  -\n    name: heading_anchors\n    type: boolean\n    description: |\n      Inject anchor links to all of your heading elements (`&lt;h1&gt;`, `&lt;h2&gt;`, etc). Default: `false`.\n  -\n    name: parser\n    type: string\n    description: >\n      The name of a customized Markdown parser. Leave blank for default.\n  -\n    name: restrict\n    type: bool\n    description: >\n      If `true`, navigation within the asset browser will be disabled. Your users will be restricted to specified the container and folder. Default: `false`.\n  -\n    name: smartypants\n    type: boolean\n    description: >\n      Automatically convert straight quotes into curly quotes, dashes into en/em-dashes, and other similar text transformations. Default: `false`.\n  -\n    name: table_of_contents\n    type: boolean\n    description: >\n      Automatically insert a table of contents at the top of your content with links to your headings. Default: `false`.\n---\n## Overview\n\nMarkdown has been around since 2004. One fateful day in December, [John Gruber](https://daringfireball.net/projects/markdown/) published his spec and first version of the Markdown parser. Since that day (it was a Friday), Markdown has grown wildly in popularity, and today has become the de facto standard format for writing portable content.\n\nBack in 2004 there was just one flavor: John's. Today's landscape has many variations, parsers, extensions, and standards groups. The most widely accepted feature set is [Github-Flavored Markdown][gfm], or GFM for short.\n\nStatamic uses the [League\\CommonMark][commonmark] library to support GFM, to enable tables, special attributes like classes and ids on block-level elements, and fenced code blocks.\n\n## Data Structure\n\nThe data will be saved exactly as written – a Markdown string.\n\n``` markdown\n## Overview\n\nThis is the Markdown fieldtype. It's for writing [Markdown](https://daringfireball.net/projects/markdown/), an easy-to-read, easy-to-write plain text format that magically transforms into HTML.\n```\n\n## Templating\n\nThe Markdown content will be automatically transformed into HTML through [augmentation](/augmentation). You need only use the variable and the rest is done for you.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ content }}\n```\n\n::tab blade\n```blade\n{!! $content !!}\n```\n::\n\n```html\n<h2>Overview</h2>\n<p>This is the Markdown fieldtype. It's for writing <a href=\"https://daringfireball.net/projects/markdown/\">Markdown</a>, an easy-to-read, easy-to-write plain text format that magically transforms into HTML.</p>\n```\n\n[commonmark]: https://commonmark.thephpleague.com/\n[gfm]: https://help.github.com/en/categories/writing-on-github\n"
  },
  {
    "path": "content/collections/fieldtypes/navs.md",
    "content": "---\ntitle: Navs\nmeta_title: 'Navs Fieldtype'\ndescription: Choose from one or more navigations.\noverview: Allows you to choose from one or more navigations.\nscreenshot: fieldtypes/screenshots/v6/navs.webp\nscreenshot_dark: fieldtypes/screenshots/v6/navs-dark.webp\noptions:\n  -\n    name: max_items\n    type: integer\n    description: >\n      The maximum number of items that may be selected. Setting this to `1` will change the UI to a dropdown.\n  -\n    name: mode\n    type: string\n    description: |\n        Set the UI style for this field. Can be one of 'default' (Stack Selector), 'select' (Select Dropdown) or 'typeahead' (Typeahead Field).\n  -\n    name: structure_types\n    type: array\n    description: >\n        Configure which types of structures you want to be selectable. Options are `collection` or `navigation`.\nid: 0669f781-bc12-44ff-bbbb-921b80aaf4f3\n---\n## Overview\n\nUse this fieldtype to create a one-way relationship with one or more navigations in your site. It's a simple-little-helper type of thing.\n\n## Data Structure\n\nThe Navs fieldtype stores the `handle` of a single navigation as a string, or an array of handles if `max_items` is greater than 1.\n\n``` yaml\nnavigations:\n  - main_nav\n  - footer\n  ```\n\n## Templating\n\nLoop through the structures to  access their handles and pass them to a [Nav](/tags/nav) tag.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ navigations }}\n  <ul>\n    {{ nav :handle=\"handle\" }}\n      <li><a href=\"{{ url }}\">{{ title }}</a></li>\n    {{ /nav }}\n  </ul>\n{{ /navigations }}\n```\n::tab blade\n```blade\n@foreach ($navigations as $nav)\n  <ul>\n    <statamic:nav\n      :handle=\"$nav\"\n    >\n      <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n    </statamic:nav>\n  </ul>\n@endforeach\n```\n::\n\n```html\n<ul>\n  <li><a href=\"/look-at-this\">Look at This!</a></li>\n  <li><a href=\"/look-at-that\">Wait, Look at That!</a></li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/radio.md",
    "content": "---\ntitle: Radio\ndescription: 'Circles you click. You can only choose one.'\nintro: |\n  Radio buttons. The \"you can only have one\" variation of checkboxes. Create some options and let your users select one and only one. May they choose wisely.\n\nscreenshot: fieldtypes/screenshots/v6/radio.webp\nscreenshot_dark: fieldtypes/screenshots/v6/radio-dark.webp\noptions:\n  -\n    name: inline\n    type: bool\n    description: |\n      Show the radio buttons next to each other in a row instead of stacked vertically. Default: `false`\n\n  -\n    name: options\n    type: array\n    description: 'Sets of key/value pairs define the values and labels of the radio options.'\nid: 0b662f17-1cd1-4c64-a705-980a2ca5aab4\n---\n## Overview\n\nThe radio fieldtype is a multiple choice input where you only get one choice. It saves the chosen option from a preset list.\n\n## Configuring\n\nUse the `options` setting to define a list of values and labels.\n\n``` yaml\nfavorite:\n  type: radio\n  instructions: Choose your favorite food.\n  options:\n    donuts: Donuts\n    icecream: Ice Cream\n    brownies: Brownies\n```\n\nYou may omit the labels and just specify keys. If you use this syntax, the value and label will be identical.\n\n``` yaml\n  options:\n    - Donuts\n    - Ice Cream\n    - Brownies\n```\n\n### Options in blueprint YAML\n\nSee [Select · Options in blueprint YAML](/fieldtypes/select#options-in-blueprint-yaml)—checklists defined in the blueprint use the same expanded `key` / `value` option rows for ordered, storage-agnostic option lists.\n\n## Data Structure\n\nThe chosen option is stored as a string. If you only specified values for the `options` array, then the label will be saved.\n\n``` yaml\nfavorite: brownies\n```\n\n\n\n## Templating\n\nIt's a string, so you can just use that value.\n\n::tabs\n\n::tab antlers\n```antlers\n<p>I love {{ favorite }}. A lot.</p>\n```\n\n::tab blade\n```blade\n<p>I love {{ $favorite }}. A lot.</p>\n```\n::\n\n```html\n<p>I love donuts. A lot.</p>\n```\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/range.md",
    "content": "---\ntitle: Range\ndescription: 'Choose a number between a min and max value.'\nintro: |\n  Range fields let the user choose a numeric value which must be _no less_ than a given value, and _no more_ than another.\n\nscreenshot: fieldtypes/screenshots/v6/range.webp\nscreenshot_dark: fieldtypes/screenshots/v6/range-dark.webp\noptions:\n  -\n    name: min\n    type: integer\n    description: |\n      The minimum, left-most value. Default `0`.\n  -\n    name: max\n    type: integer\n    description: |\n      The maximum, left-most value. Default `1000`.\n  -\n    name: step\n    type: integer\n    description: |\n      The minimum size between values. Default `1`.\n  -\n    name: append\n    type: string\n    description: |\n      Add text to the end (right-side) of the range slider.\n  -\n    name: prepend\n    type: string\n    description: |\n      Add text to the beginning (left-side) of the range slider.\nid: 5ede219c-607e-4ad2-8498-6ca55a063e73\n---\n## Data Structure\n\nThe value is stored as an integer.\n\n``` yaml\nnumber: 42\n```\n\n## Templating\n\nUse the variable in your templates to display the value. That's pretty much it.\n\n::tabs\n\n::tab antlers\n```antlers\n<p>My favorite number is {{ number }}.</p>\n```\n\n::tab blade\n```blade\n<p>My favorite number is {{ $number }}.</p>\n```\n::\n\n```html\n<p>My favorite number is 42.</p>\n```\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/replicator.md",
    "content": "---\ntitle: Replicator\ndescription: Build your content by creating sets of fields you can mix and match on the fly.\noverview: |\n  The Replicator is a meta fieldtype giving you the ability to define _sets_ of fields that you can dynamically piece together in whatever order and arrangement you imagine. You can build long-form articles like [Medium.com](http://medium.com) and take advantage of the extra markup control.\n\n  It's so much better than a WYSIWYG field.\nscreenshot: fieldtypes/screenshots/v6/replicator.webp\nscreenshot_dark: fieldtypes/screenshots/v6/replicator-dark.webp\noptions:\n  -\n    name: sets\n    type: array\n    description: An array containing sets of fields.\n  -\n    name: max_sets\n    type: integer\n    description: The maximum number of sets that may be added.\n  -\n    name: button_label\n    type: string\n    description: Allows you to define a label for the \"Add Set\" button.\nid: 00b140e3-413a-4d91-b9e7-65f58d56a41b\n---\n## Usage\n\nYou will be presented with a button for each set you’ve defined. Clicking one will replicate an empty set. You can [replicate](https://www.youtube.com/watch?v=qD4EVXkfe0w) a single set type as many times as you like as well as dragging and dropping them to adjust their order.\n\nYou may collapse your sets to conserve space. If you do, a preview of the data contained within it will be displayed. [Third party fieldtypes may control how their data will be previewed](/extending/fieldtypes#replicator-preview). You can prevent certain fields being shown in the preview text by adding `replicator_preview: false`.\n\nThe following fieldset YAML is an example of what could be used to construct the Replicator shown in the screenshot above:\n\n``` yaml\nfields:\n  my_replicator_field:\n    type: replicator\n    display: Replicator\n    sets:\n      text:\n        display: Text\n        fields:\n          text:\n            type: markdown\n      image:\n        display: Image\n        fields:\n          photo:\n            type: assets\n            container: main\n            max_files: 1\n          caption:\n            type: text\n      quote:\n        display: Pull Quote\n        fields:\n          text:\n            type: text\n          cite:\n            type: text\n          pull:\n            type: radio\n            options:\n              left: Left Align\n              right: Right Align\n```\n\n## Set Previews\n\nNew to Statamic v6, you can add an image preview of your set, _as well as_ an icon. Previews make it easy to identify sets by showing a screenshot of what the rendered set might look like on the front-end. Clients can now say “ah, that one” without pretending to know the names you carefully gave them.\n\n### Configuring Set Previews\n\nTo add a set preview, click the little \"pencil\" icon next to the set name.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-set-edit-preview.webp\" alt=\"Replicator Set Edit Preview\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-set-edit-preview-dark.webp\" alt=\"Replicator Set Edit Preview\" class=\"u-hide-in-light-mode\">\n    <figcaption>Let's give the set a preview.</figcaption>\n</figure>\n\nOnce you're in the set editor, you can add a preview image and icon. Here we're showing a lovely screenshot of what the newsletter signup form might look like on the front-end. We can even add some instructions to explain how the set is used.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-set-previews.webp\" alt=\"Replicator Set Previews\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-set-previews-dark.webp\" alt=\"Replicator Set Previews\" class=\"u-hide-in-light-mode\">\n    <figcaption>Behold a <em>Preview Image</em>, for the love of all clients.</figcaption>\n</figure>\n\n### Set Previews in Action\n\nOnce you've set a preview image, users adding a replicator set can hover over the set to preview what it might look like on the frontend.\n\nYou can view previews in two different UI modes: in a list of set names, or in a grid of sets with their preview images.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-hover-preview-list.webp\" alt=\"Replicator Set Previews in the CP\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-hover-preview-list-dark.webp\" alt=\"Replicator Set Previews in the CP\" class=\"u-hide-in-light-mode\">\n    <figcaption>A preview in a list of sets.</figcaption>\n</figure>\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-hover-preview-grid.webp\" alt=\"Replicator Set Previews in the CP\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/replicator-hover-preview-grid-dark.webp\" alt=\"Replicator Set Previews in the CP\" class=\"u-hide-in-light-mode\">\n    <figcaption>A preview in a grid of sets. Previews fall back to the set icon if no preview image is set.</figcaption>\n</figure>\n\n\n## Fieldtypes\n\nYou can use any fieldtypes inside your Replicator sets. Make sure to compare the experience with the other meta-fields: [Grid](/fieldtypes/grid) and [Bard](/fieldtypes/bard).\n\n## Data Structure {#data-structure}\n\nReplicator stores your data as an array with the set name as `type`.\n\n```yaml\nmy_replicator_field:\n  -\n    type: text\n    text: \"Let's talk about the best new show from 2017!\"\n  -\n    type: image\n    photo: /assets/night-manager.jpg\n    caption: The Night Manager\n  -\n    type: quote\n    text: Such fear, such dread, and such a dazzling script.\n    cite: Deborah Ross\n    pull: right\n```\n\n:::warning\nPlease note that you **cannot** use a Replicator fieldtype for the `content` field.\n:::\n\n## Templating {#templating}\n\nUse the tag pair syntax with an `if/else` conditions to style each set accordingly.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ my_replicator_field }}\n\n  {{ if type == \"text\" }}\n\n    <div class=\"text\">\n      {{ text|markdown }}\n    </div>\n\n  {{ elseif type == \"quote\" }}\n\n    <blockquote>{{ text }}</blockquote>\n    <p>— {{ cite }}</p>\n\n  {{ elseif type == \"image\" }}\n\n    <figure>\n      <img src=\"{{ photo }}\" alt=\"{{ caption }}\" />\n      <figcaption>{{ caption }}</figcaption>\n    </figure>\n\n  {{ /if }}\n\n{{ /my_replicator_field }}\n\n```\n::tab blade\n```blade\n@foreach($my_replicator_field as $set)\n\t@if ($set->type === 'text')\n\t\t<div class=\"text\">\n\t\t\t{!! Statamic::modify($set->text)->markdown() !!}\n\t\t</div>\n\t@elseif ($set->type === 'quote')\n\t\t<blockquote>{{ $set->text  }}</blockquote>\n\t\t<p>— {{ $set->cite }}</p>\n\t@elseif ($set->type === 'image')\n\t\t<figure>\n\t\t\t<img src=\"{{ $set->photo }}\" alt=\"{{ $set->caption }}\" />\n\t\t\t<figcaption>{{ $set->caption }}</figcaption>\n\t\t</figure>\n\t@endif\n@endforeach\n```\n::\n\nAn alternative, and often cleaner, approach is to have multiple 'set' partials and do:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ my_replicator_field }}\n  {{ partial src=\"sets/{type}\" }}\n{{ /my_replicator_field }}\n```\n::tab blade\n\n:::tip\nBy using `[...$set]`, you can access the set variables within the set's Blade file without having to reference `$set` for each variable.\n\nFor example, `{!! $set->text !!}` becomes `{!! $text !!}`.\n:::\n\n```blade\n@foreach ($my_replicator_field as $set)\n\t@include('fieldtypes.partials.sets.'.$set->type, [...$set])\n@endforeach\n```\n::\n\nThen inside your partials directory you could have:\n\n::tabs\n\n::tab antlers\n``` files theme:serendipity-light\nresources/views/partials/sets/\n  text.antlers.html\n  quote.antlers.html\n  image.antlers.html\n```\n\n::tab blade\n``` files theme:serendipity-light\nresources/views/partials/sets/\n  text.blade.php\n  quote.blade.php\n  image.blade.php\n```\n::\n\nand the `image` set partial may look something like:\n\n::tabs\n\n::tab antlers\n```antlers\n{{# this is image.antlers.html #}}\n\n<img src=\"{{ image }}\" alt=\"{{ caption }}\" >\n```\n\n::tab blade\n```blade\n{{-- this is image.blade.php --}}\n<figure>\n\t<img src=\"{{ $photo }}\" alt=\"{{ $caption }}\" />\n\t<figcaption>{{ $caption }}</figcaption>\n</figure>\n```\n::\n\n## Custom set icons\n\nYou can change the icons available in the set picker by configuring an icon set in a service provider.\n\nFor example, you can drop this into your `AppServiceProvider`'s `boot` method:\n\n```php\nuse Statamic\\Fieldtypes\\Sets;\n\npublic function boot()\n{\n    Sets::useIcons('heroicons', resource_path('svg/heroicons'));\n}\n```"
  },
  {
    "path": "content/collections/fieldtypes/revealer.md",
    "content": "---\ntitle: Revealer\ndescription: A button that reveals conditional fields like magic.\nintro: The revealer is a simple button that reveals conditional fields without saving boolean button data.\nid: 54066363-7dec-431c-86c6-7e9353380ef5\nscreenshot: fieldtypes/screenshots/v6/revealer.gif\nscreenshot_dark: fieldtypes/screenshots/v6/revealer-dark.gif\noptions:\n  -\n    name: display\n    type: string\n    description: The revealer label text.\n  -\n    name: mode\n    type: string *button*\n    description: The revealer input is displayed in `button` mode by default. Choose `toggle` mode if you wish to display a toggle input instead.\n  -\n    name: input_label\n    type: string\n    description: Optionally customize the label on the input itself.\n  -\n    name: instructions\n    type: string\n    description: Instructional text that will appear as a tooltip on the button.\n---\n\nIf you have some fields that you wish to hide until the user is ready to reveal them, throw a Revealer field in there and those fields may be shown once the button is clicked.\n\nThis fieldtype is intended to be used with our [conditional field rules](/conditional-fields), but unlike regular conditional fields, it will not [disrupt data flow](/conditional-fields#data-flow) on fields hidden by a Revealer.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/revealer-conditions.webp\" alt=\"Conditional fields used with Revealer\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/revealer-conditions-dark.webp\" alt=\"Conditional fields used with Revealer\" class=\"u-hide-in-light-mode\">\n    <figcaption>An example of field conditions used in conjunction with a Revealer field.</figcaption>\n</figure>\n\nThe example image above uses the following field configuration:\n\n``` yaml\n  -\n    handle: behold\n    field:\n      type: revealer\n      display: 'Behold!'\n  -\n    handle: revealed\n    field:\n      type: text\n      display: 'I am revealed!'\n      if:\n        behold: 'equals true'\n```\n\nRegardless of whether the button was clicked or not, no boolean data will be saved for the `behold` Revealer button itself.\n"
  },
  {
    "path": "content/collections/fieldtypes/select.md",
    "content": "---\ntitle: Select\ndescription: Choose from predefined options. This field is highly configurable.\nintro: Give your users a list of options to choose from. This select field is highly configurable with support for search, multiple choice, and creating new options on the fly.\nscreenshot: fieldtypes/screenshots/v6/select.webp\nscreenshot_dark: fieldtypes/screenshots/v6/select-dark.webp\noptions:\n  -\n    name: clearable\n    type: boolean\n    description: |\n      Allow deselecting any chosen option and making null a possible value. Default: `false`.\n  -\n    name: options\n    required: true\n    type: array\n    description: >\n      A set of key/value pairs that define the values and labels. If you don't define the keys, the value and label will be the same.\n  -\n    name: placeholder\n    type: string\n    description: |\n      Set the non-selectable placeholder text. Default: none.\n  -\n    name: default\n    type: string\n    description: |\n      Set the default option key. Default: none.\n  -\n    name: multiple\n    type: boolean\n    description: >\n      Allow multiple selections. Default: `false`.\n  -\n    name: searchable\n    type: boolean\n    description: >\n      Enable search with suggestions by typing in the select box. Default: `true`.\n  -\n    name: taggable\n    type: boolean\n    description: >\n      Use a \"tag\" style UI when selecting multiples. Default: `false`.\n  -\n    name: push_tags\n    type: boolean\n    description: >\n      Add newly created options to the list. Default: `false`.\nid: 812bd19d-ec37-42d5-b8f9-310366ef8abe\n---\n## Overview\n\nThis field is highly configurable, thanks to the fantastic [Vue Select](https://vue-select.org) component. Be sure to explore all the [config options](#options)!\n\n## Data Storage\n\nSelect fields will store the _value_ of the chosen option or options. Given this configuration...\n\n``` yaml\nhandle: select\n  field:\n    display: Select\n    options:\n      face: \"So's your face.\"\n      know: \"I know you are, but what am I?\"\n      hand: \"Talk to the hand.\"\n      beeswax: \"Mind your own beeswax.\"\n    placeholder: 'Choose your snappy comeback'\n    type: select\n```\n\nYour saved data will be:\n\n``` yaml\nselect: face\n```\n\n### Options in blueprint YAML {#options-in-blueprint-yaml}\n\nIn the blueprint file, the field’s `options` list is stored in **expanded** form (an ordered sequence of `key` / `value` pairs) so Statamic can preserve option order everywhere content is stored—including SQL-backed databases. If you author blueprints by hand, use that shape or mirror what the Blueprint Editor writes:\n\n```yaml\noptions:\n  - key: face\n    value: \"So's your face.\"\n  - key: know\n    value: \"I know you are, but what am I?\"\n```\n\nThat setting applies to the blueprint definition only, not to the entry value (`select: face` above).\n\n\n## Templating\n\nSelect fields return the **value** from your selected option. You can access the label with `select_var:label`.\n\n::tabs\n\n::tab antlers\n```antlers\n<p id=\"{{ select }}\"> Oh yeah? {{ select:label }}</p>\n```\n\n::tab blade\n```blade\n<p id=\"{{ $select }}\"> Oh yeah? {{ $select['label'] }}</p>\n```\n::\n\n```html\n<p id=\"face\">Oh yeah? So's your face.</p>\n```\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/sites.md",
    "content": "---\nid: db0162b1-c58c-4093-841c-b386cc2e5c21\nblueprint: fieldtype\ntitle: Sites\nscreenshot: fieldtypes/screenshots/v6/sites.webp\nscreenshot_dark: fieldtypes/screenshots/v6/sites-dark.webp\nintro: 'Allows you to select one or more sites when running a [multi site](/multi-site).'\noptions:\n  -\n    name: max_items\n    type: integer\n    description: >\n      The maximum number of items that may be selected. Setting this to `1` will change the UI to a select dropdwon.\n  -\n    name: mode\n    type: string\n    description: |\n        Set the UI style for this field. Can be one of 'default' (Stack Selector), 'select' (Select Dropdown) or 'typeahead' (Typeahead Field).\n---\n## Usage\n\n```yaml\nfields:\n  my_sites_field:\n    type: sites\n```\n\nThe Sites fieldtype is a [Relationship fieldtype](/relationships#fieldtypes), and will save the site or sites as their handles (the config name).\n\n```yaml\nsites:\n  - english\n  - french\n  - german\n```\n\n## Templating\n\nYou're more than likely using this field as a way to dynamically fetch content from a specific site other than the current one.\n\nThe following example assumes `max_items` has been set to `1`:\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n  {{ collection:news :site=\"my_sites_field\" }}\n    <li>{{ title }}</li>\n  {{ /collection:news }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n\t<statamic:collection:news\n\t\t:site=\"$my_sites_field\"\n\t>\n\t\t<li>{{ $title }}</li>\n\t</statamic:collection:news>\n</ul>\n```\n::\n\n```html\n<ul>\n  <li>Bonjour!</li>\n  <li>Ton tonton tond ton thon</li>\n  <li>etc</li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/slug.md",
    "content": "---\nid: cbc7ecef-155f-45c0-9ac4-e815e120fa99\nblueprint: fieldtype\ntitle: Slug\nscreenshot: fieldtypes/screenshots/v6/slug.webp\nscreenshot_dark: fieldtypes/screenshots/v6/slug-dark.webp\ndescription: A text input that automatically \"slugifies\" the value of another field.\noverview: >\n  A text field that has the ability to automatically \"slugify\" the value of any other string field to create-your-very-own-lowercase-without-spaces string of your own. This is primarily used to create a entry URL slugs based on the `title` field of that same entry.\noptions:\n  -\n    name: from\n    type: string\n    description: >\n      Target field to automatically create a slug from. **Default:** `title`\n  -\n    name: generate\n    type: boolean\n    description: >\n      Whether to generate the slug automatically. **Default:** `true`\n---\n"
  },
  {
    "path": "content/collections/fieldtypes/spacer.md",
    "content": "---\ntitle: Spacer\ndescription: An invisible field to help you structure your blueprints and forms.\noverview: \"The Spacer fieldtype is invisible, perfect for giving your forms some much-needed breathing room. \"\nid: 55043a00-28ee-4977-a10a-6295e903f41b\nscreenshot: fieldtypes/screenshots/v6/spacer.webp\nscreenshot_dark: fieldtypes/screenshots/v6/spacer-dark.webp\n---\n"
  },
  {
    "path": "content/collections/fieldtypes/structures.md",
    "content": "---\ntitle: Structures\nmeta_title: 'Structures Fieldtype'\ndescription: 'Create relationships with structures.'\nintro: |\n  For when you need to create a relationship to one or more [Structures](/structures). This could be useful to pick which version of a sidebar or footer to include on a page, or other similar things.\n\nscreenshot: fieldtypes/screenshots/v6/structures.webp\nscreenshot_dark: fieldtypes/screenshots/v6/structures-dark.webp\noptions:\n  -\n    name: max_items\n    type: integer\n    description: 'The maximum number of items that may be selected. Setting this to `1` will automatically change the UI to a dropdown.'\n  -\n    name: mode\n    type: string\n    description: Sets the UI mode for choosing your structures. Pick between `Stack Selector`, `Select Dropdown`, or `Typeahead Field`.\nrelated_entries:\n  - 3c34ef5c-781e-4a22-a09b-25f58bdb58a8\n  - ed746608-87f9-448f-bf57-051da132fef7\nid: 5a55198f-fcb6-4cb1-aacc-4aec3ad45003\n---\n## Overview\n\nUse this fieldtype to create a one-way relationship with one or more structures in your site. It's a simple-little-helper type of thing.\n\n:::tip\n[Structures](/structures) come in two flavors: Ordered Collections and Navigations.\n:::\n\n## Data Storage\n\nThe Structures fieldtype stores the `handle` of a single structure as a string, or an array of handles if `max_items` is greater than 1.\n\n``` yaml\nstructures:\n  - main_nav\n  - footer\n  ```\n\n## Templating\n\nLoop through the structures to  access their handles and pass them to a [Nav](/tags/nav) tag.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ structures }}\n  <ul>\n    {{ nav :handle=\"handle\" }}\n      <li><a href=\"{{ url }}\">{{ title }}</a></li>\n    {{ /nav }}\n  </ul>\n{{ /structures }}\n```\n::tab blade\n```blade\n@foreach ($structures as $nav)\n  <ul>\n    <statamic:nav\n      :handle=\"$nav\"\n    >\n      <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n    </statamic:nav>\n  </ul>\n@endforeach\n```\n::\n\n```html\n<ul>\n  <li><a href=\"/look-at-this\">Look at This!</a></li>\n  <li><a href=\"/look-at-that\">Wait, Look at That!</a></li>\n</ul>\n```\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/table.md",
    "content": "---\ntitle: Table\ndescription: Create and manage simple tables of limitless columns and rows.\nintro: >\n  Creating tables can be a nuisance in a WYSIWYG editor. This fieldtype gives you a way to create flexible tabular data.\nscreenshot: fieldtypes/screenshots/v6/table.gif\nscreenshot_dark: fieldtypes/screenshots/v6/table-dark.gif\nid: 11e0ab78-7698-44c8-98f1-1194cb12ce28\noptions:\n  -\n    name: min_rows\n    type: 'integer *0*'\n    description: The minimum number of required rows.\n  -\n    name: max_rows\n    type: integer\n    description: >\n      The maximum number of rows allowed. Once\n      reached the `Add Row`\n      button will disappear.\n  -\n    name: default\n    type: string\n    description: |\n      Set the default value.\n---\n## Data Structure\n\nData from the Table fieldtype is saved in an array like this:\n\n``` yaml\nmy_table:\n  -\n    cells:\n      - People\n      - Gift\n  -\n    cells:\n      - Kevin\n      - Kerosene\n  -\n    cells:\n      - Buzz\n      - Spider\n```\n\nThis data format makes it trivial when it comes time to render it templates.\n\n## Templating\n\nThis fieldtype comes with a handy [`table`](/modifiers/table) modifier, which will turn your data into a simple HTML `<table>`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ my_table | table }}\n```\n\n::tab blade\n```blade\n{!! Statamic::modify($my_table)->table() !!}\n```\n::\n\nHere’s the same thing that the modifier would have output, but we’re modifying the cells to use `| markdown`.\n\n::tabs\n\n::tab antlers\n```antlers\n<table>\n  {{ my_table }}\n    <tr>\n      {{ cells }}\n        <td>{{ value | markdown }}</td>\n      {{ /cells }}\n    </tr>\n  {{ /my_table }}\n</table>\n```\n::tab blade\n```blade\n<table>\n\t@foreach ($my_table as $row)\n\t\t<tr>\n\t\t\t@foreach ($row['cells'] as $cell)\n\t\t\t\t<td>{!! Statamic::modify($cell)->markdown() !!}</td>\n\t\t\t@endforeach\n\t\t</tr>\n\t@endforeach\n</table>\n```\n::\n\nWant even more control? This example assumes you have a boolean field in your front-matter named `first_row_headers` which toggles whether or not to render the first row of the table in a `<thead>` with `<th>` tags.\n\n::tabs\n\n::tab antlers\n```antlers\n<table>\n  {{ my_table }}\n    {{ if first && first_row_headers }}\n      <thead>\n        <tr>\n          {{ cells }}\n            <th>{{ value|markdown }}</th>\n          {{ /cells }}\n        </tr>\n      </thead>\n      <tbody>\n    {{ /if }}\n    {{ if !first && first_row_headers || !first_row_headers }}\n      {{ if first }}\n        <tbody>\n      {{ /if }}\n        <tr>\n          {{ cells }}\n            <td>{{ value|markdown }}</td>\n          {{ /cells }}\n        </tr>\n      {{ if last }}\n        </tbody>\n      {{ /if }}\n    {{ /if }}\n  {{ /my_table }}\n</table>\n```\n::tab blade\n\nThe following example uses the `fetch` helper function, which resolves `Value` instances for you and returns the underlying value. If the passed value is not a `Value` instance, you will get that original value back.\n\n```blade\n<?php\n\tuse function Statamic\\View\\Blade\\{fetch};\n?>\n<table>\n\t@php($first_row_headers = fetch($first_row_headers) ?? false)\n\t@foreach ($my_table as $row)\n\t\t@if ($loop->first && $first_row_headers)\n\t\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t@foreach ($row['cells'] as $value)\n\t\t\t\t<th>{!! Statamic::modify($value)->markdown() !!}</th>\n\t\t\t\t@endforeach\n\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t@endif\n\t\t@if (! $loop->first && $first_row_headers || ! $first_row_headers)\n\t\t\t@if ($loop->first)\n\t\t\t\t<tbody>\n\t\t\t@endif\n\n\t\t\t<tr>\n\t\t\t\t@foreach ($row['cells'] as $value)\n\t\t\t\t\t<th>{!! Statamic::modify($value)->markdown() !!}</th>\n\t\t\t\t@endforeach\n\t\t\t</tr>\n\n\t\t\t@if ($loop->last)\n\t\t\t\t</tbody>\n\t\t\t@endif\n\t\t@endif\n\t@endforeach\n</table>\n::\n"
  },
  {
    "path": "content/collections/fieldtypes/taggable.md",
    "content": "---\ntitle: Tags\nscreenshot: fieldtypes/screenshots/v6/taggable.webp\nscreenshot_dark: fieldtypes/screenshots/v6/taggable-dark.webp\ndescription: Enter a list of items with a tag-style interface.\noverview: >\n  Users can enter “taggable” values, which are formatted\n  automatically into a YAML list format. It's a lot like the [list fieldtype](/fieldtypes/list) but with a different UI.\nid: 821a636f-2ebd-4297-b459-47e702f899df\n---\n## Overview\n\nPress `enter`, `tab`, or `,` to add a tag. Click an <span class=\"bg-grey-200 text-grey-600 rounded font-bold px-1\">×</span> to remove one. That's  all there is to it.\n\n## Data Storage\nYour tags will get saved as a simple YAML list, like this:\n\n```yaml\n- applesauce\n- garbage pants\n- socks\n```\n\n## Templating\n\nLoop through the array items to display each item's `value`.\n\n::tabs\n\n::tab antlers\n```antlers\n<h1>I've heard rumors of:</h1>\n<ul>\n  {{ tags }}\n    <li>{{ value }}</li>\n  {{ /tags }}\n</ul>\n```\n\n::tab blade\n\n```blade\n<h1>I've heard rumors of:</h1>\n<ul>\n\t@foreach ($tags as $tag)\n\t\t<li>{{ $tag }}</li>\n\t@endforeach\n</ul>\n```\n::\n\n```html\n<h1>I've heard rumors of:</h1>\n<ul>\n  <li>applesauce</li>\n  <li>garbage pants</li>\n  <li>socks</li>\n</ul>\n```\n\n:::tip\nThis fieldtype uses the word \"taggable\" in a generic way. If you're looking for a way to tag/categorize your content on a _schema_-level, you should read about [taxonomies](/taxonomies).\n:::\n"
  },
  {
    "path": "content/collections/fieldtypes/taxonomies.md",
    "content": "---\nid: 88c9909c-4134-40cd-b095-79ec7207b190\nblueprint: fieldtype\ntitle: Taxonomies\ndescription: 'Choose from one or more taxonomies.'\noverview: 'Allows you to choose one or more taxonomies.'\noptions:\n  -\n    name: max_items\n    type: integer\n    description: |\n      The maximum number of items that may be selected. Setting this to `1` will change the UI to a select dropdwon.\n  -\n    name: mode\n    type: string\n    description: |\n      Set the UI style for this field. Can be one of 'default' (Stack Selector), 'select' (Select Dropdown) or 'typeahead' (Typeahead Field).\nscreenshot: /fieldtypes/screenshots/taxonomies.png\nrelated_entries:\n  - ba832b71-a567-491c-b1a3-3b3fae214703\n  - 6a18eac8-6139-419c-9d64-a2c960ccc3cd\n  - 42d2d87c-5af6-4856-9ee0-9548439df772\n---\n## Usage\n\nThis fieldtype is used to view and select from a list of Taxonomies.\n\n```yaml\nfields:\n  my_taxonomies_field:\n    type: taxonomies\n```\n\n## Data Structure\n\nThe Taxonomies fieldtype is a [Relationship fieldtype](/relationships#fieldtypes), and will save the taxonomies as their handles.\n\n```yaml\ntaxonomies:\n  - genre\n  - cool_factor\n```\n\n## Templating\n\nYou're more than likely using this field as a way to dynamically display Terms from one or more Taxonomies.\n\nThe following example assumes `max_items` has been set to `1`:\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n  {{ taxonomy :from=\"my_taxonomy_field\" }}\n    <li>{{ title }}</li>\n  {{ /taxonomy }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n\t<statamic:taxonomy\n\t\t:from=\"$my_taxonomy_field\"\n\t>\n\t\t<li>{{ $title }}</li>\n\t</statamic:taxonomy>\n</ul>\n```\n::\n\n```html\n<ul>\n  <li>Comedy</li>\n  <li>Drama</li>\n  <li>Dramedy</li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/template.md",
    "content": "---\ntitle: Template\ndescription: A template picker with autosuggest.\nintro: >\n  Used for choosing an entry’s template. Be sure to name the field `template` if you want it to be able to change the template (it's a special variable name).\nid: 76e0ee52-a3c4-4904-8b5c-f722bbb20482\nscreenshot: fieldtypes/screenshots/v6/template.webp\nscreenshot_dark: fieldtypes/screenshots/v6/template-dark.webp\noptions:\n  -\n    name: hide_partials\n    type: boolean\n    description: >\n      Since partials are rarely intended to be used as templates, they are hidden by default.\n---\n## Overview\n\nThis is generally used as a \"system\" field to control an entry's template. It points to the `resources/views` directory and will list all available templates therein.\n\n\n"
  },
  {
    "path": "content/collections/fieldtypes/terms.md",
    "content": "---\ntitle: Terms\nextends: 9dd58c40-6e33-49c8-83fa-61a69f6371be\ndescription: Attach Taxonomy Terms to your content.\nintro: >\n  Allows you attach Taxonomy Terms to your content. They could be Tags, Categories, Colors, Flavors, you name it. We highly recommend [learning more about Taxonomies](/taxonomies) before going any further.\nscreenshot: fieldtypes/screenshots/v6/terms.webp\nscreenshot_dark: fieldtypes/screenshots/v6/terms-dark.webp\noptions:\n  -\n    name: max_items\n    type: integer\n    description: >\n      The maximum number of items that may be selected. Setting this to `1` will change the UI to a dropdown.\n  -\n    name: taxonomy\n    type: string\n    description: >\n      The handle of the Taxonomy from which to fetch Terms. Not needed when placed in the fieldset's `taxonomies` array.\n      In that case, it'll get the taxonomy from the field name.\n  -\n    name: create\n    type: boolean *true*\n    description: >\n      By default you may create new terms. Set to `false` to only allow selecting from existing terms.\n  -\n    name: query_scopes\n    type: string\n    description: >\n      Allows you to specify a [query scope](/extending/query-scopes-and-filters#scopes) which should be applied when retrieving selectable assets. You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\nid: 31adcc00-4fbb-4fe9-9b48-401061273096\n---\n\n## Overview\n\nTaxonomies are usually relationships established on the collection-configuration level. Make sure to read the [Taxonomies documentation](/taxonomies) to understand how everything works.\n\n## Data Structure\n\nIf the field is being used for taxonomizing your content (ie. the field name matches the taxonomy handle), the term's _slugs_ will be saved.\n\n``` yaml\nwildlife:\n  - kangaroo\n  - three-toed-sloth\n  - panda\n  - porg\n```\n\nHowever, if you just want to store references to taxonomy terms for other purposes, the term's IDs will be saved. See [below](#without-taxonomizing) for more detail.\n\nA term ID is the taxonomy handle combined with the slug.\nThis way, you may reference terms from multiple taxonomies.\n\n``` yaml\nthings_you_may_find_adorable:\n  - wildlife/panda\n  - people/the-elderly\n```\n\n## Templating\n\nAs outlined in the [Taxonomies Guide](/taxonomies#templating), term slugs will automatically be converted to Term objects which means\nyou will have all of the term's data available as variables.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n  {{ wildlife }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n  {{ /wildlife }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n\t@foreach ($wildlife as $term)\n\t\t<li><a href=\"{{ $term->url }}\">{{ $term->title }}</a></li>\n\t@endforeach\n</ul>\n```\n::\n\n```html\n<ul>\n  <li><a href=\"/wildlife/kangaroo\">Kangaroo</a></li>\n  <li><a href=\"/wildlife/three-toed-sloth\">Three Toed Sloth</a></li>\n</ul>\n```\n\n## Using terms without taxonomizing {#without-taxonomizing}\n\nThe most common use for this fieldtype is to taxonomize, or \"tag\", your entry.\n\nHowever, sometimes you have other ideas in mind for using taxonomy terms. For instance, you might have a \"similar tags\" field, or want to create an index of many different, unrelated things. In this case, you aren't tagging the entry itself at all.\n\nWhen using the taxonomy field in this way, terms will get saved using _IDs_ instead of slugs.\n\n``` yaml\nsimilar_things:\n  - categories::hats\n  - tags::delightful\n```\n\nYou can still loop through them like your used to:\n\n``` antlers\n<ul>\n  {{ similar_things }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n  {{ /similar_things }}\n</ul>\n```\n\n```html\n<ul>\n  <li><a href=\"/categories/hats\">Hats</a></li>\n  <li><a href=\"/tags/delightful\">Delightful</a></li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/text.md",
    "content": "---\ntitle: Text\ndescription: A simple text input field for managing short, unformatted text.\noverview: >\n  A text field that has the ability to morph into an intergalactic dragon and devour entire planets! Just kidding — you just type stuff into the box. Pretty basic.\noptions:\n  -\n    name: append\n    type: string\n    description: >\n      Add text after (to the right of) the text input.\n  -\n    name: character_limit\n    type: integer\n    description: 'Set the maximum number of enterable characters. This is only a recommendation, not a hard limit. To enforce a hard limit, use the [`max`](https://laravel.com/docs/master/validation#rule-max) validation rule.'\n  -\n    name: input_type\n    type: string\n    description: >\n      Control the HTML5 input type. Options: `color`, `date`, `email`, `hidden`, `month`, `number`, `password`, `tel`, `text`, `time`, `url`, and `week`. **Default: `text`.**\n  -\n    name: prepend\n    type: string\n    description: >\n      Add text to the beginning (to the left of) the text input.\n  -\n    name: placeholder\n    type: string\n    description: >\n      Set some default placeholder text.\nscreenshot: fieldtypes/screenshots/v6/text.webp\nscreenshot_dark: fieldtypes/screenshots/v6/text-dark.webp\nid: 306b112b-b0cc-4359-b681-da353eeb50ac\n---\n\n"
  },
  {
    "path": "content/collections/fieldtypes/textarea.md",
    "content": "---\ntitle: Textarea\ndescription: |\n  A simple textarea field for managing longer, unformatted text.\noverview: |\n  A long textarea field that functions like a swimming pool for letters and numbers on a hot day. Everyone is welcome and they can stay as long as they want.\noptions:\n  -\n    name: character_limit\n    type: integer\n    description: 'Set the maximum number of enterable characters. This is only a recommendation, not a hard limit. To enforce a hard limit, use the [`max`](https://laravel.com/docs/master/validation#rule-max) validation rule.'\n  -\n    name: placeholder\n    type: string\n    description: >\n      Set some default placeholder text.\nscreenshot: fieldtypes/screenshots/v6/textarea.webp\nscreenshot_dark: fieldtypes/screenshots/v6/textarea-dark.webp\nid: 7c54484a-7ba5-4314-b9af-9d9a462090fc\n---\n\n"
  },
  {
    "path": "content/collections/fieldtypes/time.md",
    "content": "---\ntitle: Time\ndescription: A timepicker. It lets you pick a time.\nintro: The original time field from the set of Kiefer Sutherland's hit drama \"24\". It's a simple timepicker that operates in 24-hour mode and supports keyboard `up` and `down` controls.\nscreenshot: fieldtypes/screenshots/v6/time.webp\nscreenshot_dark: fieldtypes/screenshots/v6/time-dark.webp\nid: ccfbaf71-7823-4f71-a375-e874035f80ca\n---\n\n\nNone. It just works.\n"
  },
  {
    "path": "content/collections/fieldtypes/toggle.md",
    "content": "---\ntitle: Toggle\ndescription: A toggle switch for booleans (`true` and `false`).\nintro: A nice little toggle switch generally used to manage settings-type variables. It stores `true` or `false` and is delightfully uncomplicated, just like our relationship with yogurt.\nscreenshot: fieldtypes/screenshots/v6/toggle.webp\nscreenshot_dark: fieldtypes/screenshots/v6/toggle-dark.webp\nid: ac5f8f98-616f-4621-a7ee-dbc8bbc15525\n---\n\n## Can I haz green?\n\nSome people like their toggles green. It's a personal preference, just like white or milk chocolate. Since the control panel theme is customizable, you can make your toggles green! Or whatever other color for that matter… but maybe not red? Look for the Theme section in `config/statamic/cp.php` and set the `switch-bg` your preferred color. Here's green:\n\n```php\n'theme' => [\n    'switch-bg' => Color::Green[500],\n    'dark-switch-bg' => Color::Green[600],\n],\n```\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/toggle-green.webp\" alt=\"Statamic Toggle Green\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/fieldtypes/screenshots/v6/toggle-green-dark.webp\" alt=\"Statamic Toggle Green\" class=\"u-hide-in-light-mode\">\n    <figcaption>Who dares to dream? A nugget of <a target=\"_blank\" href=\"https://www.youtube.com/watch?v=TkZFuKHXa7w\">purest green!</a></figcaption>\n</figure>\n\n## Data Structure\n\nFlicking the toggle to the right sets to the value to `true`, left to `false`.\n\n``` yaml\ndo_the_thing: true\n```\n\n## Templating\n\nToggles are usually used to control logic, so you can combine them with `{{ if }}` statements in your templates to handle all manner of show/hide wizardry.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if do_the_thing }} It does it {{ /if }}\n```\n\n::tab blade\n\nThe following example uses the `fetch` helper function, which resolves `Value` instances for you and returns the underlying value. This way you always get the real \"truthy\" value, regardless of how you retrieved `$do_the_thing`.\n\n```blade\n<?php\n\tuse function Statamic\\View\\Blade\\{fetch};\n?>\n\n@if (fetch($do_the_thing))\n  It does it\n@endif\n```\n::\n"
  },
  {
    "path": "content/collections/fieldtypes/user-groups.md",
    "content": "---\nid: 006ee3c1-607e-4d65-94ae-6862c18ac516\ntitle: User Groups\nscreenshot: fieldtypes/screenshots/v6/user-groups.webp\nscreenshot_dark: fieldtypes/screenshots/v6/user-groups-dark.webp\ndescription: Create a relationship with a User Group\noverview: >\n  Use this fieldtype to create a relationship with [User Groups](/users#user-groups).\npro: true\noptions:\n  -\n    name: max_items\n    type: integer\n    required: false\n    description: 'The maximum number of user groups that may be selected.'\n  -\n    name: mode\n    type: string\n    description: |\n        Set the UI style for this field. Can be one of `default` (Stack Selector), `select` (Select Dropdown) or `typeahead` (Typeahead Field).\nrelated_entries:\n  - 57184c18-28d3-433f-b6ee-0e4539f6b504\n  - 6b691e04-8f28-4eb2-8288-b61433883fe4\n---\n## Overview\n\nThe User Group fieldtype gives your users a way to pick one or more User Groups to attach to the current entry. What you do with that relationship is up to you, but most likely you'll be either listing users or combining it with the [User:In](/tags/user-in) tag to protect content or areas of the frontend.\n\n## Data Storage\n\nThe User Group fieldtype stores the `handle` of a single group as a string, or an array of handles if `max_items` is greater than 1.\n\n## Templating\n\nThe User Group fieldtype uses [augmentation](/augmentation) to return the `title` and `handle` of each Group. You can use pass these values into the `{{ user:in }}` tag to protect content.\n\nThe following example assumes `max_items` has been set to `1`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:in :group=\"group_field:handle\" }}\n  You are in the {{ group_field:title }} group. Nice!\n{{ /user:in }}\n```\n::tab blade\n```blade\n{{-- Using Statamic Tags --}}\n<statamic:user:in\n  :group=\"$group_field->handle\"\n>\n  You are in the {{ $group_field->title }} group. Nice!\n</statamic:user:in>\n\n{{-- Using Fluent Tags --}}\n@if(Statamic::tag('user:in')->group($group_field->handle)->fetch())\n  You are in the {{ $group_field->title }} group. Nice!\n@endif\n```\n::"
  },
  {
    "path": "content/collections/fieldtypes/user-roles.md",
    "content": "---\nid: 42baf054-f3d5-4317-b7dd-466882c47c06\nblueprint: fieldtype\ntitle: 'User Roles'\nscreenshot: fieldtypes/screenshots/v6/user-roles.webp\nscreenshot_dark: fieldtypes/screenshots/v6/user-roles-dark.webp\ndescription: 'Create a relationship with a User Role'\noverview: |\n  Use this fieldtype to create a relationship with [User Roles](/users#user-roles).\npro: true\noptions:\n  -\n    name: max_items\n    type: integer\n    required: false\n    description: 'The maximum number of user roles that may be selected.'\n  -\n    name: mode\n    type: string\n    description: |\n      Set the UI style for this field. Can be one of `default` (Stack Selector), `select` (Select Dropdown) or `typeahead` (Typeahead Field).\nrelated_entries:\n  - 6b691e04-8f28-4eb2-8288-b61433883fe4\n  - 8c7f38bb-ee6f-43ee-b775-4eeae0a87bf3\n---\n## Overview\n\nThe User Role fieldtype gives your users a way to pick one or more User Roles to attach to the current entry. What you do with that relationship is up to you, but most likely you'll be either listing users or combining it with the [User:Is](/tags/user-is) tag to protect content or areas of the frontend.\n\n## Data Storage\n\nThe User Role fieldtype stores the `handle` of a single group as a string, or an array of handles if `max_items` is greater than 1.\n\n## Templating\n\nThe User Role fieldtype uses [augmentation](/augmentation) to return the `title` and `handle` of each Role. You can use pass these values into the `{{ user:is }}` tag to protect content.\n\nThe following example assumes `max_items` has been set to `1`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:is :role=\"role_field:handle\" }}\n  You are a {{ role_field:title }}. Nice!\n{{ /user:is }}\n```\n\n::tab blade\n```blade\n{{-- Using Statamic Tags --}}\n<statamic:user:is\n\t:role=\"$role_field->handle\"\n>\n\tYou are a {{ $role_field->title }}. Nice!\n</statamic:user:is>\n\n{{-- Using Fluent Tags --}}\n@if(Statamic::tag('user:is')->role($role_field->handle)->fetch())\n\tYou are a {{ $role_field->title }}. Nice!\n@endif\n```\n::\n"
  },
  {
    "path": "content/collections/fieldtypes/users.md",
    "content": "---\ntitle: Users\ndescription: Relate users with your content.\nintro: >\n  Attach users to your content to show authorship, list team members, assign the winners of a foot race, or even winners of an elbow race.\nscreenshot: fieldtypes/screenshots/v6/users.webp\nscreenshot_dark: fieldtypes/screenshots/v6/users-dark.webp\noptions:\n  -\n    name: default\n    type: string\n    description: >\n      Setting to `current` will default the field to the currently logged in user.\n  -\n    name: max_items\n    type: integer\n    description: >\n      The maximum number of users than can be selected. Leave it empty for no limit (default). Setting to `1` will save the value as a `string` instead of an `array` and will switch to a select dropdown UI.\n  -\n    name: mode\n    type: string\n    description: >\n      Choose between `select`, `typeahead`, and the `default` stack selector UI modes.\n  -\n    name: query_scopes\n    type: string\n    description: >\n      Allows you to specify a [query scope](/extending/query-scopes-and-filters#scopes) which should be applied when retrieving selectable assets. You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\nid: 0f8102b9-c948-4264-8cb8-cbfbd0415a04\n---\n## Overview\n\nThe most common use for the Users fieldtype is to set the \"author\" for entries, but it's not the only use. You could...\n\n- List people who contributed to a project\n- Link to related authors\n- Manage an \"Employee of the Weekend\" section. Everyone wants to be the King or Queen of Inventory Saturday, right?\n- Display team bios\n- Pull in customer's testimonials through their user account.\n\n## Data Structure\n\nThe Users fieldtype is a [relationship fieldtype](/extending/relationship-fieldtypes) – which mean the data will store a reference to the users IDs to main a dynamic link.\n\n```yaml\nauthor: abc-123-cba-321\n```\n\n## Templating\n\nAll relationship fields use [augmentation](/augmentation) to fetch the actual data objects, allowing you to interact with the related data automatically and dynamically.\n\nThe following example assumes `max_items` has been set to `1`.\n\n:::hint\nWhen `max_items: 1`, the field augments directly to the related user so you can use dot/colon notation (`{{ author:name }}`). If you'd rather always get a query builder back — so you can chain scopes or filters — enable [`always_augment_to_query`](/augmentation#always-augment-relationships-to-a-query).\n:::\n\n::tabs\n\n::tab antlers\n```antlers\n<div class=\"bg-white p-4 shadow flex items-center\">\n{{ author }}\n  <img class=\"w-10 h-10 rounded-full\" src=\"{{ avatar }}\" alt=\"Avatar of {{ name }}\">\n    <div class=\"text-sm ml-4\">\n      <p class=\"text-gray-900 leading-none\">{{ name }}</p>\n      <p class=\"text-gray-600\">{{ email }}</p>\n    </div>\n{{ /author }}\n</div>\n```\n::tab blade\n```blade\n<div class=\"bg-white p-4 shadow flex items-center\">\n\t<img class=\"w-10 h-10 rounded-full\" src=\"{{ $author->avatar }}\" alt=\"Avatar of {{ $author->name }}\">\n\t<div class=\"text-sm ml-4\">\n\t\t<p class=\"text-gray-900 leading-none\">{{ $author->name }}</p>\n\t\t<p class=\"text-gray-600\">{{ $author->email }}</p>\n\t</div>\n</div>\n```\n\n::\n\n```html\n<div class=\"bg-white p-4 shadow flex items-center\">\n  <img class=\"w-10 h-10 rounded-full\" src=\"/img/avatars/david.jpg\" alt=\"Avatar of David Hasselhoff\">\n    <div class=\"text-sm ml-4\">\n      <p class=\"text-gray-900 leading-none\">David Hasselhoff</p>\n      <p class=\"text-gray-600\">thehoff@statamic.com</p>\n    </div>\n</div>\n```\n"
  },
  {
    "path": "content/collections/fieldtypes/video.md",
    "content": "---\ntitle: Video\ndescription: Extract embed URLs from Youtube, Vimeo, and HTML5 compatible video links and preview them right inline.\nintro: |\n  Extract embed URLs from Youtube, Vimeo, and HTML5 compatible video links and preview them right inline. Feel free watch the whole thing instead of working – we won't tell.\nscreenshot: fieldtypes/screenshots/v6/video.webp\nscreenshot_dark: fieldtypes/screenshots/v6/video-dark.webp\nid: ced8b901-95bd-4006-b70e-4ea04d72fcb7\n---\n## Usage\n\nEnter a video URL and it will be loaded in an embedded player directly beneath the field so you can preview it.\n\nYou may enter:\n\n- YouTube URLs: `https://www.youtube.com/watch?v=s9F5fhJQo34`\n- Vimeo URLs: `https://vimeo.com/22439234`\n- mp4, ogv, mov, or webm URLs: `http://example.com/video.mp4`\n\n## Data Structure\n\nThe Video field will save the URL of the video you've entered. If you paste embed code into the field, it will extract the proper URL for you.\n\n``` yaml\nvideo: https://www.youtube.com/watch?v=s9F5fhJQo34\n```\n\n## Templating\n\nYou can use the [is_embeddable](/modifiers/is_embeddable) and\n[embed_url](/modifiers/embed_url) modifiers to display your video player.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if video | is_embeddable }}\n    <!-- Youtube and Vimeo -->\n    <iframe src=\"{{ video | embed_url }}\" ...></iframe>\n{{ else }}\n    <!-- Other HTML5 video types -->\n    <video src=\"{{ video | embed_url }}\" ...></video>\n{{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($video)->isEmbeddable()->fetch())\n\t<!-- Youtube and Vimeo -->\n\t<iframe src=\"{{ Statamic::modify($video)->embedUrl() }}\" ...></iframe>\n@else\n\t<!-- Other HTML5 video types -->\n\t<video src=\"{{ Statamic::modify($video)->embedUrl() }}\" ...></video>\n@endif\n```\n::"
  },
  {
    "path": "content/collections/fieldtypes/width.md",
    "content": "---\nid: b8b51bb8-a4bd-4aec-90bd-4f150a29c8a0\nblueprint: fieldtype\ntitle: Width\nscreenshot: fieldtypes/screenshots/v6/width.webp\nscreenshot_dark: fieldtypes/screenshots/v6/width-dark.webp\nintro: 'A slick way to select a width in your blueprints. Although you could use it for anything you want as it stores the value in your markdown as an integer. Neat!'\nwidth_field: 50\noptions:\n  -\n    name: options\n    type: array\n    required: false\n    description: 'The array of integers presented in the blueprint. Default: 25 / 33 / 50 / 66 / 75 / 100'\n  -\n    name: default\n    type: integer\n    required: false\n    description: \"The default selected width. Can be set to a value that doesn't exist in the options array if desired.\"\n---\n## Overview\n\nAn alternative to the [Button Group](/fieldtypes/button_group) or [Select](/fieldtypes/select) field types that is a bit more compact and visually appealing.\n\n## Data structure\n\nThe selected value is stored as an integer.\n\n``` yaml\nimage_width: 50\n```\n\n## Templating\n\nUse in your front end, most likely with a CSS framework like Tailwind or Bootstrap to set custom widths.\n\n::tabs\n\n::tab antlers\n\n```antlers\n<div class=\"col-12\"\">\n    <div class=\"w-25\">\n        <h1>Compact headings</h1>\n    </div>\n    <div class=\"w-{{ image_width }}\">\n        <h2>With wider sub-headings</h2>\n    </div>\n</div>\n```\n\n::tab blade\n\n```blade\n<div class=\"header-block\">\n\t<img class=\"w-[{{ $image_width }}px] src=\"/assets/mycoolicon.png>\n    <h1>Icon overview</h1>\n</div>\n```\n::"
  },
  {
    "path": "content/collections/fieldtypes/yaml.md",
    "content": "---\ntitle: YAML\ndescription: A YAML editor that _directly_ manages YAML.\noverview: >\n  A [code fieldtype](/fieldtypes/yaml) in YAML mode that _directly_ edits and stores YAML instead of an escaped string representation of said YAML.\nscreenshot: fieldtypes/screenshots/v6/yaml.webp\nscreenshot_dark: fieldtypes/screenshots/v6/yaml-dark.webp\nid: 25155800-8fd7-46c7-aad0-5daaf07543da\n---\n## Overview\n\nThis field is a [code fieldtype](/fieldtypes/code) that gets saved as YAML instead of a string. Your input is validated on save to make sure you don't write _invalid_ YAML.\n\n:::tip\nThe YAML field is one of the \"catch-all\" solutions for when there's no better way to work with an odd data structure. **Recommended for developers only.**\n:::\n\n## Data Storage\n\nYou really should [know YAML](/yaml) if you're using this field, in which case you'll understand how the data is stored – exactly as written.\n\n## Templating\n\nRefer to the [YAML guide](/yaml) on how to work with data in general. We really can't be any more specific here. You understand, right?\n"
  },
  {
    "path": "content/collections/fieldtypes.yaml",
    "content": "title: Fieldtypes\nicon: fieldsets\ntemplate: page\nlayout: layout\nmount: 25f01cb5-1ca9-44a1-af1b-885b32b05cc8\nrevisions: false\nroute: '/fieldtypes/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\ninject:\n  view_model: App\\ViewModels\\Fieldtypes\n"
  },
  {
    "path": "content/collections/modifiers/add.md",
    "content": "---\nid: 53debd55-5d53-4254-ad86-49a26cb09594\nblueprint: modifiers\nmodifier_types:\n  - math\ntitle: Add\n---\nAdd a value or another variable to your variable. Pass an integer or the name of a second variable as the parameter. Also supports `+` as shorthand.\n\n``` yaml\nbooks: 5\nmagazines: 10\n```\n\n::tabs\n\n::tab antlers\n\n```antlers\n{{ books | add:5 }}\n{{ books | add:magazines }}\n{{ books | +:magazines }}\n```\n::tab blade\n```blade\n{{-- Using Modifiers --}}\n{{ Statamic::modify($books)->add(5) }}\n{{ Statamic::modify($books)->add($magazines) }}\n{{ Statamic::modify($books)->add($magazines) }}\n\n{{-- Using PHP --}}\n{{ $books + 5 }}\n{{ $books + $magazines }}\n{{ $books + $magazines }}\n```\n::\n\n```text\n10\n15\n15\n```\n"
  },
  {
    "path": "content/collections/modifiers/add_slashes.md",
    "content": "---\nid: c5832187-290e-4701-aa74-316d8130e7bb\nmodifier_types:\n  - string\ntitle: 'Add Slashes'\n---\nModifies a string by adding backslashes before characters that need to be escaped. These characters are:\n\n- single quote `'`\n- double quote `\"`\n- backslash `\\`\n\nThis is most often used when passing string data into JavaScript.\n\n``` yaml\nsummary: >\n  \"I'm not listening!\" said the small, strange creature.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ summary | add_slashes }}\n```\n\n::tab blade\n```blade\n{{ Statamic::modify($summary)->addSlashes() }}\n```\n::\n\n``` output\n\\\"I\\'m not listening!\\\" said the small, strange creature.\n```\n"
  },
  {
    "path": "content/collections/modifiers/ampersand_list.md",
    "content": "---\nid: cbab1bb5-302e-499d-badb-f154dbae751d\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\ntitle: 'Ampersand List'\nrelated_entries:\n  - d8a8568c-bb93-4e84-8d30-e527b3b02876\n  - 6866c25b-1266-4908-8325-dce4e5146f5b\n  - eed4c5bc-0923-4f54-ad37-ca9a3384e1e0\n  - 9dfc5020-3d14-4774-a1f6-d82d051cb964\n---\nTurn a simple array into a comma delimited string with a friendly little ampersand between the last two items.\n\n```yaml\nfruits:\n  - apples\n  - bananas\n  - jerky\n```\n\n::tabs\n::tab antlers\n```antlers\n{{ fruits | ampersand_list }}\n```\n\n::tab blade\n```blade\n{{ Statamic::modify($fruits)->ampersandList() }}\n```\n::\n\n```html\napples, bananas & jerky\n```"
  },
  {
    "path": "content/collections/modifiers/antlers.md",
    "content": "---\nid: e3b58c77-0bfc-40da-918f-51a7f65950b8\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Antlers\n---\nParses the given value as an Antlers template.\n\n```yaml\ntitle: 'Hello {{ audience }}!'\naudience: world\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | antlers }}\n```\n\n::tab blade\n```blade\n{{ Statamic::modify($title)->antlers() }}\n```\n::\n\n```\nHello world!\n```\n"
  },
  {
    "path": "content/collections/modifiers/as.md",
    "content": "---\nid: ada24ec2-1b6e-4759-b2c0-06d9d464f3f9\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: As\n---\nAlias an array variable as another name, allowing you to massage your data to reused shared components and templates.\n\n```yaml\nblocks:\n  -\n    type: text\n    content: I love to eat tacos in the bathroom.\n  -\n    type: photo\n    photo: /assets/img/baño-tacos.jpg\n```\n\n```antlers\n{{ blocks as=\"sets\" }}\n    {{ sets }}\n        {{ partial:type }}\n    {{ /sets }}\n{{ /blocks }}\n```\n\n```html\n<!-- Each block would be rendered with its own partial matching the {{ type }} var -->\n\n<div class=\"text\">\n  <p>I like to eat tacos in the bathroom.</p>\n</div>\n<div class=\"photo\">\n  <img src=\"/assets/img/baño-tacos.jpg\">\n</div>\n```\n"
  },
  {
    "path": "content/collections/modifiers/ascii.md",
    "content": "---\nid: 807d2a16-dbaa-4aaf-887c-9682b63a6af8\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Ascii\n---\nReplaces all non-ASCII characters with their closest ASCII counterparts and removes any unsupported characters completely. This is very useful for converting foreign language strings into something more code-friendly.\n\n```yaml\ntitle: lemoñade\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | ascii }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->ascii() }}\n```\n::\n\n```html\nlemonade\n```\n"
  },
  {
    "path": "content/collections/modifiers/at.md",
    "content": "---\nid: 40fb38b6-a2b0-411d-b90b-543b38ac8aa3\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: At\n---\nReturns the single character at a given position in a string. It starts at zero with the first character.\n\n```yaml\ntitle: supercalifragilisticexpialidocious\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | at:21 }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->at(21) }}\n```\n::\n\n```html\nx\n```\n"
  },
  {
    "path": "content/collections/modifiers/attribute.md",
    "content": "---\nid: 9840b1ab-4576-4cc9-9b83-9226d069807d\nblueprint: modifiers\nmodifier_types:\n  - string\n  - array\ntitle: Attribute\n---\n\nWhen you're writing partials, you might find yourself passing variables that will ultimately just be used in HTML attributes, like this:\n\n```antlers\n<input\n    type=\"text\"\n    {{ if name }}name=\"{{ name }}\"{{ /if }}\n    {{ if class }}class=\"{{ class }}\"{{ /if }}\n    {{ if mandatory }}required{{ /if }}\n/>\n```\n\nYou can simplify this using the `attribute` modifier:\n\n```yaml\nname: first_name\nclass: text-sm font-mono text-gray-900\nmandatory: true\n```\n\n```antlers\n<input\n    type=\"text\"\n    {{ name | attribute:name }}\n    {{ class | attribute:class }}\n    {{ mandatory | attribute:required }}\n/>\n```\n\n```html\n<input\n    type=\"text\"\n    name=\"first_name\"\n    class=\"text-sm font-mono text-gray-900\"\n    required\n/>\n```\n\nThe `attribute` modifier supports passing booleans, *some* objects, arrays, integers, floats and strings.\n"
  },
  {
    "path": "content/collections/modifiers/background_position.md",
    "content": "---\nid: 0904f610-eee8-4b86-827b-0dc281d553ca\nblueprint: modifiers\nmodifier_types:\n  - asset\n  - string\ntitle: 'Background Position'\n---\nConverts an asset focal point value (eg. `50-30`) into a value suitable for the background-position css property.\n\n```yaml\nfocus: 50-30\n```\n\n::tabs\n\n::tab antlers\n```antlers\nbackground-position: {{ focus | background_position }};\n```\n::tab blade\n```blade\nbackground-position: {{ Statamic::modify($focus)->backgroundPosition() }};\n```\n::\n\n```html\nbackground-position: 50% 30%;\n```\n"
  },
  {
    "path": "content/collections/modifiers/backspace.md",
    "content": "---\nid: 9526d59a-3abe-444f-b9a4-b0a8ed2fa880\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Backspace\n---\nRemoves a specified number of characters from the end of a string.\n\n```yaml\ntitle: supercalifragilisticexpialidocious\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | backspace:29 }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->backspace(29) }}\n```\n::\n\n```html\nsuper\n```\n"
  },
  {
    "path": "content/collections/modifiers/bard_html.md",
    "content": "---\nid: e2b731c3-13aa-42c8-96f1-9b999a0121e0\nblueprint: modifiers\ntitle: 'Bard HTML'\nmodifier_types:\n  - array\n  - string\n  - utility\n---\nConverts any Bard data to an HTML string (excluding sets). Bard data can be either:\n\n* The raw value from a Bard field (a ProseMirror document), with or without sets\n* One or more ProseMirror nodes (from the [bard_items](bard_items) modifier)\n\n```yaml\nmain_content:\n  -\n    type: paragraph\n    content:\n      -\n        type: text\n        text: \"We're going to build a simple personal website for a fictitious young aspiring programmer named Kurt Logan.\"\n  -\n    type: set\n    attrs:\n      values:\n        type: code_block\n        code: '<?php Statamic::rocks() ?>'\n  -\n    type: paragraph\n    content:\n      -\n        type: text\n        text: \"Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in\\_CYBERSPACE.\"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ main_content | raw | bard_html }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($main_content)->bardHtml() }}\n```\n::\n\n```html\n<p>We&#039;re going to build a simple personal website for a fictitious young aspiring programmer named Kurt Logan.</p><p>Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in CYBERSPACE.</p>\n```\n"
  },
  {
    "path": "content/collections/modifiers/bard_items.md",
    "content": "---\nid: 3c1a5985-1157-4287-801f-95a44b158c82\nblueprint: modifiers\ntitle: 'Bard Items'\nmodifier_types:\n  - array\n  - utility\n---\nConverts any Bard data to a flat array of ProseMirror nodes and marks. Bard data can be either:\n\n* The raw value from a Bard field (a ProseMirror document), with or without sets\n* One or more of the ProseMirror nodes returned from this modifier\n\n```yaml\nmain_content:\n  -\n    type: paragraph\n    content:\n      -\n        type: text\n        text: \"We're going to build a \"\n      -\n        type: text\n        marks:\n          -\n            type: link\n            attrs:\n              href: 'http://localhost/'\n        text: 'simple personal'\n      -\n        type: text\n        text: ' website for a fictitious young aspiring programmer named Kurt Logan'\n  -\n    type: paragraph\n    content:\n      -\n        type: image\n        attrs:\n          src: 'asset::assets::donut.jpg'\n      -\n        type: text\n        text: \"Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in\\_CYBERSPACE.\"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ main_content | raw | bard_items }}\n{{ main_content | raw | bard_items | where:type:image | first | bard_html }}\n{{ links = main_content | raw | bard_items | where:type:link }}\n{{ links }}\n    {{ node | bard_text }} - {{ attrs:href }}\n{{ /links }}\n```\n::tab blade\n```blade\n<?php\n    Statamic::modify($bard_field_with_sets)->bardItems();\n    Statamic::modify($bard_field_with_sets)->bardItems()->where('type:text')->first()->bardHtml();\n    $links = Statamic::modify($bard_field_with_sets)->bardItems()->where('type:link')->fetch();\n?>\n\n@foreach ($links as $link)\n\t{{ Statamic::modify($link['node'])->bardText() }} - {{ $link['attrs']['href'] }}\n@endforeach\n```\n::\n\n```yaml\nvalue:\n  -\n    type: paragraph\n    content:\n      -\n        type: text\n        text: \"We're going to build a \"\n      -\n        type: text\n        marks:\n          -\n            type: link\n            attrs:\n              href: 'http://localhost/'\n        text: simple personal\n      -\n        type: text\n        text: ' website for a fictitious young aspiring programmer named Kurt Logan'\n  -\n    type: text\n    text: \"We're going to build a \"\n  -\n    type: text\n    marks:\n      -\n        type: link\n        attrs:\n          href: 'http://localhost/'\n    text: simple personal\n  -\n    type: link\n    attrs:\n      href: 'http://localhost/'\n    node:\n      type: text\n      marks:\n        -\n          type: link\n          attrs:\n            href: 'http://localhost/'\n      text: simple personal\n  -\n    type: text\n    text: ' website for a fictitious young aspiring programmer named Kurt Logan'\n  -\n    type: paragraph\n    content:\n      -\n        type: image\n        attrs:\n          src: 'asset::assets::donut.jpg'\n      -\n        type: text\n        text: \"Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in\\_CYBERSPACE.\"\n  -\n    type: image\n    attrs:\n      src: 'asset::assets::donut.jpg'\n  -\n    type: text\n    text: \"Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in\\_CYBERSPACE.\"\n```\n\n```html\n<img src=\"/assets/donut.jpg\" alt=\"\">\n```\n\n```\nsimple personal - http://localhost/\n```"
  },
  {
    "path": "content/collections/modifiers/bard_text.md",
    "content": "---\nid: 5a617e74-0878-4b29-bd39-f1e2496d01cd\nblueprint: modifiers\ntitle: 'Bard Text'\nmodifier_types:\n  - array\n  - string\n  - utility\n---\nConverts any Bard data to a plain text string (excluding sets). Bard data can be either:\n\n* The raw value from a Bard field (a ProseMirror document), with or without sets\n* One or more ProseMirror nodes (from the [bard_items](bard_items) modifier)\n\n```yaml\nmain_content:\n  -\n    type: paragraph\n    content:\n      -\n        type: text\n        text: \"We're going to build a simple personal website for a fictitious young aspiring programmer named Kurt Logan.\"\n  -\n    type: set\n    attrs:\n      values:\n        type: code_block\n        code: '<?php Statamic::rocks() ?>'\n  -\n    type: paragraph\n    content:\n      -\n        type: text\n        text: \"Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in\\_CYBERSPACE.\"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ main_content | raw | bard_text }}\n{{ main_content | raw | bard_text | read_time }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($main_content)->bardText() }}\n{{ Statamic::modify($main_content)->bardText()->readTime() }}\n```\n::\n\n```\nWe're going to build a simple personal website for a fictitious young aspiring programmer named Kurt Logan. Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in CYBERSPACE.\n```\n\n```\n1\n```\n"
  },
  {
    "path": "content/collections/modifiers/bool_string.md",
    "content": "---\nid: c3214196-3d0d-4a3d-b6c3-1ee4960cfedd\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: 'Bool String'\n---\nConverts a truthy value to the string `true` and a falsy to the string `false`. Check out [https://www.php.net/manual/en/language.types.boolean.php](https://www.php.net/manual/en/language.types.boolean.php) to see what PHP considers truthy and falsy.\n\n```yaml\nno: 0\nyes: \"hell, yea\"\nsure: -1\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ no | bool_string }}\n{{ yes | bool_string }}\n{{ sure | bool_string }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($no)->boolString() }}\n{{ Statamic::modify($yes)->boolString() }}\n{{ Statamic::modify($sure)->boolString() }}\n```\n::\n\n```html\nfalse\ntrue\ntrue\n```\n"
  },
  {
    "path": "content/collections/modifiers/camelize.md",
    "content": "---\nid: fe6dbf39-7870-4aa4-9acb-23b4cbf4bf87\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Camelize\n---\nReturns a camelCase version of a string. Trims surrounding spaces, capitalizes letters following digits, spaces, dashes and underscores, and removes spaces, dashes and underscores. It's a programmer-type thing, great for converting between code styles.\n\n```yaml\nmethod: make_everything_better\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ method | camelize }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($method)->camelize() }}\n```\n::\n\n```html\nmakeEverythingBetter\n```\n"
  },
  {
    "path": "content/collections/modifiers/cdata.md",
    "content": "---\nid: f1b59bce-43e7-41a4-b82f-e16016d90b18\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: CDATA\n---\nWraps a string in [CDATA][cdata] tags, useful for formatting characters properly in XML.\n\n```yaml\ntitle: My Very Own Podcast\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | cdata }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($title)->cdata() !!}\n```\n::\n\n```html\n<![CDATA[My Very Own Podcast]]>\n```\n\n[cdata]: https://en.wikipedia.org/wiki/CDATA\n"
  },
  {
    "path": "content/collections/modifiers/ceil.md",
    "content": "---\nid: 29863a25-6283-4338-baf5-82bd7c57541c\nblueprint: modifiers\nmodifier_types:\n  - math\n  - utility\ntitle: Ceil\n---\nRounds a number up to the next whole number.\n\n```yaml\nnumber: 25.98\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ number | ceil }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($number)->ceil() }}\n```\n::\n\n```html\n26\n```\n"
  },
  {
    "path": "content/collections/modifiers/chunk.md",
    "content": "---\nid: 10b38f50-e33c-47e0-8e94-bc4dc551600f\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\n  - utility\ntitle: Chunk\n---\nBreak arrays or collections into smaller (wait for it) chunks of any given size. This is useful for performing various gymnastics with your HTML markup.\n\n:::tip\nWant a set number of _groups_ regardless of item count? The [split](/modifiers/split) modifier does the opposite — give it a _count_ and it divides the collection into that many roughly equal pieces.\n:::\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:news as=\"posts\" limit=\"6\" }}\n  {{ posts chunk=\"3\" }}\n  <div class=\"flex space-x-4\">\n    {{ chunk }}\n      <a href=\"{{ url }}\" class=\"bg-purple-800 text-white p-4\">\n        {{ title }}\n      </a>\n    {{ /chunk }}\n  </div>\n  {{ /posts }}\n{{ /collection:news }}\n```\n::tab blade\n```blade\n<s:collection:news as=\"posts\" limit=\"6\">\n\n  @foreach (Statamic::modify($posts)->chunk(3) as $chunk)\n    <div class=\"flex space-x-4\">\n      @foreach ($chunk['chunk'] as $entry)\n        <a href=\"{{ $entry->url }}\" class=\"bg-purple-800 text-white p-4\">\n          {{ $entry->title }}\n        </a>\n      @endforeach\n    </div>\n  @endforeach\n\n</s:collection:news>\n```\n::\n\n```html\n<div class=\"flex space-x-4\">\n  <a href=\"/ideas/book\" class=\"bg-purple-800 text-white p-4\">\n    Book: Somehow I Manage\n  </a>\n  <a href=\"/ideas/party\" class=\"bg-purple-800 text-white p-4\">\n    Party: Goodbye Toby\n  </a>\n  <a href=\"/ideas/screenplay\" class=\"bg-purple-800 text-white p-4\">\n    Screenplay: Threat Level Midnight\n  </a>\n</div>\n<div class=\"flex space-x-4\">\n  <a href=\"/ideas/art\" class=\"bg-purple-800 text-white p-4\">\n    Art: A Stapler\n  </a>\n  <a href=\"/ideas/poster\" class=\"bg-purple-800 text-white p-4\">\n    Poster: Kids Playing Instruments\n  </a>\n  <a href=\"/ideas/mug\" class=\"bg-purple-800 text-white p-4\">\n    Mug: World's Best Boss\n  </a>\n</div>\n```\n"
  },
  {
    "path": "content/collections/modifiers/classes.md",
    "content": "---\nid: ef8d4f96-e811-4343-a2cf-81e455ee8227\nblueprint: modifiers\nmodifier_types:\n  - string\n  - array\ntitle: Classes\n---\nThis conditionally compiles a CSS class string using Laravel's `Arr::toCssClasses()` method.\n\nThe modifier expects an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. \n\n```yaml\nis_active: false\nhas_error: true\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<div class=\"text-sm {{ ['p-4' => true, 'font-bold' => is_active, 'bg-red' => has_error] | classes }}\">\n  //\n</div>\n```\n::tab blade\n```blade\n<?php\n  $classes = Statamic::modify([\n    'p-4' => true,\n    'font-bold' => $is_active,\n    'bg-red' => $has_error\n  ])->classes();\n?>\n\n<div class=\"text-sm {{ $classes }}\">\n  //\n</div>\n```\n\nYou can also use Blade's `@class` directive:\n\n```blade\n<div @class([\n  'text-sm',\n  'p-4',\n  'font-bold' => $is_active,\n  'bg-red' => $has_error\n])\n>\n  //\n</div>\n```\n\n::\n\n```html\n<div class=\"text-sm p-4 bg-red\">\n  //\n</div>\n```\n"
  },
  {
    "path": "content/collections/modifiers/collapse.md",
    "content": "---\nid: ea17da24-79b9-4ac7-84ba-660b29f95899\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Collapse\n---\nCollapses an array of arrays into a flat array. If duplicate keys exist they *will* get stomped over.\n\n```yaml\nnumbers:\n  - [one, two, three]\n  - [four, five, six]\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ numbers | collapse }}\n```\n::tab blade\n```blade\n<?php\n  $collapsed = Satamic::modify($numbers)->collapse()->fetch();\n?>\n```\n::\n\n```yaml\nnumbers:\n  - one\n  - two\n  - three\n  - four\n  - five\n  - six\n```\n"
  },
  {
    "path": "content/collections/modifiers/collapse_whitespace.md",
    "content": "---\nid: bfc66a6c-e4f5-462b-822e-04c3402b5b8f\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: 'Collapse Whitespace'\n---\nTrims a string and replaces consecutive whitespace characters with\na single space. This includes tabs and newline characters, as well as\nmultibyte whitespace such as the thin space and ideographic space.\n\n```yaml\ntitle: Bad   at           typing\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | collapse_whitespace }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->collapseWhitespace() }}\n```\n::\n\n```html\nBad at typing\n```\n"
  },
  {
    "path": "content/collections/modifiers/compact.md",
    "content": "---\nid: a29dcde5-5708-4ed3-8d00-76f818095477\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Compact\n---\nConverts a comma-delimited list of variable names into an array that can be used anywhere. Arrays are accepted.\n\nIt allows colon delimited syntax to target nested variables.\n\n```yaml\ntitle: 'The finest title there ever was'\nstuff:\n  one: 'Value One'\n  two: 'Value Two'\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ \"stuff:one, title, stuff:two\" | compact | ul }}\n```\n::tab blade\n```blade\n{!! Statamic::modify(\"stuff:one, title, stuff:two\")->compact()->ul() !!}\n```\n::\n\nWould produce the following output:\n\n::tabs\n\n```html\n<ul>\n    <li>Value One</li>\n    <li>The finest title there ever was</li>\n    <li>Value Two</li>\n</ul>\n```\n\n:::tip\nIt's similar to PHP's `compact()` function.\n\n```php\n$foo = 'bar';\n$baz = 'qux';\ncompact('foo', 'baz'); // ['bar', 'qux']\n```\n:::\n"
  },
  {
    "path": "content/collections/modifiers/console_log.md",
    "content": "---\nid: 985dc29c-fe71-464e-bb83-4f3f2aa455c0\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: 'Console Log'\n---\nDebug a variable by dumping its contents to your browser's JavaScript's console via `console.log`.\n\n```yaml\nfruit:\n  - apples\n  - bananas\n  - bacon\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ fruit | console_log }}\n```\n::tab blade\n```blade\n@php(Statamic::modify($fruit)->consoleLog())\n```\n::\n\n```js\n[\"apples\", \"banana\", \"jerky\"]\n```\n\n\n"
  },
  {
    "path": "content/collections/modifiers/contains.md",
    "content": "---\nid: 75145be0-966f-490e-af3d-ed122eb6445b\nblueprint: modifiers\nmodifier_types:\n  - conditions\n  - array\n  - string\ntitle: Contains\n---\nCheck if a value contains another value. Supports both strings and arrays.\n\nReturns `true` if a match is found, otherwise `false`.\n\nThe first parameter is the \"needle\" to find in the \"haystack\". It will read from the context if there is a matching variable, otherwise it will use the parameter as the value.\n\n## Strings\n\nCase-insensitive by default but can be made sensitive by setting the second parameter to `true`.\n\n```yaml\nsummary: \"It was the best of times, it was the worst of times.\"\nadjective: best\nnoun: carrot\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if summary | contains('BEST') }}\n{{ if summary | contains('BEST', true) }}\n{{ if summary | contains('adjective') }}\n{{ if summary | contains('noun') }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($summary)->contains('BEST')->fetch()) ... @endif\n@if (Statamic::modify($summary)->contains(['BEST', true])->fetch()) ... @endif\n@if (Statamic::modify($summary)->contains('adjective')->fetch()) ... @endif\n@if (Statamic::modify($summary)->contains('noun')->fetch()) ... @endif\n```\n::\n\n```html\ntrue   (the substring \"BEST\" was in the string, and it didn't care about the case.)\nfalse  (the substring \"BEST\" was in the string, however it didn't match the case.)\ntrue   (there's a field named \"adjective\", and it got the value which was \"best\")\nfalse  (there's a field named \"noun\", and it got the value which was \"carrot\")\n```\n\n## Arrays\nYou can set strict type checking by setting the second parameter to `true`.\n```yaml\nfoods:\n  - bacon\n  - bread\n  - tomato\ndelicious: bacon\ngross: broccoli\n\nnumbers: [1, 2]\nnumber: '1'\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if foods | contains('bacon') }}\n{{ if foods | contains('delicious') }}\n{{ if foods | contains('gross') }}\n{{ if (foods | contains('vegan bacon strips')) }}\n\n{{ if numbers | contains(number) }}\n{{ if numbers | contains(number, true) }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($foods)->contains('bacon')->fetch()) ... @endif\n@if (Statamic::modify($foods)->contains('delicious')->fetch()) ... @endif\n@if (Statamic::modify($foods)->contains('gross')->fetch()) ... @endif\n@if (Statamic::modify($foods)->contains('vegan bacon strips')->fetch()) ... @endif\n\n@if (Statamic::modify($foods)->contains($number)->fetch()) ... @endif\n@if (Statamic::modify($foods)->contains([$number, true])->fetch()) ... @endif\n```\n::\n\n```html\ntrue   (there's no field named \"bacon\", so it searched for literally \"bacon\")\ntrue   (there's a field named \"delicious\", and it got the value which was \"bacon\")\nfalse  (there's a field named \"gross\", and it got the value which was \"broccoli\")\ntrue   (there's no field named \"vegan bacon strips\", so it searched the expression for a literal string \"vegan bacon strips\")\n\ntrue   (the value of \"number\" is the string \"1\", which is fine in non-strict mode)\nfalse  (with strict mode enabled, the string \"1\" won't match the integer)\n```\n"
  },
  {
    "path": "content/collections/modifiers/contains_all.md",
    "content": "---\nid: e08b1034-5d58-4fae-8f6a-8efd9a65d6d9\nblueprint: modifiers\nmodifier_types:\n  - conditions\ntitle: 'Contains All'\n---\nSearch a string against multiple needles and return `true` if all are found, otherwise `false`. Case-insensitive.\n\n```yaml\nsummary: \"It was the best of times, it was the worst of times.\"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if summary | contains_all('best', 'worst') }}\n{{ if summary | contains_all('best', 'better') }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($summary)->containsAll(['best', 'worst'])->fetch()) ... @endif\n@if (Statamic::modify($summary)->containsAll(['best', 'better'])->fetch()) ... @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/contains_any.md",
    "content": "---\nid: 20ac3e9a-4a45-4c2f-9052-be222fc84016\nblueprint: modifiers\nmodifier_types:\n  - conditions\ntitle: 'Contains Any'\n---\nSearch a string against multiple needles and return `true` if any are found, otherwise `false`. Case-insensitive.\n\n```yaml\nsummary: \"It was the best of times, it was the worst of times.\"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if summary | contains_any('good', 'better', 'best') }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($summary)->containsAny(['good', 'better', 'best'])->fetch()) @endif\n```\n::\n\n```html\ntrue\n```\n"
  },
  {
    "path": "content/collections/modifiers/count.md",
    "content": "---\nid: a7b58312-3498-4807-b2bc-6fcb640fe231\nblueprint: modifiers\nmodifier_types:\n  - array\ntitle: Count\n---\nCount the number of items in an array.\n\n```yaml\nfruit:\n  - apples\n  - bananas\n  - bacon\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ fruit | count }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($fruit)->count() }}\n```\n::\n\n```html\n3\n```\n\n"
  },
  {
    "path": "content/collections/modifiers/count_substring.md",
    "content": "---\nid: 84c6f375-10ca-4296-89a8-a22b9652b5d5\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: 'Count Substring'\n---\nReturns the number of occurrences of search term in a given string. By default,\nthe comparison is case-insensitive, but can be made sensitive by setting the second parameter to `true`.\n\n```yaml\nquote: |\n  Dude! You got a tattoo!\n  So do you, dude! Dude, what does my tattoo say?\n  Sweet! What about mine?\n  Dude! What does mine say?\n  Sweet! What about mine?\n  Dude! What does mine say?\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ quote | count_substring('dude') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($quote)->countSubstring('dude') }}\n```\n::\n\n```html\n5\n```\n\n\n"
  },
  {
    "path": "content/collections/modifiers/dashify.md",
    "content": "---\nid: e50e2b3a-4377-4a74-b25a-d1ecf5d2d04a\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Dashify\n---\nReturns a lowercase and trimmed string separated by dashes. Dashes are inserted before uppercase characters (with the exception of the first character of the string), and in place of spaces as well as underscores.\n\n```yaml\ntitle: Just Because I Can\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | dashify }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->dashify() }}\n```\n::\n\n```html\njust-because-i-can\n```\n\n\n"
  },
  {
    "path": "content/collections/modifiers/days_ago.md",
    "content": "---\nid: 811c1cf5-797f-4e77-af92-fde6c03e96d2\nblueprint: modifiers\nmodifier_types:\n  - date\nparse_content: true\ntitle: 'Days Ago'\nrelated_entries:\n  - e73f1574-732e-4a74-be47-37e1fddb05d6\n  - 603701ba-5da7-4ec8-abe5-5bc9fe6861ea\n  - 06027289-825e-4205-bd3a-f375e26ab81e\n  - 7ba53a64-0266-4752-af5b-282a40dd11fa\n  - 6ebb6c28-d1f3-4362-92a0-8a16b5c9cd51\n  - 6fcbfa5c-854e-4541-9955-505eca0d6bf7\n  - 811c1cf5-797f-4e77-af92-fde6c03e96d2\n  - 40578328-3288-4c54-a475-8afad19a37e6\n---\nReturns the number of days since a given date variable. Statamic will attempt to parse any string as a date, but try to keep it in the least ambiguous date format possible.\n\n\n```yaml\n# Let's assume a server date of \"December 31 2021\"\ndate: December 25 2021\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | days_ago }}\n```\n::tab blade\n```blade`\n{{ Statamic::modify($date)->daysAgo() }}\n``\n::\n\n```output\n6\n```"
  },
  {
    "path": "content/collections/modifiers/decode.md",
    "content": "---\nid: 1fd780fd-ae92-4e73-9513-2b9c845976e9\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: Decode\n---\nConvert all HTML entities to their applicable characters via PHP's [html_entity_decode()][decode] function. Will convert both double and single quotes. This is the opposite of the [entities][entities] modifier.\n\n```yaml\nstring: \"I'll &quot;eat&quot; the &lt;b&gt;bacon&lt;/b&gt; now\";\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | decode }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->decode() }}\n```\n::\n\n```html\nI'll \"eat\" the <b>bacon</b> now\n```\n\n[decode]: http://php.net/manual/en/function.html-entity-decode.php\n[entities]: /modifiers/entities\n"
  },
  {
    "path": "content/collections/modifiers/deslugify.md",
    "content": "---\nid: 826517cc-7273-4045-bca2-fe5825fd9bda\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Deslugify\n---\nReplaces all hyphens and underscores in a string with spaces. The opposite of [dashify](dashify).\n\n```yaml\ntitle: Just-Because-I-Can\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | deslugify }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->deslugify() }}\n```\n::\n\n```html\nJust Because I Can\n```\n"
  },
  {
    "path": "content/collections/modifiers/divide.md",
    "content": "---\nid: fedbc5fc-2478-4fba-92ba-4004bc6e8845\nblueprint: modifiers\nmodifier_types:\n  - math\ntitle: Divide\n---\nDivide a value or another variable by your variable. Pass an integer or the name of a second variable as the parameter.\n\n```yaml\nbacon: 21\nskillets: 3\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ bacon | divide($skillets) }}\n{{ skillets | divide(3) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($bacon)->divide($skillets) }}\n{{ Statamic::modify($skillets)->divide(3) }}\n```\n::\n\n```html\n7\n1\n```\n"
  },
  {
    "path": "content/collections/modifiers/dl.md",
    "content": "---\nid: fbdb7bf5-ac19-444c-9536-57332ffff388\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\ntitle: DL\n---\nTurn a key/value array, otherwise known as a YAML mapping, into an HTML definition list.\n\n```yaml\nfood:\n  Delicious:\n    - bacon\n    - sushi\n  Green:\n    - broccoli\n    - kale\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ food | dl }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($food)->dl() !!}\n```\n::\n\n```html\n<dl>\n  <dt>Delicious</dt>\n  <dd>bacon</dd>\n  <dd>sushi</dd>\n\n  <dt>Green</dt>\n  <dd>broccoli</dd>\n  <dd>kale</dd>\n</dl>\n```\n"
  },
  {
    "path": "content/collections/modifiers/doesnt_overlap.md",
    "content": "---\nid: 948e8351-70ba-4263-a3d5-34dfff0551d5\nblueprint: modifiers\nmodifier_types:\n  - array\n  - conditions\ntitle: \"Doesn't Overlap\"\n---\nThe inverse of [`overlaps`](/modifiers/overlaps). Returns `true` when _none_ of the needle values are found in the haystack array, otherwise `false`.\n\nThe first parameter is the \"needle\" to compare against the \"haystack\". It will read from the context if there is a matching variable, otherwise it will use the parameter as the value. The needle can be a single value or an array.\n\n```yaml\nshopping_list:\n  - eggs\n  - flour\n  - beef jerky\navoid:\n  - kale\n  - tofu\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if shopping_list | doesnt_overlap('avoid') }} All clear! {{ /if }}\n{{ if shopping_list | doesnt_overlap('flour') }} Nope, there's flour. {{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($shopping_list)->doesntOverlap('avoid')->fetch()) All clear! @endif\n@if (Statamic::modify($shopping_list)->doesntOverlap('flour')->fetch()) Nope, there's flour. @endif\n```\n::\n\n```html\nAll clear!\n```\n"
  },
  {
    "path": "content/collections/modifiers/dump.md",
    "content": "---\nid: 12de1a6c-e8be-4703-81a3-fc270311bc84\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: Dump\n---\nDump a variable to the browser and see under the hood with data types and array exportation. Definitely just for debugging when in development.\n\n```yaml\nfood:\n  delicious:\n    - bacon\n    - sushi\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ food | dump }}\n```\n::tab blade\n```blade\n@dd($food)\n```\n::\n\n```html\narray:2 [▼\n  \"delicious\" => array:2 [▶]\n]\n```\n\n:::tip\nYou can also use the [dump tag](/tags/dump) to achieve a similar effect.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/embed_url.md",
    "content": "---\nid: 45310885-fbd3-438d-85d5-076dda1646e0\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: 'Embed Url'\n---\nConverts a Youtube or Vimeo link to their embed URLs.\n\nPlays nicely with the [Video fieldtype](/fieldtypes/video) and the [is_embeddable modifier](/modifiers/is_embeddable).\n\n```yaml\nyoutube: https://www.youtube.com/watch?v=s9F5fhJQo34\nvimeo: https://vimeo.com/22439234\nother: http://example.com/video.mp4\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ youtube | embed_url }}\n{{ vimeo | embed_url }}\n{{ other | embed_url }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($youtube)->embedUrl() }}\n{{ Statamic::modify($vimeo)->embedUrl() }}\n{{ Statamic::modify($other)->embedUrl() }}\n```\n::\n\n```html\nhttps://www.youtube.com/embed/s9F5fhJQo34\nhttps://player.vimeo.com/video/22439234\nhttp://example.com/video.mp4\n```\n"
  },
  {
    "path": "content/collections/modifiers/ends_with.md",
    "content": "---\nid: 40fc5b1e-d0e7-488a-bf6e-e6ef4a8b7dd8\nblueprint: modifiers\nmodifier_types:\n  - conditions\ntitle: 'Ends With'\n---\nReturns `true` if the value ends with a given string. This comparison is case-insensitive.\n\n```yaml\npunchline: That's what she said!\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if (punchline | ends_with('she said!')) }}\n{{ if (punchline | ends_with('your mom!')) }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($punchline)->endsWith('she said!')->fetch())\n\n@endif\n\n@if (Statamic::modify($punchline)->endsWith('your mom!')->fetch())\n\n@endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/ensure_left.md",
    "content": "---\nid: 171d9329-c456-45ca-a5e4-fc5bad7fb0ec\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Ensure Left'\n---\nEnsures that the string begins with a specified string. If it doesn't, it will now.\n\n```yaml\nlinks:\n  - statamic.com\n  - http://wilderborn.com\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ links }}\n  <li>{{ value | ensure_left('http://') }}</li>\n{{ /links }}\n```\n::tab blade\n```blade\n@foreach ($links as $link)\n  <li>{{ Statamic::modify($link)->ensureLeft('http://') }}</li>\n@endforeach\n```\n::\n\n```html\n<li>http://statamic.com</li>\n<li>http://wilderborn.com</li>\n```\n"
  },
  {
    "path": "content/collections/modifiers/ensure_right.md",
    "content": "---\nid: 6854539a-4661-483b-bb1f-2d28df0db76e\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Ensure Right'\n---\nEnsures that the string ends with a specified string. If it doesn't, it will now.\n\n```yaml\nlinks:\n  - statamic\n  - wilderborn.com\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ links }}\n  <li>{{ value | ensure_right('.com') }}</li>\n{{ /links }}\n```\n::tab blade\n```blade\n@foreach ($links as $link)\n  <li>{{ Statamic::modify($value)->ensureRight('.com') }}</li>\n@endforeach\n```\n::\n\n```html\n<li>statamic.com</li>\n<li>wilderborn.com</li>\n```\n"
  },
  {
    "path": "content/collections/modifiers/entities.md",
    "content": "---\nid: 50c06e32-9b94-4129-85ba-7cc4201b9e3f\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Entities\n---\nEncode a string with HTML entities via PHP's [htmlentities()][entities] function. This is the opposite of the [decode][decode] modifier.\n\n```yaml\nstring: \"The 'bacon' is <b>crispy</b>\"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | entities }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->entities() }}\n```\n::\n\n```html\nThe &#039;bacon&#039; is &lt;b&gt;crispy&lt;/b&gt;\n```\n\n[entities]: http://php.net/manual/en/function.htmlentities.php\n[decode]: /modifiers/decode\n"
  },
  {
    "path": "content/collections/modifiers/excerpt.md",
    "content": "---\nid: 051ecd7b-1cf7-4b47-b8c1-cfcede33289f\nblueprint: modifiers\ntitle: Excerpt\nintro: 'Generate an excerpt from your content.'\nmodifier_types:\n  - string\n---\nBreaks a string at a given marker. Uses `<!--more-->` by default.\n\n```yaml\n---\ntitle: 'Example Entry'\n---\nLorem Ipsum dolor sit amet.<!--more--> consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Massa id neque aliquam vestibulum.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ books | excerpt }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($books)->excerpt() }}\n```\n::\n\n```html\nLorem Ipsum dolor sit amet.\n```\n\nYou can override the marker by passing an alternative as the first parameter:\n\n```\n{{ books | excerpt:<!-- end --> }}\n```\n"
  },
  {
    "path": "content/collections/modifiers/explode.md",
    "content": "---\nid: c3d3e2c4-218e-4841-a594-647f10863866\nblueprint: modifiers\nmodifier_types:\n  - string\n  - array\ntitle: Explode\n---\nBreaks a string into an array of strings split on a given delimiter.\n\n```yaml\nplaces: Scotland, England, Switzerland, Italy\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ places | explode(',') | ul }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($places)->explode(',')->ul() !!}\n```\n::\n\n```html\n<ul>\n  <li>Scotland</li>\n  <li>England</li>\n  <li>Switzerland</li>\n  <li>Italy</li>\n</ul>\n```\n\nTo limit the number of splits, pass the limit as the second argument:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ places | explode(',', 2) | ul }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($places)->explode(',', 2)->ul() !!}\n```\n::\n\n```html\n<ul>\n  <li>Scotland</li>\n  <li>England, Switzerland, Italy</li>\n</ul>\n```"
  },
  {
    "path": "content/collections/modifiers/favicon.md",
    "content": "---\nid: 9c7ad945-2d02-423b-ae97-09cbe7ffed0d\nblueprint: modifiers\nmodifier_types:\n  - markup\nattributes: true\ntitle: Favicon\n---\nGiven a valid URL will generate a proper favicon meta tag.\n\n```yaml\nicon: /assets/img/favicon.png\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ icon | favicon }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($icon)->favicon() !!}\n```\n::\n\n```html\n<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/assets/img/favicon.png\">\n```\n"
  },
  {
    "path": "content/collections/modifiers/filter_empty.md",
    "content": "---\nid: a01e28a5-7c59-436c-8137-98e9481631ba\nmodifier_types:\n  - array\ntitle: 'Filter Empty'\n---\nFilters out null values from an array.\n\n```yaml\nfavorite_things:\n  - pizza\n  - null\n  - ice cream\n```\n\n```antlers\n{{ favorite_things | filter_empty }}\n```\n\n```output\npizza\nice cream\n```\n"
  },
  {
    "path": "content/collections/modifiers/first.md",
    "content": "---\nid: 3ef1e731-742e-48c2-81f0-6fa916ecda0a\nblueprint: modifiers\nmodifier_types:\n  - markup\n  - string\n  - utility\n  - array\ntitle: First\n---\nReturns the first X characters of a string, where X is any positive integer, or the first item in an array.\n\n```yaml\ntitle: 2015 Year Books Photos\narray:\n  - Sonic\n  - Knuckles\n  - Tails\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | first(4) }}\n{{ array | first }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->first(4) }}\n{{ Statamic::modify($array)->first() }}\n```\n::\n\n```html\n2015\nSonic\n```\n"
  },
  {
    "path": "content/collections/modifiers/flatten.md",
    "content": "---\nid: e893345c-03f7-466b-a400-bbd2545bd780\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Flatten\n---\nFlattens a multi-dimensional array (a Grid or Replicator field for example) into a single dimension.\n\n```yaml\ningredients:\n  spices: [garlic, cumin, ginger, turmeric, paprika, curry powder]\n  vegetables: [tomatoes, onion]\n  meat: [chicken]\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ ingredients | flatten }}\n```\n::tab blade\n```blade\n<?php\n  $flattened = Statamic::modify($ingredients)\n    ->flatten()\n    ->fetch();\n?>\n```\n::\n\n```yaml\ningredients:\n  - garlic\n  - cumin\n  - ginger\n  - turmeric\n  - paprika\n  - curry powder\n  - tomatoes\n  - onion\n  - chicken\n```\n\nYou can optionally pass a `depth` parameter to the `flatten` modifier, allowing you to specify how deeply nested arrays should be flattened.\n\n```yaml\n-\n  - garlic\n  - cumin\n  - ginger\n  - turmeric\n  - paprika\n  - curry powder\n-\n  - tomatoes\n  - onion\n-\n  - chicken\n```\n"
  },
  {
    "path": "content/collections/modifiers/flip.md",
    "content": "---\nid: f874e969-d579-4501-9140-e4005945d302\nblueprint: modifiers\nmodifier_types:\n  - array\ntitle: Flip\n---\nSwaps the keys with their corresponding values. The old switcharoo.\n\n```yaml\nfavorites:\n  food: burger\n  drink: soda\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ favorites | json }}\n{{ favorites | flip | json }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($favorites)->json() !!}\n{!! Statamic::modify($favorites)->flip()->json() !!}\n```\n::\n\n```json\n{\"food\":\"burger\",\"drink\":\"soda\"}\n{\"burger\":\"food\",\"soda\":\"drink\"}\n```\n"
  },
  {
    "path": "content/collections/modifiers/floor.md",
    "content": "---\nid: 0dc57cca-67b2-45a1-a02d-915ac64f064f\nblueprint: modifiers\nmodifier_types:\n  - math\n  - utility\ntitle: Floor\n---\nRounds a number down to the next whole number.\n\n```yaml\nnumber: 25.98\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ number | floor }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($number)->floor() }}\n```\n::\n\n```html\n25\n```\n"
  },
  {
    "path": "content/collections/modifiers/folder.yaml",
    "content": "order: alphabetical\nparse_content: false\ntemplate: modifier\n"
  },
  {
    "path": "content/collections/modifiers/format.md",
    "content": "---\nid: 756d23b4-209c-457c-b9f5-d69347bbe8fe\nblueprint: modifiers\nmodifier_types:\n  - date\n  - string\ntitle: Format\n---\nGiven a date string, or anything that sort of looks like a date string, `format` will convert it to a [Carbon][carbon] instance and allow you to format it with PHP's [datetime format][datetime] variables.\n\n```yaml\nevent_date: April 15 2016\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ event_date | format('Y-m-d') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($event_date)->format('Y-m-d') }}\n```\n::\n\n```html\n2016-04-15\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n\n## Parameters\n\n### Day\n\n| Character | Description | Example |\n| --------- | ----------- | -------------- |\n| `d` | Day of the month, 2 digits with leading zeros | `01` to `31`  |\n| `D` | A textual representation of a day, three letters  | `Mon` to `Sun` |\n| `j` | Day of the month without leading zeros  | `1` to `31` |\n| `l` | A full textual representation of the day of the week  | `Sunday` to `Saturday`|\n| `N` | ISO 8601 numeric representation of the day of the week  | `1` (for Monday) to `7` (for Sunday) |\n| `S` | English ordinal suffix for the day of the month, 2 characters | `st`, `nd`, `rd` or `th`. Works well with `j` |\n| `w` | Numeric representation of the day of the week | `0` (for Sunday) to `6` (for Saturday) |\n| `z` | The day of the year (starting from 0) | `0` to `365` |\n\n### Week\n| Character | Description | Example |\n| --------- | ----------- | -------------- |\n| `W`  | ISO 8601 week number of year, weeks starting on Monday  | `42` (the 42nd week in the year) |\n\n### Month\n| Value | Description | Example |\n| --------- | ----------- | -------------- |\n| `F`  | A full textual representation of a month, such as January or March  | `January` to `December`  |\n| `m`  | Numeric representation of a month, with leading zeros | `01` to `12` |\n| `M`  | A short textual representation of a month, three letters  | `Jan` to `Dec` |\n| `n`  | Numeric representation of a month, without leading zeros  | `1` to `12`  |\n| `t`  | Number of days in the given month | `28` to `31` |\n\n### Year\n| Value | Description | Example |\n| --------- | ----------- | -------------- |\n| `L`  | Whether it's a leap year  | `1` if it is a leap year, `0` otherwise.  |\n| `o`  | ISO 8601 week-numbering year. | `1999` or `2003` |\n| `Y`  | A full numeric representation of a year, at least 4 digits, with `-` for years BCE.| `-0055`, `0787`, `1999`, `2003` |\n| `y`  | A two digit representation of a year  | `99` or `03`  |\n\n### Time\n| Value | Description | Example |\n| --------- | ----------- | -------------- |\n| `a`  | Lowercase Ante meridiem and Post meridiem | `am` or `pm`  |\n| `A`  | Uppercase Ante meridiem and Post meridiem | `AM` or `PM`  |\n| `B`  | Swatch Internet time (it's coming back, just you wait)  | `000` to `999` |\n| `g`  | 12-hour format of an hour without leading zeros | `1` to `12`  |\n| `G`  | 24-hour format of an hour without leading zeros | `0` to `23`  |\n| `h`  | 12-hour format of an hour with leading zeros  | `01` to `12` |\n| `H`  | 24-hour format of an hour with leading zeros  | `00` to `23` |\n| `i`  | Minutes with leading zeros  | `00` to `59`  |\n| `s`  | Seconds with leading zeros  | `00` to `59` |\n| `u`  | Microseconds. | `654321` |\n| `v`  | Milliseconds. Same note applies as for `u`.| `654`  |\n\n### Timezone\n| Value | Description | Example |\n| --------- | ----------- | -------------- |\n| `e` | Timezone identifier | `UTC`, `GMT`, `Atlantic/Azores` |\n| `I`  | Whether or not the date is in daylight saving time | `1` if Daylight Saving Time, `0` otherwise. |\n| `O` | Difference to Greenwich time (GMT) without colon between hours and minutes | `+0200` |\n| `P` | Difference to Greenwich time (GMT) with colon between hours and minutes | `+02:00`  |\n| `p` | The same as `P`, but returns `Z` instead of `+00:00` | `+02:00` |\n| `T` | Timezone abbreviation, if known; otherwise the GMT offset.  | `EST`, `MDT`, `+05`  |\n| `Z` | Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. | `-43200` to `50400`  |\n\n### Full Date/Time\n\n| Value | Description | Example |\n| --------- | ----------- | -------------- |\n| `c` | ISO 8601 date | 2004-02-12T15:19:21+00:00 |\n| `r` | [RFC 2822](http://www.faqs.org/rfcs/rfc2822) or [RFC 5322](http://www.faqs.org/rfcs/rfc5322) formatted date | `Thu, 21 Dec 2000 16:01:07 +0200`  |\n\n[carbon]: http://carbon.nesbot.com\n[datetime]: https://www.php.net/manual/en/datetime.format.php\n"
  },
  {
    "path": "content/collections/modifiers/format_number.md",
    "content": "---\nid: 63b56419-6556-4174-8d26-e941460b82a4\nblueprint: modifiers\nmodifier_types:\n  - math\n  - number\ntitle: 'Format Number'\n---\nFormat a number with grouped thousands and decimal points. In other words, make it look nice.\n\n- Parameter 1: precision (number of decimal places before rounding)\n- Parameter 2: Decimal point (default `.`)\n- Parameter 3: Thousands separator (default: `,`)\n\n```yaml\nlucky_number: 130134.109\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ lucky_number | format_number(1, ',', ',') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($lucky_number)->formatNumber(1, ',', ',') }}\n```\n::\n\n```html\n130,134,1\n```\n"
  },
  {
    "path": "content/collections/modifiers/format_translated.md",
    "content": "---\nid: 8cbea367-799f-4fb6-866e-519d571f7b3e\nblueprint: modifiers\nmodifier_types:\n  - date\n  - string\ntitle: 'Format Translated'\n---\nGiven a date string, or anything that sort of looks like a date string, `format_translated` will convert it to a [Carbon][carbon] instance and allow you to format it using your site's configured locale.\n\n```yaml\nevent_date: 2024-02-28\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ event_date | format_translated('l j F Y') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($event_date)->format_translated('l j F Y') }}\n```\n::\n\nAssuming your site's locale is `fr_FR`:\n\n```html\nmercredi 28 février 2024\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n\n[carbon]: http://carbon.nesbot.com\n"
  },
  {
    "path": "content/collections/modifiers/full_urls.md",
    "content": "---\nid: 44cb7965-877e-49b3-92fe-a24970b542a2\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Full Urls'\n---\nReplaces root-relative URLs inside HTML attributes (e.g. `href` and `src` ) with absolute URLs. This is most often used in RSS feeds and other places where markup may be consumed off the domain.\n\n```html\nI had this totally <a href=\"/dreams/spiders-with-ramen-legs\">crazy dream</a> last night and I know you want to hear all about it!\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ content | full_urls }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($content)->fullUrls() !!}\n```\n::\n\n```html\nI had this totally <a href=\"https://example.com/dreams/spiders-with-ramen-legs\">crazy dream</a> last night and I know you want to hear all about it!\n```\n"
  },
  {
    "path": "content/collections/modifiers/get.md",
    "content": "---\nid: c6311d04-364d-4086-8b6b-2a58e88c6cb8\nblueprint: modifiers\ntitle: Get\nmodifier_types:\n  - asset\n  - utility\n  - relationship\n---\nGets a value from a relationship based on its ID. This is like a nicer-to-read single tag version of the [Get_Content Tag](/tags/get_content).\n\n```yaml\nfeatured_post: 4e82a520-275f-11e6-bdf4-0800200c9a66\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ featured_post | get('title') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($featured_post)->get('title') }}\n```\n::\n\n```html\nFeatured Post Title\n```\n\nThe above is equivalent to doing this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_content :from=\"featured_post\" }}\n    {{ title }}\n{{ /get_content }}\n```\n::tab blade\n```blade\n<s:get_content :from=\"$featured_post\">\n  {{ $title }}\n</s:get_content>\n```\n::\n"
  },
  {
    "path": "content/collections/modifiers/gravatar.md",
    "content": "---\nid: c0a376c1-1ade-447b-8a2d-18722a5446ba\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Gravatar\n---\nConverts an email string to a Gravatar image URL. The size can be specified by a parameter.\n\n```yaml\nemail: rswanson@inpra.org\n```\n\n::tabs\n::tab antlers\n```antlers\n{{ email | gravatar }}\n{{ email | gravatar(80) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($email)->gravatar() }}\n{{ Statamic::modify($email)->gravatar(80) }}\n```\n::\n\n```html\nhttps://www.gravatar.com/avatar/f4650388367dc01cf2acf16b412b3966\nhttps://www.gravatar.com/avatar/f4650388367dc01cf2acf16b412b3966?s=80\n```\n"
  },
  {
    "path": "content/collections/modifiers/group_by.md",
    "content": "---\nid: a070fabe-c413-4b31-9cb4-ad14bbe1aa4d\nblueprint: modifiers\nmodifier_types:\n  - array\ntitle: 'Group By'\n---\n## Overview\n\nYou may use this modifier to group items (a simple array, a collection of entries, etc) into groups\nbased on some common value.\n\n## By Key\n\nThe most basic usage example would be to take a simple array and output the groups using the key.\n\n```yaml\nsponsors:\n  -\n    sport: basketball\n    team: Jazz\n  -\n    sport: baseball\n    team: Yankees\n  -\n    sport: basketball\n    team: Bulls\n```\n\n```\n{{ sponsors | group_by('sport') }}\n  <h1>Basketball</h2>\n  {{ basketball }}\n    {{ team }}\n  {{ /basketball }}\n\n  <h1>Baseball</h2>\n  {{ baseball }}\n    {{ team }}\n  {{ /baseball }}\n{{ /sponsors }}\n```\n\n```html\n<h1>Basketball</h1>\nJazz\nBulls\n\n<h1>Baseball</h1>\nYankees\n```\n\n## Looping Over Groups\nIn the previous example, you had to know that there was going to be `basketball` and `baseball` keys ahead of time.\n\nIf you don't know the groups, you can loop over the `groups` variable.\nIt will be an array containing the name of the `group` and its `items`.\n\n```\n{{ sponsors group_by=\"sport\" }}\n    {{ groups }}\n        <h1>{{ group | upper }}</h1>\n        {{ items }}\n            {{ team }}\n        {{ /items }}\n    {{ /groups }}\n{{ /sponsors }}\n```\n\n## Nested Values\nIf you need to get a nested value for the groups, you can use the familiar colon syntax.\n\nFor example, you may have an `entries` field where you've selected entries from multiple collections, and you want to group by the collection's title.\n\n```yaml\nmenu_items:\n  - burger\n  - fries\n  - coke\n  - pepsi\n```\n```\n{{ menu_items group_by=\"collection:title\" }}\n    {{ groups }}\n        <h2>{{ group }}</h2>\n        {{ items }}\n            {{ title }}\n        {{ /items }}\n    {{ /groups }}\n{{ /menu_items }}\n```\n```\n<h2>Food</h2>\nBurger\nFries\n\n<h2>Drinks</h2>\nCoke\nPepsi\n```\n\n## Dates\n\nYou may group entries by a date field.\n\nFor example, you might want to output articles and group them by month.\n\nHere we'll group them by the `date` field using the `F Y` [PHP date format](https://www.php.net/manual/en/datetime.format.php) which\nwould output as \"Month Year\".\n\n```\n{{ collection:articles as=\"entries\" }}\n    {{ entries group_by=\"date|F Y\" }}\n        {{ groups }}\n            <h1>{{ group }}</h1>\n            {{ items }}\n                {{ title }}\n            {{ /items }}\n        {{ /groups }}\n    {{ /entries }}\n{{ /collection:articles }}\n```\n\n```\n<h1>September 2021</h1>\nEntry from September\nAnother entry from September\n\n<h1>October 2021</h1>\nEntry from October\n```\n\n:::tip\nThe date field in this example is named `date`, but you can use any date field.\n\n```\n{{ entries group_by=\"custom_date_field|F Y\" }}\n```\n:::\n\nIf you need the key to differ from how it's displayed (perhaps you want to use an additional modifier after), you can pass another date format as the\nthird argument. (Argument 2 creates the key, argument 3 creates the `{{ group }}` text).\n\n```\n{{ entries group_by=\"date|Y-m|F Y\" }}\n```\n"
  },
  {
    "path": "content/collections/modifiers/has_lower_case.md",
    "content": "---\nid: a5ce6691-840c-4bf7-b5f4-b87bb4845055\nblueprint: modifiers\nmodifier_types:\n  - conditions\ntitle: 'Has Lower Case'\n---\nReturns `true` if the string contains a lowercase character, `false` otherwise.\n\n```yaml\nloud_noises: \"I DON'T KNOW WHAT WE'RE YELLING ABOUT!\"\n```\n\n::tabs\n::tab antlers\n```antlers\n{{ if loud_noises | has_lower_case }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($loud_noises)->hasLowerCase()->fetch())\n\n@endif\n```\n::\n\n```html\nfalse\n```"
  },
  {
    "path": "content/collections/modifiers/has_upper_case.md",
    "content": "---\nid: b2936dd3-f8b1-44a7-bb40-f3b0a8b47e90\nblueprint: modifiers\nmodifier_types:\n  - conditions\ntitle: 'Has Upper Case'\n---\nReturns `true` if the string contains an uppercase character, `false` otherwise.\n\n```yaml\nloud_noises: \"I DON'T KNOW WHAT WE'RE YELLING ABOUT!\"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if loud_noises | has_upper_case }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($loud_noises)->hasUpperCase()->fetch())\n\n@endif\n```\n::\n\n```html\ntrue\n```\n"
  },
  {
    "path": "content/collections/modifiers/headline.md",
    "content": "---\nid: 2d555b32-e68c-4f9b-8570-f2e8d185989b\nblueprint: modifiers\nmodifier_types:\n  - markup\n  - string\n  - utility\ntitle: Headline\n---\nFormat the given string, usually a headline or title, with either [AP](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case) or [MLA](https://style.mla.org/capitalization-of-titles/) style.\n\nAccepts `ap` or `mla` as an argument. Defaults to `ap` if none is specified.\n\n```yaml\ntitle: I see a bad-ass mother who don't take no crap off of nobody.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | headline('ap') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->headline('ap') }}\n```\n::\n\n```\nI See a Bad-Ass Mother Who Don't Take No Crap Off of Nobody.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | headline('mla') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->headline('mla') }}\n```\n::\n\n```\nI See a Bad-ass Mother Who Don't Take No Crap Off of Nobody.\n```\n"
  },
  {
    "path": "content/collections/modifiers/hex_to_rgb.md",
    "content": "---\nid: 69000ef1-0a98-42a4-ba1a-0bab4c58ca7d\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: 'Hex To RGB'\n---\nConverts a color from hex to RGB, the perfect match for the [color fieldtype](/fieldtypes/color).\n\n```yaml\ncolor: `#FF269E`\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ color | hex_to_rgb }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($color)->hexToRgb() }}\n```\n::\n\n```html\n255, 38, 158\n```\n"
  },
  {
    "path": "content/collections/modifiers/hours_ago.md",
    "content": "---\nid: 6ebb6c28-d1f3-4362-92a0-8a16b5c9cd51\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: 'Hours Ago'\nrelated_entries:\n  - e73f1574-732e-4a74-be47-37e1fddb05d6\n  - 603701ba-5da7-4ec8-abe5-5bc9fe6861ea\n  - 06027289-825e-4205-bd3a-f375e26ab81e\n  - 7ba53a64-0266-4752-af5b-282a40dd11fa\n  - 6fcbfa5c-854e-4541-9955-505eca0d6bf7\n  - 811c1cf5-797f-4e77-af92-fde6c03e96d2\n---\nReturns the number of hours since a given date variable. Statamic will attempt to parse any string as a date, but try to keep it in the least ambiguous date format possible.\n\n```yaml\ndate: October 1 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | hours_ago }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($date)->hoursAgo() }}\n```\n::\n```html\n{{ test_date | hours_ago }}\n```"
  },
  {
    "path": "content/collections/modifiers/image.md",
    "content": "---\nid: 26045669-567d-4e93-b3ba-34c835f5c5e9\nblueprint: modifiers\nmodifier_types:\n  - asset\n  - markup\nattributes: true\ntitle: Image\n---\nGenerate an HTML image element with the variable's value as `src`.\n\n```yaml\nheader_image: /assets/img/bokeh-bunnies.jpg\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ header_image | image }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($header_image)->image() !!}\n```\n::\n```html\n<img src=\"/assets/img/bokeh-bunnies.jpg\">\n```\n"
  },
  {
    "path": "content/collections/modifiers/in_array.md",
    "content": "---\nid: 4e349523-cba6-4f3b-a0e1-bd4e8b1cf6b9\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\n  - conditions\ntitle: 'In Array'\n---\nCheck if an array contains a specific value. Returns `true` if a match is found.\n\nThe first parameter is the \"needle\" to find in the \"haystack\". It will read from the context if there is a matching variable, otherwise it will use the parameter as the value. You can pass multiple arguments.\n\n```yaml\nshopping_list:\n  - eggs\n  - flour\n  - beef jerky\nwant: eggs\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if (shopping_list | in_array('flour')) }} GOT IT! {{ /if }}\n{{ if (shopping_list | in_array('want')) }} GOT EM! {{ /if }}\n{{ if (shopping_list | in_array('eggs', 'flour')) }} YES I DID NOT FORGET! {{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($shopping_list)->inArray('flour')->fetch()) GOT IT! @endif\n@if (Statamic::modify($shopping_list)->inArray('want')->fetch()) GOT EM! @endif\n@if (Statamic::modify($shopping_list)->inArray('eggs', 'flour')->fetch()) YES I DID NOT FORGET! @endif\n```\n::\n\n\n```html\nGOT IT!\nGOT EM!\nYES I DID NOT FORGET!\n```\n"
  },
  {
    "path": "content/collections/modifiers/insert.md",
    "content": "---\nid: fa936f49-be25-432e-9543-7a201a652055\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Insert\n---\nInserts a string at the position provided. The beginning of the string is position 0.\n\n```yaml\nopinion: This is yummy.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ opinion | insert('not', 8) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($opinion)->insert(['not', 8]) }}\n```\n::\n\n```html\nThis is not yummy.\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_after.md",
    "content": "---\nid: 3c167645-5ad1-45b4-b6df-22b0d2c95abf\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is After'\n---\nReturns `true` if a date variable is after another date. That second date can be the name of another variable or a literal date string.\n\n```yaml\nstart_date: January 17 2015\nend_date: December 1 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if end_date | is_after($start_date) }}\n{{ if start_date | is_after(2014) }}\n{{ if start_date | is_after($end_date) }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($end_date)->isAfter($start_date)->fetch()) @endif\n@if (Statamic::modify($start_date)->isAfter(2014)->fetch()) @endif\n@if (Statamic::modify($start_date)->isAfter($end_date)->fetch()) @endif\n```\n::\n\n```html\ntrue\ntrue\nfalse\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_alpha.md",
    "content": "---\nid: a6aaac80-19b7-4400-af21-9147aff064c4\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is Alpha'\n---\nReturns `true` if string contains **only** alphabetic characters. Numbers, punctuation, whitespace, and another other special characters will cause a `false`.\n\n```yaml\nsecret_phrase: abcdefg\neven_more_secret_phrase: abc123\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if secret_phrase | is_alpha }}\n{{ if even_more_secret_phrase | is_alpha }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($secret_phrase)->isAlpha()->fetch()) @endif\n@if (Statamic::modify($even_more_secret_phrase)->isAlpha()->fetch()) @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_alphanumeric.md",
    "content": "---\nid: 923f34bd-d17d-4353-821f-48e986bdce3b\nblueprint: modifiers\nmodifier_types:\n  - number\n  - string\n  - conditions\ntitle: 'Is Alphanumeric'\n---\nReturns `true` if string contains **only** alphanumeric characters. Punctuation, whitespace, and another other special characters will cause a `false`.\n\n```yaml\nsecret_phrase: abc123\neven_more_secret_phrase: abc123!@#\n```\n\n\n::tabs\n\n::tab antlers:\n```antlers\n{{ if secret_phrase | is_alphanumeric }}\n{{ if even_more_secret_phrase | is_alphanumeric }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($secret_phrase)->isAlphanumeric()->fetch()) @endif\n@if (Statamic::modify($even_more_secret_phrase)->isAlphanumeric()->fetch()) @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_array.md",
    "content": "---\nid: 1ac30316-8467-425e-9a50-82e754be7df2\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\n  - conditions\ntitle: 'Is Array'\n---\nCheck if a value is an array. Returns a `bool`.\n\nThe modifier is a wrapper around PHP's [built-in](https://www.php.net/manual/en/function.is-array) `is_array` function.\n"
  },
  {
    "path": "content/collections/modifiers/is_before.md",
    "content": "---\nid: 85b0a5d9-eb77-4bc2-b60e-7b4d3f9aa406\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Before'\n---\nReturns `true` if a date variable is before another date. That second date can be the name of another variable or a literal date string.\n\n```yaml\nstart_date: January 17 2015\nend_date: December 1 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if end_date | is_before($start_date) }}\n{{ if start_date | is_before(2014) }}\n{{ if start_date | is_before($end_date) }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($end_date)->isBefore($start_date)->fetch()) @endif\n@if (Statamic::modify($start_date)->isBefore(2014)->fetch()) @endif\n@if (Statamic::modify($start_date)->isBefore($end_date)->fetch()) @endif\n```\n::\n\n```html\nfalse\nfalse\ntrue\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_between.md",
    "content": "---\nid: cfbf8926-47ee-42e1-a972-86e3dc13633b\nblueprint: modifiers\nmodifier_types:\n  - conditions\n  - date\n  - number\ntitle: 'Is Between'\n---\nReturns `true` if a date variable is between two other dates. Those dates can be the name of other variables or literal date strings.\n\n```yaml\ndate: November 15 2015\nstart_date: July 4 2015\nend_date: December 1 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_between($start_date, $end_date) }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isBetween([$start_date, $end_date])->fetch()) @endif\n```\n::\n\n```html\ntrue\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_blank.md",
    "content": "---\nid: 054a230c-a655-48b0-af8b-a963bb0b89b0\nblueprint: modifiers\nmodifier_types:\n  - conditions\ntitle: 'Is Blank'\n---\nReturns `true` if the string contains only whitespace chars.\n\n```yaml\nghost:\nzombie: BRAINSSSS\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if ghost | is_blank }}\n{{ if zombie | is_blank }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($ghost)->isBlank()->fetch()) @endif\n@if (Statamic::modify($zombie)->isBlank()->fetch()) @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_email.md",
    "content": "---\nid: 471b3281-4573-43ee-9fee-eb55edf26ad0\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is Email'\n---\nReturns `true` if a string is a valid email address.\n\n```yaml\nan_email: lknope@inpra.org\nnot_an_email: waffles\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if an_email | is_email }}\n{{ if not_an_email | is_email }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($an_email)->isEmail()->fetch()) @endif\n@if (Statamic::modify($not_an_email)->isEmail()->fetch()) @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n\n\n"
  },
  {
    "path": "content/collections/modifiers/is_embeddable.md",
    "content": "---\nid: d3ab4cb6-2aeb-4a15-93b9-79e56ad82223\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: 'Is Embeddable'\n---\nChecks to see if a video URL is embeddable. In other words: YouTube or Vimeo URL are considered as embeddable.\n\nPlays nicely with the [Video fieldtype](/fieldtypes/video) and the [embed_url modifier](/modifiers/embed_url).\n\n```yaml\nyoutube: https://www.youtube.com/watch?v=s9F5fhJQo34\nvimeo: https://vimeo.com/22439234\nother: http://example.com/video.mp4\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ youtube | is_embeddable }}\n{{ vimeo | is_embeddable }}\n{{ other | is_embeddable }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($youtube)->isEmbeddable()->fetch()) ... @endif\n@if (Statamic::modify($vimeo)->isEmbeddable()->fetch()) ... @endif\n@if (Statamic::modify($other)->isEmbeddable()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_empty.md",
    "content": "---\nid: a94a24ce-500d-4194-85db-85fcbb552e06\nblueprint: modifiers\nmodifier_types:\n  - array\ntitle: 'Is Empty'\n---\nChecks to see if an array is empty without any set values. Works with numeric indexes, associative, and string keyed arrays of all depths. It's pretty smart, as these things go.\n\n```yaml\nsome_data:\n  - is living here\nmore_data:\n  with:\n  hopes:\n  and:\n  dreams:\n    -\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if some_data | is_empty }}\n{{ if more_data | is_empty }}\n\n```\n::tab blade\n```blade\n@if (Statamic::modify($some_data)->isEmpty()->fetch()) ... @endif\n@if (Statamic::modify($more_data)->isEmpty()->fetch()) ... @endif\n```\n::\n\n```html\nfalse\ntrue\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_external_url.md",
    "content": "---\nid: 4cb0f243-72f4-45a1-83d3-d72209907875\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is External Url'\n---\nReturns `true` if a string is an external URL.\n\n```yaml\ngoogle_url: http://google.com/\nentry_url: /waffles\nnot_a_url: bacon\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if google_url | is_external_url }}\n{{ if entry_url | is_external_url }}\n{{ if not_a_url | is_external_url }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($google_url)->isExternalUrl()->fetch()) ... @endif\n@if (Statamic::modify($entry_url)->isExternalUrl()->fetch()) ... @endif\n@if (Statamic::modify($not_a_url)->isExternalUrl()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\nfalse\nfalse\n```"
  },
  {
    "path": "content/collections/modifiers/is_future.md",
    "content": "---\nid: 26bf98af-5bc1-4ec9-b533-815872606e3b\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Future'\n---\nReturns `true` if date is in the future.\n\n```yaml\ndate: October 21 2015\nanother_date: November 2030\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_future }}\n{{ if another_date | is_future }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isFuture()->fetch()) ... @endif\n@if (Statamic::modify($another_date)->isFuture()->fetch()) ... @endif\n```\n::\n\n```html\nfalse\ntrue\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_json.md",
    "content": "---\nid: a314e7fc-ad72-4afb-88b8-1ca4a0100c17\nblueprint: modifiers\nmodifier_types:\n  - conditions\n  - utility\ntitle: 'Is Json'\n---\nReturns `true` if string is valid json\n\n```yaml\ndata: '{\"book\": \"All The Places You'll Go\"}'\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if data | is_json }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($data)->isJson()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_leap_year.md",
    "content": "---\nid: 0438995d-7100-4a72-9c0c-985e00f482bb\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Leap Year'\n---\nReturns `true` if date is in a leap year. Try and find a regular use for this one, we dare you.\n\n```yaml\ndate: November 2016\nanother_date: November 2017\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_leap_year }}\n{{ if another_date | is_leap_year }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isLeapYear()->fetch()) ... @endif\n@if (Statamic::modify($another_date)->isLeapYear()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_lowercase.md",
    "content": "---\nid: 97ff9a80-1e19-4f6d-b9e8-b5f4223e19d7\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is Lowercase'\n---\nReturns `true` if string is only lowercase characters.\n\n```yaml\ntopic: fhqwhgads\nfrom: Sibbie\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if topic | is_lowercase }}\n{{ if from | is_lowercase }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($topic)->isLowercase()->fetch()) ... @endif\n@if (Statamic::modify($from)->isLowercase()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_numberwang.md",
    "content": "---\nid: f3e9d043-ff25-4778-9915-0cfafc52e166\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is Numberwang'\n---\nIs it or is it not numberwang? Returns `true` if it is indeed numberwang.\n\n```yaml\nnumber: 1002\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if number | is_numberwang }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($number)->isNumberwang()->fetch()) ... @endif\n```\n::\n\n```html\n???\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_numeric.md",
    "content": "---\nid: 02db0ee5-585b-4e40-ab2b-f15a596b341c\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is Numeric'\n---\nReturns `true` if variable is a number or numeric string.\n\n```yaml\nsequence: 4815162342\nanother_sequence: just type 4 8 15 16 23 42\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if sequence | is_numeric }}\n{{ if another_sequence | is_numeric }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($sequence)->isNumeric()->fetch()) ... @endif\n@if (Statamic::modify($another_sequence)->isNumeric()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_past.md",
    "content": "---\nid: fbf6eab4-0769-4e13-9205-f9f64fd44572\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Past'\n---\nReturns `true` if date is in the past.\n\n```yaml\ndate: October 21 2015\nanother_date: November 2019\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_past }}\n{{ if another_date | is_past }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isPast()->fetch()) ... @endif\n@if (Statamic::modify($another_date)->isPast()->fetch()) ... @endif\n```\n::\n```html\ntrue\nfalse\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_today.md",
    "content": "---\nid: 50aa52bf-8c6c-4ec3-9af7-e610f65f8202\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Today'\n---\nReturns `true` if a given date is today, using the server's time.\n\n```yaml\ndate: January 1, 2000\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_today }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isToday()->fetch()) ... @endif\n```\n::\n\n```html\nfalse\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_tomorrow.md",
    "content": "---\nid: 2e700683-1fb1-4cde-a019-67770ceabadf\nblueprint: modifiers\ntitle: 'Is Tomorrow'\nmodifier_types:\n  - date\n  - conditions\n---\nReturns `true` if a given date is tomorrow, using the server's time.\n\n```yaml\ndate: January 1, 2000\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_tomorrow }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isTomorrow()->fetch()) ... @endif\n```\n::\n\n```html\nfalse\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_uppercase.md",
    "content": "---\nid: d5635238-7d7a-4543-9afc-912bee6ad6fd\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is Uppercase'\n---\nReturns `true` if string is only uppercase characters.\n\n```yaml\ndeclaration: NOISES\ncite: anonymous\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if declaration | is_uppercase }}\n{{ if cite | is_uppercase }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($declaration)->isUppercase()->fetch()) ... @endif\n@if (Statamic::modify($cite)->isUppercase()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/is_url.md",
    "content": "---\nid: 85303071-213b-4e6c-8fb7-10f703a4a52e\nblueprint: modifiers\nmodifier_types:\n  - string\n  - conditions\ntitle: 'Is Url'\n---\nReturns `true` if a string is a valid URL.\n\n```yaml\na_url: http://google.com/\nnot_a_url: waffles\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if a_url | is_url }}\n{{ if not_a_url | is_url }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($a_url)->isUrl()->fetch()) ... @endif\n@if (Statamic::modify($not_a_url)->isUrl()->fetch()) ... @endif\n```\n::\n\n```html\ntrue\nfalse\n```\n\n\n"
  },
  {
    "path": "content/collections/modifiers/is_weekday.md",
    "content": "---\nid: a190fa95-c405-4e2c-b3c0-adfbe21f9bb2\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Weekday'\n---\nReturns `true` if date is a weekday.\n\n```yaml\ndate: December 25 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_weekday }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isWeekday()->fetch()) ... @endif\n```\n::\n\n\n```html\ntrue\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_weekend.md",
    "content": "---\nid: 22a4460a-b24a-4e24-bd8c-655d03e6d3de\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Weekend'\n---\nReturns `true` if date is on the weekend.\n\n```yaml\ndate: December 25 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_weekend }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isWeekend()->fetch()) ... @endif\n```\n::\n\n\n```html\nfalse\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/is_yesterday.md",
    "content": "---\nid: bd468407-617a-4cb8-93d8-cfd7148ec157\nblueprint: modifiers\nmodifier_types:\n  - date\n  - conditions\ntitle: 'Is Yesterday'\n---\nReturns `true` if a given date is yesterday, using the server's time.\n\n```yaml\ndate: January 1, 2000\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if date | is_yesterday }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($date)->isYesterday()->fetch()) ... @endif\n```\n::\n\n```html\nfalse\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/iso_format.md",
    "content": "---\nid: f72ffc08-4294-4c1c-9085-2794ee57962d\nblueprint: modifiers\nmodifier_types:\n  - date\n  - string\ntitle: 'Iso Format'\n---\nGiven a date string, or anything that even sorta kinda looks like a date string, will convert it to a [Carbon][carbon] instance and allow you to format it with ISO format. This allows you to use inner translations rather than language packages you need to install on every machine where you deploy your site.\n\nThe language that will be used for translations depends on what you configured in your `config/statamic/sites.php` file. The `locale` and `fallback_locale` settings from the `config/app.php` file have **no influence** on this modifier.\n\nThis is also compatible with [momentjs format method](https://momentjs.com/), it means you can use same format strings as you may have used in moment from your front-end or other node.js application.\n\nCheck out the [complete list of available replacements](https://carbon.nesbot.com/guide/getting-started/localization.html#iso-format-available-replacements).\n\n```yaml\nevent_date: June 19 2020\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ event_date | iso_format(\"MMMM Do YYYY, h:mm:ss a\") }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($event_date)->isoFormat([\"MMMM Do YYYY, h:mm:ss a\"]) }}\n```\n::\n\n```html\nJune 15th 2018, 5:34:15 pm\n```\n\nYou can use macro-formats to format and localize dates as well.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ event_date | iso_format('ll') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($event_date)->isoFormat('ll') }}\n```\n::\n\nWill output this on your English site:\n\n```html\nJan 5, 2017\n```\n\nAnd this on your French site:\n\n```html\n5 janv. 2017\n```\n\nCheck out the [complete list of available macro-formats](https://carbon.nesbot.com/guide/getting-started/localization.html#iso-format-available-replacements).\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n\n[carbon]: http://carbon.nesbot.com\n"
  },
  {
    "path": "content/collections/modifiers/join.md",
    "content": "---\nid: 9dfc5020-3d14-4774-a1f6-d82d051cb964\nblueprint: modifiers\nmodifier_types:\n  - string\n  - array\n  - utility\ntitle: Join\n---\nTurn an array into a string by gluing together all the data with any specified delimiter. It uses a comma by default.\n\n```yaml\ntasks:\n  - take a shower\n  - brush hair\n  - clip toenails\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ tasks | join }}\n{{ tasks | join(\" + \") }} = ready\n```\n::tab blade\n```blade\n{{ Statamic::modify($tasks)->join() }}\n{{ Statamic::modify($tasks)->join(' + ') }} = ready\n```\n::\n\n```html\ntake a shower, brush hair, clip toenails\ntake a shower + brush hair + clip toenails = ready\n```\n"
  },
  {
    "path": "content/collections/modifiers/kebab.md",
    "content": "---\nid: eef6642f-053a-4720-a373-78b950d949f2\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Kebab Case\n---\nConverts a string into `kebab-case`.\n\n```yaml\nstring: statamicIsAwesome\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | kebab }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->kebab() }}\n```\n::\n\n```html\nstatamic-is-awesome\n```\n"
  },
  {
    "path": "content/collections/modifiers/key_by.md",
    "content": "---\nid: 66b59b24-e908-a7df-fe01-f10e79487d22\nblueprint: modifiers\ntitle: 'Key By'\nmodifier_types:\n  - array\n---\nRe-keys a numerically indexed array using the value of a given key on each item. Handy when you want to target specific items by name instead of looping, like the `fields` array inside a form tag.\n\n```yaml\nfields:\n  -\n    handle: name\n    display: 'Your Name'\n  -\n    handle: email\n    display: 'Email Address'\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ rekeyed = fields | key_by('handle') }}\n\n<div class=\"name-field\">{{ rekeyed:name:display }}</div>\n<div class=\"email-field\">{{ rekeyed:email:display }}</div>\n```\n::tab blade\n```blade\n@php $rekeyed = Statamic::modify($fields)->keyBy('handle')->fetch(); @endphp\n\n<div class=\"name-field\">{{ $rekeyed['name']['display'] }}</div>\n<div class=\"email-field\">{{ $rekeyed['email']['display'] }}</div>\n```\n::\n\n```html\n<div class=\"name-field\">Your Name</div>\n<div class=\"email-field\">Email Address</div>\n```\n\n:::tip\nIf two items share the same key, the last one wins. Pick a key that's unique across the array (like `handle` or `id`).\n:::\n"
  },
  {
    "path": "content/collections/modifiers/keys.md",
    "content": "---\nid: 9197be05-5e2d-400f-a0f0-c52a4d460e60\nblueprint: modifiers\ntitle: Keys\nmodifier_types:\n  - array\n  - utility\n---\nRetrieves just the keys from the given array.\n\n```yaml\nthe_team:\n    jack: Jack McDade\n    jason: Jason Varga\n    jesse: Jesse Leite\n    joshua: Joshua Blum\n    duncan: Duncan McClean\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ the_team | keys }}\n```\n::tab blade\n```blade\n<?php\n  $keys = Statamic::modify($the_team)->keys()->fetch();\n?>\n```\n::\n\n```yaml\n- jack\n- jason\n- jesse\n- joshua\n- duncan\n```\n"
  },
  {
    "path": "content/collections/modifiers/last.md",
    "content": "---\nid: 4444ed5b-b543-424b-b7cf-a1eeff0213f9\nblueprint: modifiers\nmodifier_types:\n  - markup\n  - string\n  - utility\n  - array\ntitle: Last\n---\nReturns the last X characters of a string, where X is any positive integer, or the last item in an array.\n\n```yaml\ntitle: 2015 Denver Nuggets\narray:\n  - Sonic\n  - Knuckles\n  - Tails\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | last(7) }}\n{{ array | last }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->last(7) }}\n{{ Statamic::modify($array)->last() }}\n```\n::\n\n```html\nNuggets\nTails\n```\n"
  },
  {
    "path": "content/collections/modifiers/lcfirst.md",
    "content": "---\nid: 638e875e-2cc8-4b7b-953a-4f1a44c76e4d\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Lcfirst\n---\nConverts the first character of the supplied string to lower case.\n\n```yaml\ntitle: Wow\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | lcfirst }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->lcfirst() }}\n```\n::\n\n```html\nwow\n```\n"
  },
  {
    "path": "content/collections/modifiers/length.md",
    "content": "---\nid: 9002885e-20e9-4d1c-8396-1c8011076d2c\nblueprint: modifiers\nmodifier_types:\n  - array\n  - string\n  - utility\ntitle: Length\n---\nReturns the number of items in an array or characters in a string.\n\n```yaml\narray:\n  - Taylor Swift\n  - Left Shark\n  - Leroy Jenkins\nstring: LEEEEROOOYYYY JEEENKINNNSS!\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ array | length }}\n{{ string | length }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($array)->length() }}\n{{ Statamic::modify($string)->length() }}\n```\n::\n\n```html\n3\n27\n```\n"
  },
  {
    "path": "content/collections/modifiers/limit.md",
    "content": "---\nid: a3c9e1c5-10ec-44da-b8d8-fdc603fce5a3\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Limit\n---\nLimits the number of items returned in an array.\n\n```yaml\nplaylist:\n  - Emancipator\n  - Gong Gong\n  - Possom Posse\n  - Justin Bieber\n```\n\nUse with the pipe syntax to continue chaining in a single tag like so:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ playlist | limit(2) | join }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($playlist)->limit(2)->join() }}\n```\n::\n\n```html\nEmancipator, Gong Gong\n```\n\nOr using the parameter syntax:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ playlist | limit(2) }}\n    <li>{{ value }}</li>\n{{ /playlist }}\n```\n::tab blade\n```blade\n<?php\n  $limitedPlaylist = Statamic::modify($playlist)->limit(2);\n?>\n\n@foreach ($limitedPlaylist as $item)\n  <li>{{ $item }}</li>\n@endforeach\n```\n::\n\n```html\n<li>Emancipator</li>\n<li>Gong Gong</li>\n```\n"
  },
  {
    "path": "content/collections/modifiers/link.md",
    "content": "---\nid: 16312447-a597-4a98-9726-8e97718c9788\nblueprint: modifiers\nmodifier_types:\n  - markup\nattributes: true\ntitle: Link\n---\nGenerate an HTML link element with the value as `href`.\n\n```yaml\nneat_site: http://example.com\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ neat_site | link }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($neat_site)->link() }}\n```\n::\n\n```html\n<a href=\"http://example.com\">http://example.com</a>\n```\n"
  },
  {
    "path": "content/collections/modifiers/list.md",
    "content": "---\nid: d8a8568c-bb93-4e84-8d30-e527b3b02876\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\ntitle: List\nrelated_entries:\n  - 6866c25b-1266-4908-8325-dce4e5146f5b\n  - eed4c5bc-0923-4f54-ad37-ca9a3384e1e0\n  - cbab1bb5-302e-499d-badb-f154dbae751d\n  - 9dfc5020-3d14-4774-a1f6-d82d051cb964\n---\nTurn a simple array into a comma delimited list with no comma after the last item.\n\n```yaml\nthings:\n  - batman\n  - zombies\n  - scrunchies\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ things | list }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($things)->list() }}\n```\n::\n\n```html\nbatman, zombies, scrunchies\n```"
  },
  {
    "path": "content/collections/modifiers/lower.md",
    "content": "---\nid: 14ef311c-b49a-45af-a0aa-e80f68793ba8\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Lower\n---\nConverts all characters in the string to lowercase.\n\n```yaml\nyelling: I DON'T KNOW WHAT WE'RE YELLING ABOUT\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ yelling | lower }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($yelling)->lower() }}\n```\n::\n\n```html\ni don't know what we're yelling about\n```\n"
  },
  {
    "path": "content/collections/modifiers/macro.md",
    "content": "---\nid: c0ab61af-c0c2-4ead-b64f-2e23325e917f\nblueprint: modifiers\nmodifier_types:\n  - string\n  - array\n  - utility\n  - markup\ntitle: Macro\n---\nMacro is a very special modifier. It performs no modifications of its own, but rather lets you create reusable groups of modifiers and give them a name. Those groups are each called a \"macro\" and are stored in your `resources/macros.yaml` file. Keep in mind that the order of modifiers within a macro matter, the same way as regular modifiers.\n\n\n```yaml\n# /resources/macros.yaml\nheadline:\n  title: true\n  widont: true\n  remove_right: .\n\n# Page content\ntitle: Actually i don't know what we're talking about.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | macro('headline') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($title)->macro('headline') }}\n```\n::\n\n```html\nActually I Don't Know What We're Talking&nbsp;About\n```\n\nWhen passing multiple parameters to a modifier, you'll need to pop down into a simple list:\n\n```yaml\n# /resources/macros.yaml\nexcerpt:\n  safe_truncate:\n    - 175\n    - ...\n```\n\nThis is equivalent to:\n\n::tabs\n::tab antlers\n```antlers\n{{ content | safe_truncate(175, '...') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($content)->safeTruncate([175, '...']) }}\n```\n::\n"
  },
  {
    "path": "content/collections/modifiers/mailto.md",
    "content": "---\nid: 65bcc454-2731-4f83-97cf-03659fb38db5\nblueprint: modifiers\nmodifier_types:\n  - markup\nattributes: true\ntitle: Mailto\n---\nGenerate a `mailto` link element with the value as the email address. If it's _not_ an email address, it's going to be one busted link.\n\n```yaml\nholler: holler@example.com\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ holler | mailto }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($holler)->mailto() }}\n```\n::\n\n```html\n<a href=\"mailto:holler@example.com\">holler@example.com</a>\n```\n"
  },
  {
    "path": "content/collections/modifiers/mark.md",
    "content": "---\nid: 390ae639-0964-4d51-b7ed-efd3b810913c\nblueprint: modifiers\ntitle: Mark\nmodifier_types:\n  - string\n  - utility\n---\nWrap any matched words in `<mark>` tags to highlight them on the page.\n\n```yaml\ndescription: This cat video is the okayest thing ever.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ description | mark('cat thing') }}\n{{ description | mark('video', 'class:highlight') }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($description)->mark('cat thing') !!}\n{!! Statamic::modify($description)->mark(['video', 'class:highlight']) !!}\n```\n::\n\n```html\nThis <mark>cat</mark> video is the okayest <mark>thing</mark> ever.\n```\n\n```html\nThis cat <mark class=\"highlight\">video</mark> is the okayest thing ever.\n```\n\nIf no words are specified the `get:q` value will be used by default.\n\n:::tip\nThis modifier expects HTML input. While most plain text strings will work just fine you should escape the value with the `entities` modifier if your text contains less than or greater than symbols: `{{ plain_text | entities | mark }}`\n:::"
  },
  {
    "path": "content/collections/modifiers/markdown.md",
    "content": "---\nid: 39dcb2b1-a319-4a0b-b7d0-5c7a1b8aa31b\nblueprint: modifiers\nmodifier_types:\n  - markup\nattributes: true\ntitle: Markdown\n---\nTransform a string with [Markdown][markdown].\n\n```yaml\nquote: You can't wait for inspiration. **You have to go after it with a club.**\n\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ quote | markdown }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($quote)->markdown() !!}\n```\n::\n\n```html\n<p>\n    You can't wait for inspiration. <strong>You have to go after it with a club.</strong>\n</p>\n```\n\n[markdown]: https://daringfireball.net/projects/markdown/\n"
  },
  {
    "path": "content/collections/modifiers/md5.md",
    "content": "---\nid: 3d293101-4bbf-45ab-9291-a37fa898d2de\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Md5\n---\nCreates an md5 hash of a variable.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ \"hello\" | md5 }}\n```\n::tab blade\n```blade\n{{ Statamic::modify('hello')->md5() }}\n\n-- or --\n\n{{ md5('hello') }}\n```\n::\n\n```html\n5d41402abc4b2a76b9719d911017c592\n```\n"
  },
  {
    "path": "content/collections/modifiers/merge.md",
    "content": "---\nid: d15bda69-36ee-4871-82b2-e66447868643\nblueprint: modifiers\ntitle: Merge\n---\n\nMerge an array variable with another array variable.\n\n```yaml\ngood_ideas:\n  - Exercise regularly\n  - Brush your teeth\n  - Use Oxford Commas\nbad_ideas:\n  - Bath in beans\n  - Wear sandpaper underwear\n  - Eat turtle shells\n```\n\nIn this template example we'll merge the two arrays and then pull out a single random item from the combined list. For fun!\n\n::tabs\n\n::tab antlers\n```antlers\n<h2>Picking a random idea!</h2>\n{{ good_ideas | merge($bad_ideas) | sort(\"random\") | limit(1) }}\n<p>{{ value }}</p>\n{{ /good_ideas }}\n```\n::tab blade\n```blade\n<?php\n  $merged = Statamic::modify($good_ideas)\n    ->merge($bad_ideas)\n    ->sort('random')\n    ->limit(1)\n    ->fetch();\n?>\n\n<h2>Picking a random idea!</h2>\n@foreach ($merged as $item)\n  <p>{{ $item }}</p>\n@endforeach\n```\n::\n\n```\n<p>Use Oxford Commas</p>\n```\n"
  },
  {
    "path": "content/collections/modifiers/minutes_ago.md",
    "content": "---\nid: 06027289-825e-4205-bd3a-f375e26ab81e\nblueprint: modifiers\nmodifier_types:\n  - date\ndate: 'October 1 2015 8:30:am'\ntitle: 'Minutes Ago'\nrelated_entries:\n  - e73f1574-732e-4a74-be47-37e1fddb05d6\n  - 603701ba-5da7-4ec8-abe5-5bc9fe6861ea\n  - 7ba53a64-0266-4752-af5b-282a40dd11fa\n  - 6fcbfa5c-854e-4541-9955-505eca0d6bf7\n  - 6ebb6c28-d1f3-4362-92a0-8a16b5c9cd51\n  - 811c1cf5-797f-4e77-af92-fde6c03e96d2\n---\nReturns the number of minutes since a given date variable. Statamic will attempt to parse any string as a date, but try to keep it in the least ambiguous date format possible.\n\n```yaml\ndate: October 1 2015 8:30:am\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | minutes_ago }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($date)->minutesAgo() }}\n```\n::\n\n```html\n{{ test_date | minutes_ago }}\n```"
  },
  {
    "path": "content/collections/modifiers/mod.md",
    "content": "---\nid: 10875a6a-9cb8-4e2a-9d39-0d8e4059d815\nblueprint: modifiers\nmodifier_types:\n  - math\ntitle: Mod\n---\nGet the modulus value (remainder after division) of a value split by another numeric value. Pass an integer or the name of a second variable as the parameter. Also supports `%` as shorthand.\n\n```yaml\nbottles: 3\nglasses: 14\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glasses | mod(14) }}\n{{ glasses | mod($bottles) }}\n{{ glasses | %($bottles) }}\n\n```\n::tab blade\n```blade\n{{ Statamic::modify($glasses)->mod(14) }}\n{{ Statamic::modify($glasses)->mod($bottles) }}\n```\n::\n\n```html\n0\n2\n2\n```\n"
  },
  {
    "path": "content/collections/modifiers/modify_date.md",
    "content": "---\nid: 18596c62-5535-41a8-91c4-b5769fb11085\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: 'Modify Date'\n---\nAlters a timestamp by incrementing or decrementing in a format accepted by PHP's native [`strtotime()`](http://php.net/manual/en/function.strtotime.php) method.\n\n\n```yaml\ndate: January 1, 2000\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | modify_date(\"-1 day\") }}\n{{ date | modify_date(\"next Sunday\") }}\n{{ date | modify_date(\"+3 months\") }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($date)->modifyDate('-1 day') }}\n{{ Statamic::modify($date)->modifyDate('next Sunday') }}\n{{ Statamic::modify($date)->modifyDate('+3 months') }}\n```\n::\n\n```html\nDecember 31, 1999\nJanuary 2, 2000\nApril 1, 2000\n```\n\n:::tip\nAs of Statamic 5, this modifier will return a copy of the Date. Earlier versions would **modify the variable directly** which will be passed onto any additional modifiers.\n:::\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/months_ago.md",
    "content": "---\nid: 7ba53a64-0266-4752-af5b-282a40dd11fa\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: 'Months Ago'\nrelated_entries:\n  - e73f1574-732e-4a74-be47-37e1fddb05d6\n  - 603701ba-5da7-4ec8-abe5-5bc9fe6861ea\n  - 06027289-825e-4205-bd3a-f375e26ab81e\n  - 6fcbfa5c-854e-4541-9955-505eca0d6bf7\n  - 811c1cf5-797f-4e77-af92-fde6c03e96d2\n  - 6ebb6c28-d1f3-4362-92a0-8a16b5c9cd51\n---\nReturns the number of months since a given date variable. Statamic will attempt to parse any string as a date, but try to keep it in the least ambiguous date format possible.\n\n```yaml\ndate: October 1 2017\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | months_ago }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($date)->monthsAgo() }}\n```\n::\n\n```html\n{{ test_date | months_ago }}\n```"
  },
  {
    "path": "content/collections/modifiers/multiply.md",
    "content": "---\nid: b1241017-a321-42ad-b550-a49ae8b3a805\nblueprint: modifiers\nmodifier_types:\n  - math\ntitle: Multiply\n---\nMultiply a value or another variable to your variable. Pass an integer or the name of a second variable as the parameter. Also supports `*` as shorthand.\n\n```yaml\nsmiles: 3\nwinks: 4\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ smiles | multiply(10) }}\n{{ smiles | multiply($winks) }}\n{{ smiles | *($winks) }}\n\n```\n::tab blade\n```blade\n{{ Statamic::modify($smiles)->multiply(10) }}\n{{ Statamic::modify($smiles)->multiply($winks) }}\n```\n::\n\n```html\n30\n12\n12\n```\n"
  },
  {
    "path": "content/collections/modifiers/nl2br.md",
    "content": "---\nid: ecbf79c1-be5d-412e-a677-30a847cfffa6\nblueprint: modifiers\nmodifier_types:\n  - string\n  - markup\nattributes: true\ntitle: Nl2br\n---\nReplaces line breaks with `<br>` tags.\n\n```yaml\nsummary: |\n  This is a summary\n  on multiple lines\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ summary | nl2br }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($summary)->nl2br() !!}\n```\n::\n\n```html\nThis is a summary\non multiple lines\n```\n"
  },
  {
    "path": "content/collections/modifiers/obfuscate.md",
    "content": "---\nid: 84aca464-65b1-4cc9-8bc9-e64b33797579\nblueprint: modifiers\nmodifier_types:\n  - markup\n  - utility\nattributes: true\ntitle: Obfuscate\n---\nObfuscates a string with special characters making it hard for spam bots to sniff out and scrape off your site. Still appears like the same string to the reader. This is usually used for email addresses.\n\n```yaml\nmagic_word: Abracadabra\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ magic_word | obfuscate }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($magic_word)->obfuscate() }}\n```\n::\n\n```html\n# visibly appears as Abracadabra\nA&#98;r&#97;&#99;&#x61;d&#97;&#98;&#114;&#97;\n```\n"
  },
  {
    "path": "content/collections/modifiers/obfuscate_email.md",
    "content": "---\nid: 536ac4b3-bfc7-4ced-8c83-d389fb94a262\nblueprint: modifiers\nmodifier_types:\n  - markup\nattributes: true\ntitle: 'Obfuscate Email'\n---\nObfuscates an email address with special characters making it hard for spam bots to sniff out and scrape off your site. Still reads like an email address as far as readers are concerned.\n\n```yaml\nholler: holler@example.com\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ holler | obfuscate_email }}\n```\n::tab blade\n{{ Statamic::modify($holler)->obfuscateEmail() }}\n::\n\n```html\n# output appears as holler@example.com\n&#104;o&#108;le&#x72;&#x40;&#x65;&#x78;&#x61;&#109;&#x70;&#108;&#101;&#x2e;&#x63;&#x6f;m\n```\n"
  },
  {
    "path": "content/collections/modifiers/offset.md",
    "content": "---\nid: 9433b8cd-b2e0-4fbf-85bd-85edf317efa4\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Offset\n---\nOffsets the items returned in an array.\n\n```yaml\nplaylist:\n  - Emancipator\n  - Gong Gong\n  - Possom Posse\n  - Justin Bieber\n```\n\nUse with the pipe syntax to continue chaining in a single tag like so:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ playlist | offset(1) | join }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($playlist)->offset(1)->join() }}\n```\n::\n\n```html\nGong Gong, Possom Posse, Justin Bieber\n```\n\nOr using the parameter syntax:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ playlist | offset(1) }}\n    <li>{{ value }}</li>\n{{ /playlist }}\n```\n\n::tab blade\n\n```blade\n@foreach (Statamic::modify($playlist)->offset(1)->fetch() as $value)\n    <li>{{ $valuie }}</li>\n@endforeach\n```\n::\n\n```html\n<li>Gong Gong</li>\n<li>Possom Posse</li>\n<li>Justin Bieber</li>\n```\n"
  },
  {
    "path": "content/collections/modifiers/ol.md",
    "content": "---\nid: 327f4a3b-04d4-4069-881a-fe50ddb9be23\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\ntitle: OL\n---\nTurn an array into an HTML ordered list element.\n\n```yaml\nfood:\n  - sushi\n  - broccoli\n  - kale\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ food | ol }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($food)->ol() !!}\n```\n::\n\n```html\n<ol>\n  <li>sushi</li>\n  <li>broccoli</li>\n  <li>kale</li>\n</ol>\n```\n"
  },
  {
    "path": "content/collections/modifiers/option_list.md",
    "content": "---\nid: 6866c25b-1266-4908-8325-dce4e5146f5b\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: 'Option List'\nrelated_entries:\n  - d8a8568c-bb93-4e84-8d30-e527b3b02876\n  - eed4c5bc-0923-4f54-ad37-ca9a3384e1e0\n  - cbab1bb5-302e-499d-badb-f154dbae751d\n  - 9dfc5020-3d14-4774-a1f6-d82d051cb964\n---\nTurn an array into a pipe-delimited string. Useful when passing an array of things into a parameter. Generally unnecessary though as most parameters can now accept a wide range of datatypes.\n\n```yaml\ncollections:\n  - blog\n  - news\n  - wigs\n```\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection from=\"{collections|option_list}\" }}\n```\n::tab blade\n```blade\n<statamic:collection\n  :from=\"Statamic::modify($collections)->optionList()->fetch()\"\n>\n\n</statamic:collection>\n```\n::\n\nCan also be used by its alias, [`piped`](/modifiers/piped).\n"
  },
  {
    "path": "content/collections/modifiers/output.md",
    "content": "---\nid: b5409a41-cc62-4ca1-bb8a-f6700b2c8c29\nblueprint: modifiers\ntitle: Output\nmodifier_types:\n  - asset\n  - utility\n---\nGiven the URL to an Asset file, returns the string output of an Asset file's contents. This is primarily useful for rendering inline SVGs, but could also be used to display a lot of gibberish to your users if you're into that kind of thing.\n\n```yaml\nicon: /img/icons/heart.svg\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ icon | output }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($icon)->output() !!}\n```\n::\n\n```html\n<svg width=\"52px\" height=\"50px\" viewBox=\"0 0 52 50\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n    <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n        <g id=\"Icons\" transform=\"translate(-965.000000, -2883.000000)\" fill=\"#1A1718\">\n            <g id=\"Heart\" transform=\"translate(965.000000, 2883.000000)\">\n                <path d=\"M51.912,15.338 C51.153,6.983 45.24,0.923 37.841,0.923 C32.911,0.923 28.396,3.576 25.856,7.828 C23.34,3.522 19.011,0.922 14.159,0.922 C6.76,0.922 0.847,6.982 0.088,15.337 C0.028,15.706 -0.218,17.647 0.53,20.814 C1.608,25.383 4.099,29.537 7.729,32.827 L25.845,49.267 L44.271,32.829 C47.901,29.538 50.392,25.384 51.47,20.815 C52.218,17.649 51.972,15.707 51.912,15.338 L51.912,15.338 Z M16,9 C11.589,9 8,12.589 8,17 C8,17.553 7.553,18 7,18 C6.447,18 6,17.553 6,17 C6,11.486 10.486,7 16,7 C16.553,7 17,7.447 17,8 C17,8.553 16.553,9 16,9 L16,9 Z\"></path>\n            </g>\n        </g>\n    </g>\n</svg>\n```\n"
  },
  {
    "path": "content/collections/modifiers/overlaps.md",
    "content": "---\nid: d66ba89d-2451-497d-bd6d-37710e9de171\nblueprint: modifiers\nmodifier_types:\n  - array\n  - conditions\ntitle: Overlaps\n---\nCheck if any values in an array are found in another array. Returns `true` if at least one value matches, otherwise `false`.\n\nThe first parameter is the \"needle\" to find in the \"haystack\". It will read from the context if there is a matching variable, otherwise it will use the parameter as the value. The needle can be a single value or an array.\n\nThis is a loose comparison that mirrors how [`whereJsonOverlaps()`](https://laravel.com/docs/queries#json-where-clauses) works on the query builder side.\n\n```yaml\nshopping_list:\n  - eggs\n  - flour\n  - beef jerky\nwant:\n  - eggs\n  - oatmeal\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if shopping_list | overlaps('want') }} GOT SOMETHING! {{ /if }}\n{{ if shopping_list | overlaps('flour') }} GOT IT! {{ /if }}\n{{ if shopping_list | overlaps('kale') }} Not today. {{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($shopping_list)->overlaps('want')->fetch()) GOT SOMETHING! @endif\n@if (Statamic::modify($shopping_list)->overlaps('flour')->fetch()) GOT IT! @endif\n@if (Statamic::modify($shopping_list)->overlaps('kale')->fetch()) Not today. @endif\n```\n::\n\n```html\nGOT SOMETHING!\nGOT IT!\n```\n"
  },
  {
    "path": "content/collections/modifiers/pad.md",
    "content": "---\nid: 0a1595bc-5b41-401c-bb9c-45c35e8e5d7c\nblueprint: modifiers\nmodifier_types:\n  - array\ntitle: Pad\n---\nPad an array to a given number of items with a value. By default the value is null, but you can specify it as the second parameter.\n\n```yaml\nepic_meal_time:\n  - jack daniels\n  - bacon strips\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ epic_meal_time | pad(4, \"bacon strips\") }}\n    {{ value }}\n{{ /epic_meal_time }}\n```\n::tab blade\n```blade\n<?php\n  $meals = Statamic::modify($epic_meal_time)\n    ->pad(4, 'bacon strips')\n    ->fetch();\n?>\n\n@foreach ($meals as $meal)\n  {{ $meal }}\n@endforeach\n```\n::\n\n```html\njack daniels\nbacon strips\nbacon strips\nbacon strips\n```\n"
  },
  {
    "path": "content/collections/modifiers/parse_url.md",
    "content": "---\nid: e1c41970-5894-4c23-a5af-08f4dd1901ed\nblueprint: modifiers\ntitle: 'Parse Url'\nmodifier_types:\n  - string\n  - utility\n---\nGet information about a URL.\n\n``` yaml\nurl: 'http://example.com/path?query=1'\n```\n\n::tabs\n\n::tab antlers\n``` antlers\n{{ url | parse_url }}\n{{ url | parse_url('host') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($url)->parseUrl() }}\n{{ Statamic::modify($url)->parseUrl('host') }}\n```\n::\n\n```php\n[\n  'scheme'   => 'http',\n  'host'     => 'example.com',\n  'path'     => '/path',\n  'query'    => 'query=1',\n]\n```\n\n```\nexample.com\n```\n"
  },
  {
    "path": "content/collections/modifiers/partial.md",
    "content": "---\nid: 23d51738-5043-49e3-8ca3-d5848427216f\nblueprint: modifiers\nmodifier_types:\n  - utility\n  - string\n  - array\ntitle: Partial\n---\nInject a variable's data into a partial and render it without any page scopes whatsoever. This is really just syntactical sugar, but it _is_ delicious.\n\n```yaml\ndata:\n  title: Bubble Guppies\n  content: Science died a little bit today.\n```\n\n```\n<!-- /site/themes/<your_theme>/partials/demo.html -->\n<h1>{{ title }}</h1>\n{{ content | markdown }}\n\n<!-- Template Markup -->\n{{ data | partial('demo') }}\n```\n\n```html\n<h1>Bubble Guppies</h1>\n<p>Science died a little bit today.</p>\n```\n"
  },
  {
    "path": "content/collections/modifiers/pathinfo.md",
    "content": "---\nid: 1fb22c41-eb1e-4a14-98fc-9d3158871476\nblueprint: modifiers\ntitle: Pathinfo\nmodifier_types:\n  - string\n  - utility\n---\nGet information about a file path.\n\n``` yaml\npath: '/local/file/example.pdf'\n```\n\n::tabs\n\n::tab antlers\n``` antlers\n{{ path | pathinfo }}\n{{ path | pathinfo('extension') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($path)->pathinfo() }}\n{{ Statamic::modify($path)->pathinfo('extension') }}\n```\n::\n\n```php\n[\n    'dirname'   => '/local/file',\n    'basename'  => 'example.pdf',\n    'filename'  => 'example',\n    'extension' => 'pdf',\n]\n```\n\n```\npdf\n```\n"
  },
  {
    "path": "content/collections/modifiers/piped.md",
    "content": "---\nid: 124c1470-a9da-11e7-8f1a-0800200c9a66\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Piped\n---\nAlias of [option_list](/modifiers/option_list)\n"
  },
  {
    "path": "content/collections/modifiers/pluck.md",
    "content": "---\nid: 17075d87-df8f-4ab7-b957-67cdae80ac0a\nblueprint: modifiers\ntitle: Pluck\nmodifier_types:\n  - array\n  - utility\n---\nRetrieves the values of a given `key` from each item.\n\n```yaml\ngames:\n  -\n    feeling: love\n    title: Dominion\n  -\n    feeling: love\n    title: Netrunner\n  -\n    feeling: hate\n    title: Chutes and Ladders\n```\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ games | pluck('title') }}\n```\n::tab blade\n```blade\n<?php\n  $plucked = Statamic::modify($games)\n    ->pluck('title')\n    ->fetch();\n?>\n```\n::\n\n```yaml\ngames:\n  - Dominion\n  - Netrunner\n  - Chutes and Ladders\n```"
  },
  {
    "path": "content/collections/modifiers/plural.md",
    "content": "---\nid: d464d979-7e57-40a6-8892-a08d08bd7ccf\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Plural\n---\nGet the plural form of an English word. Accepts a numerical parameter, either as a literal value or a variable, to control plurality. It's important to note that you should use the singular form of the word to ensure the best results.\n\n```yaml\nshopping_list:\n  - item: pickle\n    quantity: 1\n  - item: apple\n    quantity: 12\n  - item: donut\n    quantity: 500\n```\n\n::tabs\n\n::tab antlers\n```antlers\nPlease pick up the following items:\n{{ shopping_list }}\n  - {{ quantity }} {{ item | plural($quantity) }}.\n{{ /shopping_list }}\n```\n::tab blade\n```blade\n@foreach ($shopping_list as $item)\n  - {{ $item['quantity'] }} {{ Statamic::modify($item)->plural($item['quantity']) }}\n@endforeach\n```\n::\n\n```html\nPlease pick up the following items:\n- 1 pickle\n- 3 apples\n- 500 donuts\n```\n"
  },
  {
    "path": "content/collections/modifiers/random.md",
    "content": "---\nid: 48178377-da99-4754-ae9e-d294720cff33\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Random\nrelated_entries:\n  - 63acdaa6-9724-4179-b210-ea5d507672e9\n---\n## Overview\nPicks a _single_ random item from an array or collection.\n\nIf you are trying to get _multiple_ random items, consider the [shuffle modifier](/modifiers/shuffle).\n\n## Example\n\n### The YAML\n```yaml\narr:\n  - Soda\n  - Pop\n  - Coke\n```\n\n### The Template\n\n::tabs\n\n::tab antlers\n```antlers\n{{ arr | random }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($arr)->random() }}\n```\n::\n\n### The Output\n```\nSoda (maybe)\n```\n"
  },
  {
    "path": "content/collections/modifiers/raw.md",
    "content": "---\nid: e100a366-b69c-4d59-bec7-eac18c0b286b\nblueprint: modifiers\nmodifier_types:\n  - string\n  - array\n  - utility\ntitle: Raw\n---\n## Overview\nReturns the [unaugmented](/augmentation) version of the variable.\n\n## Example\n\nIf you had a Markdown field and wanted to render the actual Markdown-formatted text instead of rendered HTML, you can do this:\n\n### The YAML\n```yaml\nmarkdown_field: >\n  # How to Breakdance\n\n  First you do the fancy kicky thing with your feets, and then\n  you flail your legs around like a battery operated fan at a hot\n  summer ballgame.\n```\n\n### The Template\n\n::tabs\n\n::tab antlers\n```antlers\n{{ markdown_field | raw }}\n```\n::tab blade\n```blade\n{{ $markdown_field->raw() }}\n```\n::\n\n### The Output\n```\n# How to Breakdance\n\nFirst you do the fancy kicky thing with your feets,\nand then you flail your legs around like a battery\noperated fan at a hot summer ballgame.\n```\n"
  },
  {
    "path": "content/collections/modifiers/rawurlencode.md",
    "content": "---\nid: a4bf0c06-9210-4200-881b-feb9011ea2f7\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Raw URL Encode\n---\nURL-encode a variable according to [RFC 3986][rfc-3986].\n```yaml\nexample: please and thank you/Mommy\n```\n\n::tabs\n\n::tab antlers\n```antlers\nhttp://example.com/{{ example | rawurlencode }}\n```\n::tab blade\n```blade\nhttps://example.com/{{ Statamic::modify($example)->rawurlencode() }}\n```\n::\n\n```html\nhttp://example.com/please%20and%20thank%20you%2FMommy\n```\n\nIf you don't want forward slashes (`/`) to be encoded, use the [rawurlencode_except_slashes](/modifiers/rawurlencode_except_slashes) modifier instead.\n\n[rfc-3986]: http://php.net/manual/en/function.rawurlencode.php\n"
  },
  {
    "path": "content/collections/modifiers/rawurlencode_except_slashes.md",
    "content": "---\nid: b3da88d2-7251-4827-aa88-e746647ee00b\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Raw URL Encode Except Slashes\n---\nURL-encode a variable according to [RFC 3986][rfc-3986]. Just like [rawurlencode](/modifiers/rawurlencode), but doesn't encode forward slashes (`/`).\n```yaml\nexample: please and thank you/Mommy\n```\n\n::tabs\n\n::tab antlers\n```antlers\nhttp://example.com/{{ example | rawurlencode_except_slashes }}\n```\n::tab blade\n```blade\nhttps://example.com/{{ Statamic::modify($example)->rawurlencode_except_slashes() }}\n```\n::\n\n```html\nhttp://example.com/please%20and%20thank%20you/Mommy\n```\n\n[rfc-3986]: http://php.net/manual/en/function.rawurlencode.php\n"
  },
  {
    "path": "content/collections/modifiers/ray.md",
    "content": "---\nid: 1bb00dc7-f4b2-4bba-aaf9-45e3a2a19518\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: Ray\nrelated_entries:\n    - 12de1a6c-e8be-4703-81a3-fc270311bc84\n    - 985dc29c-fe71-464e-bb83-4f3f2aa455c0\n---\nSend a variable to Spatie's [Ray](https://myray.app) app.\n\nYou can pass a string with a color name as parameter to get it colored in Ray. Note that you need to have the [spatie/laravel-ray](https://github.com/spatie/laravel-ray) package installed.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ your_field | ray }}\n{{ your_field | ray('red'} }\n```\n::tab blade\n```blade\n@php(Statamic::modify($your_field)->ray())\n@php(Statamic::modify($your_field)->ray('red'))\n\n-- or --\n\n@php(ray($your_field))\n@php(ray($your_field)->red())\n```\n::\n"
  },
  {
    "path": "content/collections/modifiers/read_time.md",
    "content": "---\nid: 6a0abbe0-860a-4e30-b1e7-ecac343149ce\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Read Time'\n---\nProvide an estimate of the read time in minutes based on a given number of words per minute. Defaults to 200/wpm.\n\n```yaml\n---\ntitle: A long post\n---\nPretend there are lots of words here...\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<h1>{{ title }}</h1>\n<p>{{ content | read_time(180) }} min</p>\n```\n::tab blade\n```blade\n<h1>{{ $title }}</h1>\n<p>{{ Statamic::modify($content)->readTime(180) }} min</p>\n```\n::\n\n```html\n<h1>A long post</h1>\n<p>10 min</p>\n```\n"
  },
  {
    "path": "content/collections/modifiers/regex_mark.md",
    "content": "---\nid: 053e62e4-be01-4e82-944d-fe72b18a1a7c\nblueprint: modifiers\ntitle: 'Regex Mark'\nmodifier_types:\n  - string\n  - utility\n---\nWrap any regex matches in `<mark>` tags to highlight them on the page.\n\n```yaml\ndescription: This cat video is the okayest thing ever.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ description | regex_mark('cat video|thing') }}\n{{ description | regex_mark('video', 'class:highlight') }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($description)->regexMark('cat video|thing') !!}\n{!! Statamic::modify($description)->regexMark(['video', 'class:highlight']) !!}\n```\n::\n\n```html\nThis <mark>cat video</mark> is the okayest <mark>thing</mark> ever.\n```\n\n```html\nThis cat <mark class=\"highlight\">video</mark> is the okayest thing ever.\n```\n\n:::tip\nThis modifier expects HTML input. While most plain text strings will work just fine you should escape the value with the `entities` modifier if your text contains less than or greater than symbols: `{{ plain_text | entities | mark }}`\n:::"
  },
  {
    "path": "content/collections/modifiers/regex_replace.md",
    "content": "---\nid: 711d7eb2-8748-42a8-90c6-c91efb3ed818\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Regex Replace'\n---\nRun a find and replace regex on a string of content.\n\n```yaml\nmessage: 'This is a great video: https://www.youtube.com/watch?v=YO_spdAYjPk'\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ message | regex_replace('watch\\?v=[\\w-]+', 'watch?v=dQw4w9WgXcQ') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($message)->regexReplace(['watch\\?v=[\\w-]+', 'watch?v=dQw4w9WgXcQ']) }}\n```\n::\n\n```html\nCheck out this video: https://www.youtube.com/watch?v=eBGIQ7ZuuiU\n```\n\nGreat for when your client keeps putting YouTube links in their content and you want to, uh, help them out.\n"
  },
  {
    "path": "content/collections/modifiers/relative.md",
    "content": "---\nid: 40578328-3288-4c54-a475-8afad19a37e6\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: Relative\n---\nReturns a date difference in a nice, human readable, string format. This modifier will add a phrase after the difference value relative to the current date and the passed in date.\n\nYou can turn off the extra words \"ago\", \"until\", and so on by passing `true` as a parameter\n\nThe string will be localized into your current site locale.\n\n```yaml\npast_date: October 1 2020\nfuture_date: October 1 2024\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ past_date | relative }}\n{{ past_date | relative(true) }}\n{{ future_date | relative }}\n{{ future_date | relative(true) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($past_date)->relative() }}\n{{ Statamic::modify($past_date)->relative(true) }}\n{{ Statamic::modify($future_date)->relative() }}\n{{ Statamic::modify($future_date)->relative(true) }}\n```\n::\n\n```html\n2 years ago\n2 years\n1 year from now\n1 year\n```\n\n:::warning\nBy default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date.\n\nPlease refer to our [Timezones](/tips/timezones) guide for more information.\n:::\n"
  },
  {
    "path": "content/collections/modifiers/remove_left.md",
    "content": "---\nid: 71feed11-dcb7-4405-8f50-a17cb4c021ef\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Remove Left'\n---\nEnsures that the string never begins with a specified string.\n\n```yaml\ntwitter: @statamic\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"http://twitter.com/{{ twitter | remove_left('@') }}\">Twitter</a>\n```\n::tab blade\n```blade\n<a href=\"http://twitter.com/{{ Statamic::modify($twitter)->removeLeft('@') }}\">Twitter</a>\n```\n::\n\n```html\n<a href=\"http://twitter.com/statamic\">Twitter</a>\n```\n"
  },
  {
    "path": "content/collections/modifiers/remove_right.md",
    "content": "---\nid: ec6e096f-ee52-449e-96b5-e0d759f982f0\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Remove Right'\n---\nEnsures that the string never ends with a specified string.\n\n```yaml\nurls:\n  - http://statamic.com/\n  - http://laravel.com/\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ urls }}\n  {{ value | remove_right('/') }}\n{{ /urls}}\n```\n::tab blade\n```blade\n@foreach ($urls as $url)\n  {{ Statamic::modify($url)->removeRight('/') }}\n@endforeach\n```\n::\n\n```html\nhttp://statamic.com\nhttp://laravel.com\n```\n"
  },
  {
    "path": "content/collections/modifiers/repeat.md",
    "content": "---\nid: 828a52f2-469e-4a20-82c1-15fd4d0e568c\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: Repeat\n---\nRepeats a value any given number of times. For fun.\n\n```yaml\nlyric: can't touch this\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ lyric | repeat(3) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($lyric)->repeat(3) }}\n```\n::\n\n```html\ncan't touch this can't touch this can't touch this\n```\n"
  },
  {
    "path": "content/collections/modifiers/replace.md",
    "content": "---\nid: ab02d964-9a39-4f2b-ae87-d5248af9101e\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Replace\n---\nFind and replace all occurrences of a string with a totally different string.\n\n```yaml\ndescription: This cat video is the okayest thing ever.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ description | replace('cat', 'dog') }}\n```\n::tab blade\n{{ Statamic::modify($description)->replace(['cat', 'dog']) }}\n::\n\n```html\nThis dog video is the okayest thing ever.\n```\n"
  },
  {
    "path": "content/collections/modifiers/resolve.md",
    "content": "---\nid: de1fb94c-a0e5-43a9-8971-e9b839e373e8\nblueprint: modifiers\ntitle: Resolve\nmodifier_types:\n  - array\n  - utility\n  - relationship\n---\nResolves a query builder and returns either a specific item by key/index or the full array of results. Also works on arrays and collections.\n\nHandy for grabbing a single item out of a relationship field (like the first asset from an asset field) without having to loop.\n\n```yaml\ngallery:\n  - hero.jpg\n  - action-shot.jpg\n  - outtake.jpg\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<img src=\"{{ gallery | resolve(0):url }}\" />\n```\n::tab blade\n```blade\n<img src=\"{{ Statamic::modify($gallery)->resolve(0)->fetch()['url'] }}\" />\n```\n::\n\n```html\n<img src=\"/assets/hero.jpg\" />\n```\n\n## Without a key\n\nOmit the key to resolve the query builder into an array of all items. Useful when you want to pass the resolved data into another modifier.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ gallery | resolve | count }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($gallery)->resolve()->count() }}\n```\n::\n\n```html\n3\n```\n"
  },
  {
    "path": "content/collections/modifiers/reverse.md",
    "content": "---\nid: 5ee6e776-5361-4d87-9227-c0461e33853f\nblueprint: modifiers\nmodifier_types:\n  - array\n  - string\n  - utility\ntitle: Reverse\n---\nReverse the order of the characters in a string or the items in an array.\n\n```yaml\nstatus: repaid\norder_of_ceremony:\n  - photos\n  - service\n  - eat\n  - party\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ status | reverse }}\n{{ order_of_ceremony | reverse | list }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($status)->reverse() }}\n{{ Statamic::modify($status)->reverse()->list() }}\n```\n::\n\n```html\ndiaper\nparty, eat, service, photos\n```\n"
  },
  {
    "path": "content/collections/modifiers/round.md",
    "content": "---\nid: 5166ff7e-2fad-414c-b232-f04108c08900\nblueprint: modifiers\nmodifier_types:\n  - math\n  - utility\ntitle: Round\n---\nRounds a number to a specified precision (number of digits after the decimal point). Defaults to `0`, or whole numbers.\n\n```yaml\npi: 3.14159265359\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ pi | round }}\n{{ pi | round(2) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($pi)->round() }}\n{{ Statamic::modify($pi)->round(2) }}\n```\n::\n\n```html\n3\n3.14\n```\n"
  },
  {
    "path": "content/collections/modifiers/safe_truncate.md",
    "content": "---\nid: 1267d7b0-8a07-4103-9570-86327fb8e250\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: 'Safe Truncate'\n---\nTruncates the string to a given length (parameter 1), while ensuring that\nit does not split words. You can append a string with parameter 2, and if truncating occurs the string is further truncated so that it may be appended without exceeding the desired length.\n\n```yaml\nadvice: >\n  So, here’s some advice I wish I woulda got when I was your age:\n  Live every week like it’s Shark Week.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ advice | safe_truncate(90, '...') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($advice)->safeTruncate([90, '...']) }}\n```\n::\n\n```html\nSo, here’s some advice I wish I woulda got when I was your age:\nLive every week like...\n```\n"
  },
  {
    "path": "content/collections/modifiers/sanitize.md",
    "content": "---\nid: 9b9a3494-ebb0-4e28-95d3-6d7986b0386e\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Sanitize\n---\nConvert special characters to HTML entities with [htmlspecialchars][htmlspecialchars].\n\n```yaml\nexample: <b>NEAT</b>\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ example | sanitize }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($example)->sanitize() !!}\n```\n::\n\n```html\n&lt;b&gt;NEAT&lt;b&gt;\n```\n\n## Double Encoding\n\nYou can double encode HTML entities by passing `true` as an argument. This is useful for preserving JSON formatting.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ example | sanitize(true) }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($example)->sanitize(true) !!}\n```\n::\n\n[htmlspecialchars]: http://php.net/manual/en/function.htmlspecialchars.php\n"
  },
  {
    "path": "content/collections/modifiers/scope.md",
    "content": "---\nid: 4944d222-cc5b-451c-8ff9-17688de7764c\nblueprint: modifiers\ntitle: Scope\nmodifier_types:\n  - array\n  - utility\n---\nAdds a named scope prefix to each item in an array or collection, letting you access variables with that prefix to avoid collisions with the parent context.\n\nThis is the modifier equivalent of the [`scope` parameter](/tags/collection#scope) on the Collection tag, but usable on any array — like the results of a [`taxonomy`](/tags/taxonomy) tag, a [Replicator](/fieldtypes/replicator) field, or any custom array of entries.\n\n```yaml\ntitle: My Favorite Posts\nposts:\n  - title: Bear Hibernation Tips\n    slug: bear-hibernation\n  - title: Cactus Cuddles\n    slug: cactus-cuddles\n```\n\nWithout scoping, `{{ title }}` inside the loop falls back to the page's `title`. Prefix the variables with a scope to get exactly what you want.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ posts | scope('post') }}\n  <a href=\"/{{ post:slug }}\">{{ post:title }}</a>\n{{ /posts }}\n```\n::tab blade\n```blade\n@foreach (Statamic::modify($posts)->scope('post')->fetch() as $item)\n  <a href=\"/{{ $item['post']['slug'] }}\">{{ $item['post']['title'] }}</a>\n@endforeach\n```\n::\n\n```html\n<a href=\"/bear-hibernation\">Bear Hibernation Tips</a>\n<a href=\"/cactus-cuddles\">Cactus Cuddles</a>\n```\n\nThe original (unprefixed) variables are still available inside the loop — the scope is added alongside them, not as a replacement.\n"
  },
  {
    "path": "content/collections/modifiers/seconds_ago.md",
    "content": "---\nid: 603701ba-5da7-4ec8-abe5-5bc9fe6861ea\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: 'Seconds Ago'\nrelated_entries:\n  - e73f1574-732e-4a74-be47-37e1fddb05d6\n  - 06027289-825e-4205-bd3a-f375e26ab81e\n  - 7ba53a64-0266-4752-af5b-282a40dd11fa\n  - 6fcbfa5c-854e-4541-9955-505eca0d6bf7\n  - 6ebb6c28-d1f3-4362-92a0-8a16b5c9cd51\n  - 811c1cf5-797f-4e77-af92-fde6c03e96d2\n---\nReturns the number of seconds since a given date variable. Statamic will attempt to parse any string as a date, but try to keep it in the least ambiguous date format possible.\n\n```yaml\ndate: October 1 2015 8:30:am\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | seconds_ago }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($date)->secondsAgo() }}\n```\n::\n\n```html\n{{ test_date | seconds_ago }}\n```"
  },
  {
    "path": "content/collections/modifiers/segment.md",
    "content": "---\nid: 87cb26b4-3eb1-4bd7-8d80-913f1ba21932\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Segment\n---\nReturns a segment by number from any valid URL or URI.\n\n```yaml\nexample: /this/is/pretty/neat\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ example | segment(4) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($example)->segment(4) }}\n```\n::\n\n```html\nneat\n```\n"
  },
  {
    "path": "content/collections/modifiers/select.md",
    "content": "---\nid: 864d101a-c0a2-42d8-a644-80440316de8b\nblueprint: modifiers\ntitle: Select\nmodifier_types:\n  - array\n  - utility\n---\nRetrieves the selected values of a given array.\n\n```yaml\ngames:\n  -\n    feeling: love\n    title: Dominion\n    publisher: Rio Grande Games\n  -\n    feeling: love\n    title: Netrunner\n    publisher: Wizards of the Coast\n  -\n    feeling: hate\n    title: Chutes and Ladders\n    publisher: Unknown\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ games | select('title', 'publisher') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($games)->select(['title', 'published'])->fetch() }}\n```\n::\n\n```yaml\n-\n    title: Dominion\n    publisher: Rio Grande Games\n-\n    title: Netrunner\n    publisher: Wizards of the Coast\n-\n    title: Chutes and Ladders\n    publisher: Unknown\n```\n"
  },
  {
    "path": "content/collections/modifiers/sentence_list.md",
    "content": "---\nid: eed4c5bc-0923-4f54-ad37-ca9a3384e1e0\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\ntitle: 'Sentence List'\nrelated_entries:\n  - d8a8568c-bb93-4e84-8d30-e527b3b02876\n  - 6866c25b-1266-4908-8325-dce4e5146f5b\n  - cbab1bb5-302e-499d-badb-f154dbae751d\n  - 9dfc5020-3d14-4774-a1f6-d82d051cb964\n---\nTurn a simple array into a friendly comma delimited list with the word \"and\" before the last item.\n\n```yaml\nthings:\n  - batman\n  - zombies\n  - scrunchies\n```\n\n::tabs\n\n::tab antlers\n```antlers\nI like {{ things | sentence_list }}.\n```\n::tab blade\n```blade\nI like {{ Statamic::modify($things)->sentenceList() }}.\n```\n::\n\n```html\nI like batman, zombies, and scrunchies.\n```\n\nBy default, the \"glue\" is the word \"and\", and will be translated appropriately. But, you can change it with the first argument:\n\n::tabs\n\n::tab antlers\n```antlers\nI like {{ things | sentence_list('&') }}.\n```\n::tab blade\n```blade\nI like {{ Statamic::modify($things)->sentenceList('&') }}.\n```\n::\n\n```html\nI like batman, zombies, & scrunchies.\n```\n\nThe second argument controls the oxford comma. Set that to 0 and it'll get removed:\n\n::tabs\n\n::tab antlers\n```antlers\nI like {{ things | sentence_list('and', 0) }}.\n```\n::tab blade\n```blade\nI like {{ Statamic::modify($things)->sentenceList(['and', 0]) }}.\n```\n::\n\n```html\nI like batman, zombies and scrunchies.\n```\n"
  },
  {
    "path": "content/collections/modifiers/shuffle.md",
    "content": "---\nid: 63acdaa6-9724-4179-b210-ea5d507672e9\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\n  - string\ntitle: Shuffle\nrelated_entries:\n  - 48178377-da99-4754-ae9e-d294720cff33\n---\nShuffles a string or an array to make it all random.\n\n```yaml\nstring: Mr. Roboto was the original hipster.\narray:\n  - Sonic\n  - Knuckles\n  - Tails\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | shuffle }}\n{{ array | shuffle }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->shuffle() }}\n{{ Statamic::modify($array)->shuffle()->fetch() }}\n```\n::\n\n```yaml\nstring: a nhglRsws.oMtiotr hprriao eeo.b ti\narray:\n  - Tails\n  - Knuckles\n  - Sonic\n```\n"
  },
  {
    "path": "content/collections/modifiers/singular.md",
    "content": "---\nid: ef2a1b64-d2a6-4ade-988d-33b279e47bfd\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Singular\n---\nGet the singular form of an English word.\n\n```yaml\nword: nickles\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ word | singular }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($word)->singular() }}\n```\n::\n\n```html\nnickle\n```\n"
  },
  {
    "path": "content/collections/modifiers/slugify.md",
    "content": "---\nid: 15ab735c-a877-423a-8e7f-c61e3f68744b\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Slugify\n---\nConverts the string into an URL slug. This includes replacing non-ASCII characters with their closest ASCII equivalents, removing remaining non-ASCII\nand non-alphanumeric characters, and replacing whitespace with dashes. And then everything is lowercased.\n\n\n```yaml\nstring: Please, have some lemoñade.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | slugify }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->slugify() }}\n```\n::\n\n```html\nplease-have-some-lemonade\n```\n"
  },
  {
    "path": "content/collections/modifiers/smartypants.md",
    "content": "---\nid: 287d14d7-6660-4ac6-aa1b-0ca4d298cdcc\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Smartypants\n---\nTranslate plain ASCII punctuation characters into “smart” typographic punctuation HTML entities. It performs the following transformations:\n\n- Straight quotes (`\"` and `'`) into “curly” quote HTML entities\n- Backticks-style quotes (&#96;&#96;like this\\'\\') into “curly” quote HTML entities\n- Two dashes (`--`) into an em dash.\n- Three consecutive dots (`...`) into an ellipsis entity\n\n```yaml\nconversation: |\n  \"What's your favorite album?\" asked Lars. ``...And Justice for All'' replied\n  Kirk -- who was icing his hands after a 20 minute guitar solo.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ conversation | smartypants }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($conversation)->smartypants() }}\n```\n::\n\n```html\n“What’s your favorite album?” asked Lars. “…And Justice for All” replied\nKirk — who was icing his hands after a 20 minute guitar solo.\n```\n\nor more precisely...\n\n```html\n&#8220;What&#8217;s your favorite album?&#8221; asked Lars. &#8220;&#8230;And Justice for All&#8221; replied\nKirk &#8212; who was icing his hands after a 20 minute guitar solo.\n```\n"
  },
  {
    "path": "content/collections/modifiers/snake.md",
    "content": "---\nid: bc02aa00-6b1f-42f6-8cc3-737f803c5070\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Snake Case\n---\nConverts a string into `snake_case`.\n\n```yaml\nstring: statamicIsAwesome\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | snake }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->snake() }}\n```\n::\n\n```html\nstatamic_is_awesome\n```\n"
  },
  {
    "path": "content/collections/modifiers/sort.md",
    "content": "---\nid: 9e3bb06e-6f3f-460d-9693-c433452d0f96\nblueprint: modifiers\nmodifier_types:\n  - array\ntitle: Sort\n---\nSort an array by key as parameter 1 and direction (`asc`/`desc`) as parameter 2. If sorting a primitive list no parameters are necessary.\n\n```yaml\nprimitive:\n  - Zebra\n  - Alpha\n  - Bravo\ncomplex:\n  -\n    last_name: Zebra\n    first_name: Zealous\n  -\n    last_name: Alpha\n    first_name: Altruistic\n  -\n    last_name: Bravo\n    first_name: Blathering\n```\n\n```\n{{ primitive | sort | list }}\n\n{{ complex | sort('last_name') }}\n    Hello, {{ first_name }} {{ last_name }}\n{{ /complex }}\n\n{{ complex | sort('last_name', 'desc') }}\n    Hello, {{ first_name }} {{ last_name }}\n{{ /complex }}\n```\n\n```html\nAlpha, Bravo, Zebra\n\nHello, Altruistic Alpha\nHello, Blathering Bravo\nHello, Zealous Zebra\n\nHello, Zealous Zebra\nHello, Blathering Bravo\nHello, Altruistic Alpha\n```\n"
  },
  {
    "path": "content/collections/modifiers/spaceless.md",
    "content": "---\nid: 3bcdbffa-8020-4364-8c2a-1fe1a9ff0a5c\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\nadded_in: 2.8.4\ntitle: Spaceless\n---\nRemoves excess whitespace and line breaks from a string. A definite OCD pleaser.\n\n```\nhtml: |\n    <p>I copy & pasted\n        <a href=\"http://goodnightchrome.show\">this link\n        </a>   <strong>for you!</strong>    </p>\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ html | spaceless }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($html)->spaceless() !!}\n```\n::\n\n```html\n<p>I copy & pasted <a href=\"http://goodnightchrome.show\">this link </a><strong>for you!</strong></p>\n```\n"
  },
  {
    "path": "content/collections/modifiers/split.md",
    "content": "---\nid: f1f9e882-e9e1-4161-8d6e-13fa9838dde1\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\n  - utility\ntitle: Split\n---\nBreak an array or collection into a given number of (roughly equal) groups.\n\n:::tip\nNeed a fixed number of items _per group_ instead of a fixed group count? The [chunk](/modifiers/chunk) modifier does the opposite — give it a _size_ and it makes as many groups as needed.\n:::\n\nFor example, `split:3` on a 6-item array produces 3 groups of 2. Each group is available as `{{ items }}` in Antlers (or `$group['items']` in Blade).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:news as=\"posts\" limit=\"6\" }}\n  {{ posts split=\"3\" }}\n  <div class=\"flex space-x-4\">\n    {{ items }}\n      <a href=\"{{ url }}\" class=\"bg-purple-800 text-white p-4\">\n        {{ title }}\n      </a>\n    {{ /items }}\n  </div>\n  {{ /posts }}\n{{ /collection:news }}\n```\n::tab blade\n```blade\n<s:collection:news as=\"posts\" limit=\"6\">\n\n  @foreach (Statamic::modify($posts)->split(3) as $group)\n    <div class=\"flex space-x-4\">\n      @foreach ($group['items'] as $entry)\n        <a href=\"{{ $entry->url }}\" class=\"bg-purple-800 text-white p-4\">\n          {{ $entry->title }}\n        </a>\n      @endforeach\n    </div>\n  @endforeach\n\n</s:collection:news>\n```\n::\n\n```html\n<div class=\"flex space-x-4\">\n  <a href=\"/ideas/book\" class=\"bg-purple-800 text-white p-4\">\n    Book: Somehow I Manage\n  </a>\n  <a href=\"/ideas/party\" class=\"bg-purple-800 text-white p-4\">\n    Party: Goodbye Toby\n  </a>\n</div>\n<div class=\"flex space-x-4\">\n  <a href=\"/ideas/screenplay\" class=\"bg-purple-800 text-white p-4\">\n    Screenplay: Threat Level Midnight\n  </a>\n  <a href=\"/ideas/art\" class=\"bg-purple-800 text-white p-4\">\n    Art: A Stapler\n  </a>\n</div>\n<div class=\"flex space-x-4\">\n  <a href=\"/ideas/poster\" class=\"bg-purple-800 text-white p-4\">\n    Poster: Kids Playing Instruments\n  </a>\n  <a href=\"/ideas/mug\" class=\"bg-purple-800 text-white p-4\">\n    Mug: World's Best Boss\n  </a>\n</div>\n```\n"
  },
  {
    "path": "content/collections/modifiers/starts_with.md",
    "content": "---\nid: 1b1581b6-859f-4559-850b-398b7437929a\nblueprint: modifiers\nmodifier_types:\n  - conditions\ntitle: 'Starts With'\n---\nReturns `true` if the value starts with a given string. This comparison is case-insensitive.\n\n```yaml\nreply: Actually, I disagree because this is the internet.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if reply | starts_with('actually') }}\n{{ if reply | starts_with('respectfully') }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($reply)->startsWith('actually')->fetch())\n\n@endif\n\n@if (Statamic::modify($reply)->startsWith('respectfully')->fetch())\n\n@endif\n```\n::\n\n```html\ntrue\nfalse\n```\n"
  },
  {
    "path": "content/collections/modifiers/str_pad_left.md",
    "content": "---\nid: d01d449b-42fa-48d5-b0be-4884ea587fe4\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: 'String Pad Left'\n---\nThis modifier returns the string padded on the left to the specified padding length (paramameter 1) with a character of choice (parameter 2).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ 1 | str_pad_left(2,0) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify(1)->strPadLeft([2, 0]) }}\n```\n::\n\n```html\n01\n```\n"
  },
  {
    "path": "content/collections/modifiers/strip_tags.md",
    "content": "---\nid: f3538ff6-b658-45d0-b3e0-fbac49f05da9\nblueprint: modifiers\nmodifier_types:\n  - markup\n  - string\n  - utility\ntitle: 'Strip Tags'\n---\nStrip HTML tags from a string, allowing you optionally to pass in a list of tags or a variable name containing the specific tags you want stripped.\n\n```yaml\nhtml: >\n  <blockquote><p>\"Things we lose have a way of coming back to us in the end,\n  if not always in the way we expect.\"</p></blockquote>\n\nunwanted: [p, blockquote]\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ html | strip_tags }}\n{{ html | strip_tags('p') }}\n{{ html | strip_tags($unwanted) }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($html)->stripTags() !!}\n{!! Statamic::modify($html)->stripTags('p') !!}\n{!! Statamic::modify($html)->stripTags($unwanted) !!}\n```\n::\n\n```html\n\"Things we lose have a way of coming back to us in the end,\nif not always in the way we expect.\"\n\n<blockquote>\n  \"Things we lose have a way of coming back to us in the end,\n  if not always in the way we expect.\"\n</blockquote>\n\n\"Things we lose have a way of coming back to us in the end,\nif not always in the way we expect.\"\n```\n"
  },
  {
    "path": "content/collections/modifiers/studly.md",
    "content": "---\nid: 5a1d121e-0401-49e2-8460-842717d01047\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Studly Case\n---\nConverts a string into `StudlyCase`.\n\n```yaml\nstring: statamic_is_awesome\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | studly }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->studly() }}\n```\n::\n\n```html\nStatamicIsAwesome\n```\n"
  },
  {
    "path": "content/collections/modifiers/substr.md",
    "content": "---\nid: 963d5e43-7bf5-4669-93af-d1990f7f3c97\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Substr\n---\nReturns the string beginning at a given position with an optional length.\nIf length not specific, will return the rest of the string.\n\n```yaml\nstring: How neat is that?\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | substr(0, 3) }}\n{{ string | substr(4, 4) }}\n{{ string | substr(-8, 8) }}\n\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->substr([0, 3]) }}\n{{ Statamic::modify($string)->substr([4, 4]) }}\n{{ Statamic::modify($string)->substr([-8, 8]) }}\n```\n::\n\n```html\nHow\nneat\nis that?\n```\n"
  },
  {
    "path": "content/collections/modifiers/subtract.md",
    "content": "---\nid: baad70cf-0af2-48bc-b102-b0da0293baf4\nblueprint: modifiers\nmodifier_types:\n  - math\ntitle: Subtract\n---\nSubtract a value or another variable to your variable. Pass an integer or the name of a second variable as the parameter. Also supports `-` as shorthand.\n\n```yaml\ncapacity: 2500\nreservations: 1900\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ capacity | subtract(1900) }}\n{{ capacity | subtract($reservations) }}\n{{ capacity | -($reservations) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($capacity)->subtract(1900) }}\n{{ Statamic::modify($capacity)->subtract($reservations) }}\n```\n::\n\n```html\n600\n600\n600\n```\n"
  },
  {
    "path": "content/collections/modifiers/sum.md",
    "content": "---\nid: ee2da74a-0788-400f-804f-c85ad9b635c0\nblueprint: modifiers\nmodifier_types:\n  - array\n  - math\ntitle: Sum\n---\nReturns the sum of all items in an array, optionally specified by a specific key.\n\n```yaml\nnumbers:\n  - 5\n  - 10\n  - 20\n  - 40\nstats:\n  - player: Luke Skywalker\n    score: 750\n  - player: Wedge Antilles\n    score: 688\n  - player: Jar Jar Binks\n    score: 1425\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ numbers | sum }}\n{{ stats | sum('score') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($numbers)->sum() }}\n{{ Statamic::modify($numbers)->sum('score') }}\n```\n::\n\n```html\n75\n2863\n```\n"
  },
  {
    "path": "content/collections/modifiers/surround.md",
    "content": "---\nid: 1fea97b2-c42f-495b-846a-6c688d3b5eca\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Surround\n---\nSurrounds a string with another string.\n\n```yaml\nstring:  ͜\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | surround('ʘ') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->surround('ʘ') }}\n```\n::\n\n```html\nʘ ͜ ʘ\n```\n"
  },
  {
    "path": "content/collections/modifiers/swap_case.md",
    "content": "---\nid: 5c1714c1-83fe-4690-8607-60d1f269408b\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Swap Case'\n---\nReturns a case swapped version of the string.\n\n```yaml\nstring: IpHONE\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | swap_case }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->swapCase() }}\n```\n::\n\n```html\niPhone\n```\n"
  },
  {
    "path": "content/collections/modifiers/table.md",
    "content": "---\nid: 4d8e0392-af96-4b46-a73a-d3a47b57ccbe\nblueprint: modifiers\ntitle: Table\nmodifier_types:\n  - string\n  - markup\n  - utility\n---\nTakes an array generated by the [Table Fieldtype](/fieldtypes/table), and converts into a basic HTML `<table>`.\n\n```yaml\nmy_table:\n  -\n    cells:\n      - One\n      - Two\n  -\n    cells:\n      - Three\n      - Four\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ my_table | table }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($my_table)->table() !!}\n```\n::\n\n```html\n<table>\n    <tr>\n        <td>One</td>\n        <td>Two</td>\n    </tr>\n    <tr>\n        <td>Three</td>\n        <td>Four</td>\n    </tr>\n</table>\n```\n\nYou can pass `true` as an argument to parse the cell data as markdown.\n\n```\n{{ my_table | table(true) }}\n```\n"
  },
  {
    "path": "content/collections/modifiers/tidy.md",
    "content": "---\nid: be5541eb-4e33-4699-8035-61ce09de3247\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Tidy\n---\nReturns a string with smart quotes, ellipsis characters, and dashes from Windows-1252 (commonly used in Word documents) replaced by their ASCII equivalents.\n\n```yaml\nstring: >\n  “I see…”\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | tidy }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->tidy() }}\n```\n::\n\n```html\n\"I see...\"\n```\n"
  },
  {
    "path": "content/collections/modifiers/timezone.md",
    "content": "---\nid: c5a7e328-3d6a-4866-970f-5a4e0061d606\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: Timezone\n---\nApplies a timezone to a date value. Aliased as `tz`.\n\nYou may pass a [PHP timezone value](http://php.net/manual/en/timezones.php) to specify a timezone.\n\n```yaml\nwhen: 2015-01-27 11:00\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ when | format('r') }}\n{{ when | timezone('Australia/Sydney') | format('r') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($when)->format('r') }}\n{{ Statamic::modify($when)->timezone('Australia/Sydney')->format('r') }}\n```\n::\n\n```html\nTue, 27 Jan 2015 11:00:00 -0500\nWed, 28 Jan 2015 03:00:00 +1100\n```\n\nUsing no parameter will simply use the `display_timezone` configured in your `config/statamic/system.php` config file. This is useful if your date value already contains a timezone, and you want to output it in the display timezone.\n\n```yaml\nwhen: Tue, 27 Jan 2015 16:00:00 +0000  # Date in UTC\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ when | timezone | format('r') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($when)->timezone()->format('r') }}\n```\n::\n\n```html\nTue, 27 Jan 2015 11:00:00 -0500  <!-- Assuming my system timezone is America/New_York -->\n```\n"
  },
  {
    "path": "content/collections/modifiers/title.md",
    "content": "---\nid: 2293d024-32ad-4bb6-a7ff-46ec2e1d9f2f\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Title\n---\nReturns a trimmed string with the first letter of each word capitalized, ignoring articles, coordinating conjunctions, and short propositions: `a`, `an`, `the`, `at`, `by`, `for`, `in`, `of`, `on`, `to`, `up`, `and`, `as`, `but`, `or`, and `nor`.\n\n```yaml\nstring: It was one of the best adventures of my life\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | title }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->title() }}\n```\n::\n\n```html\nIt Was One of the Best Adventures of My Life\n```\n"
  },
  {
    "path": "content/collections/modifiers/to_json.md",
    "content": "---\nid: c3214196-3d0d-4a3d-b6c3-1ee4960cfe5d\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: 'To Json'\n---\nConverts any variable into JSON.\n\n```yaml\nstats:\n  - player: Luke Skywalker\n    score: 750\n  - player: Wedge Antilles\n    score: 688\n  - player: Jar Jar Binks\n    score: 1425\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ stats | to_json }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($stats)->toJson() !!}\n```\n::\n\n```html\n[\n  {\"player\":\"Luke Skywalker\",\"score\":750},\n  {\"player\":\"Wedge Antilles\",\"score\":688},\n  {\"player\":\"Jar Jar Binks\",\"score\":1425}\n]\n```\n"
  },
  {
    "path": "content/collections/modifiers/to_qs.md",
    "content": "---\nid: 6ff17a73-c631-433b-9727-0f72fa543807\nblueprint: modifiers\ntitle: 'To qs'\nmodifier_types:\n  - array\n  - utility\n---\nConverts an array or array-like value into a query string using Laravel's [Arr::query()](https://laravel.com/docs/13.x/helpers#method-array-query) helper method.\n\n```yaml\n$params = [\n    'mode' => 'plaid',\n    'area' => [51, 52],\n    'hat' => null,\n    'transportation' => [\n        'bike' => true,\n        'delorian' => false,\n    ],\n];\n```\n\n```antlers\n<a href=\"/search?{{ params | to_qs }}\">Search Now</a>\n```\n\n```html\n<a href=\"/search?mode=plaid&area%5B0%5D=51&area%5B1%5D=52&transportation%5Bbike%5D=1&transportation%5Bdelorian%5D=0\">Search Now</a>\n```\n"
  },
  {
    "path": "content/collections/modifiers/to_spaces.md",
    "content": "---\nid: d5ee1b1e-0ffa-45cd-b3aa-bfecb9a93325\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: 'To Spaces'\n---\nConverts all tabs in a string to a given number of spaces, `4` by default. This is a boring modifier to output examples of. Here's just a few examples on how the syntax looks.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | to_spaces }}\n{{ string | to_spaces(2) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->toSpaces() }}\n{{ Statamic::modify($string)->toSpaces(2) }}\n```\n::\n"
  },
  {
    "path": "content/collections/modifiers/to_tabs.md",
    "content": "---\nid: e13ecc17-1458-42f5-a55c-f7fcbac743ad\nblueprint: modifiers\nmodifier_types:\n  - utility\ntitle: 'To Tabs'\n---\nConverts all instances of a specified number of spaces in a string to tabs. `4` by default. This is a boring modifier to output examples of. Here's just a few examples on how the syntax looks.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | to_tabs }}\n{{ string | to_tabs(4) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->toTabs() }}\n{{ Statamic::modify($string)->toTabs(4) }}\n```\n::\n"
  },
  {
    "path": "content/collections/modifiers/trans.md",
    "content": "---\nid: c77b02b6-3ce5-40e0-964c-a669685c12d3\nblueprint: modifiers\ntitle: Trans\n---\nRetrieve a string from a language file in the current locale. It is the equivalent of the [trans and trans_choice methods](https://laravel.com/docs/localization) provided by Laravel.\n\n:::tip\nThere's also a [tag](/tags/trans) version that you may prefer.\n:::\n\n## Usage {#usage}\n\nGet the `bar` string from the `lang/en/foo.php` translation file (where `en` is the current locale).\n\n```php\n<?php\nreturn [\n    'bar' => 'Bar!',\n    'welcome' => 'Welcome, :name!',\n    'apples' => 'There is one apple|There are :count apples',\n];\n```\n\n``` yaml\nkey: 'foo.bar'\nthis_many: 2\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ key | trans }} or {{ \"foo.bar\" | trans }}\n```\n::tab blade\n```blade\n{{ trans($key) }} {{ trans('foo.bar') }}\n```\n::\n\n```html\nBar!\n```\n\n## Replacements\n\nParameter replacements are only supported in the [tag version](/tags/trans).\n\n## Pluralization {#pluralization}\n\nTo pluralize, use the `trans_choice` modifier with the count as the parameter. You can use a number or a variable.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ \"foo.apples\" | trans_choice(2) }}\n{{ \"foo.apples\" | trans_choice($this_many) }}\n```\n::tab blade\n```blade\n{{ trans_choice('foo.apples', 2) }}\n{{ trans_choice('foo.apples', $this_many) }}\n```\n::\n\n```html\nThere are 2 apples\n```\n\n## Strings\n\nAs you can see above, you may use the modifier on inline strings. Instead of translation keys, you can use simple strings.\nThese will be referenced from `lang/fr.json` (where `fr` is the locale).\n\n``` json\n{\n  \"Hello\": \"Bonjour\"\n}\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ \"Hello\" | trans }}\n```\n::tab blade\n```blade\n{{ trans('Hello') }}\n```\n::\n\n```html\nBonjour\n```\n"
  },
  {
    "path": "content/collections/modifiers/trim.md",
    "content": "---\nid: 64e41d8f-fedb-4639-bb09-d4e4cbfe3555\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Trim\n---\nReturns a string with whitespace removed from the start and end of the string. Supports the removal of unicode whitespace.\n\n```yaml\nstring: \"    This is so sloppy   \"\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | trim }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->trim() }}\n```\n::\n\n```html\nThis is so sloppy\n```\n"
  },
  {
    "path": "content/collections/modifiers/truncate.md",
    "content": "---\nid: cc80cc58-f73a-47fd-8f4d-e1cfc23c5d56\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Truncate\n---\nTruncates the string to a given length (parameter 1). You can append a string with parameter 2, and if truncating occurs the string is further truncated so that it may be appended without exceeding the desired length.\n\nThis differs from [safe_truncate][safe_truncate] in that it _may_ truncate in the middle of a word.\n\n```yaml\nadvice: >\n  So, here’s some advice I wish I woulda got when I was your age:\n  Live every week like it’s Shark Week.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ advice | truncate(90, '...') }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($advice)->truncate([90, '...']) }}\n```\n::\n\n```html\nSo, here’s some advice I wish I woulda got when I was your age:\nLive every week like i...\n```\n\n[safe_truncate]: /modifiers/safe_truncate\n"
  },
  {
    "path": "content/collections/modifiers/ucfirst.md",
    "content": "---\nid: d977361d-0576-469d-9430-f4d82b5666b4\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Ucfirst\n---\nConverts the first character of a string to upper case.\n\n```yaml\nstring: i wanna go home.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | ucfirst }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->ucfirst() }}\n```\n::\n\n```html\nI wanna go home.\n```\n"
  },
  {
    "path": "content/collections/modifiers/ul.md",
    "content": "---\nid: 85910466-876b-4fc7-9dd1-c9baa7f7870a\nblueprint: modifiers\nmodifier_types:\n  - array\n  - markup\ntitle: UL\n---\nTurn an array into an HTML unordered list element.\n\n```yaml\nfood:\n  - sushi\n  - broccoli\n  - kale\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ food | ul }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($food)->ul() !!}\n```\n::\n\n```html\n<ul>\n  <li>sushi</li>\n  <li>broccoli</li>\n  <li>kale</li>\n</ul>\n```\n"
  },
  {
    "path": "content/collections/modifiers/underscored.md",
    "content": "---\nid: 61a30026-8f98-454e-bcf2-fa7ec1435438\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Underscored\n---\nReturns a lowercase and trimmed string separated by underscores.\nUnderscores are inserted before uppercase characters (with the exception\nof the first character of the string), and in place of spaces as well as dashes.\n\n\n```yaml\nstring: Please and thank you\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | underscored }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->underscored() }}\n```\n::\n\n```html\nplease_and_thank_you\n```\n"
  },
  {
    "path": "content/collections/modifiers/unique.md",
    "content": "---\nid: f1486fa5-7cce-4c75-90cd-e131f5f6d184\nblueprint: modifiers\nmodifier_types:\n  - array\n  - utility\ntitle: Unique\n---\nReturns all of the unique items in the array:\n\n```yaml\nchecklist:\n  - zebra\n  - hippo\n  - hyena\n  - giraffe\n  - zebra\n  - hippo\n  - hippo\n  - hippo\n  - hippo\n\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ checklist | unique | list }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($checklist)->unique()->list() }}\n```\n::\n\n```html\nzebra, hippo, hyena, giraffe\n```\n"
  },
  {
    "path": "content/collections/modifiers/upper.md",
    "content": "---\nid: 33b1003c-6ce8-47db-a4ec-bbc323e15820\nblueprint: modifiers\nmodifier_types:\n  - string\ntitle: Upper\n---\nTransform a string into uppercase. Multi-byte friendly.\n\n```yaml\nstring: That is über neat.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | upper }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->upper() }}\n```\n::\n\n```html\nTHAT IS ÜBER NEAT.\n```\n"
  },
  {
    "path": "content/collections/modifiers/url.md",
    "content": "---\nid: eb68e4e1-c1b2-4806-a477-6c0491616b88\nblueprint: modifiers\nmodifier_types:\n  - asset\n  - string\n  - relationship\ntitle: Url\n---\nGet the URL of an Asset, Page, Entry, or Taxonomy term from an ID.\n\n```yaml\nhero_image: 98hf98-sfq4h8f94-fd9s0fj0l\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ hero_image | url }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($hero_image)->url() }}\n```\n::\n\n```html\n/assets/flying-bacon-wearing-a-batman-mask.jpg\n```\n\n:::tip\nIf your field is defined in a [Blueprint](/blueprints), Statamic would have already used [augmentation](/augmentation) to convert the ID to an object. You can access the URL like so:\n\n```\n{{ hero_image:url }}\n```\n:::\n"
  },
  {
    "path": "content/collections/modifiers/urldecode.md",
    "content": "---\nid: bf5018c9-3c15-4f93-927d-0ad3f728ac50\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: URL Decode\n---\nURL-decodes a string. The inverse of [urlencode](/modifiers/urlencode)\n\n```yaml\nstring: I+just+want+%26+need+%24pecial+characters%21\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | urldecode }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($string)->urldecode() !!}\n```\n::\n\n```html\nI just want & need $pecial characters!\n```\n"
  },
  {
    "path": "content/collections/modifiers/urlencode.md",
    "content": "---\nid: b27e8b53-f9bd-471d-8f36-17d51ec11a32\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: URL Encode\n---\nURL-encodes a string. The inverse of [urldecode](/modifiers/urldecode).\n\n```yaml\nstring: I just want & need $pecial characters!\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | urlencode }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($string)->urlencode() !!}\n```\n::\n\n```html\nI+just+want+%26+need+%24pecial+characters%21\n```\n\nIf you don't want forward slashes (`/`) to be encoded, use the [urlencode_except_slashes](/modifiers/urlencode_except_slashes) modifier instead."
  },
  {
    "path": "content/collections/modifiers/urlencode_except_slashes.md",
    "content": "---\nid: 81ba1f6a-0eaf-441b-a26d-1aeb81f135a0\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: URL Encode Except Slashes\n---\nURL-encodes a string. Just like [urlencode](/modifiers/urldecode), but doesn't encode forward slashes (`/`).\n\n```yaml\nstring: please and thank you/Mommy\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | urlencode_except_slashes }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($string)->urlencode_except_slashes() !!}\n```\n::\n\n```html\nplease+and+thank+you/Mommy\n```\n"
  },
  {
    "path": "content/collections/modifiers/values.md",
    "content": "---\nid: b57caa5c-ae9e-4220-b8a6-f7c82d16f0df\nblueprint: modifiers\ntitle: Values\nmodifier_types:\n  - array\n  - utility\n---\nRetrieves just the values from the given array.\n\n```yaml\nthe_team:\n    jack: Jack McDade\n    jason: Jason Varga\n    jesse: Jesse Leite\n    joshua: Joshua Blum\n    duncan: Duncan McClean\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ the_team | values }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($the_team)->values()->fetch() }}\n```\n::\n\n```yaml\n- Jack McDade\n- Jason Varga\n- Jesse Leite\n- Joshua Blum\n- Duncan McClean\n```\n"
  },
  {
    "path": "content/collections/modifiers/weeks_ago.md",
    "content": "---\nid: 6fcbfa5c-854e-4541-9955-505eca0d6bf7\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: 'Weeks Ago'\nrelated_entries:\n  - e73f1574-732e-4a74-be47-37e1fddb05d6\n  - 603701ba-5da7-4ec8-abe5-5bc9fe6861ea\n  - 7ba53a64-0266-4752-af5b-282a40dd11fa\n  - 06027289-825e-4205-bd3a-f375e26ab81e\n  - 6ebb6c28-d1f3-4362-92a0-8a16b5c9cd51\n  - 811c1cf5-797f-4e77-af92-fde6c03e96d2\n---\nReturns the number of weeks since a given date variable. Statamic will attempt to parse any string as a date, but try to keep it in the least ambiguous date format possible.\n\n```yaml\ndate: October 1 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | weeks_ago }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($date)->weeksAgo() }}\n```\n::\n\n```html\n{{ test_date | weeks_ago }}\n```"
  },
  {
    "path": "content/collections/modifiers/where-in.md",
    "content": "---\nid: c1141498-eff1-4fab-b24b-4d942784a748\nblueprint: modifiers\nmodifier_types:\n  - conditions\n  - array\ntitle: 'Where In'\n---\nFilter an array (such as a Replicator field's data) to items where a `key` matches specific `values`.\n\n```yaml\ngames:\n  -\n    feeling: love\n    title: Dominion\n  -\n    feeling: happy\n    title: Netrunner\n  -\n    feeling: hate\n    title: Chutes and Ladders\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<h2>I love...</h2>\n{{ games | where_in('feeling', ['love', 'happy']) }}\n  {{ title }}<br>\n{{ /games }}\n```\n::tab blade\n```blade\n<?php\n  $filteredGames = Statamic::modify($games)\n    ->whereIn(['feeling', ['love', 'happy']])\n    ->fetch();\n?>\n\n<h2>I love...</h2>\n@foreach ($filteredGames as $game)\n  {{ $game['title'] }}\n@endforeach\n```\n::\n\n```html\nDominion\nNetrunner\n```\n"
  },
  {
    "path": "content/collections/modifiers/where.md",
    "content": "---\nid: c36a6f62-aaf4-478b-a469-29cdb1eab8dc\nblueprint: modifiers\nmodifier_types:\n  - conditions\n  - array\ntitle: Where\n---\nFilter an array (such as a Replicator field's data) to items where a `key` has a specific `value`.\n\n```yaml\ngames:\n  -\n    feeling: love\n    title: Dominion\n  -\n    feeling: love\n    title: Netrunner\n  -\n    feeling: hate\n    title: Chutes and Ladders\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<h2>I love...</h2>\n{{ games | where('feeling', 'love') }}\n  {{ title }}<br>\n{{ /games }}\n```\n::tab blade\n```blade\n<?php\n  $filteredGames = Statamic::modify($games)\n    ->where(['feeling', 'love'])\n    ->fetch();\n?>\n\n<h2>I love...</h2>\n@foreach ($filteredGames as $game)\n  {{ $game['title'] }}\n@endforeach\n```\n::\n\n```html\nDominion\nNetrunner\n```\n\nYou can also pass an operator to the modifier, so you can do checks like \"where not\" and \"where greater than\". Under the hood, this uses [the `where` method of Laravel Collections](https://laravel.com/docs/13.x/collections#method-where), so you can use any operators it supports.\n\n```\n<h2>I hate...</h2>\n{{ games | where('feeling', '!=', 'love') }}\n  {{ title }}<br>\n{{ /games }}\n```\n"
  },
  {
    "path": "content/collections/modifiers/widont.md",
    "content": "---\nid: cbb290f3-ffd0-4dc1-8989-1d10a92ff17d\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: Widont\n---\nAttempts to prevent widows (a line with a single word) in a string by adding non-breaking spaces between the last two words of each paragraph.\n\nThe first parameter allows you to customize the number of words to add non-breaking spaces to.\n\n```yaml\nstring: I Just Want Pretty Headlines and Sentences\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | widont }}\n{{ string | widont(4) }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->widont() }}\n{{ Statamic::modify($string)->widont(4) }}\n```\n::\n\n```html\nI Just Want Pretty Headlines and&nbsp;Sentences\nI Just Want Pretty&nbsp;Headlines&nbsp;and&nbsp;Sentences\n```\n"
  },
  {
    "path": "content/collections/modifiers/word_count.md",
    "content": "---\nid: a9b3b597-9075-4320-bb6d-721f78c2de78\nblueprint: modifiers\nmodifier_types:\n  - string\n  - utility\ntitle: 'Word Count'\n---\nReturns the number of words in a given string.\n\n```yaml\nstring: There are probably seven words in this sentence.\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ string | word_count }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($string)->wordCount() }}\n```\n::\n\n```html\n8\n```\n"
  },
  {
    "path": "content/collections/modifiers/wrap.md",
    "content": "---\nid: ee9e1c05-8b5d-47f9-b476-3d108a9c14af\nblueprint: modifiers\nmodifier_types:\n  - markup\n  - string\ntitle: Wrap\n---\nWraps a string with a given HTML tag. Has the nice benefit of returning null if there is no data, eliminating the need for simple `{{ if }}` wrappers.\n\n```yaml\ntitle: As the World Turns\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | wrap('h1') }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($title)->wrap('h1') !!}\n```\n::\n\n```html\n<h1>As the World Turns</h1>\n```\n\nYou may also use Emmet-style CSS classes to be added to the tag.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title | wrap('h1.fast.furious') }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($title)->wrap('h1.fast.furious') !!}\n```\n::\n\n```html\n<h1 class=\"fast furious\">As the World Turns</h1>\n```\n\nThe `wrap` modifier also accepts passing in arrays. \n\n```yaml\nteam_members:\n  - Jack\n  - Jason\n  - Jesse\n  - Josh\n  - Duncan\n  - The Hoff\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ team_members | wrap('li') | join(' ') }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($team_members)->wrap('li')->join(' ') !!}\n```\n::\n\n```html\n<li>Jack</li>\n<li>Jason</li> \n<li>Jesse</li> \n<li>Josh</li> \n<li>Duncan</li> \n<li>The Hoff</li>\n```"
  },
  {
    "path": "content/collections/modifiers/years_ago.md",
    "content": "---\nid: e73f1574-732e-4a74-be47-37e1fddb05d6\nblueprint: modifiers\nmodifier_types:\n  - date\ntitle: 'Years Ago'\nrelated_entries:\n  - 603701ba-5da7-4ec8-abe5-5bc9fe6861ea\n  - 06027289-825e-4205-bd3a-f375e26ab81e\n  - 7ba53a64-0266-4752-af5b-282a40dd11fa\n  - 6fcbfa5c-854e-4541-9955-505eca0d6bf7\n  - 6ebb6c28-d1f3-4362-92a0-8a16b5c9cd51\n  - 811c1cf5-797f-4e77-af92-fde6c03e96d2\n---\nReturns the number of years since a given date variable. Statamic will attempt to parse any string as a date, but try to keep it in the least ambiguous date format possible.\n\n```yaml\ndate: October 1 2015\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date | years_ago }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($date)->yearsAgo() }}\n```\n::\n\n```html\n{{ test_date | years_ago }}\n```"
  },
  {
    "path": "content/collections/modifiers.yaml",
    "content": "title: Modifiers\nicon: return-square\ntemplate: page\nlayout: layout\nmount: ccb11ef2-eef3-4c56-9052-e55905cffd1a\ntaxonomies:\n  - modifier_types\nrevisions: false\nroute: '/modifiers/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\ninject:\n  view_model: App\\ViewModels\\Modifiers\n"
  },
  {
    "path": "content/collections/pages/3-0-to-3-1.md",
    "content": "---\nid: 769f1c97-3fb4-4303-a60b-4096c06b7870\nblueprint: page\ntitle: 'Upgrade from 3.0 to 3.1'\nintro: 'A guide for upgrading from 3.0 to 3.1'\ntemplate: page\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1630938227\n---\n## Overview\nFirst read through this guide to see if there's anything that you might need to adjust.\nWhen upgrading, Statamic may automate some things for you. They'll be noted below.\n\nIn your `composer.json`, change the `statamic/cms` requirement:\n\n```json\n\"statamic/cms\": \"3.1.*\"\n```\n\nThen run:\n\n``` shell\ncomposer update statamic/cms --with-dependencies\n```\n\n## Changes\n\n### High impact changes\n- [Update Scripts](#update-scripts)\n- [Collection and Nav trees](#collection-and-nav-trees)\n- [Opt-in REST API Resources](#optin-rest-api-resources)\n\n### Low impact changes\n- [REST API Filtering drafts](#rest-api-filtering-drafts)\n- [Entry author permissions](#entry-author-permissions)\n- [Date fieldtype augmentation](#date-fieldtype-augmentation)\n- [Static Caching interface](#static-caching-interface)\n- [Broadcasting auth endpoint](#broadcasting-auth-endpoint)\n\n## Update scripts\n\nStatamic 3.1 introduced the concept of update scripts, which you can think of like database migrations. It'll let\nStatamic perform adjustments for any particular upgrade path.\n\nSome of the changes listed in this upgrade guide will be performed automatically by an update script.\n\nIn order for it to work, you'll need a section added to your `composer.json`.\n\n**When upgrading using Composer, Statamic should add this automatically for you.**\n\n(An update script adding its own update script, say whaaat?)\n\nIf it doesn't, add this `pre-update-cmd` under the `scripts` section of your `composer.json`:\n\n```json\n\"scripts\": {\n    \"pre-update-cmd\": [\n        \"Statamic\\\\Console\\\\Composer\\\\Scripts::preUpdateCmd\"\n    ],\n    ...\n}\n```\n\n\n\n\n## Collection and Nav Trees\nThe trees for collections and navs are now stored separately from the collections and navs themselves.\n\n**When upgrading using Composer, Statamic should update these automatically for you.**\n\nIf for whatever reason it doesnt, follow these steps:\n\nIf you've only got a single site:\n- The `tree` array found in each `content/collections/[handle].yaml` should be moved into a `content/trees/collections/[handle].yaml` file.\n- The `tree` array found in each `content/navigation/[handle].yaml` should be moved into a `content/trees/navigation/[handle].yaml` file.\n\nIf you've got multiple sites:\n- In each `content/collections/[handle].yaml` with a `tree`, you should move each site's nested tree into a `content/trees/collections/[site]/[handle].yaml` file.\n- Each `content/navigation/[site]/[handle.yaml]` should be moved to `content/trees/navigation/[site]/[handle].yaml` file.\n\nBasically, the `trees` should be moved to their own dedicated directory.\n\n## Opt-in REST API Resources\nAll API endpoints have been made opt-in to improve security. This means that everything will 404 until you opt into the ones you need.\n\nAdd the following array to `config/statamic/api.php` and set the resources you want to `true`.\n\n```php\n'resources' => [\n    'collections' => false,\n    'navs' => false,\n    'taxonomies' => false,\n    'assets' => false,\n    'globals' => false,\n    'forms' => false,\n    'users' => false,\n],\n```\n\n## REST API Filtering drafts\nThe API will now filter out draft entries.\n\nThis is more of a bug fix than a breaking change. However, if you were relying on seeing draft entries, you should be aware they won't be there now. If you want to see both, you can use the status filter:\n\n```\n/api/endpoint?filter[status:in]=published|draft\n```\n\n## Entry author permissions\n\n3.1 introduces the ability to control how much you can control entries belonging to other users.\nThis feature only has any effect if your entry blueprint has an `author` field. If you don't already have an `author` field, no functionality changes for you.\n\nIf you _do_ have an `author` field, the permission logic will be enabled.\n\n**When upgrading using Composer, Statamic should automatically apply the following updates for you.**\n\nIf for whatever reason it doesn't, you'll need to add the corresponding permissions in order to continue\nediting, deleting, and managing the publish state of other users' entries:\n\n- `edit other authors blog entries` (where `blog` is the collection's handle)\n- `publish other authors blog entries`\n- `delete other authors blog entries`\n\ne.g. If you had an `edit blog entries`, you should add `edit other authors blog entries`.\n\n## Date fieldtype augmentation\nThe `date` fieldtype now augments to `Carbon` instances.\n\nIf you use `{{ a_date_field }}` in Antlers without any modifiers, they will now be output using the `date_format` configured in `config/statamic/system.php` (e.g. `January 1st, 2020`).\n\nActual entry dates (i.e. the `{{ date }}` field) would have been formatted using the configured date format this way already. No change necessary.\n\nIf you were using a modifier (e.g. `format`), there will also be no change.\n\nFor other arbitrary date fields, the raw value (e.g. `2020-01-02`) would have been output. If you _want_ to continue to output that, you can explicitly use a modifier. e.g. `{{ a_date_field format=\"Y-m-d\" }}`\n\n\n## Static caching interface\nA `hasCachedPage` method has been added to the `Statamic\\StaticCaching\\Cacher` interface.\n\nIf you're making a custom Static Caching class, you'll need to add this method.\nHowever, you're more than likely just extending `AbstractCacher`. In that case, no action is needed.\n\n\n## Broadcasting auth endpoint\n\nThis only affects you if you use broadcasting *and* you've dedicided to use custom routing instead of using Laravel's `Broadcast::routes()`.\n\nTo tell Statamic about your custom auth endpoint, you should define it in `config/broadcasting.php`:\n\n```php\n'auth_endpoint' => '/your-endpoint'\n```\n"
  },
  {
    "path": "content/collections/pages/3-1-to-3-2.md",
    "content": "---\nid: db244b81-13ad-42f1-b593-533c6e165ee6\nblueprint: page\ntitle: 'Upgrade from 3.1 to 3.2'\nintro: 'A guide for upgrading from 3.1 to 3.2'\ntemplate: page\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1630938184\n---\n\n## Overview\n\nFirst read through this guide to see if there's anything that you might need to adjust.\nWhen upgrading, Statamic may automate some things for you. They'll be noted below.\n\nIn your `composer.json`, change the `statamic/cms` requirement:\n\n```json\n\"statamic/cms\": \"3.2.*\"\n```\n\nThen run:\n\n``` shell\ncomposer update statamic/cms --with-dependencies\n```\n\n## Changes\n\n### Medium impact changes\n- [Nav Page IDs](#nav-page-ids)\n\n### Low impact changes\n- [Nav GraphQL Changes](#nav-graphql-changes)\n- [Getting tree pages by ID](#getting-tree-pages-by-id)\n- [Tree root is now an array](#tree-root-is-now-an-array)\n\n## Nav Page IDs\n\nIn a **Navigation**, each branch now has its own automatically generated ID.\n(This doesn't apply to collection trees.)\n\nIn 3.1:\n\n``` yaml\n-\n  url: /some-manually-added-link\n-\n  entry: some-entry-id\n```\n\nIn 3.2:\n\n``` yaml\n-\n  id: abc-def\n  url: /some-manually-added-link\n-\n  id: ghi-jkl\n  entry: some-entry-id\n```\n\nIn 3.1, if you were using a `nav` tag, the `{{ id }}` variable would be the ID of the entry for entry branches and `null` for non-entry branches.\n\nIn 3.2, the `{{ id }}` will be the ID of the branch.\nTo get the ID of the entry, you can use `{{ entry_id }}`.\n\nIf you had this in 3.1:\n```\n{{ nav:links }} ... {{ id }} ... {{ /nav:links }}\n```\n\nChange to this for 3.2:\n```\n{{ nav:links }} ... {{ entry_id }} ... {{ /nav:links }}\n```\n\n:::tip\nIf you're using the `nav` tag to output a _collection's_ tree (e.g. your `pages` collection), the `id` will still be the entry ID.\n:::\n\nIn PHP land, if you were doing `$page->id()`, you can now do `$page->reference()` or `optional($page->entry())->id()`.\n\n## Nav GraphQL Changes\n\n### TreeBranch type split\nThe `TreeBranch` type has been split into `NavTreeBranch` and `CollectionTreeBranch` types.\nLikely the only place you would use this is if you were using the [Recursive Tree Branches example](/graphql#recursive-tree-branches):\n\n```graphql\nfragment Fields on TreeBranch {\n    # ...\n}\nfragment RecursiveChildren on TreeBranch {\n    # ...\n}\n```\n\n### PageInterface\nThe `PageInterface` now only applies to navs.\n\n#### Usage in navs\nThe `PageInterface` no longer contains all the entry's fields. It now has it's own subset of fields (`id`, `title`, `url`, `permalink`).\n\nIf you needed entry specific fields, you can explicitly query the interface. In this example, `url` is included, but not `edit_url` anymore.\n\nBefore:\n\n```graphql\npage {\n    url\n    edit_url\n}\n```\n\nAfter:\n\n```graphql\npage {\n    url\n    ... on EntryInterface {\n        edit_url\n    }\n}\n```\n\nIf you added any [custom fields](/graphql#custom-fields) to `EntryInterface`, it will no longer be added to `PageInterface`. If you need it there, you can explicitly add it to `PageInterface`.\n\n#### Usage in collections\n\nWithin a collection's tree, the `page` is now an `EntryInterface`.\nIf you're using `EntryPage_x` implementations, you should now just use `Entry_x` implementations:\n\n```graphql\npage {\n    ... on EntryPage_Blog_Post { # change to Entry_Blog_post\n        # ...\n    }\n}\n```\n\nAlso, within collection trees, since you're now getting the actual entry, `page` has been deprecated in favor of `entry`. Both still work but you may want to update it while you're here.\n\n```graphql\npage { # change to entry\n  # ...\n}\n```\n\n\n## Getting tree pages by ID\n\n`$tree->page($id)` would previously get a page in a tree by an entry ID.\n\nNow, `$tree->page()` has been deprecated in favor of `$tree->find()` which will get a page by its ID, not the entry's ID.\n(For collection trees, since `entry` _is_ the ID, it'll work the same way.)\n\nIf you want to find a page by the entry ID, there's a new `findByEntry($id)` method.\n\n\n## Tree root is now an array\n\n`$tree->root()` would previous return the `entry` of the root tree branch.\n\nNow, it'll be the entire array.\n\nIf you need the entry, you can do `$tree->root()['entry']`.\n"
  },
  {
    "path": "content/collections/pages/3-2-to-3-3.md",
    "content": "---\nid: 4947cda2-d17d-4289-ab37-1f4f64bfa1d4\nblueprint: page\ntitle: 'Upgrade from 3.2 to 3.3'\nintro: 'A guide for upgrading from 3.2 to 3.3. For most sites, the process will take less than 5 minutes. If your site is running an old version of PHP or Laravel, it may take a bit longer.'\ntemplate: page\n---\n## Overview\n\nFirst read through this guide to see if there's anything that you might need to adjust. When upgrading, Statamic may automate some things for you. They'll be noted below.\n\nIn your `composer.json`, change the `statamic/cms` requirement:\n\n```json\n\"statamic/cms\": \"3.3.*\"\n```\n\nThen run:\n\n``` shell\ncomposer update statamic/cms --with-dependencies\n```\n\n## High impact changes\n\n### PHP >=7.4 required {#php-version}\n\nStatamic 3.3 now requires at least PHP 7.4.\n\nIf you're running a lower version, we recommend upgrading all the way to 8.1.\n\n### Laravel 8 required\n\nStatamic 3.3 now requires at least Laravel 8.\n\nIf you're running a lower version, we recommend upgrading all the way to 9.\n\nYou can check your version of Laravel by running `php artisan -V`.\n\nFind out [how to upgrade from Laravel 7 to Laravel 8](/upgrade-guide/laravel-7-to-8).\n\n## Medium impact changes\n\n### Entries fieldtype augments to query builders {#entries-fieldtype}\n\nThe `entries` fieldtype would previously augment to an `EntryCollection` of `Entry` objects. In 3.3, they will augment to a query builder.\n\n#### In Antlers\n\nIf you were adding modifiers to your loop, you'll need to apply them to an inner aliased loop.\n\nBefore:\n\n```\n{{ related_posts modifier=\"param\" }}\n  ...\n{{ /related_posts }}\n```\n\nAfter:\n\n```\n{{ related_posts as=\"whatever\" }}\n  {{ whatever modifier=\"param\" }}\n    ...\n  {{ /whatever }}\n{{ /related_posts }}\n```\n\n#### In PHP\n\nIf you're using them in PHP, you'll need to add a `->get()` to grab the entries from the query before continuing.\n\nBefore:\n\n```php\n$relatedPosts->someCollectionMethod(...);\n```\n\nAfter:\n\n```php\n$relatedPosts->get()->someCollectionMethod(...);\n```\n\n## Low impact changes\n\n### New Experimental Antlers Parser {#antlers}\n\n3.3 includes an overhauled Antlers parser. It fixes many issues with the existing parser and brings many new features.\n\nBy upgrading to 3.3 **you will not automatically get the new parser**.\n\nIf you'd like to use the new parser, [read about it in the docs](/new-antlers-parser), where it explains the experimental nature and how to use it.\n\n\n### Date field's time_required setting has been removed {#date-time-required}\n\nThe `time_required` option has been removed from the `date` fieldtype.\nThe `time_enabled` setting still exists, and we suggest you use that.\n\n\n### v-calendar upgraded to v2 {#v-calendar}\n\nIf you were relying on the [v-calendar](https://github.com/nathanreyes/v-calendar) package within the Control Panel, be aware that it has been upgraded to v2.\n\n### Property access on items now performs augmentation\n\nSee PR [#5297](https://github.com/statamic/cms/issues/5297) for more details.\n\nPreviously if you were to do `$entry->something`, it would get the raw value on the entry. It would _not_ factor in fallbacks from any origin entries, or the collection's injected data. It would _not_ do any augmentation. It would _not_ give you method-based values you might expect automatically in templates (like `id`, `slug`, etc).\n\nIn 3.3, doing `$entry->something` will do all of those. It will factor in fallbacks, augment, and give you method based values.\n\n```yaml\nid: 123\nintro: 'hello'   # (a \"text\" fieldtype)\nfoo: 'bar'       # (not even in the blueprint at all)\ncontent: |       # (a \"markdown\" fieldtype)\n  # Heading\n  Paragraph\n```\n```php\n// 3.2\n$entry->content; // \"# Heading\\nParagraph\"\n$entry->intro;   // \"hello\"\n$entry->foo;     // \"bar\"\n$entry->id;      // null\n$entry->slug;    // null\n$entry->url;     // null\n\n// 3.3\n$entry->content; // \"<h1>Heading</h1><p>Paragraph</p>\n$entry->intro;   // \"hello\"\n$entry->foo;     // \"bar\"\n$entry->id;      // 123\n$entry->slug;    // \"my-entry\"\n$entry->url;     // \"/blog/my-entry\"\n```\n\nOn 3.2, property access on terms did nothing. You'd get a warning and null.\n\n\n### Augmentation methods return Value instances {#augmentation-value-instances}\n\nSee PR [#5302](https://github.com/statamic/cms/issues/5302) for more details. This change will only affect custom PHP code.\n\nThis is considered a low impact change since it should only affect edge cases.\n\n- If you were explicitly coding something expecting a non-`Value`, it will now be a `Value`.\n- If it was being cast to a string or array, no change is needed since the `Value` class knows how to cast itself.\n\nFor example, previously `toAugmentedArray()` you'd only get `Value` objects for fields that exist in the blueprint.\n\n```php\n$arr = $entry->toAugmentedArray();\n// [\n//   'published'            => false,\n//   'url'                  => '/blog/my-post'\n//   'some_blueprint_field' => Value('some value'),\n//   ...\n// ]\n\n$inBlog = (Str::startsWith($arr['url'], '/blog')) ? 'yes' : 'no'; // yes\n$isPublished = ($arr['published']) ? 'yes' : 'no'; // 'no'\n```\n\nIn 3.3, everything in the array would be wrapped in `Value` objects.\n\n```php\n$arr = $entry->toAugmentedArray();\n// [\n//   'published'            => Value(false),\n//   'url'                  => Value('/my-post')\n//   'some_blueprint_field' => Value('some value'),\n//   ...\n// ]\n\n// ✅ No change. It would cast the Value to a string, giving you the same outcome.\n$inBlog = (Str::startsWith($arr['url'], '/blog')) ? 'yes' : 'no'; // yes\n\n// 🚨 This is changing.\n// Previously it would check \"if true/false\". But now it's \"if object\" which will always be true.\n$isPublished = ($arr['published']) ? 'yes' : 'no'; // 'yes'\n```\n\nYou'll now need to add `->value()` to get the underlying value.\n\n```php\n$isPublished = ($arr['published']->value()) ? 'yes' : 'no'; // 'yes'\n```\n\nThe same goes for other augmentation methods:\n- `$entry->toAugmentedArray()` will only contain `Value` instances.\n- `$entry->toAugmentedCollection()` will only contain `Value` instances.\n- `$entry->augmentedValue('field')` will always return a `Value` instance.\n- `$entry->augmented()->get('field')` will always return a `Value` instance.\n\nBut really, the fix would be to avoid the manual augmentation methods entirely and just do `$entry->fieldname`, `$entry->published`, etc.\n\n```php\n$isPublished = ($entry->published) ? 'yes' : 'no'; // 'yes'\n```\n\n### Form submission data is an unfiltered collection\n\nIn 3.2, if you did `$submission->data()` you would sometimes get an array, sometimes an `Illuminate\\Support\\Collection` (the inconsistency was a bug) and any keys that didn't exist in the blueprint would get filtered out.\n\nIn 3.3, you will always get a Collection, and it will not have any filtering applied.\n\n### Live Preview\n\nIf you're not overriding the `toLivePreviewResponse` in the `Entry` or `Term` classes, there is no change for you.\n\nThe `toLivePreviewResponse` methods have been removed in favor of a token based system. You can instead migrate to a dedicated route that will get the Live Preview version of the entry/term via a token.\n\nSee the [Live Preview Custom Rendering docs](/live-preview#custom-rendering) for how to do this.\n\n### Custom date fields are now Carbon instances\n\nCustom date fields are now stored in the Stache as `Carbon` instances.\n\nThis will only affect you if you're performing a query on a `date` field expecting it to be a string. For instance, `$query->where('datefield', 'like', '2020-%')` or `:datefield:starts_with=\"2020\"`\n\nThe actual `date` field on an entry (i.e. the publish date) would have already been a Carbon instance. This only applies to `date` fields that aren't named `date`.\n\n\n### Grids, Replicators, and Bards augment differently\n\nPreviously, these fields all augment to arrays containing arrays for each row or set.\n\nIn 3.3, it will be an array of `Values` instances representing each row or set.\n\nIn your templates there will be no changes. There's only a change necessary if you are writing PHP and expecting those to be arrays. You can get the underlying array by doing `$row->all()`.\n\n\n### Commonmark 2 is supported\n\nLaravel 8 and Statamic 3.3 now support both CommonMark 1 or 2. When you do a `composer update`, Composer will try to install the latest available version, which may be v2.\n\nIf you have any [custom Markdown extensions](/extending/markdown#customizing-markdown-behavior) you will need to either:\n- [upgrade them for Commonmark 2](https://commonmark.thephpleague.com/2.0/upgrading/developers/)\n- **or** you may require `league/commonmark ^1.6` in your project.\n\nIf you don't have any custom extensions, the switch from Commonmark v1 to v2 should make no difference to you.\n\n### Control panel forms now only submit visible fields\n\nControl Panel forms now only submit visible fields (as originally intended) which fixes `sometimes` / `required_if` / etc. validation rules, among other things.\n\nThis could potentially be a breaking change if you were using field conditions purely for cosmetic showing/hiding of form fields, in which case we might recommend using the [revealer fieldtype](/fieldtypes/revealer).\n\nRead more: [Conditional Fields Data Flow](/conditional-fields#data-flow)\n\n\n## Zero impact changes\n\nThese are items that you can completely ignore. They are suggestions on how to improve your code using newly added features.\n\n### You probably don't need to manually augment\n\nIf you were manually grabbing an augmented value instance, then getting the actual augmented value from that - you can now just use the magic getters to get the underlying augmented value.\n\n```php\n$entry->augmentedValue('fieldname')->value(); // [tl! --]\n$entry->fieldname; // [tl! ++]\n```\n\n### Leverage relationship magic methods\n\nIf you were manually performing a new query based on selections from an entries fieldtype, you can now use the magic method to get a query builder.\n\n```php\n$relatedIds = $entry->get('related_posts'); // [tl! --]\n$selectedFeaturedEntries = Entry::query()->whereIn('id', $relatedIds)->where('featured', true)->get(); // [tl! --]\n$selectedFeaturedEntries = $entry->related_posts()->where('featured', true)->get(); // [tl! ++]\n```\n\n### Use Tags in Blade\n\nIf you were using the [Blade Directives](https://github.com/edalzell/statamic-blade) addon to work with Statamic data in your Blade templates, you can now use a whole bunch of native features instead.\n\n```blade\n@collection('pages', ['title:is' => 'My Title', 'author:is' => 'Erin', 'limit' => 3, 'sort' => 'title:desc']) {{-- [tl! --] --}}\n{{-- [tl! ++:start] --}}\n@foreach (Statamic::tag('collection:pages')\n            ->params(['title:is' => 'My Title', 'author:is' => 'Erin'])\n            ->limit(3)->sort('title:desc')\n            as $entry\n) {{-- [tl! ++:end] --}}\n    @if($entry['no_results']) {{-- [tl! --] --}}\n    @if($entry->no_results) {{-- [tl! ++] --}}\n        <p>There are no results</p>\n    @else\n        {{ $entry['title'] }} {{-- [tl! --] --}}\n        {{ $entry->title }} {{-- [tl! ++] --}}\n    @endif\n@endcollection\n```\n\n```php\nreturn [\n  'providers' => [\n    Edalzell\\Blade\\Augmentation\\AugmentationViewServiceProvider::class, // [tl!--]\n    Illuminate\\View\\ViewServiceProvider::class, // [tl!++]\n  ]\n]\n```\n"
  },
  {
    "path": "content/collections/pages/3-3-to-3-4.md",
    "content": "---\nid: a1d1a50e-fb42-4a9e-b03f-59dc47f21dc2\nblueprint: page\ntitle: 'Upgrade from 3.3 to 3.4'\nintro: 'A guide for upgrading from 3.3 to 3.4. For most sites, the process will take less than 5 minutes.'\ntemplate: page\n---\n## Overview\n\nFirst read through this guide to see if there's anything that you might need to adjust. When upgrading, Statamic may automate some things for you. They'll be noted below.\n\nIn your `composer.json`, change the `statamic/cms` requirement:\n\n```json\n\"statamic/cms\": \"3.4.*\"\n```\n\nThen run:\n\n``` shell\ncomposer update statamic/cms --with-dependencies\n```\n\n## High impact changes\n\n### The runtime Antlers parser is the default\n\nIn 3.3 we introduced a new Antlers parser but you had to opt into it.\n\nIn 3.4 it becomes the default.\n\nYou may continue to use the old \"regex\" parser, but you'll need to make sure to specify that in `config/statamic/antlers.php`.\n\n```php\n'version' => 'regex', // or \"runtime\" for the new parser\n```\n\n### Bard addons / extensions will no longer work\n\nBard's underlying editor, Tiptap, has been updated from v1 to v2 which required significant changes to Bard. This means that Bard addons will no longer work until they've been updated to support Statamic 3.4.\n\nYou should check if the Bard addons you have installed have been updated for 3.4, or if they are even necessary anymore since the native Bard editor now has more features.\n\n[Read how to upgrade your addons for Bard 2](/upgrade-guide/bard-v1-to-v2)\n\n### Draft entries no longer get added to search indexes\n\nIn 3.3 drafts would be indexed, in 3.4 they are not.\n\nIf you're linking a search index to a collection, when you use the control panel since drafts are no longer indexed they will not show up in results. You can cancel out that behavior by using a filter that allows everything:\n\n```yaml\n# content/collections/articles.yaml\nsearch_index: articles\n```\n\n```php\n// config/statamic/search.php\n'articles' => [\n    'driver' => 'local',\n    'searchables' => ['collection:articles'],\n    'filter' => fn () => true, // [tl! ++]\n]\n```\n\n## Medium impact changes\n\n### Term queries return all localizations\n\n**This will not affect any native tags, like `taxonomy`.** It will only affect you if you are performing term queries in PHP, such as:\n\n```php\nTerm::all();\nTerm::query()->get();\n```\n\nIn 3.3, these would deduplicate the terms and only give you a single localization. i.e. If you had 2 sites configured, and 3 terms, you'd end up with 3 results.\n\nIn 3.4, you will get all the localizations. i.e. You would get 6 results.\n\nTo get the previous behavior (even though it was buggy, and the reason for the change), you can query for a specific site:\n\n```php\n$query = Term::query();\n$query->where('site', 'english'); // [tl! ++]\n$terms = $query->get();\n```\n\n### Search queries return Result classes\n**This will not affect the `search:results` tag.** This would only affect you if you were performing searches manually in PHP. Depending on what you were doing with the results, it's possible no changes would be necessary anyway.\n\nSearch queries now return `Result` instances. Previously they returned instances of the actual searchable items. (e.g. an Entry)\n\nYou can continue to get the underlying classes by mapping using the `getSearchable()` method.\n\n```php\n$results = Search::index('default')->search('foo')->get();\n$results = $results->map->getSearchable(); // [tl!++]\n```\n\n## Low impact changes\n\n### Slugs no longer include extra characters\n\nIn some forms, the slug will get automatically generated. e.g. When you type into the `title` field on a publish form.\n\nIn 3.3, some special characters would get converted. e.g. The `&` would become `and`.\n\nIn 3.4, **these characters will not get converted**.\n\nTo enable these character conversions, you can enable a new setting in `config/statamic/system.php`.\n\n```php\n'ascii_replace_extra_symbols' => true,\n```\n\n### Replicator, Bard, and Grid IDs\n\nAll three fieldtypes will now supply their `id` to templates. Previously, the `id` would have been the entry's ID.\n\nIf you still need to access the entry's ID within the field tag pair, you may use the `page` variable:\n\n```\n{{ grid }}\n    {{ id }} the entry id {{# [tl! --] #}}\n    {{ id }} the grid item's id {{# [tl! ++] #}}\n    {{ page:id }} the entry id {{# [tl! ++] #}}\n{{ /grid }}\n```\n\n### LocalizedTerm reference now includes site handle\n\nThe `reference` method of the `LocalizedTerm` class now includes the site handle, even when only a single site is configured.\n\n```yaml\nterm::categories::hats # [tl! --]\nterm::categories::hats::en # [tl! ++]\n```\n\n### Entries etc must implement Searchable\n\nThe `Entry`, `LocalizedTerm`, `Asset`, and `User` classes must implement the `Searchable` interface.\n\nIf you've customized any of these classes, you'll need to make sure to implement the appropriate methods. If you are extending from the native classes, then you probably don't need to make any changes.\n\n### Custom search index drivers accept locale\n\nThis only affects users that have created custom search index drivers.\n\nNow that search indexes can be localized, when they are constructed, a nullable `$locale` string will be passed to it. You should make sure that the name includes it.\n\n```php\nSearch::extend('custom', function ($app, $config, $name, $locale) {\n    return new CustomDriver(\n        $config,\n        $name,\n        $locale // [tl!++]\n    );\n});\n```\n\nIf you are extending the native Statamic `Index` class, this will be done for you.\n\n### Search default index\n\nWhen using the `Search` facade, certain methods would apply directly to the default index.\n\nSince 3.4 introduces localized indexes, if your default index is localized, these magic methods will target the current site.\n\nFor example:\n\n```php\n// If currently on the \"english\" site,\nSearch::for('foo'); // applies to english index\n\n// If currently on the \"french\" site\nSearch::for('foo'); // applies to french index\n```\n\nIf needed, you can be explicit:\n\n```php\nSearch::index('default', 'french')->for('foo');\n```\n\n### Search methods removed\n\nThe `clearIndex` and `indexExists` methods have been removed.\n\n## Zero impact changes\n\nThese are items that you can completely ignore. They are suggestions on how to improve your code using newly added features.\n\n### Utility and Permission registration\n\n3.4 fixes an issue where translations for utilities and permissions may not have been displayed in the right language when a user has a custom locale preference.\n\nThe core utilities and preferences were fixed, however in order to fix it for addons, you will need to wrap your existing code in closures.\n\n```php\nPermission::extend(function () { // [tl! ++]\n    Permission::register('foo', '...');\n}); // [tl! ++]\n\nUtility::extend(function () { // [tl! ++]\n    Utility::make('foo')->register();\n}); // [tl! ++]\n```\n\n### Utility fluent methods\n\nThe utility class was updated to be consistent with permissions syntax. You no longer need to chain `register` to the end. You can simply use `register` at the start instead of `make`.\n\n```php\nUtility::make('foo')->title('Foo')->etc()->register(); // [tl! --]\nUtility::register('foo')->title('Foo')->etc(); // [tl! ++]\n```\n"
  },
  {
    "path": "content/collections/pages/3-4-to-4-0.md",
    "content": "---\nid: e077f513-45c1-4eff-ba87-210340dd6f54\nblueprint: page\ntitle: 'Upgrade from 3.4 to 4.0'\nintro: 'A guide for upgrading from 3.4 to 4.0. For most sites (those running Laravel > 8), the process will take less than 5 minutes.'\ntemplate: page\n---\n## Overview\n\nFirst read through this guide to see if there's anything that you might need to adjust. While there are many items on this page, a majority of them only apply to addons or custom code. We've noted who each item would apply to so you can more easily scan through the changes.\n\n### Upgrade using Composer\n\nIn your `composer.json`, change the `statamic/cms` requirement:\n\n```json\n\"statamic/cms\": \"3.4.*\" // [tl!--]\n\"statamic/cms\": \"^4.0\" // [tl!++]\n```\n\nThen run:\n\n``` shell\ncomposer update statamic/cms --with-dependencies\n```\n\n## High impact changes\n\n### PHP and Laravel support\n**Affects apps using PHP < 8 or Laravel < 9.**\n\n- The minimum version of PHP is now 8.0.\n- The minimum version of Laravel is now 9.\n\n\n### AMP has been removed\n**Affects apps using the AMP feature.**\n\nAMP is considered a dead project, and no longer provides SEO benefits, so the entire AMP feature has been removed.\n\n\n### API filters are opt-in\n**Affects apps using the GraphQL or REST API features.**\n\nAll filters are disabled by default now for increased security. You must opt into each one you want made available.\n\n```php\n// config/statamic/api.php or config/statamic/graphql.php\n\n'resources' => [\n    'collections' => true, // [tl!--]\n    'collections' => [ // [tl! ++:start]\n        'blog' => [\n            'allowed_filters' => ['title', 'slug']\n        ]\n    ] // [tl! ++:end]\n]\n```\n\nIf you try to use a filter that has not been explicitly allowed, it will result in a validation error.\n\n## Medium impact changes\n\n### Route namespaces have been removed\n**Affects apps or addons using PHP-based routes.**\n\nWhen using the following various methods of adding custom routes, previously Statamic would assume a namespace. In 4.0, the namespace is removed.\n\nStandard Laravel routes that you've added to your app routes files are not affected.\n\n- `Statamic::pushCpRoutes()`\n- `Statamic::pushWebRoutes()`\n- `Statamic::pushActionRoutes()`\n- Addon route files\n- Addon service provider `$this->registerCpRoutes()`\n- Addon service provider `$this->registerWebRoutes()`\n- Addon service provider `$this->registerActionRoutes()`\n\nFor example, in an addon's `cp.php` routes file:\n\n```php\nRoute::get('example', 'ExampleController@foo');\n// v3 = Your\\Addon\\Http\\Controllers\\ExampleController@foo\n// v4 = ExampleController@foo\n```\n\nIn an addon, if you _want_ a namespace, you can add one with a property:\n\n```php\nprotected $routeNamespace = 'Your\\Addon\\Http\\Controllers';\n```\n\nHowever, we recommend using the class reference syntax:\n\n```php\nuse Your\\Addon\\Http\\Controllers\\ExampleController;\n\nRoute::get('example', [ExampleController::class, 'foo']);\n```\n\n\n### Str::replace arguments changed\n**Affects apps or addons using `Statamic\\Support\\Str::replace()`.**\n\nThe `Statamic\\Support\\Str::replace()` method changed the argument order. The `$subject` argument moved from first to last.\n\n```php\nStr::replace($subject, $search, $replace); // [tl! --]\nStr::replace($search, $replace, $subject); // [tl! ++]\n```\n\n### Tailwind 3\n**Affects apps or addons with custom CP components.**\n\nThe Control Panel has been upgraded from Tailwind 1 to 3. The sizing scale has been adjusted. Custom components may render unexpectedly and may need to have classes renamed.\n\n### Entry date behavior\n**Affects apps or addons with custom code.**\n\nWhen using the `date` method on an entry, an exception will be thrown if the entry is not in a dated collection.\n\nBecause of this, if you are creating an entry instance, you should set the collection _before_ the date.\n\n```php\nEntry::make()\n    ->date($date) // [tl! --]\n    ->collection($collection)\n    ->date($date) // [tl! ++]\n    ->set('foo', 'bar');\n```\n\n## Low impact changes\n\n### Control Panel Composer actions have been removed\n**Affects users who are used to updating Statamic and addons using the Control Panel.**\n\nThe ability to update Statamic, as well as installing and updating addons through the Control Panel has been removed. You will now need to use Composer on the command line.\n\nThe Control Panel sections remain, however the buttons will now give you the Composer commands rather than running them for you.\n\n### jQuery removed\n**Affects apps or addons with custom CP components using jQuery.**\njQuery and jQuery UI were seldom used, and have been removed to lower the CP's overhead. We suggest replacing with Vue or Alpine equivalents.\n\n### vue-reactive-provide removed\n**Affects custom CP Vue components using the `reactiveProvide` property.**\n\nThe `vue-reactive-provide` package was removed. We suggest using providing an observed object.\n\n```js\nreactiveProvide: { // [tl! --:start]\n    name: 'foo',\n    include: ['alfa', 'bravo']\n} // [tl! --:end]\nprovide: { // [tl! ++:start]\n    foo: this.foo\n},\ndata() {\n    foo: this.makeProvidedFoo();\n},\nmethods: {\n    makeProvidedFoo() {\n        const foo = {};\n        Object.defineProperties(grid, {\n            alfa: { get: () => this.alfa },\n            bravo: { get: () => this.bravo },\n        });\n        return foo;\n    }\n} // [tl! ++:end]\n```\n\n### Flysystem v1 support dropped\n**Affects apps or addons explicitly using Flysystem v1 code.**\n\nSupport for `league/flysystem` v1 has been removed. Only v3 is supported.\n\n### CommonMark v1 support dropped\n**Affects apps or addons with custom CommonMark v1 extensions.**\n\nSupport for `league/commonmark` v1 has been removed. Only v2 is supported.\n\n### Typography CSS styling\n**Affects apps or addons with custom CP components using the `clean-content` CSS class.**\n\nThe custom `.clean-content` css class in the Control Panel has been replaced by `.prose` from Tailwind's typography plugin.\n\n### FontAwesome and Entypo fonts have been removed\n**Affects apps or addons with custom CP components using these fonts.**\n\nBoth icon fonts have been removed, and replaced with Streamline SVG icons.\n\n### SortableList delay has been reduced to zero\n**Affects apps or addons with custom CP components using the `SortableList` component.**\n\nThe delay was reduced from 200 to 0. The prop still exists, so you can manually bring it back.\n\n```html\n<sortable-list> <!-- [tl!--] -->\n<sortable-list :delay=\"200\"> <!-- [tl!++] -->\n```\n\nHowever, we suggest using a distance over delay in most cases.\n\n\n```html\n<sortable-list :distance=\"10\"> <!-- [tl!++] -->\n```\n\n\n### Panes have been removed\n**Affects apps or addons with custom CP components using the `Pane` component.**\n\nThe \"pane\" feature has been removed. You can replace it with a narrow stack.\n\n```html\n<pane> <!-- [tl!--] -->\n<stack narrow> <!-- [tl!++] -->\n  Some content\n</pane> <!-- [tl!--] -->\n</stack> <!-- [tl!++] -->\n```\n\n\n### Color fieldtype has been simplified\n**Affects apps using the `color` fieldtype in their blueprints.**\n\nThe color fieldtype now only supports hex values with no alpha channel.\n\n\n### PortalVue and vue-js-modal components have been renamed.\n**Affects apps or addons with custom CP components using the `<portal>` or `<vue-modal>` components.**\n\nStatamic v4 introduces our own `<portal>` component, so to prevent conflicts, the underlying PortalVue package's component has been renamed to `<v-portal>`.\n\n```html\n<portal><!-- [tl!--]-->\n<v-portal><!-- [tl!++]-->\n  ...\n</portal><!-- [tl!--]-->\n</v-portal><!-- [tl!++]-->\n```\n\nFor consistency, we renamed the underlying modal component from `<vue-modal>` to `<v-modal>`.\n\n```html\n<vue-modal><!-- [tl!--]-->\n<v-modal><!-- [tl!++]-->\n  ...\n</vue-modal><!-- [tl!--]-->\n</v-modal><!-- [tl!++]-->\n```\n"
  },
  {
    "path": "content/collections/pages/4-to-5.md",
    "content": "---\nid: 91e8f239-2f99-47bc-b4dd-3518cd3e36ae\nblueprint: page\ntitle: 'Upgrade from 4 to 5'\nintro: 'A guide for upgrading from 4 to 5. For most sites (those running Laravel > 9), the process will take less than 5 minutes.'\ntemplate: page\n---\n## Overview\n\nFirst read through this guide to see if there's anything that you might need to adjust. While there are many items on this page, a majority of them only apply to addons or custom code. We've noted who each item would apply to so you can more easily scan through the changes.\n\n### Upgrade using Composer\n\nIn your `composer.json`, change the `statamic/cms` requirement:\n\n```json\n\"statamic/cms\": \"^4.0\" // [tl!--]\n\"statamic/cms\": \"^5.0\" // [tl!++]\n```\n\nThen run:\n\n``` shell\ncomposer update statamic/cms --with-dependencies\n```\n\n## High impact changes\n\n### PHP and Laravel support\n**Affects apps using PHP < 8.1 or Laravel < 10.**\n\n- The minimum version of PHP is now 8.1.\n- The minimum version of Laravel is now 10.\n\nWe highly recommend upgrading all the way to Laravel 11 and PHP 8.3.\n\n:::tip\nIf you want to (semi-)automate the Laravel upgrade process, we recommend using [Laravel Shift](https://laravelshift.com/discounts/statamic-1983) (use that link for a special 19.83% discount 🤘).\n:::\n\n### Site configuration changes\n**Affects everyone.**\n\n_Note that Statamic will attempt to migrate this for you automatically during the upgrade._\n\nThe site config has been moved from `config/statamic/sites.php` into `resources/sites.yaml`. This allows you to manage your sites from the Control Panel.\n\n```php\n// config/statamic/sites.php\nreturn [ // [tl! --:start]\n  'sites' => [\n    'default' => [\n      'name' => 'First Site',\n      'url' => config('app.url'),\n      'locale' => 'en_US',\n    ],\n    'two' => [\n      'name' => 'Second Site',\n      'url' => config('app.url').'/fr/',\n      'locale' => 'fr_FR',\n    ]\n  ]\n]; // [tl! --:end]\n```\n\n```yaml\n# resources/sites.yaml\ndefault: # [tl! ++:start]\n  name: First Site\n  url: '{{ config:app:url }}'\n  locale: en_US\ntwo:\n  name: Second Site\n  url: '{{ config:app:url }}/fr/'\n  locale: fr_FR # [tl! ++:end]\n```\n\n_**Note:** Text direction is now also automatic, [based on each site's language](/multi-site#text-direction). You do not need to migrate `direction` values to your `resources/sites.yaml`._\n\nThere is now also a new `multisite` boolean in `config/statamic/system.php`. Previously, the multi-site feature would be considered \"enabled\" as soon as you configured a second site. Now there is an explicit option to enable it.\n\nThis should be set to `true` if you previously had 2 or more sites configured.\n\n```php\n// config/statamic/system.php\n'multisite' => true,\n```\n\n## Medium impact changes\n\n### Blueprint default value usage\n**Affects apps that have `default` defined in their blueprints or fieldsets.**\n\nIn v4, setting a `default` value for a field would only make it show up in the respective publish forms. In v5, these values will actually be used where appropriate, such as within front-end templates.\n\n```yaml\nhandle: myfield\nfield:\n  type: text\n  default: my default value\n```\n```yaml\ntitle: My Entry\n# the \"myfield\" is missing\n```\n```antlers\n{{ if myfield }}yes{{ else }}no{{ /if }}: {{ myfield }}\n\nv4 outputs: \"no: \" {{# [tl! --] #}}\nv5 outputs: \"yes: my default value\"  {{# [tl! ++] #}}\n```\n\nIn most cases, this is what you would have expected to happen anyway.\n\n### Site methods\n**Affects apps or addons using the `Site::hasMultiple()` method.**\n\nContinuing from the multi-site configuration changes above, there are now two separate methods for determining multi-site state.\n\n- The new `Site::multiEnabled()` method will return true if the `multisite` boolean in `system.php` is enabled.\n- The existing `Site::hasMultiple()` method will return `true` if at least two sites are configured.\n\nYou should decide whether each existing usage of `Site::hasMultiple()` should imply that the feature is enabled entirely, or if you need to actually count the number of sites.\n\n### Laravel Helpers package has been removed\n**Affects apps or addons relying on methods from that package in custom code.**\n\nThe `laravel/helpers` package provided support for the older global-style string/array/misc functions like `array_get`, `str_contains`, `snake_case`, `ends_with`, etc. You will now need to either require this package yourself, or update to use the underlying methods.\n\nFor example:\n\n```php\nnamespace App\\Example;\n\nuse Statamic\\Support\\Arr; // [tl! ++,**]\nuse Statamic\\Support\\Str; // [tl! ++,**]\n\nclass Example\n{\n  function example()\n  {\n    array_get($arr, $key); // [tl! --,**]\n    Arr::get($arr, $key); // [tl! ++,**]\n\n    str_start($str, $prefix); // [tl! --,**]\n    Str::start($str, $prefix); // [tl! ++,**]\n\n    ends_with($str, $needle); // [tl! --,**]\n    Str::endsWith($str, $needle); // [tl! ++,**]\n  }\n}\n```\n\nWe recommend making the code changes. However, if you just want to require the package:\n\n```sh\ncomposer require laravel/helpers\n```\n\n### Statamic will now use your app's default pagination view\n**Affects apps using the `auto_links` pagination variable in templates.**\n\nPreviously, Statamic used the `pagination::default` view to rendering pagination links. In Statamic 5, it will use your app's default pagination view, typically `pagination::tailwind`.\n\nTo avoid making code changes, you may wish to change the default view back to `pagination::default`:\n\n```php\n// app/Providers/AppServiceProvider.php\n\nuse Illuminate\\Pagination\\Paginator; // [tl! ++]\n\npublic function boot(): void\n{\n    Paginator::defaultView('pagination::default'); // [tl! ++]\n}\n```\n\n### Updated `please` file for Laravel 11\n**Affects apps upgrading to Laravel 11 and the new application skeleton (including all upgrades via Laravel Shift).**\n\nPreviously, our `please` command line utility assumed an `app/Console/Kernel.php` file existed in your application. However, with the introduction of Laravel 11, this file is no longer included with the new application structure.\n\nWhen upgrading apps to Laravel 11 with the new application skeleton, you'll need to update the `please` file in your app's root directory:\n\n```php\n#!/usr/bin/env php\n<?php\n\nuse Symfony\\Component\\Console\\Input\\ArgvInput;\n\ndefine('LARAVEL_START', microtime(true));\n\n// Register the Composer autoloader...\nrequire __DIR__.'/vendor/autoload.php';\n\n// Bootstrap Laravel...\n$app = require_once __DIR__.'/bootstrap/app.php';\n\n// Rebind the kernel...\nStatamic\\Console\\Please\\Application::rebindKernel();\n\n// Handle the command...\n$status = $app->handleCommand(new ArgvInput);\n\nexit($status);\n```\n\n## Low impact changes\n\n### Regex Antlers Parser has been removed\n**Affects apps that are still using the `regex` parser.**\n\nThe new Antlers parser was introduced in 3.3 and was made the default in 3.4.\n\nYou would have been using the old Parser if either of these apply:\n- In `config/statamic/antlers.php`, the `version` was set to `regex`.\n- The `antlers.php` file doesn't exist at all.\n\nThe newer parser should be backwards compatible but if you encounter any errors, you may need to check the [docs](/antlers).\n\n### Form submission path config\n**Affects apps that have customized the `submissions` path in `config/statamic/forms.php`.**\n\nThe place to customize the directory for form submissions has moved from `forms.php` into `stache.php`.\n\n```php\n// config/statamic/forms.php\n'submissions' => 'custom path', // [tl! --]\n\n// config/statamic/stache.php\n'stores' => [\n    'form-submissions' => [ // [tl! ++:start]\n        'directory' => 'custom path',\n    ], // [tl! ++:end]\n],\n```\n\n### Validation rule changes\n**Affects apps using `unique_entry_value`, `unique_term_value`, or `unique_user_value` rules.**\n\n_Note that assuming you haven't done anything too unusual, the following changes may have been performed automatically by Statamic during the update process._\n\nWe have updated our custom validation rules to use the more modern Laravel syntax. This means dropping the string based aliases in favor of classes.\n\n```yaml\nvalidate:\n  - 'unique_entry_value:{collection},{id},{site}' #[tl!--]\n  - 'new \\Statamic\\Rules\\UniqueEntryValue({collection},{id},{site})' #[tl!++]\n\n  - 'unique_term_value:{taxonomy},{id},{site}' #[tl!--]\n  - 'new \\Statamic\\Rules\\UniqueTermValue({taxonomy},{id},{site})' #[tl!++]\n\n  - 'unique_user_value:{id}' #[tl!--]\n  - 'new \\Statamic\\Rules\\UniqueUserValue({id})' #[tl!++]\n```\n\n### Antlers sanitization\n**Affects apps or addons using the `sanitize` modifier or the `Html::sanitize` method.**\n\nThe `sanitize` modifier (and `Html::sanitize()` method) method has changed under the hood from using `htmlentities` to `htmlspecialchars`.\n\nThis change allows for things like accent characters (ü, í, etc) to remain unescaped. This is likely what you want to happen anyway, but if you have a reason for them to be converted, you should use `entities` modifier or `Html::entities()` method respectively.\n\n### Seed removed from `shuffle` modifier\n**Affects apps using the shuffle modifier with an argument.**\n\nIn Laravel 11, the underlying randomization technique was made more secure and no longer supports seeds. If you need to support seeds, you will need to use a custom modifier.\n\n```\n{{ my_array | shuffle:123 }} {{# [tl! --] #}}\n{{ my_array | custom_shuffle_with_seed:123 }} {{# [tl! ++] #}}\n```\n\nThe shuffle modifier without an argument will continue to work without any modification needed.\n\n### The `modify_date` modifier is now immutable\n**Affects apps using the modify_date modifier.**\n\nIn Statamic 4, the `modify_date` modifier would modify date variable which would then be reflected elsewhere in your template.\n\nIn Statamic 5, this is fixed, but if you were relying on this incorrect behavior you will need to handle it.\n\n```antlers\n{{ date }} // 1st of may\n{{ date | modify_date('+1 day') }} // 2nd of may\n{{ date }} // 2nd of may {{# [tl! --] #}}\n{{ date }} // 1st of may {{# [tl! ++] #}}\n```\n\n### The `svg` tag sanitizes by default\n**Affects apps that use the `svg` tag.**\n\nThe `{{ svg }}` will now sanitize the output by default. This meant things like JavaScript or other valid but potentially insecure elements will be filtered out.\n\nFor most people this won't be a problem but if you rely on this advanced SVG features, you may want to disable it.\n\n```antlers\n{{ svg\n   src=\"foo.svg\"\n   sanitize=\"false\" {{# [tl! ++] #}}\n}}\n```\n\nAlternatively, you can opt out of it completely in a service provider:\n\n```php\npublic function boot()\n{\n    \\Statamic\\Tags\\Svg::disableSanitization(); // [tl! ++]\n}\n```\n\n### Bard JS value is now an object\n**Affects apps or addons that are manually targeting Bard's value in JS**\n\nPreviously, to prevent issues with how Laravel would trim whitespace on submitted strings, Bard's value would be a JSON stringified version of an object. In Statamic 5, it will just be the object.\n\n```js\nlet bardValue = JSON.parse(getBardValue()); // [tl! --]\nlet bardValue = getBardValue(); // [tl! ++]\n```\n\n### User roles inherit from groups\n**Affects apps or addons using the `roles`/`hasRole` methods on the `User` class, or the `user:is`/`is` tags.**\n\nIn v4, the `User` class's `roles` method would only return roles defined explicitly on the user. It would not return roles inherited through any assigned groups.\n\nThis affected calling the `roles` method directly, the `hasRole` method, or the `user:is` and `is` tags.\n\nThis was a common point of confusion. So in v5, including the inherited roles is the more \"default\" behavior.\n\n```php\n// To get explicit roles...\n$user->roles(); // [tl! --]\n$user->explicitRoles(); // [tl! ++]\n\n// To check if a user has a role explicitly defined...\n$user->hasRole($role); // [tl! --]\n$user->explicitRoles()->has($role->handle()); // [tl! ++]\n\n// To set explicit roles...\n$user->roles($roles); // [tl! --]\n$user->explicitRoles($roles); // [tl! ++]\n\n// To get all roles, including ones through user groups...\ngetAllRoles($user); // (It was complicated) [tl! --]\n$user->roles(); // [tl! ++]\n\n// To check if a user has a role, including ones through user groups...\ngetAllRoles($user)->has($role->handle()); // (It was complicated) [tl! --]\n$user->hasRole($role); // [tl! ++]\n```\n\n\n### Misc class method changes\nThe following methods have been removed:\n- `Statamic\\Entries\\Collection::revisions()` removed. Use `revisionsEnabled()`.\n\nThe following interfaces have added `findOrFail()` methods:\n- `Statamic\\Contracts\\Assets\\AssetContainerRepository`\n- `Statamic\\Contracts\\Assets\\AssetRepository`\n- `Statamic\\Contracts\\Auth\\UserRepository`\n- `Statamic\\Contracts\\Entries\\CollectionRepository`\n- `Statamic\\Contracts\\Entries\\EntryRepository`\n- `Statamic\\Contracts\\Globals\\GlobalVariablesRepository`\n- `Statamic\\Contracts\\Structures\\NavigationRepository`\n- `Statamic\\Contracts\\Taxonomies\\TermRepository`\n\nThe following methods have changed:\n- `Statamic\\StaticCaching\\Cacher::getCachedPage()` now returns a `Statamic\\StaticCaching\\Page`.\n\n### Glide filename parameter has been removed\n\nThis was an undocumented relic of a feature that let you add a \"fake\" or \"vanity\" filename to the end of Glide URLs to be able to improve SEO. This now  happening automatically based on the original filename, rendering this feature unnecessary (this feature was broken in certain situations and as mentioned before — undocumented).\n\nIf you use this `{{ glide filename=\"\" }}` parameter, you'll need to remove it, otherwise nothing will be output.\n\nIf you are hot-linking to any images rendered with manually set filenames in this way, you'll also need to correct those links.\n\n### Entries may now only be queried by a single status\n**Affects any apps or addons filtering entries by `status`.**\n\nPreviously, when querying entries, you could use *any* condition to filter entries.\n\nHowever, in v5, as part of improvements to how statuses work behind the scenes, statuses can now only be filtered using the `is` / `equals` conditions, and the `whereStatus` query method. Some examples:\n\nCollection tag:\n```antlers\n{{ collection:blog status:in=\"published\" }} {{# [tl! --] #}}\n{{ collection:blog status:is=\"published\" }} {{# [tl! ++] #}}\n```\n\nPHP:\n```php\nEntry::query()\n    ->where('collection', 'blog')\n    ->where('status', 'published') // [tl! --]\n    ->whereStatus('published') // [tl! ++]\n    ->get();\n```\n\nREST API:\n```php\n/api/collections/blog/entries?filter[status:in]=published // [tl! --]\n/api/collections/blog/entries?filter[status]=published // [tl! ++]\n```\n\nGraphQL API:\n```graphql\n{\n    entries(\n        collection: \"blog\"\n        filter: {\n            status: { in: \"published\" }         #[tl! --]\n            status: \"published\"                 #[tl! ++]\n        }\n    ) {\n        data {\n            title\n        }\n    }\n}\n```\n\nIf you need to query entries regardless of status, you can pass `any`.\n\n## Zero impact changes\n\nThese are items that you can completely ignore. They are suggestions on how to improve your code using newly added features.\n\n### JS Slug generation\n\nThe `$slugify` JS API has been deprecated. This could be a good time to use the new slug helpers.\n\nIf you are generating simple slug/handles, you can use the global helper:\n\n```js\nlet slug = this.$slugify('Foo Bar'); // foo-bar [tl! --]\nlet slug = str_slug('Foo Bar'); // foo-bar [tl! ++]\n\nlet handle = this.$slugify('Foo Bar', '_'); // foo_bar [tl! --]\nlet handle = snake_case('Foo Bar'); // foo_bar [tl! ++]\n```\n\nIf your slugs need to factor in language logic (like an entry's slug would), then you can use the new server side feature:\n\n```js\nlet slug = getSlug('Foo Bar'); // [tl! --:start]\n\nfunction getSlug(value) {\n    return this.$slugify(value);\n} // [tl! --:end]\n\nlet slug = await getSlug('Foo Bar'); // [tl! ++:start]\n\nasync function getSlug(value) {\n  return this.$slug.async().create('Foo Bar');\n} // [tl! ++:end]\n```\n\n\n### Addon test case\n\nIf you have an addon, there's a good chance your `TestCase` is a bit complicated.\n\nYou should be able to extend the new `AddonTestCase` and specify your service provider in favor of manually wiring up all the Testbench bits.\n\n```php\nuse Orchestra\\Testbench\\TestCase as OrchestraTestCase; // [tl! --]\nuse Statamic\\Testing\\AddonTestCase; // [tl! ++]\n\nabstract class TestCase extends OrchestraTestCase // [tl! --]\nabstract class TestCase extends AddonTestCase // [tl! ++]\n{\n    protected string $addonServiceProvider = YourServiceProvider::class; // [tl! ++]\n\n    protected function getPackageProviders($app) // [tl! --:start]\n    {\n        return [\n            GraphQLServiceProvider::class,\n            StatamicServiceProvider::class,\n            YourServiceProvider::class,\n        ];\n    }\n\n     // etc... [tl! --:end]\n}\n```\n"
  },
  {
    "path": "content/collections/pages/5-to-6.md",
    "content": "---\nid: 9a013ab0-bd21-42e1-84ea-fecd052466e9\nblueprint: page\ntitle: 'Upgrade from 5 to 6'\nintro: 'A guide for upgrading from 5 to 6. For most sites (those running Laravel >= 12), the process will take less than 5 minutes.'\ntemplate: page\n---\n## Overview\n\nFirst read through this guide to see if there's anything that you might need to adjust. While there are many items on this page, a majority of them only apply to addons or custom code. We've noted who each item would apply to so you can more easily scan through the changes.\n\n### Upgrade using Composer\n\nIn your `composer.json`, change the `statamic/cms` requirement:\n\n```json\n\"statamic/cms\": \"^5.0\" // [tl!--]\n\"statamic/cms\": \"^6.0\" // [tl!++]\n```\n\nThen run:\n\n``` shell\ncomposer update statamic/cms --with-dependencies\n```\n\n## High impact changes\n\n### PHP and Laravel support\n**Affects apps using PHP < 8.3 or Laravel < 12.**\n\n- The minimum version of PHP is now 8.3.\n- The minimum version of Laravel is now 12.\n\nWe highly recommend upgrading all the way to Laravel 13 and PHP 8.5.\n\n:::tip\nIf you want to (semi-)automate the Laravel upgrade process, we recommend using [Laravel Shift](https://laravelshift.com/discounts/statamic-1983) (use that link for a special 19.83% discount 🤘).\n:::\n\n### Vue 3\n**Affects apps or addons that use Vue.**\n\nWe have upgraded the Control Panel's version of Vue.js from 2 to 3.\n\nTo keep this upgrade guide manageable, we have a [dedicated page for upgrading from Vue 2 to Vue 3](/upgrade-guide/vue-2-to-3).\n\nIf you do not have any custom Vue components in your app, or in your own addons, you can skip this.\n\n### Timezones\n**Affects apps using dated collections or date fields**\n\n**If your `timezone` setting in `config/app.php` is set to `UTC`, then nothing will change for you.**\n\nDates remain stored in your application's timezone. But now Statamic will convert them to UTC at runtime, which makes it much easier for Statamic to localize them as needed.\n\nThis applies to dated entries or date fields.\n\nFor example, if you have your timezone set to New York (GMT-5:00) and you have a date at 10pm, when it gets converted to UTC it will be 5 hours ahead - in the next day!\n\n```php\n// config/app.php\n'timezone' => 'America/New_York',\n```\n```yaml\n# an-entry.md\nmy_date_field: '2025-03-06 22:00'\n```\n```php\n$entry->my_date_field;\n// 5.x: Carbon { 2025-03-06 22:00 America/New_York } [tl! --]\n// 6.x: Carbon { 2025-03-07 03:00 UTC } [tl! ++]\n```\n\n::tabs\n::tab antlers\n```antlers\n{{ my_date_field | iso_format('JJJJ') }}\n5.x: Thursday, March 6, 2025 10:00 PM {{# [tl! --] #}}\n6.x: Friday, March 7, 2025 3:00 AM {{# [tl! ++] #}}\n```\n::tab blade\n```blade\n{{ Statamic::modify($my_date_field)->iso_format('JJJJ') }}\n5.x: Thursday, March 6, 2025 10:00 PM {{-- [tl! --] --}}\n6.x: Friday, March 7, 2025 3:00 AM {{-- [tl! ++] --}}\n```\n::\n\nIt's best practice to keep dates as UTC until you're ready to display them, which means modifiers will deal with UTC versions. But, you can opt into automatic conversion to your display timezone by changing the following in `config/statamic/system.php`:\n\n```php\n'localize_dates_in_modifiers' => true, // [tl! ++] \n```\n\nThis settings _should_ have been automatically set to `true` by Statamic during the upgrade, but you should confirm it.\n\nFor more information on how Timezones work in Statamic 6, please see the [Timezones guide](/knowledge-base/tips/timezones).\n\n#### Control Panel\nDates in the Control Panel are now localized to the user's operating system timezone, rather than the application timezone.\n\nFor example, on Statamic 5, if you were in a different timezone to what your app was configured in, and you select a date from the date picker, that date would be treated as the date for the app's timezone. Not your timezone.\n\nThis was a common cause of confusion, which was one of the main reasons for all these changes.\n\nNow in Statamic 6, the date you pick will be the date in **your** timezone.\n\nThere is nothing for you to change except your expectations when working with dates, and instructing your clients about it. \n\n#### REST API & GraphQL\nDates will now be returned by Statamic's REST API and GraphQL API in UTC, allowing you to localize them as needed on your frontend.\n\n#### Changing your app timezone\nIt's best practice to set your app's timezone to UTC. However, changing the timezone in an existing project is a big undertaking and could mean lots of content and dates need to be updated.\n\nStatamic 6 **does not** require that you change your timezone to UTC. But if you *want* to, we have provided a way to automate it.\n\n[Read how to change your timezone to UTC](/tips/change-timezone-to-utc).\n\n## Medium impact changes\n\n### Carbon 3\n\nSupport for [Carbon 2.x](https://carbon.nesbot.com/docs/) has been removed. All Statamic 6 sites now require [Carbon 3.x](https://carbon.nesbot.com/docs/#api-carbon-3).\n\nIf you're using any of Statamic's `months_ago`, `weeks_ago`, `days_ago`, `hours_ago`, `minutes_ago`, and `seconds_ago` modifiers, you will notice that they now return floats instead of integers. Comparing against past timestamps will also result in negative numbers.\n\nYou _may_ need to updates your templates to account for these changes.\n\n### Globals\n\nWe have made various changes to how globals are stored and localized. If you use globals in your app, please read through these changes and take any necessary action.\n\n#### Single site installs: Variables are now stored separately from the global set config\nGlobal Variables are now stored separately from the global set's config, allowing config and content to be properly separated.\n\nInstead of living under a `data` key in the global set's YAML file, they now live in a separate YAML file in a directory named after your default site, usually called `default`.\n\n**Before:**\n```yaml\n# content/globals/seo.yaml\n\ntitle: SEO\ndata:\n  meta_description: Synthwave nostalgia with soaring sax and heartfelt vibes.\n  meta_image: the-midnight.jpg\n```\n\n**After:**\n```yaml\n# content/globals/seo.yaml\n\ntitle: SEO\n```\n\n```yaml\n# content/globals/default/seo.yaml\n\nmeta_description: Synthwave nostalgia with soaring sax and heartfelt vibes.\nmeta_image: the-midnight.jpg\n```\n\n_This change may have been performed automatically by Statamic during the upgrade process._\n\n**Note:** This change _doesn't_ affect multi-sites or sites storing global variables in the database, since they're already stored separately.\n\n#### Multi-sites: Localized sites are now determined by the `sites` array\nPreviously, when you configured the sites a global set was localized into, it created the global variable files for you, then used the existence of those files to determine which sites the global set was localized into.\n\nNow, Statamic will use the `sites` array in the global set's config file to determine which sites the global set is localized into, as well as mapping the origins for localizations.\n\n```yaml\n# content/globals/seo.yaml\n\ntitle: SEO\nsites:\n  en: null\n  fr: en # Localized from en\n  de: null # No origin\n```\n\n_This change may have been performed automatically by Statamic during the upgrade process._\n\n**Note:** This change _doesn't_ affect single-site installs.\n\n#### Events\nPreviously, when saving global variables in the Control Panel, the entire global set would have been saved, causing the `GlobalSetSaving`, `GlobalSetCreated` and `GlobalSetSaved` events to be dispatched. However, now, only the global variable _itself_ will be saved.\n\nThis means that if you were listening to any of these events to pick up changes to global variables, you should instead listen for the [`GlobalVariablesSaving`](https://statamic.dev/extending/events#globalvariablessaving), [`GlobalVariablesCreated`](https://statamic.dev/extending/events#globalvariablescreated) and [`GlobalVariablesSaved`](https://statamic.dev.test/extending/events#globalvariablessaved) events.\n\n#### Removed methods on `GlobalSet` class\nThe `addLocalization` and `removeLocalization` methods have been removed from the `GlobalSet` class. \n\nIf you were calling these methods in your app, you should update your code to call `save` and `delete` on the `Variables` class instead.\n\n```php\n$globalSet->addLocalization($globalSet->makeLocalization('en')->data(['foo' => 'bar'])); // [tl! remove]\n$globalSet->removeLocalization('en'); // [tl! remove]\n\n$globalSet->in('en')->data(['foo' => 'bar'])->save(); // [tl! add]\n$globalSet->in('en')->delete(); // [tl! add]\n```\n\n### Search: `'searchables' => 'all'`\n**Affects apps using `'searchables' => 'all'` in their search config.**\n\nPreviously, you could set `'searchables' => 'all'` on a search index to include entries, terms, assets, users and anything provided by [custom searchables](/frontend/search#custom-searchables).\n\nHowever, in v6, to split out search between the frontend and the Control Panel, support for `'searchable' => 'all'` has been removed.\n\nYou can now either use `'searchables' => 'content'` - which includes entries, terms and assets (**not** users) - or explicitly list [the searchables](/frontend/search#searchables) you want:\n\n```php\n// config/statamic/search.php\n\n'indexes' => [  \n  \n    'default' => [  \n        'driver' => 'local',  \n        'searchables' => ['collection:blog', 'taxonomy:categories', 'assets:*'],  \n        'fields' => ['title'],  \n    ],\n    \n],\n```\n\nWe’ve avoided automating this migration so you can intentionally decide whether users should be included.\n\n### Breadcrumbs\n**Affects apps or addons displaying breadcrumbs in the Control Panel.**\n\nBreadcrumbs are now generated from items in the Control Panel navigation, rather than needing to be passed into views manually.\n\nYou should remove references to the `Breadcrumb` class in your code, as well as the `<breadcrumbs>` Vue component.\n\n``` php\nuse Statamic\\CP\\Breadcrumbs; // [tl! remove:start]\n\n$crumbs = Breadcrumbs::make([\n    ['text' => 'First', 'url' => '/first'],\n    ['text' => 'Second', 'url' => '/second'],\n]);\n\nreturn view('myview', ['crumbs' => $crumbs]);// [tl! remove:end]\nreturn view('myview'); // [tl! add]\n```\n\n``` blade\n<breadcrumbs :crumbs='@json($crumbs)'></breadcrumbs> {{-- [tl! remove] --}}\n```\n\n``` vue\n<template>\n    <breadcrumbs :crumbs=\"crumbs\" /> <!-- [tl! remove] -->\n</template>\n<script>\nexport default {\n    data()\n        return {\n            crumbs: [ // [tl! remove:start]\n                ['text' => 'First', 'url' => '/first'],\n                ['text' => 'Second', 'url' => '/second'],\n            ] // [tl! remove:end]\n        ]\n    }\n}\n</script>\n```\n\nTo learn more about customizing breadcrumbs, please refer to the [CP Navigation documentation](/extending/cp-navigation#breadcrumbs).\n\n### Starter Kits: Removed `export_as` option\n**Affects starter kits using the `export_as` option.**\n\nThe `export_as` option has been removed in favor of the new `package` [folder convention](/starter-kits/creating-a-starter-kit#the-starter-kit-package), which makes dealing with multiple README.md files easier, for example.\n\n### Custom Icons\n\nIf you are using any `icon` fieldtypes with the `directory` option, you will need to register your icon set and reference the set name instead.\n\n```php\n// AppServiceProvider.php\nuse Statamic\\Facades\\Icon; // [tl! ++]\n\npublic function boot(): void\n{\n    Icon::register('heroicons', base_path('resources/heroicons')); // [tl! ++]\n}\n```\n\n```yaml\n-\n  handle: favourite_icon\n  field:\n    type: icon\n    directory: resources/heroicons # [tl! --]\n    set: heroicons # [tl! ++]\n```\n\nIf you are using custom icons for your Replicator or Bard sets, the method changed:\n\n```php\nuse Statamic\\Fieldtypes\\Sets;\n\nSets::setIconsDirectory(directory: 'path/to/heroicons'); // [tl! --]\nSets::useIcons('heroicons', 'path/to/heroicons'); // [tl! ++]\n```\n\nOr if you need to do both, you can reference the set name:\n\n```php\nIcon::register('heroicons', base_path('resources/heroicons'));\nSets::useIcons('heroicons');\n```\n\n### Bard: `inline: break`\n**Affects apps using `inline: break` on Bard fields.**\n\nWe've simplified the `inline` config option on Bard fields. It is now a toggle, as opposed to a select dropdown with various modes.\n\nIf you were using the `inline: break` option, you should use the new `inline_hard_breaks` option instead:\n\n```yaml\ninline: inline # [tl! --]\ninline: true # [tl! ++]\ninline_hard_breaks: true # [tl! ++]\n```\n\n### Custom Control Panel Pages\n**Affects apps or addons with custom Control Panel pages.**\n\nIf your app or addon includes custom Control Panel pages, we recommend migrating them to Vue with [Inertia.js](https://inertiajs.com/) for the best experience. This provides SPA-style page transitions and a more consistent experience alongside the rest of the Control Panel. See the [CSS & JavaScript](/control-panel/css-javascript#inertia) page for more details.\n\nFor simpler addons, or if you prefer not to use Vue, you may continue to build Control Panel pages using Blade, but there are a few limitations to be aware of:\n\n- Blade-rendered pages trigger a full page reload rather than the SPA-style transitions used elsewhere in the Control Panel.\n- Under the hood, Blade views are rendered inside a Vue component, which means `<script>` tags are not supported within your views.\n\nStatamic 6 ships with a growing set of [Control Panel UI components](https://ui.statamic.dev). To help your pages feel more native, you may want to integrate these components where possible.\n\n### Custom Utilities\n**Affects apps or addons with custom utilities.**\n\nUtilities may now [return Inertia.js components](/control-panel/utilities#creating-a-utility) instead of Blade views, by replacing `->view()` with `->inertia()`:\n\n```php\n$utility\n    ->view('my-utility', fn ($request) => ['foo' => 'bar']) // [tl! --]\n    ->inertia('my-addon/MyUtility', fn ($request) => ['foo' => 'bar']) // [tl! ++]\n```\n\nUtilities that use Blade will continue to work without any changes. However, the limitations mentioned above for custom Control Panel pages also apply to utilities.\n\nIf your Blade view uses `@extends('statamic::layout')`, you can remove the layout wrapper:\n\n``` blade\n@extends('statamic::layout') {{-- [tl! --] --}}\n\n@section('content') {{-- [tl! --] --}}\n    <div>...</div>\n@endsection {{-- [tl! --] --}}\n```\n\n## Low impact changes\n\n### Database users\n**Affects apps that store users in the database.**\n\nDuring the upgrade process, Statamic will attempt to publish two database migrations: \n\n- One to add columns to the `users` table to support [two-factor authentication](/users#two-factor-authentication)\n- One to create a `webauthn` table for [passkeys](/users#passkeys)\n\nRun `php artisan migrate` to apply them. \n\nYou should also add a cast for the new `two_factor_confirmed_at` column on your `User` model:\n\n```php\nprotected function casts(): array  \n{  \n    return [  \n        'email_verified_at' => 'datetime',  \n        'preferences' => 'json',  \n        'two_factor_confirmed_at' => 'datetime',  // [tl! add]\n    ];  \n}\n```\n\n#### Migrations aren't published\nIf the migrations aren't published automatically, please follow these steps:\n\n1. Generate the auth migrations:\n    ```php\n    php please auth:migration\n    ```\n   This creates two migrations. You can safely delete the `update_users_table` migration.\n2. Create a migration to add the required columns to the `users` table:\n    ```php\n    <?php  \n      \n    use Illuminate\\Database\\Migrations\\Migration;  \n    use Illuminate\\Database\\Schema\\Blueprint;  \n    use Illuminate\\Support\\Facades\\Schema;  \n      \n    return new class extends Migration  \n    {  \n        /**  \n         * Run the migrations.     \n         */    \n        public function up(): void  \n        {  \n            Schema::table('users', function (Blueprint $table) {  \n                $table->text('two_factor_secret')->nullable();  \n                $table->text('two_factor_recovery_codes')->nullable();  \n                $table->timestamp('two_factor_confirmed_at')->nullable();  \n            });  \n        }  \n      \n        /**  \n         * Reverse the migrations.     \n         */    \n        public function down(): void  \n        {  \n            Schema::table('users', function (Blueprint $table) {  \n                $table->dropColumn(['two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at']);  \n            });  \n        }\n    };\n    ```\n3. Run the migrations:\n    ```php\n    php artisan migrate\n    ```\n### Moment.js has been removed\n**Affects addons and custom code directly using moment.js**\nIf you are using moment.js you should replace it with an alternative. We suggest using native JS code which should be enough these days. For example:\n\n```js\nmoment().seconds(); // [tl! --]\nnew Date().getSeconds(); // [tl! ++]\n```\n\nYou should search for `moment` or `$moment` references in your code and replace appropriately.\n\n[Here is a good resource](https://github.com/you-dont-need/You-Dont-Need-Momentjs) on how to migrate away from Moment.js.\n\n### Glide 3\n\nStatamic is now using Glide 3, which uses `intervention/image` 3.x under the hood.\n\nIn most cases, you shouldn't notice any difference, however, if you have a custom manipulator, it will need to be updated. Please refer to the [Glide 3 changelog](https://glide.thephpleague.com/3.0/changelog/) for more information.\n\n### Algolia\n\nStatamic now requires v4 of the `algolia/algoliasearch-client-php` package. \n\nIf you interact with this package directly, please [refer to its changelog](https://github.com/algolia/algoliasearch-client-php/blob/main/CHANGELOG.md) to review any changes that may affect your code.\n\n### Wildcard tags\n**Affects apps using the `{{ session }}`, `{{ cookie }}`, `{{ nav }}` and `{{ redirect }}` tags.**\n\nIn previous versions of Statamic, these tags accepted a wildcard value, allowing you to pass a handle or key directly:\n\n```antlers\n{{ session:foo }}\n```\n\nHere, `foo` is the wildcard value. However, if a variable named foo existed in the template’s context, its value would be used instead of the literal string `\"foo\"`, potentially causing unintended behaviour.\n\nIn Statamic 6, wildcard values are always treated as literal strings. If you need to pass a variable dynamically, you should use the appropriate parameter instead:\n\n```antlers\n{{ session :handle=\"foo\" }}\n```\n\nThis ensures that `foo` is interpreted as a variable rather than a fixed string.\n\n### Removed methods\n\nThe following methods have been removed in Statamic 6.\n\n#### `Site::setConfig()`\n\nThe `Site::setConfig()` method was deprecated in Statamic 5. It has now been removed. You should use the `Site::setSites()` method instead:\n\n```php\nSite::setConfig([ // [tl! remove:5]\n    'sites' => [\n        'english' => ['name' => 'English', 'locale' => 'en_US', 'url' => '/en'],\n        'french' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => '/fr'],\n    ],\n]);\n\nSite::setSites([ // [tl! add:3]\n    'english' => ['name' => 'English', 'locale' => 'en_US', 'url' => '/en'],\n    'french' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => '/fr'],\n]);\n```\n\n#### `NavItem::active()`\n\nWhen adding nav items to the Control Panel, it was previously possible to specify a regex pattern used to determine if the nav item was active. \n\nHowever, after some improvements in Statamic, this method is no longer needed and has been removed after a deprecation period. You can safely remove it from your nav items:\n\n```php\nNav::extend(function ($nav) {\n    $nav->create(ucfirst($type))\n        ->section('SEO')\n        ->route(\"ecommerce.orders.index\")\n        ->active(\"ecommerce/orders\") // [tl! --]\n        ->icon($defaults->first()['type_icon']);\n});\n```\n\n#### `Entry::addLocalization()`\n\nThe `Entry::addLocalization()` method has been removed. If you were using it, you should now use the `Entry::makeLocalization()` method instead.\n\n```php\n$entry->addLocalization($entry); // [tl! --]\n$entry->makeLocalization('german'); // [tl! ++]\n```\n\n#### `ApiController::filterSortAndPaginate()`\n\nThe `filterSortAndPaginate()` method on the `ApiController` class has been renamed to `updateAndPaginate()`.\n\n```php\n$this->filterSortAndPaginate($query); // [tl! --]\n$this->updateAndPaginate($query); // [tl! ++]\n```\n\n### `statamic` cache driver has been removed\n**Affects apps or addons using the `statamic` cache driver.**\n\nThe `statamic` cache driver has been removed. If you were using it, you should switch to the `file` cache driver instead.\n\n```php\n// config/cache.php\n\n'driver' => 'statamic',  // [tl! --]\n'driver' => 'file', // [tl! ++]\n```\n\n### Relate tag has been removed\n\nThe `relate` tag left over from Statamic 2 has been removed. You can safely remove it and rely on [augmentation](/augmentation) instead.\n\n```antlers\n{{ relate:products }} {{# [tl! remove:2] #}}\n    {{ title }} \n{{ /relate:products }}\n\n{{ products }} {{# [tl! add:2] #}}\n    {{ title }}\n{{ /products }}\n```\n\n### `docs-callout` partial has been replaced\n**Affects apps or addons with custom Blade views in the Control Panel.**\n\nStatamic's `docs-callout` partial has been replaced with a Vue component. If you were using this partial in your custom Blade views, you should update your code to use the component instead.\n\n**Before:**\n```blade\n@include(\n    'statamic::partials.docs-callout',\n    [\n        'topic' => __('Blueprints'),\n        'url' => Statamic::docsUrl('blueprints'),\n    ]\n)\n```\n\n**After:**\n```blade\n<ui-docs-callout :topic=\"__('Blueprints')\" url=\"blueprints\" />\n```\n\n### Section fieldtype has been deprecated\n\nThe Section fieldtype has been deprecated and will be removed in Statamic 7. We recommend using sections in the blueprint builder instead.\n\n### `urlencode` and `rawurlencode` modifiers now encode forward slashes\n**Affects apps or addons using the `urlencode` or `rawurlencode` modifiers.**\n\nThe `urlencode` and `rawurlencode` modifiers now encode forward slashes (`/`). If you were relying on forward slashes not being encoded, you can use the `urlencode_except_slashes` and `rawurlencode_except_slashes` modifiers instead.\n\n```antlers\n{{ my_string | urlencode }} // [tl! --]\n{{ my_string | urlencode_except_slashes }} // [tl! ++]\n\n{{ my_string | rawurlencode }} // [tl! --]\n{{ my_string | rawurlencode_except_slashes }} // [tl! ++]\n```\n\n### `logged_in` variable now uses auth guard from Statamic's `users` config\n**Affects app with custom auth guards using the `logged_in` variable** \n\nThe `logged_in` variable now uses the authentication guard configured in Statamic's `users` config file, rather than the default Laravel guard.\n\n```php\n// config/auth.php [tl! **]\nreturn [\n    'defaults' => [ // [tl! **]\n        // v5 used this 👇 [tl! -- **]\n        'guard' => env('AUTH_GUARD', 'web'), // [tl! **]\n        'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),\n    ],\n]\n\n// config/statamic/users.php [tl! **]\nreturn [\n    'guards' => [  // [tl! **]\n        'cp' => 'web',\n        // v6 uses this 👇 [tl! ++ **]\n        'web' => 'web',  // [tl! **]\n    ],   \n];\n```\n\nIn the majority of cases, this results in the exact same behavior.\n\n\n### Full-measure Static caching\n**Affects apps that use full-measure static caching and customize the JS behavior**\n\nThe `nocache_js_position` config option has been removed.\n\nThe `StaticCache::nocacheJs($script)` method now only replaces the nocache script. It doesn't touch the CSRF token script. Use the new `StaticCache::csrfTokenJs($script)` method if you want to customize the CSRF script.\n\nThe `statamic:nocache.replaced` JavaScript event will no longer be dispatched when CSRF tokens are updated. They now get their own separate event: `statamic:csrf.replaced`.\n\n```js\ndocument.addEventListener('statamic:nocache.replaced', (event) => {\n    console.log('nocache and csrf have both been replaced');  // [tl! --]\n    console.log('nocache has been replaced');  // [tl! ++]\n});\ndocument.addEventListener('statamic:csrf.replaced', (event) => {  // [tl! ++]\n    console.log('csrf has been replaced');  // [tl! ++]\n}); // [tl! ++]\n```\n\n### Link fieldtype API and GraphQL responses\n\nThe `link` fieldtype in API responses would previously just output the url. In v6 they output an array with more data.\n\nGraphQL needs a subselection:\n\n```graphql\ndata {\n  link_field #[tl! --]\n  link_field { #[tl! ++]\n    url #[tl! ++]\n    title #[tl! ++]\n  }\n}\n```\n\nThe REST API will now just give you sub-keys:\n\n```json\n{\n  \"data\" {\n    \"link_field\": \"/the-url\"  // [tl!--]\n    \"link_field\": { // [tl!++]\n        \"url\": \"/the-url\",  // [tl!++]\n        \"title\": \"The Title\"  // [tl!++]\n    },  // [tl!++]\n  }\n}\n```\n\n### Search: Changes to custom searchables\n\nSearchables should now return collections of references (eg. `entry::the-entry-id`) instead of objects.\n\n```php\nreturn Thing::query()->lazy(); // [tl! remove]\nreturn Thing::query()->lazy()->pluck('reference'); // [tl! add]\n```\n\nIf your searchable allows for it, you may want to consider adding support for query scopes, which we now recommend over filtering.\n\n```php\n$query = Thing::query();\n\n$this->applyQueryScope($query);\n\nreturn $query->pluck('reference');\n```\n\nIf you support filtering, you may want to split the \"with filter\" & \"without filter\" cases into separate return statements.\n\nThe `->filter()` method evaluates every item in the collection, which is an unnecessary performance hit when no filter is configured:\n\n```php\n$query = Thing::query();\n\nif ($this->hasFilter()) {\n\treturn $query\n\t\t->lazy(config('statamic.search.chunk_size'))  \n\t\t->filter($this->filter())  \n\t\t->values()  \n\t\t->map->reference();\n}\n\nreturn $query->pluck('reference');\n```\n\n### Search: Changes to custom search drivers\n\nThe `insertDocument` method is now public:\n\n```php\nprotected function insertDocuments(Documents $documents) // [tl! remove]\npublic function insertDocuments(Documents $documents) // [tl! add]\n```\n\nIf you were previously overriding the `insertMultiple` method to chunk documents, you don't need to do that anymore (chunking is now handled by the base method).\n\nIf you need to manipulate the fields array before it gets sent to your index, you may define a `fields` method:\n\n```php\npublic function fields(Searchable $searchable)\n{\n    return array_merge(\n        $this->searchables()->fields($searchable),\n        [\n            '_some_special_field_' => $searchable->id(),\n        ]\n    );\n}\n```\n\n### Bard: Strikes now output `<strike>` tags, rather than `<s>`\n**Affects apps relying on the `<s>` tag for strikes.**\n\nStrikes in Bard would previously output `<s>` tags. However, as part of a TipTap update, they now output `<strike>` tags.\n\nIf you were styling `<s>` tags, you should update your CSS to target the `<strike>` tag instead.\n\n### Super user authorization\n\nStatamic previously granted super users _all_ permissions globally, bypassing any custom policies or authorisation checks in custom application code and third-party packages.\n\nIn Statamic 6, super users are only granted permission for Statamic-related functionality. If you were relying on the previous behaviour, you may need to update your authorisation logic or register a `Gate::before()` hook to restore it:\n\n```php\nuse Illuminate\\Support\\Facades\\Gate;\nuse Statamic\\Facades\\User;\n\nGate::before(function ($user, $ability): ?bool {\n    return optional(User::fromUser($user))->isSuper() ? true : null;\n});\n```\n\n### `->where('status', '...')` no longer supported for querying entries\n**Affects apps or addons querying entries with `->where('status', '…')`**\n\nQuerying entries by status using `->where('status', '...')` [was deprecated in v5](/upgrade-guide/4-to-5#entries-may-now-only-be-queried-by-a-single-status) and will now throw an exception in v6.\n\nYou should use the `->whereStatus()` method instead:\n\n```php\nEntry::query()\n    ->where('collection', 'blog')\n    ->where('status', 'published') // [tl! --]\n    ->whereStatus('published') // [tl! ++]\n    ->get();\n```\n\n\n## Zero impact changes\n\n### URLs for database nocache regions are now MD5-hashed\n**Affects apps storing nocache regions in the database.**\n\nTo avoid database column length limits, Statamic now stores an MD5 hash of the URL in the `nocache_regions.url` column instead of the plain URL.\n\nAfter upgrading, any previously cached nocache regions will be treated as uncached and re-inserted into the database using the hashed URL format.\n\nIf you’d prefer to hash existing URLs rather than letting them be regenerated, you can do so manually with a migration:\n\n```php\npublic function up(): void\n{\n    DB::transaction(function () {\n        DB::connection(config('statamic.static_caching.nocache_db_connection'))\n            ->table('nocache_regions')\n            ->whereLike('url', 'http%')\n            ->orderBy('key')\n            ->chunk(100, function ($regions) {\n                foreach ($regions as $region) {\n                    DB::connection(config('statamic.static_caching.nocache_db_connection'))\n                        ->table('nocache_regions')\n                        ->where('key', $region->key)\n                        ->where('url', $region->url)\n                        ->update(['url' => md5($region->url)]);\n                }\n            });\n    });\n}\n```\n"
  },
  {
    "path": "content/collections/pages/actions.md",
    "content": "---\ntitle: Actions\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569347227\nintro: Actions allow you to perform tasks on one or more items. You can trigger actions by selecting multiple items in a listing, or using each item's contextual menu.\nid: ba2e6172-b4dc-443b-8230-b770dec1423c\n---\n\n:::tip\nActions allow you to perform tasks on PHP-based items like Entries. If you'd like to perform actions on specific fields within the publish form, check out [Field Actions](/extending/field-actions).\n:::\n\n## Defining an action\n\nYou may create an action using the following command, which will generate a class in the `App\\Actions` namespace.\n\n``` shell\nphp please make:action\n```\n\n### Basics\n\nThe most basic action should have a `run` method.\n\n``` php\nuse Statamic\\Actions\\Action;\n\nclass Delete extends Action\n{\n    public function run($items, $values)\n    {\n        $items->each->delete();\n\n        return trans_choice('Item deleted.|:count items deleted.', $items);\n    }\n}\n```\n\nThe `run` method is for executing the task. You will be provided with a collection of `$items`, and any submitted `$values` (more about those later).\n\nYou may customize the outcome of the action by providing a [response](#responses).\n\n### Redirects\n\nIf you want to redirect after your action completes, override the `redirect` method and return a route or URL:\n\n``` php\npublic function redirect($items, $values)\n{\n    return route('some.where.over.the', $rainbow);\n}\n```\n\n### Downloads\n\nTo produce a download, override the `download` method and return a file path or download response:\n\n``` php\npublic function download($items, $values)\n{\n    return storage_path('some/file.pdf');\n}\n```\n\n## Registering an Action\n\nAny action classes in the `App\\Actions` namespace will be automatically registered.\n\nIf you would like to store them elsewhere, you can manually register an action in a service provider by calling the static `register` method on your action class.\n\n``` php\npublic function boot()\n{\n    Your\\Action::register();\n}\n```\n\n## Setting a Title\n\nEach action button will have an automatic name provided by the `HasTitle` trait that can be set with a static `title()` method on your action class.\n\n``` php\npublic static function title()\n{\n    return __(\"That's Some Sweet Action\");\n}\n```\n\n## Setting an Icon\n\nYou may set an icon for an action via the `$icon` property:\n\n``` php\nprotected $icon = 'trash';\n```\n\nFor a full list of available icons, please see the [Icon component's docs](https://ui.statamic.dev/?path=/docs/components-icon--docs#available-icons).\n\n## Filtering Actions\n\nYou may limit which items an action can be applied to using the `visibleTo` method. For example, if you want your action to only be used by entries, you can return a boolean like this:\n\n``` php\nuse Statamic\\Contracts\\Entries\\Entry;\n\npublic function visibleTo($item)\n{\n    return $item instanceof Entry;\n}\n```\n\n:::best-practice\nDon't include authorization in your `visibleTo` method. Instead, use the authorize method below.\n:::\n\n## Collection Actions\n\nActions aren't just for entries, terms, assets, and users. You can also write actions that target [collections](/collections) themselves. They appear in the contextual menu on the collections listing page, and in the twirldown menu on a collection's entries listing page.\n\nUse the `visibleTo` method to scope your action to collections, and you'll receive `Collection` instances in `run`.\n\n``` php\nuse Statamic\\Contracts\\Entries\\Collection;\n\nclass ClearCache extends Action\n{\n    protected $icon = 'refresh';\n\n    public function visibleTo($item)\n    {\n        return $item instanceof Collection;\n    }\n\n    public function run($collections, $values)\n    {\n        $collections->each(fn ($collection) => cache()->forget(\"collection.{$collection->handle()}\"));\n\n        return trans_choice('Cache cleared.|:count caches cleared.', $collections);\n    }\n}\n```\n\nAfter the action runs from a collection's entries listing page, the page will reload (unless you return a [redirect](#redirects) or [download](#downloads) response).\n\n## Authorizing Actions\n\nBefore any actions are run, Statamic will make sure the user is allowed to run them. You can return a boolean like this:\n\n``` php\npublic function authorize($user, $item)\n{\n    return $user->can('edit', $item);\n}\n```\n\nBy default, there is no authorization.\n\n## Dangerous Actions\n\nYou can mark an action as dangerous, which will give it red text and more sinister looking confirmation dialog.\n\n``` php\nprotected $dangerous = true;\n```\n\n## Context\n\nEach action may have additional contextual data passed to it depending on which listing it's being used within. For example, you may find\nthe collection handle when used inside an entry listing, or the asset container handle when used in an asset listing\n\n``` php\n$this->context; // ['collection' => 'blog']\n```\n\nYou may find this useful when building confirmation dialog fields:\n\n## Adding Fields\n\nBy default, an action will prompt you with an \"Are you sure?\" dialog.\n\nYou're free to add additional fields to the action by adding a `$fields` property with a fieldset-style definition. Each will be added to the confirmation dialog.\n\n``` php\nprotected $fields = [\n    'message' => [\n        'type' => 'text',\n        'validate' => 'required|min:40',\n    ]\n];\n```\n\nIf you need more control over the fields, you can use the `fieldItems` method instead. Within this method, you can use `$this->context`. Then return the same array as described above.\n\n``` php\nprotected function fieldItems()\n{\n    return [\n        'message' => ['type' => 'text'],\n    ];\n}\n```\n\nThe values entered into these fields when a user runs the action will be passed into the `run` method.\n\n\n## Responses\n\nWithin an action's `run` method, you may return different things.\n\n### Void\nReturning nothing will result in a generic success toast notification saying \"Action Completed\".\n\n```php\npublic function run($values)\n{\n    // do something, but don't return anything\n}\n```\n\n### String\nReturning a string will customize the toast notification text.\n\n```php\npublic function run($values)\n{\n    // do something\n\n    return __('The thing was done.');\n}\n```\n\n### Array\nYou may return an array with a `message` key in it. The message will be shown in the toast notification. Any additional keys will be passed into the event handler, useful if you are implementing your own listing component.\n\n```php\npublic function run($values)\n{\n    // do something\n\n    return [\n        'message' => 'This will be in the toast.',\n        'foo' => 'bar',\n    ];\n}\n```\n```html\n<ItemActions ... @completed=\"completed\" />\n```\n```js\ncompleted(success, response) {\n  this.$toast.success(response.message);\n  this.doSomethingWithFoo(response.foo);\n}\n```\n\n### Custom JavaScript Callback\n\nYou may return an array with a `callback` key in it. This should be an array with the name of the callback, and any arguments it should receive.\n\n```php\npublic function run($values)\n{\n    // do something\n\n    return [\n        'callback' => ['myCallback', 'arg1', 'arg2'],\n    ];\n}\n```\n\nYou can provide the callback from your JavaScript.\n\n```js\nStatamic.$callbacks.add('myCallback', function (foo, bar) {\n  console.log(foo, bar); // \"arg1\", \"arg2\"\n});\n```\n\n:::tip\nA common reason for wanting to use JavaScript here is to copy a value to the user's clipboard. There's a native callback you can use so you don't need to write the JavaScript yourself:\n\n```php\nreturn [\n    'callback' => ['copyToClipboard', 'text to copy']\n];\n```\n:::\n\n### Disabling the toast\nYou may wish to disable the toast notification, perhaps if you are planning to trigger your own notification as part of your JavaScript callback. You can disable it by passing a value of `false`.\n\n```php\npublic function run($values)\n{\n    // do something\n\n    return [\n        'message' => false,\n    ];\n}\n```\n"
  },
  {
    "path": "content/collections/pages/addons.md",
    "content": "---\nid: a7cd76f6-cef0-43d3-b561-498777673308\ntitle: Addons\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/advanced-topics.md",
    "content": "---\nid: a8166db6-860a-44d7-8bb0-8e6baaab995f\ntitle: 'Advanced Topics'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/all-fieldtypes.md",
    "content": "---\nid: 25f01cb5-1ca9-44a1-af1b-885b32b05cc8\nblueprint: page\ntitle: 'All Fieldtypes'\ntemplate: fieldtypes/index\n---\n"
  },
  {
    "path": "content/collections/pages/all-modifiers.md",
    "content": "---\nid: ccb11ef2-eef3-4c56-9052-e55905cffd1a\nblueprint: page\ntitle: 'All Modifiers'\ntemplate: modifiers/index\n---\n"
  },
  {
    "path": "content/collections/pages/all-tags.md",
    "content": "---\nid: 424f5092-be39-449a-b660-f4e077631487\nblueprint: page\ntitle: 'All Tags'\nintro: 'Tags are Antlers expressions giving you the ability to fetch, filter, and display content, enhance and simplify your markup, build forms, and build dynamic functionality.'\ntemplate: tags.index\nmount: tags\n---\n"
  },
  {
    "path": "content/collections/pages/all-variables.md",
    "content": "---\nid: 0574b585-8ed7-4a51-acc4-6f234f8c42e8\nblueprint: page\ntitle: 'All Variables'\nintro: 'Context-aware variables are always available in your views, giving you access to dynamic information about the current URL, user, loaded entry, site settings, and more.'\ntemplate: variables.index\nmount: variables\n---\n"
  },
  {
    "path": "content/collections/pages/all-widgets.md",
    "content": "---\nid: 95da21f5-6011-4eae-973e-dfccc23ddfc3\nblueprint: page\ntitle: 'All Widgets'\ntemplate: listing\n---\n"
  },
  {
    "path": "content/collections/pages/antlers.md",
    "content": "---\nid: d37b2af2-f2bf-493a-9345-7087fb5929ce\nblueprint: page\ntitle: 'Antlers Templates'\nintro: |-\n  Antlers is a simple and powerful templating engine provided with Statamic.  It can fetch and filter content, display, modify, and set variables, tap into core features like user authentication and search, and handle complex logic. Coming from Laravel and want to stick to Blade? [We got you covered](/blade).\ntemplate: page\n---\n## Overview\n\nAntlers is one of Statamic's foundational features. It consists of a tightly coupled template language, runtime engine, and library of [Tags](#tags) that can be used to fetch and manipulate data, handle logic, and help you write easier to maintain HTML.\n\nAntlers templates are also called views. Any files in the `resources/views` directory with an `.antlers.html` file extension is an \"Antlers Template\", and will be parsed with the Antlers Engine.\n\n:::tip\nThe `.antlers.html` extension is important. Without it, your template will be rendered as **unparsed, static HTML**.\n:::\n\n\n### Basic example\n\nAntlers adds dynamic features to HTML in the form of \"tags\" – expressions contained inside a pair of curly braces: `{{` and `}}` Those curly braces (often called double mustaches or squiggly gigglies) look a whole lot like _antlers_ to us, hence the name.\n\nThis is a very simple Antlers tag:\n\n```\n{{ hello_world }}\n```\n\n### Configuring\n\nYou can configure advanced settings, like guarded variables, tags & modifiers in `config/statamic/antlers.php`.\n\n```php\n// config/statamic/antlers.php\nreturn [\n    'guardedVariables' => [\n        'config.app.key',\n    ],\n\n    'guardedTags' => [\n        //\n    ],\n\n    'guardedModifiers' => [\n        //\n    ],\n];\n```\n\n## The book\nIf you want to go beyond these docs and really dive into Antler's advanced capabilities, check out [Antlers: Building Beautiful Websites with Statamic](https://stillat.com/books/antlers-building-beautiful-websites-with-statamic), the official companion book by the great John Koster.\n\n\n## The basics\n\n### Delimiters\n\nThere are three kinds of delimiters.\n\n- `{{ }}`  The basic and primary delimiter pair, used to render variables, evaluate expressions, call Tags, and do almost all core Antlers things.\n- `{{? ?}}` and `{{$ $}}` Allow you to write, execute, and echo PHP.\n- `{{# #}}` Are for code comments.\n\n### Formatting rules\n\n1. Each set of curly braces **must** stay together always, like Kenan & Kel or Wayne & Garth. There must be a left pair and a right pair, just like HTML's `<` and `>` angle braces.\n1. Expressions are **case sensitive**.\n3. Use underscores and not dashes to separate words in variable names.\n1. Whitespace between the curly braces expression is optional, but **recommended** for readability.\n1. You **may** break up an expression onto multiple lines.\n\nConsistency is important. We recommend using a single space between braces and the inner expression, lowercase variable names, and underscores as word separators. Pick your style and stick to it. Future you will thank you, but don't expect a postcard.\n\n``` antlers\nThis is great!\n{{ perfectenschlag }}\n\nThis is allowed.\n{{squished}}\n\nThis can make sense when you have lots of parameters.\n{{\n  testimonials\n  limit=\"5\"\n  order=\"username\"\n}}\n\nThis is terrible in every possible way.\n{{playSad_Tromb0ne            }}\n```\n\n:::tip\nWe recommend indenting the markup in your HTML for **human readability and maintainability**, not for final rendered output. Anyone still caring about that this day and age probably needs a long vacation and strong Mai Tai or two. 🍹🍹\n:::\n\n### IDE integrations\n\nSyntax highlighting and auto-completion packages are available for many of the popular IDEs:\n- [Antlers Toolbox for VS Code](https://antlers.dev) (We recommend this one!)\n- [Antlers for Sublime Text](https://github.com/addisonhall/antlers-statamic-sublime-syntax)\n- [Antlers for Atom](https://github.com/addisonhall/language-antlers)\n- [Antlers for Panic Nova](https://extensions.panic.com/extensions/teriyaki/teriyaki.antlers/)\n\n## Variables\n\nData passed into your Antlers views can be rendered by wrapping the name of a variable with double curly braces. For example, given the following data:\n\n``` yaml\n---\ntitle: DJ Jazzy Jeff & The Fresh Prince\n---\n```\n\nThe `title` variable can be rendered like this:\n\n``` antlers\n<h1>{{ title }}</h1>\n```\n\n``` html\n<h1>DJ Jazzy Jeff & The Fresh Prince</h1>\n```\n\n### Valid characters\n\nVariables must start with an alpha character or underscore, followed by any number of additional uppercase or lowercase alphanumeric characters, hyphens, or underscores, but must not end with a hyphen. Spaces or other special characters are not allowed. A valid variable name matches this regex `[_A-Za-z][-_0-9A-Za-z]*[_A-Za-z0-9]`.\n\nDon't be weird and mix-and-match them like a serial killer though:\n\n```\n<!-- Get outta here. -->\n{{ this_iS-RiDicuL-ou5_ }}\n```\n\n### Strings\n\nStrings (simple sequences of text) are one of the most basic data types. They come in the form of variables or static expressions. To render a string variable, wrap the name with double curly braces.\n\n```\n<h1>{{ title }}</h1>\n```\n\nAntlers also handles static expressions, which are useful when concatenating strings together, setting fallback or default values, combining with [modifiers](#modifiers), and numerous other situations we can't think of right now but you may find yourself in eventually.\n\nTo render a static string, wrap it in single or double quotes, inside a pair of curly braces.\n\n```\n<h1>{{ \"I will eat you, donut\" | upper }}</h1>\n```\n\n``` html\n<h1>I WILL EAT YOU, DONUT</h1>\n```\n\n### Arrays\nAn array is a collection of elements (values and/or variables). Elements inside the array may be iterated or looped through using the `{{ value }}` variable. You may also \"reach in\" and pluck out specific elements by their index.\n\n#### Looping\n\n``` yaml\n---\nsongs:\n  - Brand New Funk\n  - Parents Just Don't Understand\n  - Summertime\n---\n```\n\n```\n<ul>\n{{ songs }}\n  <li>{{ value }}</li>\n{{ /songs }}\n</ul>\n```\n\n``` html\n<ul>\n  <li>Brand New Funk</li>\n  <li>Parents Just Don't Understand</li>\n  <li>Summertime</li>\n</ul>\n```\n\n#### Next / previous\n\nWhile in a loop, you can get the respective iterations using the `next` or `prev` variables.\n\n```\n<ul>\n{{ songs }}\n  <li>{{ value }} (Next: {{ next:value }}) (Prev: {{ prev:value }})</li>\n{{ /songs }}\n</ul>\n```\n``` html\n<ul>\n  <li>Brand New Funk (Next: Parents Just Don't Understand) (Prev: )</li>\n  <li>Parents Just Don't Understand (Next: Summertime) (Prev: Brand New Funk)</li>\n  <li>Summertime (Next: ) (Prev: Parents Just Don't Understand)</li>\n</ul>\n```\n\n#### Loop variables {#loop-variables}\n\nInside any loop — whether you're iterating an array, the results of a tag like [`collection`](/tags/collection) or [`taxonomy`](/tags/taxonomy), or a relationship field — Antlers automatically exposes a handful of helper variables describing where you are in the loop.\n\n| Variable | Type | Description |\n| --- | --- | --- |\n| `first` | boolean | `true` on the first iteration. |\n| `last` | boolean | `true` on the last iteration. |\n| `count` | integer | The current iteration, starting at `1`. |\n| `index` | integer | The current iteration, starting at `0`. |\n| `total_results` | integer | The total number of items in the loop. |\n| `no_results` | boolean | `true` when the loop has zero results. Available in tag loops. |\n\n```antlers\n<ul>\n{{ collection:blog }}\n    <li class=\"{{ if first }}featured{{ /if }}\">\n        {{ count }} of {{ total_results }} — {{ title }}\n        {{ if last }} (that's all, folks!){{ /if }}\n    </li>\n{{ /collection:blog }}\n</ul>\n```\n\n:::tip\nThese loop variables are an **Antlers-only** feature. If you're writing Blade, use Laravel's native [`$loop` variable](/blade#loop-variables) instead.\n:::\n\n#### Plucking\n\nTo pluck values out of an array, you may use \"colon\", \"dot\", or \"bracket\" notation to pull out values by their array key. All three of these syntaxes are equivalent, so feel free to use the one that feels most natural to you. Note that the first item of the array starts with a zero-index key.\n\n``` yaml\n---\nsports:\n  - BMXing\n  - rollerblading\n  - skateboarding\n  - scootering\n---\n```\n\n```\n<p>Let's go {{ sports:0 }}, {{ sports.1 }} or {{ sports[2] }}.</p>\n```\n\n``` html\n<p>Let's go BMXing, rollerblading, or skateboarding.</p>\n```\n\n#### Dictionaries\n\nDictionaries are represented in YAML by nested key:value pairs, _inside_ another variable name. These are sometimes called element maps, or associative arrays.\n\n``` yaml\nmailing_address:\n  address: 123 Foo Ave\n  city: Barville\n  province: West Exampleton\n  country: Docsylvania\n```\n\nYou can access the keys inside the dictionary \"colon\", \"dot\", or \"bracket\" notation to traverse the levels of the array. All three of these syntaxes are equivalent, so feel free to use the one that feels most natural to you.\n\n``` antlers\nI live in {{ mailing_address:city }}. It's in {{ mailing_address:province }}.\n```\n\n#### Multi-dimensional arrays\n\nMore complex data is stored in objects or arrays inside arrays. This is usually called a multi-dimensional array.\n\n``` yaml\nskaters:\n  -\n    name: Tony Hawk\n    style: Vert\n  -\n    name: Rodney Mullen\n    style: Street\n  -\n    name: Bob Burnquist\n    style: Vert\n```\n\nIf you know the names of the variables inside the array, you can loop through the items and access their variables.\n\n``` antlers\n{{ skaters }}\n<div class=\"card\">\n  <h2>{{ name }}</h2>\n  <p>{{ style }}</p>\n</div>\n{{ /skaters }}\n```\n\n``` html\n<div class=\"card\">\n  <h2>Tony Hawk</h2>\n  <p>Vert</p>\n</div>\n<div class=\"card\">\n  <h2>Rodney Mullen</h2>\n  <p>Street</p>\n</div>\n<div class=\"card\">\n  <h2>Bob Burnquist</h2>\n  <p>Vert</p>\n</div>\n```\n\nYou may also use \"colon\", \"dot\", or \"bracket\" notation to access individual values. Note that the first iteration of the array starts with a zero-index.\n\n```\n{{ skaters:0:name }}\n{{ skaters.1.name }}<br>\n{{ skaters[2]['name'] }}<br>\n```\n\n``` html\nTony Hawk<br>\nRodney Mullen<br>\nBob Burnquist\n```\n#### Dynamic access\n\nIf you don't know the names of the keys inside the array – which can happen when working with dynamic or user submitted data – you can access the elements dynamically using variables for the key names.\n\nUsing the mailing list example, we could use a `field` variable to access specific keys.\n\n``` md\n---\nfield: country\nmailing_address:\n  address: 123 Scary Mansion Lane\n  country: Docsylvania\n  city: Arteefem\n  postal_code: RU 7337\n---\n{{ mailing_address[field] }}\n\n// Output\nDocsylvania\n```\n\nYou can combine literal and dynamic keys and get real fancy if you need to.\n\n```\n{{ complex_data[3][field]['title'] }}\n```\n\n### Disambiguation {#disambiguating-variables}\n\nAs your templates grow and increase in complexity, you _may_ find yourself unsure if you're working with a variable or a [Tag](#tags). You may optionally disambiguate your variables by prefixing them with a `$` dollar sign, just like PHP.\n\n```\n{{ $content }}\n```\n\n:::tip\nThis includes variables that Statamic creates for you. For example on taxonomy pages, [the terms are available in a variable named after the taxonomy](/taxonomies#outputting-terms). If you've named the taxonomy the same as a tag (like \"section\") then you will need to be clear you mean the variable and not the tag.\n:::\n\n### Modifiers\n\nModifiers change the output of an Antlers variable. They are used inside any expression and are separated by a pipe character `|`.\n\nMultiple modifiers can be chained on one output, each separated by another pipe `|`, and are are applied in order from left to right. Let's look at an example.\n\n```yaml\n---\ntitle: Nickelodeon Studios\n---\n```\n```\n<!-- NICKELODEON STUDIOS rocks! -->\n<h1>{{ title | upper | ensure_right('rocks!') }}</h1>\n\n<!-- NICKELODEON STUDIOS ROCKS! (order matters) -->\n<h1>{{ title | ensure_right('rocks!') | upper }}</h1>\n```\n\nSome modifiers accept parameters to control their behavior. Arguments can be passed inside a pair of `()` braces, just like a native PHP function. If you don't have any arguments to pass, you may omit the braces.\n\nYou may pass `strings`, `arrays`, `booleans`, `integers`, `floats`, `objects`, or references to existing variables as arguments.\n\n```\n{{ var | modifier('hi', ['pooh', 'pea'], true, 42, $favoriteVar) }}\n```\n\n##### Examples\nHere are a few examples of modifiers in action.\n\n```yaml\nsummary: \"It was the best of times, it was the worst of times.\"\nnoun: soups\n```\n\n```\n{{ summary | replace('worst', 'yummiest') }}\n{{ summary | replace('It was', 'It was also') | replace('times', $noun) }}\n{{ summary | explode(' ') | ul }}\n{{ (summary | contains('best')) ?= \"It was lunch, is what it was.\" }}\n```\n\n```\nIt was the best of times, it was the yummiest of times.\nIt was also the best of soups, it was the worst of soups.\n<ul><li>It</li><li>was</li><li>the</li><li>best</li><li>of</li><li>times,</li><li>it</li><li>was</li><li>the</li><li>worst</li><li>of</li><li>times.</li></ul>\nIt was lunch, is what it was.\n```\n\nThere are more than 150 built-in [modifiers](/reference/modifiers) that can do anything from array manipulations to automatically writing HTML for you. You can also [create your own modifiers](/extending/modifiers) to do unthinkable things we assumed nobody would ever need to do, until you arrived.\n\nYou can even create [Macros](/modifiers/macro) to combine sets of often used modifiers into one, new reusable one.\n\n### Creating variables\n\nYou can now set variables by using the assignment operator, `=`.\n\n```\n{{ total = 0 }}\n\n{{ loop from=\"1\" to=\"9\" }}\n  {{ total += 1 }}\n{{ /loop}}\n\n<p>I can count to {{ total }}!</p>\n```\n\n```\n<p>I can count to 9!</p>\n```\n\n#### Arrays\nYou can also create arrays, if you find the need. Keep in mind that more complex data _might_ be better suited to being managed in Entries, Globals, View Models, or Controllers.\n\n```\n{{ todo = ['Get haircut', 'Bake bread', 'Eat soup'] }}\n\n<ul>\n  {{ todo }}\n    <li>{{ value }}</li>\n  {{ /todo }}\n</ul>\n```\n\n#### Sub-expressions\n\nYou can assign sub-expressions or interpolated statements to variables too. In this example, you can use `{{ items }}` as if it were the actual Collection Tag. Because it is.\n\n```\n{{ items = {collection:products sort=\"rating:desc\" limit=\"5\"} }}\n\n<h2>Our Top Products</h2>\n<ul>\n  {{ items }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n  {{ /items }}\n</ul>\n```\n\n### Truthy and falsy\n\nAll variables are considered \"truthy\" if they exist _and_ contain a value. Variables that _don't_ exist, contain an empty string, or are structured and empty (e.g. an empty array or object) are considered \"falsy\".\n\nThis is a powerful pattern that can help keep template logic simple and uncluttered. For instance, you can set a series of \"fallback\" variables all in one expression, allowing you to have default values and optionally override them instead of having to do a bunch of `if`/`else` checks.\n\n```\n<!-- Which one is better? -->\n<title>\n  {{ if meta_title }}\n    {{ meta_title }}\n  {{ elseif title }}\n    {{ title }}\n  {{ else }}\n    {{ site:name }}\n  {{ /if }}\n</title>\n\n<!-- Don't be ridiculous, the answer is this one.  -->\n<title>{{ meta_title ?? title ?? site:name }}</title>\n```\n\nAnother use case is when you _sometimes_ have an array variable to loop through in a template to render some markup. You may skip the existence check entirely, keep the markup inside the loop, and if the variable doesn't exist, nothing inside the tag pair will be rendered.\n\n```\n{{ nothing_to_see_here }}\n  <!-- Doesn't matter, won't see it -->\n{{ /nothing_to_see_here }}\n```\n\n### Escaping\n\nBy default, Antlers `{{ }}` statements are _not_ automatically escaped. This is because in a CMS context (vs a web application), content is very often stored inside HTML markup, and this is the most logical, default behavior.\n\nThe simplest way to escape data is by using the [sanitize](/modifiers/sanitize) modifier. This will run the data through PHP's [`htmlspecialchars()`](https://www.php.net/manual/en/function.htmlspecialchars.php) function to prevent XSS attacks.\n\n```\n{{ user_submitted_content | sanitize }}\n```\n\n:::tip\nJust remember: **never render user-submitted data without escaping it first!**\n:::\n\n\n## Operators\n\nAn operator is a special symbol or phrase that you use to check, change, or combine values. For example, the addition operator (`+`) adds numbers, as in `1 + 2`. Statamic supports many of the operators you may already know from PHP, and adds a few new ones to make your life as a developer easier.\n\n### Control flow\n\nStatamic provides a variety of control flow statements. These include `if`, `else`, `or`, `unless`, and `switch` statements to run different branches of template code based on defined conditions.\n\n#### if\n\nExecutes a block of code only if a condition is `true` or \"[truthy](#truthy-and-falsy)\".\n\n```\n{{ if logged_in }}\n  Welcome to Narnia!\n{{ /if }}\n```\n\nYou may also close if statements with `{{ endif }}` if you prefer.\n\n#### unless\n\nUnless is the opposite of `if` – executing a block of code only if a condition is **not** met.\n\n```\n{{ unless logged_in }}\n  You see a large wardrobe in front of you.\n{{ /unless }}\n```\n\nYou may also close unless statements with `{{ endunless }}` if you prefer.\n\n#### elseif / else\n\nAdds more conditions with an `if` or `unless` block.\n\n```\n{{ if neighbor == \"Kramer\" }}\n  These pretzels are making me thirsty!\n{{ elseif neighbor == \"Newman\" }}\n  Hello...Newman.\n{{ else }}\n  Who are you?\n{{ /if }}\n```\n\n#### switch\n\nThe `switch` is perfect for complex conditions with many possible cases, or using inside interpolated regions that don't support tag pairs, like [Tag Parameters](#tag-parameters).\n```\n{{ size = 'lg' }}\n\n{{ switch(\n        (size == 'sm') => '(min-width: 768px) 35vw, 90vw',\n        (size == 'md') => '(min-width: 768px) 55vw, 90vw',\n        (size == 'lg') => '(min-width: 768px) 75vw, 90vw',\n        (size == 'xl') => '90vw',\n        () => '100vw'\n    )\n}}\n```\n\n```html\n(min-width: 768px) 75vw, 90vw\n```\n\n### Comparison\n\nComparison operators, as their name implies, allow you to compare two values or expressions.\n\n| Name | Example {.w-32} | Description |\n|------|----------------|-------------|\n| Equal | `$a == $b` | `true` if `$a` is equal to `$b` after type juggling. |\n| Identical | `$a === $b` | `true` if `$a` is equal to `$b`, and are of the same type. |\n| Greater than | `$a > $b` | `true` if `$a` is greater than `$b`. |\n| Greater than or equal to | `$a >= $b` | `true` if `$a` is greater than or equal to `$b`. |\n| Less than | `$a < $b` | `true` if `$a` is less than `$b`. |\n| Less than or equal to | `$a <= $b` | `true` if `$a` is less than or equal to `$b`. |\n| Not equal | `$a != $b` |  `true` if `$a` is not equal to `$b` after type juggling. |\n| Not identical | `$a !== $b` | `true` if `$a` is not equal to `$b`, only if they are of the same type. |\n| Spaceship | `$a <=> $b` | Returns -1, 0, or 1 when `$a` is less than, equal to, or greater than `$b`, respectively. |\n\n#### Examples\n\nLet's compare some numbers.\n\n```\n{{ if songs === 1 }}\n  <p>This is a song!</p>\n{{ elseif songs > 100 }}\n  <p>This is noisy!</p>\n{{ elseif songs }}\n  <p>There are some songs here.</p>\n{{ else }}\n  <p>It is quiet.</p>\n{{ /if }}\n```\n\nHere's a more complicated condition involving the output from a Tag.\n\n```\n{{ if {collection:count from=\"episodes\"} >= 100 }}\n  This show is ready to be syndicated!\n{{ /if }}\n```\n\n### Logical\n\nLogical operators join two or more expressions to create compound conditions.\n\n| Name | Example | Description |\n|------|---------|-------------|\n| And |  `$a && $b` or `$a and $b` |`true` if both `$a` and `$b` are `true`. |\n| Or | `$a \\|\\| $b` or `$a or $b` | `true` if either `$a` or `$b` is `true`. |\n| Not | `!$a` | `true` if `$a` is not `true`.|\n| Xor | `$a xor $b` | `true` if either `$a` or `$b` is `true`, but not both. |\n\n\n### Ternary statements {#ternary}\n\nTernary statements let you write a simple condition and return one value if `true` and another if `false`, all in one expression.\n\n```\nThis item is {{ is_sold ? \"sold\" : \"for sale\" }}.\n```\n\n:::best-practice\n**Ternary statements are a double-edged sword** – they can simplify template code when used effectively, and greatly complicate it if pushed too far — like nesting one ternary inside another using a sub-expression. Make sure other developers will be able wrap their Mind Grapes™ around your ternary statements.\n\n```\n<!-- While valid, this can be hard to follow. -->\n{{ is_sold ? \"sold\" : (on_sale ? \"on sale\" : \"for sale\") }}\n```\n:::\n\n### Null Coalescence\n\nThe null coalescing operator (`$a ?? $b`) considers each variable in a statement _optional_, returning the first one that passes a \"truthy\" check. This lets you set fallback or default values for optional data.\n\n```\n{{ meta_title ?? title ?? \"Someone Forgot the Title\" }}\n```\n\n:::tip\nUse `???` to fall through only on `null`. Keeps `0`, `false`, `''` intact.\n\n```antlers\n{{ power_level ??? \"It's over 9000!\" }}\n```\n:::\n\n### The Gatekeeper (Truthy assignment) {#gatekeeper}\n\nThe Gatekeeper operator (`a ?= b`) will execute an expression **if and only if** it passes a \"truthy\" check. It doesn't exist in any programming language — we invented this one. Enjoy!\n\n```\n{{ show_bio ?= author:bio }}\n\n{{ show_newsletter ?= {partial:newsletter} }}\n```\n\nThis syntax can handle any valid expression on the right-hand side of the operator. Just make sure that when using the Gatekeeper that it's the most readable way to construct the template.\n\n\n### Concatenation\n\nThere are two methods for concatenating strings.\n\nFirst, to concatenate and render a string in a single tag, you may use a `+` plus sign between variables and string literals to combine them. (You may also  use multiple tags. Opt for whatever makes the code most readable.)\n\n```yaml\ntitle: Marv's Coffee Shop\nquality: pretty good\n```\n\n```\n{{# These are equivalent #}}\n<p>{{ $title + \" makes \" + $quality + \" donuts.\" }}</p>\n<p>{{ title }} makes {{ quality }} donuts.</p>\n```\n\n```html\n<p>Marv's Coffee Shop makes pretty good donuts.</p>\n<p>Marv's Coffee Shop makes pretty good donuts.</p>\n```\n\nYou may also concatenate through assignment, allowing you to render the result later in a template.\n\n```\n{{ string = \"Hello\" }}\n\n{{ if something }}\n  {{ string += \" World\"}}\n{{ else }}\n  {{ string += \" Universe\" }}\n{{ /if }}\n\n{{ string }}\n```\n\n### Math\n\nMath is all the rage. Teenagers have been found in back rooms and back alleys doing math and nobody can seem to stop them. And since the cool kids are doing it, Antlers does math now too!\n\n| Name | Example | Description |\n|------|---------|-------------|\n| Addition |  `$a + $b` | Sum of `$a` and `$b`. |\n| Subtraction |  `$a - $b`. | Difference of `$a` and `$b`. |\n| Multiplication |  `$a * $b`. | Product of `$a` and `$b`. |\n| Division |  `$a / $b`. | Quotient of `$a` and `$b`. |\n| Modulo |  `$a % $b`. | Remainder of `$a` divided `$b`. |\n| Exponentiation |  `$a ** $b` | Result of raising `$a` to the `$b`'th power. |\n| Factorial |  `$a!` | Factorial of `$a`. |\n\n### Assignment\n\nThe basic assignment operator is `=`. You might immediately think that means \"equal to\", but stop right there. Do not pass go and do not collect $200. This means left operand gets set to the value of the expression on the right.\n\nThis is how you create variables as well as increment, decrement, or otherwise manipulate numerical variables.\n\n| Name | Example {.w-32} | Description |\n|------|---------|-------------|\n| Left Assignment | `$a = $b` | Sets the value of `$a` to the value of `$b`. |\n| Addition | `$a += $b` | Assigns the sum `$a` and `$b` to `$a`. |\n| Subtraction | `$a -= $b` | Assigns the difference of `$a` and `$b` to `$a`. |\n| Multiplication | `$a *= $b` | Assigns the product of `$a` and `$b` to `$a`. |\n| Division | `$a /= $b` | Assigns the quotient of `$a` and `$b` to `$a`. |\n| Modulus | `$a %= $b` | Assigns the remainder of `$a` divided by `$b` to `$a`. |\n\n### Self-iterating assignments\n\nThe left assignment operator has a super power not shared by the others. If the value of the **right-hand** expression returns a value that can be iterated (arrays, objects, etc.), the captured variable name can be used as a tag pair to iterate the returned value immediately.\n\n```\n{{ pages = {collection:pages} }}\n    {{ title }}\n{{ /pages }}\n```\n\n## Advanced operators\n\nThese operators are here for the edge cases, the wild ideas, and the unexpected client requests at midnight the night before a site launch. **These are the data wangjanglers.**\n\nMuch of what they do is already handled by Modifiers or Tag Parameters (and you should use those if ever and whenever you can), but these operators become very useful as part of **assignment expressions** — when you've left the safety of a Tag or simplicity of a primitive variable in the dust behind you.\n\n### Merge\n\nThe `merge` operator can merge two or more \"array-like\" variables or expressions. The resulting data is immediately iterable without any kind of intermediate step, if you desire.\n\n```\n{{ articles = favourite_articles merge not_favourite_articles }}\n\n{{ articles }}\n  {{# do your thing here #}}\n{{ /articles }}\n\n{{ items = {collection:headlines} merge {collection:news limit=\"5\"} }}\n  {{# your thing can be done here too #}}\n{{ /items }}\n```\n\n:::best-practice\nYou shouldn't need to merge collections this way because the [Collection Tag](/tags/collection) already supports the feature (and is more performant), but we want to show what's technically possible.\n\n```\n{{ %collection from=\"headline|news\" }}\n\n{{ /%collection }}\n```\n:::\n\n### OrderBy\n\nThe `orderby` operator can be applied to any array and supports ordering by multiple properties as well as dynamic fields and directions.\n\nArguments are passed into a pair of parenthesis `()` in the following format, which accepts variables of literal `'asc'` and `'desc'` strings or boolean `true` and `false` for ascending and descending sort directions, respectively.\n\n```\n{{ var orderby (FIELD_1 DIRECTION, FIELD_2 DIRECTION) }}\n```\n\n#### Examples\n\n```yaml\ndir: 'asc'\nshouldSortAscending: false\n```\n\n```\n{{ people orderby (age 'desc', last_name 'asc', first_name 'asc') }}\n\n{{ places orderby (state $dir, city $dir, zip_code $dir) }}\n\n{{ things =\n  {collection:hats} merge {collection:books}\n  orderby (rating $shouldSortAscending)\n}}\n```\n\n### GroupBy\n\nThe `groupby` operator can be applied to any array or tag output as part of an assignment expression, which automatically iterates through the newly created groups.\n\nArguments are passed into a pair of parenthesis `()`. Each argument accepts the name of a field to group by and an optional alias, with additional arguments for additional fields separated by commas `,`.  If you don't set an alias, it will match the name of the field you pass in.\n\nAdditionally you may set the name of the per-group `values` array with `as 'anything_you_want'` at the end of the expression.\n\n```\ngroupby (FIELD 'KEY1', FIELD2 'KEY2') as 'things'\n```\n#### Examples\n\nWe'll use the following data for a few of the next examples.\n\n``` yaml\nplayers:\n  - { team: Chicago Bulls, name: Michael Jordan, position: Guard }\n  - { team: Chicago Bulls, name: Scottie Pippen, position: Forward }\n  - { team: Chicago Bulls, name: Dennis Rodman, position: Forward }\n  - { team: Detroit Pistons, name: Isiah Thomas, position: Guard }\n  - { team: Detroit Pistons, name: Terry Mills, position: Forward }\n  - { team: Detroit Pistons, name: Joe Dumars, position: Guard }\n```\n\n##### Group by Single Field\n\n```\n{{ items = players groupby (team) }}\n   <h2>{{ key }}</h2>\n   <ul>\n       {{ values }}\n        <li>{{ name }} - {{ position }}</li>\n       {{ /values }}\n    </ul>\n{{ /items }}\n```\n\n```html\n<h2>Chicago Bulls</h2>\n<ul>\n  <li>Michael Jordan</li>\n  <li>Scotty Pippen</li>\n  <li>Dennis Rodman</li>\n</ul>\n<h2>Detroit Pistons</h2>\n<ul>\n  <li>Isiah Thomas</li>\n  <li>Terry Mills</li>\n  <li>Joe Dumars</li>\n</ul>\n```\n\n##### Group by Multiple Fields\n```\n{{ items = players groupby (team, position) }}\n   <h2>{{ key:team }} - {{ key:position }}</h2>\n   <ul>\n       {{ values }}\n        <li>{{ name }}</li>\n       {{ /values }}\n    </ul>\n{{ /items }}\n```\n\n``` html\n<h2>Chicago Bulls - Guard</h2>\n<ul>\n  <li>Michael Jordan</li>\n</ul>\n\n<h2>Chicago Bulls - Forward</h2>\n<ul>\n  <li>Scottie Pippen</li>\n  <li>Dennis Rodman</li>\n</ul>\n\n<h2>Detroit Pistons - Guard</h2>\n<ul>\n  <li>Isiah Thomas</li>\n  <li>Joe Dumars</li>\n</ul>\n\n<h2>Detroit Pistons - Forward</h2>\n<ul>\n  <li>Terry Mills</li>\n</ul>\n```\n#### Group collection entries by year\n\n```\n{{ blog = {collection:blog} groupby (date|format('Y') 'year') as 'entries' }}\n  <h2>{{ year }}</h2>\n  <ul>\n    {{ entries }}\n      <li><a href=\"{{ url }}\">{{ title }}</a></li>\n    {{ /entries }}\n  </ul>\n{{ /blog }}\n```\n\n### Where\n\nEverything you can do inside a regular Antlers condition can be performed inside a `where` statement. Additionally, you can use an \"arrow function\" (`x => x.field`) to establish a scoped context inside an array or object.\n\n#### Examples\n\n```\nproducts:\n  - [name: Talkboy, price: 30]\n  - [name: Super Nintendo, price: 90]\n  - [name: Pogs, price: 1]\nbudget: 50\n```\n\n```\n{{ bulls = players where (team == \"Chicago Bulls\") }}\n{{# returns [Michael Jordan, Scottie Pippen, Dennis Rodman] #}}\n\n{{ afford = products where (x => x.price < budget) }}\n{{# returns [Talkboy, Pogs] #}}\n\n{{ electronic = products where\n  (name == \"Talkboy\" || name == \"Super Nintendo\")\n}}\n{{# returns [Talkboy, Super Nintendo] #}}\n```\n\n### Take\n\nYou may use the `take` operator to limit the number of results returned from an assignment operation.\n\n```\n{{ players = players take (2) }}\n```\n\n### Skip\n\nYou may use the `skip` operator to skip a given number of results returned from an assignment operation.\n\n```\n{{ players = players skip (2) }}\n```\n\n### Pluck\n\nIf you would like to retrieve the values from a single field, use `pluck`.\n\n```\n{{ players = players pluck ('name') }}\n  {{ value }}\n{{ /players }}\n```\n\n```output\nMichael Jordan\nScottie Pippen\nDennis Rodman\nIsiah Thomas\nTerry Mills\nJoe Dumars\n```\n\n### The Terminator\n\nMultiple expressions or statements can be performed inside a single Antlers tag pair by terminating each with `;`. These terminators can often lend to more readable code for multi-line statements. However, if you don't like them, you can tell 'em \"hasta la vista, baby\" because they're optional (just like in JavaScript).\n\n```\n{{\n    $michael = 9986000;\n    $minutes_in_a_year = 60 * 24 * 365;\n    (($michael / $minutes_in_a_year) | format_number(0)) + \" years\";\n}}\n```\n\n```\n19 years\n```\n\n## Expressions and statements\n\nIf you want the computer science answer, an \"expression\" is a combination of values and functions that are combined and interpreted to create new values, whereas a \"statement\" is a standalone unit of execution that doesn't return anything. 🥱\n\nSimply put, expressions show things and statements do things. Even more simply put — they're the stuff between `{{ }}` braces. It's not terribly important to remember the semantic differences as it is usually clear from context whether you're trying to show a thing or do a thing.\n\nLet's just go through the list of valid \"in between braces stuff\" so you can  accomplish your goals and hopefully win a trophy of some kind. 🏆\n\n```\n{{ \"This is a single expression that renders this very text. Nothing more and nothing less.\" }}\n\n{{# This statement fetches Entries and begins iterating through them #}}\n{{ collection:blog limit=\"5\" }}\n\n{{# This statement runs a condition check #}}\n{{ if template == \"home\" }}\n\n{{# Brace yourself — this complex statement assigns a boolean value\n    to a new variable based on a the result of a condition inside a\n    sub-expression, and then writes a value to the user session in\n    a separate statement, all inside a single Antlers region. 😅 #}}\n{{ $show_sale_popup = (\n    global:active_sale === true\n    && !{session:has key=\"seen_popup\"}\n  );\n  {session:set seen_popup=\"true\"};\n}}\n```\n\n### Literals\n\nLiterals are the simplest type of expression. They include strings, arrays, booleans, integers, and so on. Antlers can handle literals as stand-alone expressions, as arguments, and during assignments (creating and updating variables) .\n\nTo check the type of any variable or value, use the `type_of` modifier:\n\n```\n{{ \"Wazzaaap\" | type_of }}    -> string\n{{ [1, 2, 3] | type_of }}     -> array\n{{ false | type_of }}         -> boolean\n{{ 42 | type_of }}            -> integer\n{{ 26.2 | type_of }}          -> double (aka float)\n```\n\n### Sub-Expressions\n\nSub-expressions are indicated by wrapping a pair of parenthesis around `()` a portion of text. Anything inside a sub-expression will be parsed immediately and independently, which allows you to control the order of operations inside an Antlers tag and improve code readability.\n\n```\n{{ 5 + 3 * 2 }} -> 11\n{{ (5 + 3) * 2 }} -> 16\n\n{{ if (gallery | length) >= 12 && (content | read_time) > 5 }}\n```\n\nSub-expressions are supported everywhere: variable assignments, logic conditions, interpolated Tag arguments, you name it.\n\n## Tags\n\nTags (note the capital \"T\") are the primary method for accessing data from Statamic and tapping into many of the available dynamic features like search, forms, nav building, pagination, entry listing, filtering, image resizing, and so on. Check out the [full list of Tags](/reference/tags) to see what's available.\n\nTags usually operate as pairs as they're often fetching data (like entries or assets) and looping through the results.\n\n```\n<ul>\n  {{ collection:blog }}\n    <li><a href=\"{{ url }}\">{{ title }}</a>\n  {{ /collection:blog }}\n</li>\n```\n\n### Disambiguation {#disambiguating-tags}\n\nYou may optionally disambiguate your tags by prefixing them with a `%` percent sign. If you're already [disambiguating your variables](#disambiguating-variables), you may find this unnecessary, but it's here if you need it.\n\n```\n{{ %collection:blog }}\n```\n\n### Tag parameters\n\nMost Tags can be configured through the use of Parameters, which accepts arguments — much like an HTML attribute. In following example, the [SVG Tag](/tags/svg) is accepting a filename and string of classes to apply while rendering an inline `<svg>` element.\n\n```\n{{ svg src=\"icons/hamburger\" class=\"w-8 h-8\" }}\n```\n\nTag Parameters are **interpolated**, so you can include variables and primitive forms of logic, using _{single braces}_ instead of double. Avoid using tag _pairs_.\n\n```\n{{ nav from=\"{segment_1}/{segment_2}\" }}\n{{ collection:blog limit=\"{entry_limit ?? 10}\" }}\n```\n\nYou can use **dynamic binding** to pass the value of any variable by prefixing the parameter with a colon and using the _name_ of the variable as your argument:\n\n```\n{{ nav :from=\"segment_1\" }}\n```\n\nYou can \"void\" a parameter using the `void` keyword. A voided parameter will act like you haven't used it at all. It's most useful when you may or may not need a parameter:\n\n```\n{{ if wide }}                                  {{# [tl! --:start] #}}\n    {{ svg src=\"hamburger\" }}\n{{ else }}\n    {{ svg src=\"hamburger\" class=\"w-full\" }}\n{{ /if }}                                      {{# [tl! --:end] #}}\n\n{{ svg src=\"hamburger\" class=\"{wide ? 'w-full' : void}\" }} {{# [tl! ++] #}}\n```\n\n### Shorthand parameter syntax\n\nWhen passing variables into tags where the parameter and variable have the **same name**, the standard syntax:\n\n```\n{{ collection:blog :id=\"id\" }}\n```\n\nCan be simplified to this:\n\n```\n{{ collection:blog :$id }}\n```\n\nThe parser will internally expand this to the longer form and continue as normal. This syntax can be mixed with normal parameters:\n\n```\n{{ collection:blog limit=\"5\" :$class offset=\"1\" }}\n```\n\n\n### Self-closing tags\n\nSome Tags can function as single or paired expressions. For example, the [Partial Tag](/tags/partial) can be used to include a partial template, or it can be used to wrap a portion of your template and inject it as a slot into a partial.\n\nIn the below example, you can **self-close** the first partial tag much like an HTML element to ensure the second tag is paired properly.\n\n```\n{{ partial :src=\"hero_panel\" /}}\n{{ partial :src=\"sidebar\" }}\n  <nav>\n    <a href=\"/\">Home</a>\n    <a href=\"/about\">About</a>\n  </nav>\n{{ /partial }}\n```\n\n## Working with templates\n\n### Layouts\n\nMost websites maintain the same general layout across various pages. Any markup you always want to present should go into a layout.\n\nBy default, Statamic uses `/resources/views/layout.antlers.html`, but you can create other layouts and configure specific entries or collections to use those instead by setting `layout: your_layout` on the entry or collection config file respectively.\n\nLayouts often contain `<head></head>` markup, navs, footer, JavaScript includes, and so on. Somewhere in all that HTML you should add the `{{ template_content }}` variable — the place where content-defined templates will be injected.\n\n```\n<!-- resources/views/layout.antlers.html -->\n<html>\n  <head>\n    <title>{{ title }} | {{ site:name }}</title>\n    <link rel=\"stylesheet\" href=\"/css/tailwind.css\">\n  </head>\n  <body>\n    {{ partial:nav }}\n\n    {{ template_content }}\n\n    {{ partial:footer }}\n    <script src=\"/js/site.js\"></script>\n  </body>\n</html>\n```\n\n### Partials\n\nStatamic's [`{{ partial }}`](/tags/partial) tag allows you to include a view from within another view. All variables that are available to the parent view will be made available to the included partial view.\n\n```\n{{ partial:footer }}\n```\n\nEven though the included view will inherit all data available in the parent view, you may also pass an array of additional data that will be made available to the included view:\n\n```\n{{ partial:blog/card mode=\"stacked\" }}\n```\n\nIf you attempt to use a `partial` that doesn't exist, Statamic will throw an error. If you would like to include a partial that may or may not exist (for example, using a variable in the partial name), you should use the [`{{ partial:if_exists }}` ](/tags/partial-if-exists) tag.\n\n```\n{{ partial:if_exists src=\"blog/card\" }}\n```\n\nAll views inside your `/resources/views/` directory can be used as a partial, including [Blade](/blade) views.\n\n#### Slots\n\nSometimes you might need to pass a large chunk of content into a partial. Jamming a bunch of HTML through a parameter would be like trying to shove a pizza through a donut. Entertaining, but futile.\n\nSlots are the solution. By using the `partial` tag as a pair, everything inside will be passed into the partial, mapped to the {{ slot }} variable. Let's look at an example \"modal\" type of design component.\n\n```\n{{# /resources/views/partials/modal.antlers.html #}}\n\n<div class=\"modal\">\n  {{ slot }}\n</div>\n```\n\nWe can now pass whatever we want into the slot by injecting content into the partial:\n\n```\n{{ partial:modal }}\n  <h2>50% off everything, today only!</h2>\n  <a href=\"/sale\">\n    <img src=\"/img/sale.jpg\" alt=\"Man eating banana on sale.\" />\n  </a>\n{{ /partial:modal }}\n```\n\n#### Named slots\n\nSometimes you might want to render multiple different slots in different locations inside a partial. Let's modify our example to allow of the injection of a \"title\" slot:\n\n```\n{{# /resources/views/partials/modal.antlers.html #}}\n\n<div class=\"modal\">\n  <div class=\"modal-header\">{{ slot:header }}</div>\n  <div class=\"modal-content\">\n    {{ slot }}\n  </div>\n</div>\n```\n\nNow you can define the context of the named slot using the `slot:name` tag format. Any content not within an explicit `slot:name` tag will be passed to the partial in the `slot` variable.\n\n```\n{{ partial:modal }}\n  {{ slot:header }}\n    {{ svg src=\"icons/flag\" class=\"w-4 h-4 mr-2\" }}\n  {{ /slot:header }}\n\n  <a href=\"/sale\">\n    <img src=\"/img/sale.jpg\" alt=\"Man eating banana on sale.\" />\n  </a>\n{{ /partial:modal }}\n```\n### Stacks\n\nAntlers allows you to push template code to a \"stack\" which can be rendered somewhere else in your layout (most commonly) or another view. This can be particularly useful for specifying any JavaScript libraries required by your child views:\n\n```\n{{ push:scripts }}\n    <script src=\"//unpkg.com/alpinejs\" defer></script>\n{{ /push:scripts }}\n```\n\nYou may push to a stack as many times as needed. To render the complete stack contents, pass the name of the stack to the `{{ stack }}` tag:\n\n```\n<head>\n  <!-- All that heady stuff here -->\n  {{ stack:scripts }}\n</head>\n```\n\nIf you would like to prepend content onto the **beginning** of a stack, you should use the `{{ prepend }}` tag:\n\n```\n{{ push:scripts }}\n    This will be second...\n{{ /push:scripts }}\n\n{{# Later... #}}\n\n{{ prepend:scripts }}\n    This will be first...\n{{ prepend:scripts }}\n```\n\n### Once\n\nThe `{{ once }}` tag allows you to define a portion of the template that will only be evaluated once per rendering cycle. This may be useful for pushing a given piece of JavaScript into the page's header using [stacks](#stacks). For example, if you are looping through entries and rendering them with a partial, you may wish to only push the JavaScript to the header once, not every single time.\n\n```\n{{ collection:blog }}\n\n  {{ once }}\n    {{ push:scripts }}\n      <script src=\"//unpkg.com/alpinejs\" defer></script>\n    {{ /push:scripts }}\n  {{ /once }}\n\n  {{ partial:blog/card }}\n\n{{ /collection:blog }}\n```\n### Section & Yield\n\nYou may find that you wish to define areas of a layout that may need to change depending on which template is being rendered.\n\nLet's peek at this basic layout as an example:\n\n```\n<html>\n    <head>\n        <title>{{ title }} / {{ site:name }}</title>\n    </head>\n    <body>\n      <div class=\"container\">\n        {{ template_content }}\n      </div>\n\n      {{ yield:footer }}\n        <footer>\n          This is the main footer\n        </footer>\n      {{ /yield:footer }}\n    </body>\n</html>\n```\n\nNotice the [`yield`](/tags/yield) tag. The contents of that tag will be rendered unless another template injects content into it using the [`section`](/tags/section) tag.\n\n```\n{{# /resources/views/landing/special.antlers.html #}}\n\n{{ section:footer }}\n  <p>Hi, I am a special footer! 👋</p>\n{{ /section:footer }}\n```\n\n## Prevent parsing\n\nYou may find you need to prevent Antlers statements from being parsed. This is common when working with a JavaScript library like [Vue.js](https://vuejs.org), writing code examples, like we do in these docs. In either case, you have a few options.\n\n### The `@` ignore symbol\n\nFirst, you may use an `@` symbol on the outside of your curly braces to tell Antlers to leave it alone like a jellyfish on the beach. The `@` symbol will be stripped out automatically leaving nothing but your full expression behind.\n\n```\nHey, look at that @{{ noun }}!\n```\n\n``` html\nHey, look at that {{ noun }}!\n```\n\nThe `@` can also be used to escape individual braces within tag parameters or strings.\n\n```\n{{ partial:example attributes=\"class='@{font-bold: isImportant@}'\" }}\n// attributes=\"class='{font-bold: isImportant}'\"\n```\n\n```\n{{ \"string @{foo@} bar\" }}\n// \"string {foo} bar\"\n```\n\n### Ignoring Tag parameters\n\nYou may ignore the contents of tag parameters by prefixing the parameter with a backslash. This could be useful allow you to avoid having to escape each curly brace like the example above if you are providing some JS/JSON in a parameter:\n\n```\n{{ form:create \\x-data=\"{ submittable: false }\" }}\n```\n\n### The `noparse` Tag\n\nUse this method if you need to prevent entire code blocks from being parsed.\n\n```\n{{ noparse }}\n  Welcome to {{ fast_food_chain }},\n  home of the {{ fast_food_chain_specialty_item }},\n  can I take your order?\n{{ /noparse }}\n```\n\n## Using Antlers in content\n\nAntlers template code inside your content **is not** parsed automatically for security and performance reasons.\n\nYou may **enable** Antlers parsing on a per-field basis by setting `antlers: true` in a given field's blueprint config.\n\n### Opting into tags and modifiers {#allowing-tags-and-modifiers-in-content}\n\nWhen Antlers parses content (fields with `antlers: true`, or anything run through `Antlers::parse()`), it runs in a hardened mode that disables PHP syntax and restricts which tags and modifiers are available. This is **not** the same as a regular `.antlers.html` view — views still get the full, unrestricted Antlers experience.\n\n#### What's allowed by default\n\nOut of the box — with the `allowedContentTags` and `allowedContentModifiers` keys unset (the default shipped state) — you get:\n\n**Default tags:**\n\n- `link:*`\n- `obfuscate:*`\n- `trans:*`\n- `trans_choice:*`\n- `widont:*`\n- Any tags you've created in your own `App\\Tags\\` namespace (auto-allowed)\n\n**Default modifiers:** The broad set of safe built-in modifiers, including:\n\n- `markdown`\n- `sanitize`\n- `upper`\n- `lower`\n- `format`\n- `where`\n- `excerpt`\n- `nl2br`\n- `slugify`\n- ~150 others\n- Custom modifiers in your `App\\Modifiers\\` namespace (auto-allowed)\n\nThe full list lives in `Statamic\\Providers\\ViewServiceProvider::defaultAllowedContentModifiers()`.\n\nThe defaults are deliberately narrow. Any tag that can **fetch or expose site data** is excluded, because a content author typing `{{ collection from=\"private_drafts\" }}` or `{{ users }}` into an `antlers: true` field could otherwise \"leak\" data they they may or may not be entitled to. If you want those tags in content, opt in explicitly.\n\n#### Common tags you may want to opt into\n\nThese aren't on by default — add them to `allowedContentTags` if your editors need them:\n\n| Tag | Why it's not default |\n| --- | --- |\n| `collection:*` | Fetches entries from any collection |\n| `taxonomy:*` | Fetches terms from any taxonomy |\n| `nav`, `structure:*` | Fetches navigation trees |\n| `users:*` | Fetches user data |\n| `form:*` | Fetches form fields and submissions |\n| `assets:*`, `asset:*` | Fetches assets from any container |\n| `glide:*` | Image manipulation (generally safe, just not in the default set) |\n| `search:*` | Runs search queries |\n| `get_content`, `get_files` | Arbitrary data fetching |\n| `relate:*` | Follows relationships |\n\n#### Extending or replacing the defaults\n\nIf you need to allow additional tags or modifiers (like `{{ glide }}` or `{{ nav }}`), opt into them via `config/statamic/antlers.php`. Use the `@default` token to **keep the defaults and add to them** — without it, your array **replaces** the defaults entirely:\n\n```php\n// config/statamic/antlers.php\nreturn [\n    'allowedContentTags' => [\n        '@default',     // keep the built-in defaults\n        'glide:*',      // allow {{ glide }}, {{ glide:src }}, etc.\n        'nav',          // allow just {{ nav }}\n    ],\n\n    'allowedContentModifiers' => [\n        '@default',     // keep the built-in defaults\n        'my_custom',\n    ],\n];\n```\n\nTag entries are **patterns** — append `:*` to allow the tag and any of its parameters/sub-tags (`glide`, `glide:src`, `glide:generate`, etc). Modifier entries are exact handle matches.\n\n### Allowing config values\n\nReferencing config in content fields (e.g. `{{ config:app:url }}`) goes through a separate allowlist. Statamic's `@default` list covers the common safe keys. If you're pulling a custom key, or chaining modifiers onto a config value, add it to `view_config_allowlist` in `config/statamic/system.php`:\n\n```php\n// config/statamic/system.php\n'view_config_allowlist' => [\n    '@default',\n    'app.url2',\n    'services.stripe.key',\n],\n```\n\n## Code comments {#comments}\n\nAntlers code comments are not rendered in HTML (unlike HTML comments), which allows you to use them to \"turn off\" chunks of code, document your work, or leave notes and inside jokes for yourself and other developers.\n\n```\n{{# Remember to replace the lorem ipsum this time, Karen! #}}\n\n{{#\n  <h1>{{ title }}</h1>\n  <div>{{ date }}</div>\n  <div class=\"markdown\">{{ content }}</div>\n#}}\n```\n\n## Using PHP in Antlers\n\nYou can write PHP inside special delimiters. You may use `{{?...?}}` to write raw PHP and manipulate the current context (variables that exist in a given request), and `{{$...$}}` to `echo` the result of a PHP expression and render HTML.\n\n### Syntax\n\nThe following two syntax examples are functionally equivalent, but each uses a different approach based on the delimiter.\n\n```antlers\n{{? $register = route('account.register'); ?}}\n\n<a href=\"{{ $register }}\">Register for a new account</a>\n```\n\n``` antlers\n<a href=\"{{$ route('account.register') $}}\">Register for a new account</a>\n```\n\n### Accessing Data in PHP\n\nData in the [Cascade](/data-inheritance) can be accessed in much of the same way it is inside of a regular Antlers expression.\n\n```antlers\n\n<h1>{{? $page->title ?}}</h1>\n<p>{{? $globals->get('company_address') ?}}</p>\n```\n\n### PHP File Extension\n\nYou can also change your view's file extension from `.antlers.html` to `.antlers.php` and you can write all the raw PHP you want using native PHP tags.\n\n```php\n<?php\n  echo 'Keep it simple, please';\n?>\n```\n\n## Debugbar profiler 🎊 {#debugbar-profiler}\n\nAntlers has an experimental new Profiler tab in the [Debugbar](/debugging#debug-bar) that helps you see the performance impact of all of your template code.\n\n<figure>\n    <img src=\"/img/antlers-profiler.png\" alt=\"Antlers Profiler\">\n    <figcaption>Profiling some Antlers code.</figcaption>\n</figure>\n\nInside this Profiler there are 3 separate views that give you different glimpses into your site.\n\n### View graph\n\nThis view groups your Antlers expressions by the view files (templates, layouts, and partials) they exist in, allowing you to more easily tease out the location of any potential slowdowns or redundant calls.\n\nEach parsed expression in this view gets its own row in the table that shows various metrics and details that may prove to be useful.\n\n### Expression graph\n\nThis view shows all parsed expressions in a given request, listing them in **execution order**.\n\nEach parsed expression in this view gets its own row in the table that shows various metrics and details that may prove to be useful.\n\n### Source view\n\nThe Source View shows the final rendered template and highlights any content rendered by Antlers with a color corresponding to how fast it was executed. Green is fast, Yellow is a little slow, and Red is very slow.\n\n\n### Profiler columns\n\n| Column | Explanation |\n|--|--|\n| Time | Starting from 00:00, exactly when on the timer this expression was run. This helps to see the order your code is executed in. |\n| Type | Shows whether the expression is an imported view, a variable, or a [Tag](/tags). |\n| View Path | When using the **Expression Graph**, shows the path to the view file the expression exists in. |\n| Line | Shows what line the expression is in. If you have configured the debugbar by publishing its config and have specified which code editor you use, you can click the line and open your editor straight to this bit of template code. |\n| Memory Usage | How much memory was used to execute this expression. |\n| Execution | The number of separate times the expression was executed. |\n| Tag Time | The amount of time it took to run the expression |\n| Total Time | The amount of time it took to run the expression along with any child expressions (e.g. a tag pair) |\n| % | The percentage of total load time dedicated to run this expression. |\n\n### How to use the profiler\n\nIf you have some pages in your site that are running slow, the Profiler can help you narrow down and find bottlenecks in your template code. Look for anything colored — yellow, orange, and red all _may_ point to some logic that is performing extra slow.\n\nSlow expressions don't necessarily mean that _Antlers_ is slow at parsing them, but rather Statamic is doing a lot of work in order to fetch, filter, and/or manipulate your data to render your view.\n\nWith this information you can look for opportunities to cache bits of your template, open support requests and get clarification, ask questions in Discord, or pop the hood on your custom code to see what the hold up is.\n\n:::tip\nThis feature is new and experimental! It's recommendations and \"slow code\" thresholds may need to be updated after getting more real world data.\n:::\n\n## Even more advanced stuff\n\n[John Koster's blog](https://stillat.com/blog) is full of really useful tips and tricks covering some really advanced features not documented here. Be sure to check it out!\n\n## Thank you to John Koster 👏\n\nThis Antlers parser was a huge rewrite by the incomparable [John Koster](https://github.com/JohnathonKoster), who apparently found it a relaxing break from his day job. You can see the effort involved in this [massive PR](https://github.com/statamic/cms/pull/4257).\n\nWe owe him a debt of gratitude for this amazing gift.\n"
  },
  {
    "path": "content/collections/pages/assets.md",
    "content": "---\nid: 7277432d-bb25-458a-a3a2-a72976b44ad5\nblueprint: page\ntitle: Assets\nintro: 'Assets are files managed by Statamic and made available to your writers and developers with tags and fieldtypes. They can be images, videos, PDFs, or any other type of file. Assets can have fields and content attached to them, just like entries, making them very powerful.'\ntemplate: page\nrelated_entries:\n  - 5b748a3f-be0e-41c1-8877-73f6b7ee1d0a\n  - b70a3d9a-6605-446e-b278-de99ba561fe0\n  - 7277432d-bb25-458a-a3a2-a72976b44ad5\n  - 0c30a664-9bc3-4c5e-ad8c-66452b049748\n  - b50310b0-64ae-4ae4-b219-a637ed89e4d7\n  - 458b8203-e330-4d78-9bf5-82aaec8d458b\n  - d0c65546-74f1-4a15-89d5-1562a95ee2c6\n  - 420f083d-99be-4d54-9f81-3c09cb1f97b7\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1633025886\n---\n## Overview\n\nAssets live in directories on your local server, in an [Amazon S3 bucket](https://aws.amazon.com/s3), or other cloud storage services. Each defined location is called a **container**.\n\nStatamic scans the files in each container and caches [meta information](#metadata) (like `width` and `height` for images) on them. This cache is used to speed up interactions and response times when working with them on the [frontend](/frontend) of your site.\n\n## Asset browser\n\nYou can explore these files in the Control Panel's asset browser. You can edit, sort, search, move, rename, replace, reupload, preview, and — if working with images — even set focal crop points to make dynamically resized images look their best.\n\n<figure>\n    <img src=\"/img/asset-browser-v6.webp\" alt=\"Assets browser\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/asset-browser-v6-dark.webp\" alt=\"Assets browser\" class=\"u-hide-in-light-mode\">\n    <figcaption>Browsing some assets.</figcaption>\n</figure>\n\n## Asset actions\n\nThere are a number of actions that can be taken on assets while in the asset browser. Some can be run in bulk (on multiple assets at once), while others are only available on individual assets.\n\nSingle asset actions are available by clicking the options menu (three-dot icon) associated with the asset, and picking the desired action from the dropdown list.\n\nBulk asset actions are available in a floating toolbar at the bottom of the asset browser whenever you have one or more assets selected.\n\n<figure>\n    <img src=\"/img/asset-actions.webp\" alt=\"Assets actions\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/asset-actions-dark.webp\" alt=\"Assets actions\" class=\"u-hide-in-light-mode\">\n    <figcaption>Check out those sweet actions.</figcaption>\n</figure>\n\n### Edit\nEditing an asset opens a new modal window with a number of additional options, as well as any blueprint fields, like title, alt text, description, or other meta data defined on your asset container.\n\nMost of the asset actions are also available inside the editor, along with the ability to set a Focal Point for images.\n\n<figure>\n    <img src=\"/img/asset-editor-v6.webp\" alt=\"The Statamic Asset Editor\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/asset-editor-v6-dark.webp\" alt=\"The Statamic Asset Editor\" class=\"u-hide-in-light-mode\">\n    <figcaption>The asset editor is pretty slick, if we say so ourselves.</figcaption>\n</figure>\n\n### Crop\nThe crop action lets you visually crop an image directly in the Control Panel. It's available from the toolbar inside the [Asset Editor](#edit) for any image asset (except GIFs) when the current user has permission to upload to the container.\n\nYou can drag to define a custom crop area, or pick one of the [aspect ratio presets](#crop-aspect-ratios). A flip button rotates the ratio between landscape and portrait orientation. Hold the <kbd>Option</kbd> / <kbd>Alt</kbd> key while resizing to resize from the center, and press <kbd>Enter</kbd> to apply the crop.\n\nAfter cropping, you'll be asked whether you want to save the crop as a **new copy** (uploaded to the same folder with a timestamped filename) or **replace the original** image. Replacing requires the user to also have the `reupload` permission on the asset.\n\n:::tip\nCropping external images (for example, from an S3 container on a different domain) requires that the source be served with proper CORS headers. If the image can't be loaded cross-origin, the crop editor will warn you and close.\n:::\n\nBulk\n: No\n\n#### Crop aspect ratios\n\nStatamic ships with five aspect ratio presets (`16:9`, `4:3`, `3:2`, `2:1`, and `1:1`) available in the crop editor. You can customize them — or remove the dropdown entirely — via the `crop_aspect_ratios` array in `config/statamic/assets.php`.\n\nEach entry can be a `W:H` string, or an array with a `label` and a `ratio`. Labels are passed through Laravel's translator, so you can use translation keys to localize them.\n\n```php\n// config/statamic/assets.php\n\n'crop_aspect_ratios' => [\n    '16:9',\n    '4:3',\n    ['label' => 'Wide', 'ratio' => '16:9'],\n    ['label' => 'US Letter', 'ratio' => '8.5:11'],\n    ['label' => 'Golden', 'ratio' => 1.618],\n],\n```\n\nSet `crop_aspect_ratios` to an empty array to hide the preset dropdown entirely and force users to drag custom selections.\n\n```php\n'crop_aspect_ratios' => [],\n```\n\n### Copy URL\nRunning this action allows you to copy the URL of an asset. You can use the copied URL to share or reference the asset in other places, such as in emails, documents, or on other websites.\n\nBulk\n: No\n\n### Download\nWith this action, you can download an asset to your local device. It allows you to save a copy of the asset on your computer, making it accessible even when you're offline or outside Statamic.\n\nBulk\n: Yes\n\n### Duplicate\nThe duplicate action creates a copy of an asset. It's useful when you want to have multiple copies of the same asset, either for organizational purposes or to make variations or modifications to the duplicated version without affecting the original asset.\n\nWhen duplicated, the new filename will be appended with `-{numberOfDuplicates}`. If you duplicate a file 3 times, you will have new copies named `yourFile-1.ext`, `yourFile-2.ext`, `yourFile-3.ext`. Feel free to rename these. In fact, we encourage it.\n\nBulk\n: Yes\n\n### Move\nMoving an asset involves changing its location within the folder structure of your Statamic assets. This action is handy when you want to reorganize your assets or place them in a different folder for better categorization and management.\n\nAssets moved with the move action will update any references to it throughout your content wherever the [Assets field](/fieldtypes/assets) is used.\n\nBulk\n: Yes\n\n### Rename\nAs the name suggests, the rename action allows you to change the name of an asset. It's useful when you want to give a more descriptive or meaningful name to an asset or when you need to update the name to match changes in its content.\n\nAssets renamed with the rename action will update any references to it throughout your content wherever the [Assets field](/fieldtypes/assets) is used.\n\nBulk\n: Yes*\n\n_*Each rename action only accepts one new filename, so this is only useful in bulk for renaming files of different extensions._\n\n### Replace\nThe replace action lets you replace an existing asset with a new version with a new filename. This helps to ensure that your visitors don't run into browser-cached, old versions of your assets. Replaced assets with the replace action will update any references to it throughout your content wherever the [Assets field](/fieldtypes/assets) is used.\n\nBulk\n: No\n\n### Reupload\nReuploading an asset involves uploading a new version of an existing asset, effectively replacing the previous version with the **same exact filename**. Keep in mind that by not changing the filename, your visitors may encounter browser-cached, old versions of the asset.\n\nBulk\n: No\n\n### Delete\nThe delete action removes an asset from your site and server, permanently. Exercise caution when using this action, as deleted assets cannot always be easily restored.\n\nBulk\n: Yes\n\n## Asset fields\n\nAsset fields are configured like a [blueprint](/blueprints) and attached to the [container](#containers). Whenever you edit an asset in the Control Panel, you'll see the fields from the configured blueprint.\n\nThis data is stored in the asset's [meta data](#metadata) file.\n\n<figure>\n    <img src=\"/img/asset-editor.webp\" alt=\"The asset editor editing an image\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/asset-editor-dark.webp\" alt=\"The asset editor editing an image\" class=\"u-hide-in-light-mode\">\n    <figcaption>Editing an image with the asset editor.</figcaption>\n</figure>\n\n## Metadata\n\nAsset metadata is stored in YAML files inside a hidden `.meta` subdirectory inside each container. For example, `images/tree.jpg` gets an `images/.meta/tree.jpg.yaml` cache file.\n\nThese files contain cached data, including but not limited to: image dimensions, file size, last modification dates, and so on.\n\nThese cache files can also contain user created data. The fields are defined by the asset container's blueprint. Typically these are alt text, focal points, descriptions, and so on, but they could be anything you want at all.\n\n``` yaml\nsize: 9151\nlast_modified: 1558533973\nwidth: 216\nheight: 104\ndata:\n  alt: 'A tree with a tire swing'\n  focus: 54-54-1\n```\n\n:::tip\nYou should consider version controlling these files if you plan to set data like alt tags and focal points. Make sure your efforts are preserved.\n:::\n\n### Cleaning orphaned metadata\n\nWhen asset files are deleted outside of Statamic (e.g., directly via the filesystem or an S3 console), their metadata `.yaml` files can be left behind. Run the `assets:meta-clean` command to find and remove these orphaned metadata files, along with any now-empty `.meta` directories.\n\n``` shell\nphp please assets:meta-clean\n```\n\nPass a container handle to scope the cleanup to a single container, or use `--dry-run` to preview what would be deleted without making any changes.\n\n``` shell\nphp please assets:meta-clean images --dry-run\n```\n\n## Containers\n\nEach container has its own settings, configurable permissions, and [blueprint](#blueprints). One container might be a local filesystem with upload, download, rename, and move permissions enabled, and another could be a read-only remote S3 bucket or stock image service.\n\nContainers can be created through the Control Panel and are defined as YAML files located in `content/assets`. Each container's filename becomes its `handle`.\n\n``` yaml\n# content/assets/assets.yaml\ntitle: 'Assets'\ndisk: 'assets'\n```\n\nEach container implements a \"disk\", also known as a [Laravel Filesystem](https://laravel.com/docs/filesystem). This native Laravel feature groups a [driver](#drivers), URL, location, and [visibility](#container-visibility) together. Statamic includes a local disk on fresh installs. You can modify or delete it, but many sites can simply use it as is.\n\n``` php\n'disks' => [\n    'assets' => [\n        'driver' => 'local',\n        'root' => public_path('assets'),\n        'url' => '/assets',\n        'visibility' => 'public', // (more info about visibility below)\n    ],\n]\n```\n\nFilesystems are defined in `config/filesystems.php`.  They can point to the local filesystem, S3, or any [Flysystem adapter](https://flysystem.thephpleague.com/v2/docs/).\n\n### Private containers\n\nSometimes it’s handy to store assets that shouldn’t be publicly visible through a direct URL or browser.\n\n:::tip\nIf your asset container's disk does not have a `url` property, Statamic will not output URLs.\n:::\n\nPrivate containers should be located above webroot. If you leave the disk within the webroot, the files will still be accessible directly outside of Statamic if you know the file path.\n\n``` files theme:serendipity-light\n/\n  app/\n  content/\n  config/\n  public/\n    not-in-here/ # [tl! ~~]\n    index.php\n  put-it-out-here/ # [tl! ~~]\n  resources/\n  vendor/\n```\n\nMake sure to also set the [visibility](#container-visibility) to `private`.\n\n\n### Container visibility\n\nYour filesystem's disk can have a `visibility`, which is an abstraction of file permissions. You can set it to `public` or `private`,\nwhich essentially controls whether they're accessible or not.\n\nBe sure to set `'visibility' => 'public'` if you want to be able to see, interact with, and manipulate files in your container.\n\n:::tip\nIf you're using a service based driver like Amazon S3, and you want the files to be accessible by URL, make sure you set the [visibility](#container-visibility) to `public`.\n:::\n\n## Blueprints\n\nThe default container [Blueprint](/blueprints) contains a single \"alt text\" field — just useful and simple enough to get you started.\n\nYou can customize the fields on the blueprint by visiting the container in the Control Panel and choosing \"Edit Blueprint\" in the options dropdown.\n\nIf you want to edit the blueprint file directly, you can do so in `resources/blueprints/assets/{handle}.yaml`.\n\n## Ordering\n\n### Default sort order in listings\n\nYou can choose which field and direction to sort the list of assets in the Control Panel by setting the `sort_by` and `sort_dir` variables in your container.yaml. By default the file name will be used.\n\n## Drivers\n\nStatamic uses Flysystem and includes the core `local` driver. S3, SFTP, and other drivers can be [installed with composer](https://laravel.com/docs/filesystem#driver-prerequisites).\n\nFlysystem is not limited to these three, however. There are adapters for many other storage systems. You can [create a custom driver](https://laravel.com/docs/filesystem#custom-filesystems) if you want to use one of these additional adapters in your Laravel application.\n\n\n## Frontend templating {#templating}\n\nThere are two main methods for working with Asset data on the frontend. The Assets Fieldtype, and the Assets Tag.\n\n### Assets fieldtype\n\nThe [Assets Fieldtype](/fieldtypes/assets) can be used in your content Blueprints to attach assets to your entries, taxonomy terms, globals, or user accounts. It can be used to create image galleries, video players, zip downloads, or anything else you can think of.\n\nAll of the data stored on your Assets will be available on the frontend without having to create any kind of duplication.\n\n#### Example\n\nIf you had a `slideshow` field with a whole bunch of images selected, you can render them by looping through them.\n\n::tabs\n\n::tab antlers\n```antlers\n<div class=\"slideshow\">\n    {{ slideshow }}\n        <img src=\"{{ url }}\" alt=\"{{ alt }}\">\n    {{ /slideshow }}\n</div>\n```\n::tab blade\n```blade\n<div class=\"slideshow\">\n  @foreach($slideshow as $image)\n      <img src=\"{{ $image->url }}\" alt=\"{{ $image->alt }}\">\n  @endforeach\n</div>\n```\n::\n\nLearn more about the [Assets Fieldtype](/fieldtypes/assets).\n\n### Assets tag\n\nIf you ever find yourself needing to loop over all of the assets in a container (or folder inside a container) instead of selecting them manually with the Assets Fieldtype, this is the way.\n\n#### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ assets container=\"photoshoots\" limit=\"10\" sort=\"rating\" }}\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /assets }}\n```\n::tab blade\n```blade\n<statamic:assets\n  container=\"photoshoots\"\n  limit=\"10\"\n  sort=\"rating\"\n>\n  <img src=\"{{ $url }}\" alt=\"{{ $alt }}\" />\n</statamic:assets>\n```\n::\n\nLearn more about the [Assets Tag](/tags/assets) and what you can do with it.\n\n### Manipulating images\n\nStatamic uses the [Glide library](https://glide.thephpleague.com/) to dynamically resize, crop, and manipulate images. It's really easy to use and has [its own tag](/tags/glide).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide:image width=\"120\" height=\"500\" filter=\"sepia\" }}\n```\n::tab blade\n```blade\n{{-- Using Statamic Tags --}}\n<statamic:glide\n  :src=\"$img\"\n  width=\"120\"\n  height=\"500\"\n  filter=\"sepia\"\n/>\n\n{{-- Using Fluent Tags --}}\n\n{{\n  Statamic::tag('glide')\n    ->src($img)\n    ->width(120)\n    ->height(500)\n    ->filter('sepia')\n    ->fetch()\n}}\n```\n::\n\n## Search indexes\n\nYou can configure search indexes for your collections to improve the efficiency and relevancy of your users searches. Learn [how to connect indexes](search#connecting-indexes).\n\n## Allowed file extensions\n\nFor security reasons, Statamic restricts the file extensions that can be uploaded via the Control Panel and the Assets field on [Forms](/forms).\n\nCommon extensions like `.jpg`, `.csv` and `.txt` are permitted by default. To upload additional file extensions, specify them in `config/statamic/assets.php`:\n\n```php\n// config/statamic/assets.php\n\n'additional_uploadable_extensions' => [\n    'gpx', 'vcf', // ...\n],\n```\n\n## Upload validation\n\nEach [container](#containers) can define [Laravel validation rules](https://laravel.com/docs/validation#available-validation-rules) that are applied to every file uploaded to it — through the Control Panel asset browser, the [Assets fieldtype](/fieldtypes/assets), or [Forms](/forms).\n\nYou can configure rules in the Control Panel by editing the container and filling out the **Validation Rules** field, or by editing the container's YAML file directly:\n\n``` yaml\n# content/assets/images.yaml\ntitle: Images\ndisk: assets\nvalidate:\n  - 'mimes:jpg,jpeg,png,webp'\n  - 'max:2048'\n  - 'dimensions:min_width=600,min_height=600'\n```\n\nRules are merged with Statamic's built-in `file` and [allowed extension](#allowed-file-extensions) checks, so you only need to specify the additional constraints you care about. Failing uploads return a `422` response and surface the first validation message in the uploader UI.\n\n## Filename character replacements\n\nWhen files are uploaded, Statamic sanitizes the filename by replacing a handful of characters (spaces, `#`, `:`, `/`, `\\`, `?`, `<`, `>`, `\"`, `|`, `*`, `%`, `'`, and double dashes) with a single dash to keep filenames URL-safe.\n\nIf you need to replace additional characters — for example, commas and parentheses that clients keep sneaking their ways into filenames — you can add them to `config/statamic/assets.php`. These are **merged** with the native replacements and cannot override them.\n\n```php\n// config/statamic/assets.php\n\n'additional_filename_replacements' => [\n    ',' => '',\n    '(' => '',\n    ')' => '',\n],\n```\n\nWith the config above, `My Photo, (v2).jpg` would be saved as `my-photo-v2.jpg`.\n\n## SVG sanitization\n\nFor security reasons, Statamic automatically sanitizes uploaded SVG files.\n\nHowever, if you **trust your users** and need to upload SVG files without them being sanitization, you may disable it:\n\n```php\n// config/statamic/assets.php\n\n'svg_sanitization_on_upload' => false,\n```\n\n## Video thumbnails\n\nStatamic can generate thumbnails for video assets so they display alongside images in the Control Panel's asset browser, instead of showing a generic file icon.\n\n### Requirements\n\nVideo thumbnail generation relies on [FFmpeg](https://ffmpeg.org/) being installed and available on your server.\n\n``` shell\n# macOS (Homebrew)\nbrew install ffmpeg\n\n# Ubuntu/Debian\nsudo apt install ffmpeg\n```\n\nIf FFmpeg isn't found on the system `PATH`, you can point Statamic at the binary explicitly in `config/statamic/assets.php`:\n\n```php\n// config/statamic/assets.php\n\n'ffmpeg' => [\n    'binary' => '/usr/local/bin/ffmpeg',\n    'cache_path' => storage_path('statamic/glide/ffmpeg'),\n],\n```\n\nGenerated thumbnails are cached to disk at `cache_path` so FFmpeg only runs once per video.\n\n### Disabling\n\nVideo thumbnail generation is enabled by default. To disable it, set `video_thumbnails` to `false` in `config/statamic/assets.php`:\n\n```php\n//config/statamic/assets.php\n\n'video_thumbnails' => false,\n```\n\n## Custom cache stores\n\nStatamic leverages [Laravel's application cache](https://laravel.com/docs/cache) to cache asset metadata and folders. However, this means that whenever you run `php artisan cache:clear`, the cached asset information will be cleared.\n\nIf you have a lot of assets and/or folders, you might want to specify a custom cache store so the cached assets are persisted when you clear your application cache.\n\nThe cache store can be customized in `config/cache.php`.\n\n```php\n// config/cache.php\n\n'asset_meta' => [\n    'driver' => 'file',\n    'path' => storage_path('statamic/asset-meta'),\n],\n'asset_container_contents' => [\n    'driver' => 'file',\n    'path' => storage_path('statamic/asset-container-contents'),\n],\n```\n\nTo clear these caches, run `php please assets:clear-cache`.\n\n## Performance\n\nIf you're using [custom asset cache stores](#custom-cache-stores) and you're experiencing performance issues with Assets, like slow queries or a slow asset browser, it might be worth moving your assets to the database using the Eloquent Driver. It takes a different approach to caching asset metadata, which sometimes works better for sites with more assets.\n\nYou can find out more about [moving assets to the database here](/tips/storing-content-in-a-database#moving-content-to-the-database).\n"
  },
  {
    "path": "content/collections/pages/augmentation.md",
    "content": "---\nid: 9b2f6f55-5355-4334-b90d-d1236fb58887\nblueprint: page\ntitle: Augmentation\nintro: 'Augmentation automatically transforms the rendered output of all Blueprint-defined variables based on their fieldtype.'\n---\n## Overview\n\nAugmentation is kind of like magic. ✨\n\nFor example, while using a [Markdown field](/fieldtypes/markdown), your content will **automatically be converted to HTML** without needing to use a [markdown modifier](/modifiers/markdown). Each [fieldtype](/fieldtypes) documents if and how augmentation affects output.\n\n:::hint\nVariables created \"on the fly\" with Front Matter won't be augmented.\n:::\n\n## Example\n\nLet's look at an example with and without augmentation using the following `content`:\n\n``` md\n## How to jump higher\nBend your knees more and then spring upwards a _lot_ faster.\n```\n\n### With augmentation\n\nIf you're using a [Markdown field](/fieldtypes/markdown), the output will be as follows:\n\n```html\n<h2>How to Jump Higher</h2>\n<p>Bend your knees more and then spring upwards a <em>lot</em> faster.</p>\n```\n\n### Without augmentation\n\nWhile using a [Textarea field](/fieldtypes/textarea) — which _is not_ augmented — the output will be exactly the same as the input:\n\n```text\n  ## How to Jump Higher\n  Bend your knees more and then spring upwards a _lot_ faster.\n```\n\n## Digging deeper\n\n### What is Augmentation?\n\nAs explained above, augmentation lets Statamic convert one simple thing into a more complicated thing. \n\nIf you're familiar with Laravel, you can think of it like a `toArray()` method converting something into a different representation of itself. However, Augmentation does this in a more on-demand way to prevent extra overhead where possible.\n\nThere are two \"augmentable\" things: Field values, and objects.\n\n#### Field Values\n\nA field defined in a Blueprint will have a fieldtype associated with it. The raw value of the field can be converted by its fieldtype.\nThis is packaged together inside a [Value](#value) object.\n\nFor example, an [entries fieldtype](/fieldtypes/entries) will save an array of IDs. Simple strings. The fieldtype will know how to convert those IDs into Entry objects.\n\n#### Objects\n\nClasses like `Entry`, `Asset`, and `User` are [Augmentables](#augmentable), which means they're able to convert themselves to a bunch of usable values. Similar to the `toArray` method.\n\nFor example, a Statamic Asset may have a path and some alt text. However it also has the ability to check the file size, dimensions, timestamp, and so on.\n\n### Where do items get augmented?\n\nThe two main areas where augmentation is leveraged is in Antlers templates, and the API.\n\n#### Antlers Templates\n\nAntlers templates handle Augmentable and Value classes.\n\n- When it encounters an [Augmentable](#augmentable) object, it will convert it to its [Augmented](#augmented) counterpart. (Which is filled with Value classes)\n- When it encounters a [Value](#value) object, it will know when to augment it.\n\nFor example, when you visit an entry's URL, its values are provided to the template as Value objects.\nNone of the values have been augmented yet. It will only happen if/when the variable is used in the template.\n\n#### REST API\n\nSimilar to Antlers templates, when viewing an item via the API, you will see its augmented version.\n\nFor example, when viewing an Asset, you will get all the extras like filesize, dimensions, etc.\n\n``` json\n// /api/endpoint/asset/foo.jpg\n{\n    \"path\": \"foo.jpg\",\n    \"alt\": \"a thing\",\n    \"width\": 600,\n    \"height\": 400\n}\n```\n\nIf you use the `fields` filtering feature of the API, only the corresponding items will be fetched.\nHere, the width and height will never need to be evaluated at all.\n\n``` json\n// /api/endpoint/asset/foo.jpg?fields=path,alt\n{\n    \"path\": \"foo.jpg\",\n    \"alt\": \"a thing\"\n}\n```\n\n\n### Value\n\nAs mentioned earlier, the `Statamic\\Field\\Value` class wraps up field's raw value and the fieldtype that defines it. It will allow the [fieldtype](/extending/fieldtypes) to \"augment\" the value lazily, allowing the performance overhead to happen only when necessary.\n\nAn entries field will happily send around a lightweight array of ID strings. It will only try to convert those IDs to entries when it's the right time. No sense in performing unnecessary queries.\n\nThe `value` method is used to get the augmented value via the fieldtype. It'll be used under the hood when casting to a string or array. The `raw` method will get the un-augmented value.\n\n``` php\n$markdown = new Value('# Heading', 'content', $markdownFieldtype);\n$markdown->value();   // '<h1>Heading</h1>'\n(string) $markdown;   // '<h1>Heading</h1>'\n$markdown->raw();     // '# Heading'\n```\n\n``` php\n$posts = new Value(['1', '2'], 'posts', $entriesFieldtype);\n$posts->value();   // [Entry(1), Entry(2)]\n$posts->raw();     // ['1', '2']\n\nforeach ($posts as $entry) {\n    // Entry(1), Entry(2)\n}\n```\n\n### Augmentable\n\nAn augmentable object lets Statamic know that it could potentially have more data available to it than at first glance.\n\nFor example, a Statamic Asset may have a path and some alt text. However it also has the ability to check the file size, dimensions, timestamp, and so on.\nAll of these things can be lazy-loadable too. You can imagine the wasted overhead involved in checking for all those things for them to never be used.\n\nA class may be marked as \"augmentable\" by implementing the `Augmentable` interface.\n\n``` php\nuse Statamic\\Contracts\\Data\\Augmentable;\n\nclass Product implements Augmentable\n{\n    //\n}\n```\n\nRather than manually implementing all of `Augmentable`'s methods, we provide two traits, depending on how you plan to work.\n\nFor simple classes, you can use the `HasAugmentedData` trait.\n\n``` php\nuse Statamic\\Contracts\\Data\\Augmentable;\nuse Statamic\\Data\\HasAugmentedData;\n\nclass Product implements Augmentable\n{\n    use HasAugmentedData;\n\n    public function augmentedArrayData()\n    {\n        return [\n            'title' => $this->title,\n            'price' => $this->price,\n        ];\n    }\n}\n```\n\nHowever, since you're providing all the augmented values up front, you aren't really gaining anything. It's a nice way to start providing augmentable classes to templates, though.\n\nFor more advanced classes, you can use the `HasAugmentedInstance` trait, which lets you define an `Augmented` version of itself (more on that below).\n\n``` php\nuse Statamic\\Contracts\\Data\\Augmentable;\nuse Statamic\\Contracts\\Data\\Augmented;\nuse Statamic\\Data\\HasAugmentedInstance;\n\nclass Product implements Augmentable\n{\n    use HasAugmentedInstance;\n\n    public function newAugmentedInstance(): Augmented\n    {\n        return new AugmentedProduct($this);\n    }\n}\n```\n\n#### Augmentable Methods\n\n| Method | Description |\n|--------|-------------|\n| `augmentedValue($key)` | Gets a single augmented value by the key. |\n| `toAugmentedArray($keys = null)` | Gets an array of augmented values. You can specify which keys or leave it blank for all of them. |\n| `toAugmentedCollection($keys = null)` | Same as toAugmentedArray, but you get a collection object. |\n| `toShallowAugmentedArray()` | Gets an array of augmented values, but limited to a specific subset of them. See [shallow augmentation](#shallow-augmentation) |\n| `toShallowAugmentedCollection()` | Same as toShallowAugmentedArray, but a collection object. |\n\nThe difference betwen the array and collection methods are that when casting to JSON, the collection will [shallow augment](#shallow-augmentation) nested values.\n\n### Augmented\n\nYour objects may have \"Augmented\" counterparts. These classes allow you define which values will be available once augmented.\n\nTake this augmented product class as an example:\n\n``` php\nuse Statamic\\Data\\AbstractAugmented;\n\nclass AugmentedProduct extends AbstractAugmented\n{\n    public function keys()\n    {\n        return [\n            'title',\n            'price',\n            'perceived_price',\n        ];\n    }\n\n    public function perceivedPrice()\n    {\n        return BigMacPrice::calculateFor(User::current(), $this->data->price());\n    }\n}\n```\n\nWhen Statamic tries to retrieve a value from this class, it will check in this order:\n\n- A camelCased method defined on the class (requesting `perceived_price` will look for a `perceivedPrice` method)\n- A camelCased method defined on the original class, and wrap it in a Value object.\n- A value from the original class's data, and wrap it in a Value object.\n\nYou are required to provide a `keys` method, which lets Statamic know all of the potentially available values, used when it tries to retrieve \"all\" values.\n\n``` php\n$augmented->all();\n\n// [\n//     'title' => 'My Product',\n//     'price' => 50,\n//     'perceived_price' => 55,\n// ]\n```\n\nLet's assume that the `perceivedPrice` method performs an intensive operation, like making an API request or a complex calculation. If we were to request a different value, we would be avoiding the overhead entirely.\n\n``` php\n$augmented->select(['title', 'price']);\n\n// [\n//     'title' => 'My Product',\n//     'price' => 50,\n// ]\n```\n\nAs mentioned earlier, the REST API does exactly this. When you request specific fields on the URL, it will be selecting the corresponding augmented values under the hood:\n\n``` php\n// Request to /api/endpoint/something?fields=one,two\n$augmented->select(['one', 'two']);\n```\n\n### Shallow Augmentation\n\nTo prevent potentially enormous or unnecessary amounts of deeply nested data being output in a number of places, we have the concept of shallow augmentation, which just displays a subset of the available augmented values.\n\nFor example, in the REST API, if you were to request a entry, you'd see all of its fields, but they will only show a limited, single-depth subset of data.\n\n``` yaml\nid: 1\ntitle: My Post\nrelated_posts:\n  - 1\n  - 2\ncontent: 'The post body content'\n```\n\n``` json\n{\n    \"id\": \"1\",\n    \"title\": \"My Post\",\n    \"related_posts\": [\n        {\n            \"id\": \"1\",\n            \"title\": \"My Post\",\n            \"api_url\": \"/api/collections/posts/entries/1\",\n        },\n        {\n            \"id\": \"2\",\n            \"title\": \"Another Post\",\n            \"api_url\": \"/api/collections/posts/entries/2\",\n        }\n    ]\n}\n```\n\nNotice that the `content` and `related_posts` fields do *not* appear in the nested list of entries. You get the basics (there are few more, but kept simple for this example) and enough to make an extra API request if you need it.\n\nAlso, as this particular example references itself, not only would it add a lot of extra data to the response, it would create an infinite loop!\n\nWe provide a sensible set of fields to include when shallow augmenting, but if you need to add more fields, you can [override](/extending/repositories#custom-data-classes) the `shallowAugmentedArrayKeys` method on the object.\n\n``` php\nclass CustomEntry extends Entry\n{\n    public function shallowAugmentedArrayKeys()\n    {\n        return ['id', 'title', 'field_i_must_have', 'api_url'];\n    }\n}\n```\n\n### Always Augment Relationships to a Query\n\nBy default, [relationship fieldtypes](/fieldtypes/entries) ([Entries](/fieldtypes/entries), [Terms](/fieldtypes/terms), [Users](/fieldtypes/users), and [Assets](/fieldtypes/assets)) augment differently based on their `max_items` setting:\n\n- When `max_items` is **not** `1`, the field augments to a **query builder** that you can chain, filter, and iterate.\n- When `max_items` **is** `1`, the field short-circuits and augments to the **first result** (e.g. an `Entry`, `Term`, `User`, or `Asset` instance).\n\nThat shortcut is convenient, but it's inconsistent with the multi-item case and has a subtle side-effect: the automatic \"first result\" query only considers **published** items. If you need to alter the query (e.g. include drafts or scheduled entries), you can't — you never see the query builder.\n\nTo force relationship fields to **always** augment to a query builder regardless of `max_items`, enable the `always_augment_to_query` option in `config/statamic/system.php`:\n\n``` php\n// config/statamic/system.php\n'always_augment_to_query' => true,\n```\n\n:::hint\n**This is opt-in and will change your templates.** With it enabled, a `max_items: 1` field no longer returns a single object — it returns a query builder. That means shorthand like `{{ product:title }}` stops working and you must loop (or call `.first()`) explicitly.\n:::\n\n::tabs\n\n::tab antlers\n```antlers\n{{# Before: max_items: 1 augments to the related entry #}}\n{{ product:title }}\n\n{{# After: max_items: 1 augments to a query builder #}}\n{{ product }}\n    {{ title }}\n{{ /product }}\n```\n\n::tab blade\n```blade\n{{-- Before: $product is an Entry --}}\n{{ $product->title }}\n\n{{-- After: $product is a query builder --}}\n@foreach ($product as $item)\n    {{ $item->title }}\n@endforeach\n```\n\n::\n\nThe upside is that you can now modify the query anywhere a relationship field is used — apply scopes, include drafts, change ordering, etc. — using the same fluent API you'd use on a collection query."
  },
  {
    "path": "content/collections/pages/backend-apis.md",
    "content": "---\nid: 9ca16d8c-a5cf-4fdb-8252-898626e2390b\ntitle: 'Backend & APIs'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/bard-v1-to-v2.md",
    "content": "---\nid: dbad308e-fdae-42ed-9168-75521bc5d5fe\nblueprint: page\ntitle: 'Upgrade from Bard v1 to v2'\nintro: 'A guide for upgrading from Bard v1 to v2.'\ntemplate: page\n---\n## Overview\n\nA new version of Bard (and Tiptap, the library that Bard is built on) was introduced in Statamic 3.4.\n\nThere is nothing you need to do to upgrade Bard or Tiptap since they're included in the Statamic upgrade, however if you have any custom extensions, you'll need to make some adjustments.\n\n\n## JavaScript Extensions\n\nExtensions no longer need to use our wrappers. You can now use regular Tiptap extensions.\n\nHere's how you'd import and bootstrap the extension into Statamic.\n\n```js\nimport AwesomeExtension from './AwesomeExtension'; // [tl!--]\nimport { AwesomeExtension } from './AwesomeExtension'; // [tl!++]\n\nStatamic.$bard.addExtension(({ mark }) => mark(new AwesomeExtension)); // [tl!--]\nStatamic.$bard.addExtension(() => AwesomeExtension); // [tl!++]\n```\n\nHere's just the \"container\" of your extension (so you don't get overwhelmed by a large diff).\n\n```js\nconst { Mark } = Statamic.$bard.tiptap.core; // [tl!++]\n\nexport default class AwesomeExtension { // [tl!--]\nexport const AwesomeExtension = Mark.create({ // [tl!++]\n    // ...\n} // [tl!--]\n}) // [tl!++]\n```\n\nAnd here are the various inner parts of the extension. Starting with name now being a property, and should be camelCased:\n\n```js\nname() { // [tl! --:start]\n    return 'awesome_extension'\n} // [tl! --:end]\nname: 'awesomeExtension' // [tl! ++]\n```\n\nSchema is split into parseHTML and renderHTML:\n\n```js\nschema() { // [tl! --:start]\n    return {\n        parseDOM: [\n            { style: 'color: red; font-family: cursive' }\n        ],\n        toDOM: () => ['span', { style: 'color: red; font-family: cursive' }, 0],\n    }\n} // [tl! --:end]\nparseHTML() { // [tl! ++:start]\n    return [\n        { style: 'color: red; font-family: cursive' }\n    ];\n},\nrenderHTML() {\n    return ['span', {style: 'color: red; font-family: cursive'}, 0];\n} // [tl! ++:end]\n```\n\nAll commands are explicitly added by name:\n\n```js\ncommands({ type, toggleMark }) {// [tl! --:start]\n    return () => toggleMark(type)\n}// [tl! --:end]\naddCommands() {// [tl! ++:start]\n    return {\n        toggleAwesome: () => ({ commands }) => {\n            return commands.toggleMark(this.type);\n        }\n    }\n}// [tl! ++:end]\n```\n\n## Buttons\n\nButtons can largely remain the same. You need to use the new camelCase name, and use a function to call your command manually. In this example we'll use the `toggleAwesome` command defined above.\n\n```js\nStatamic.$bard.buttons((buttons, button) => {\n    return button({\n        name: 'awesome_extension', // [tl! --]\n        name: 'awesomeExtension', // [tl! ++]\n        text: __('Awesome'),\n        command: 'fancy',  // [tl! --]\n        command: (editor) => editor.chain().focus().toggleAwesome().run()  // [tl! ++]\n    });\n});\n```\n\n## PHP Extensions\n\nOn the PHP side, we no longer use the `prosemirror-to-html` package. We now use the more official `tiptap-php` package. The classes have changed a little.\n\n```php\n<?php\n\nnamespace App\\Bard;\n\nuse ProseMirrorToHtml\\Marks\\Mark; // [tl!--]\nuse Tiptap\\Core\\Mark; // [tl!++]\nuse Tiptap\\Utils\\HTML; // [tl!++]\n\nclass Awesome extends Mark\n{\n    protected $markType = 'awesome_extension'; // [tl!--]\n    public static $name = 'awesomeExtension'; // [tl!++]\n\n    public function tag()  // [tl!--]\n    public function renderHTML($mark, $attributes = [])  // [tl!++]\n    {\n        return [\n            [  // [tl!--:start]\n                'tag' => 'span',\n                'attrs' => [\n                    'style' => 'color: red; font-family: cursive'\n                ]\n            ] // [tl!--:end]\n            'span', // [tl!++:start]\n            HTML::mergeAttributes([\n                'style' => 'color: red; font-family: cursive'\n            ], $attributes),\n            0 // [tl!++:end]\n        ];\n    }\n}\n```\n\nTo add or replace extensions, there is no longer a difference between marks and nodes. You now specify the name and pass the extension.\n\n```php\nAugmentor::addMark(Awesome::class); // [tl!--]\nAugmentor::addExtension('awesome_extension', new Awesome); // [tl!++]\n\nAugmentor::replaceMark(DefaultBold::class, CustomBold::class);  // [tl!--]\nAugmentor::replaceExtension('bold', new CustomBold); // [tl!++]\n```\n"
  },
  {
    "path": "content/collections/pages/blade-form-fields.md",
    "content": "---\nid: e8504150-db55-4986-b567-19c046cc03de\nblueprint: page\ntitle: 'Blade Form Field Templates'\nintro: 'By default, [Pre-rendered Field](/tags/form-create#pre-rendered-field-html) templates are implemented in Antlers. If you prefer to use Blade, you may use the following snippets as a starting point in your project.'\n---\n\n## Publishing Existing Templates\n\nYou can publish the existing field templates by running `php artisan vendor:publish --tag=statamic-forms`. It will expose editable templates snippets in your `views/vendor/statamic/forms/fields` directory that will be used by each fieldtype.\n\nThese templates are used when rendering the pre-rendered `field` variable inside a [form](/forms):\n\n```blade\n@foreach ($fields as $field)\n  <div class=\"mb-2\">\n    <label class=\"block\">{{ $field['display'] }}</label>\n    {!! $field['field'] !!}\n  </div>\n@endforeach\n```\n\n## Assets\n\n`resources/views/vendor/statamic/forms/fields/assets.blade.php`\n\n```blade\n@php\n  $isMultiple = ! isset($max_files) || $max_files !== 1;\n  $fieldName = $handle;\n\n  if ($isMultiple) {\n    $fieldName .= '[]';\n  }\n@endphp\n\n<input\n  type=\"file\"\n  name=\"{{ $fieldName }}\"\n  @if (isset($js_driver)) {!! $js_attributes !!} @endif\n  @if ($isMultiple) multiple @endif\n  @required(in_array('required', $validate ?? []))\n>\n```\n\n## Checkboxes\n\n`resources/views/vendor/statamic/forms/fields/checkboxes.blade.php`\n\n```blade\n@php\n  $inline = isset($inline) && $inline === true;\n@endphp\n\n<input type=\"hidden\" name=\"{{ $handle }}[]\">\n@foreach ($options as $option => $label)\n  <label>\n    <input\n      type=\"checkbox\"\n      name=\"{{ $handle }}[]\"\n      value=\"{{ $option }}\"\n      @if (isset($js_driver)) {!! $js_attributes !!} @endif\n      @checked(in_array($option, $value ?? []))\n      @required(in_array('required', $validate ?? []))\n    >\n    {{ $label === null ? $option : $label }}\n  </label>\n  @unless ($inline) <br> @endunless\n@endforeach\n```\n\n## Default\n\n`resources/views/vendor/statamic/forms/fields/default.blade.php`\n\n```blade\n<input\n   type=\"{{ $input_type ?? 'text' }}\"\n   name=\"{{ $handle }}\"\n   value=\"{{ $value ?? '' }}\"\n   @if (isset($placeholder)) placeholder=\"{{ $placeholder }}\" @endif\n   @if (isset($character_limit)) maxlength=\"{{ $character_limit }}\" @endif\n   @if (isset($js_driver)) {!! $js_attributes !!} @endif\n   @required(in_array('required', $validate ?? []))\n>\n```\n\n## Dictionary\n\n`resources/views/vendor/statamic/forms/fields/dictionary.blade.php`\n\n```blade\n@php\n  $isMultiple = ! isset($max_items) || $max_items !== 1;\n  $inline = isset($inline) && $inline === true;\n  $placeholderText = $placeholder ?? __('Please select...');\n\n  $fieldName = $handle;\n\n  if ($isMultiple) {\n    $fieldName .= '[]';\n  }\n@endphp\n\n<select\n  name=\"{{ $fieldName }}\"\n>\n  @unless ($isMultiple)\n    <option value>{{ $placeholderText }}</option>\n  @endunless\n  @foreach ($options as $option => $label)\n    @php\n      $selected = false;\n\n      if ($isMultiple) {\n        $selected = in_array($option, $value ?? []);\n      } else {\n        $selected = $option == $value;\n      }\n    @endphp\n    <option\n      value=\"{{ $option }}\"\n      @selected($selected)\n    >{{ $label === null ? $option : $label }}</option>\n  @endforeach\n</select>\n```\n\n## Files\n\n`resources/views/vendor/statamic/forms/fields/files.blade.php`\n\n```blade\n@php\n  $isMultiple = ! isset($max_files) || $max_files !== 1;\n  $fieldName = $handle;\n\n  if ($isMultiple) {\n    $fieldName .= '[]';\n  }\n@endphp\n\n<input\n  type=\"file\"\n  name=\"{{ $fieldName }}\"\n  @if (isset($js_driver)) {!! $js_attributes !!} @endif\n  @if ($isMultiple) multiple @endif\n  @required(in_array('required', $validate ?? []))\n>\n```\n\n## Radio\n\n`resources/views/vendor/statamic/forms/fields/radio.blade.php`\n\n```blade\n@php\n  $inline = isset($inline) && $inline === true;\n@endphp\n\n@foreach ($options as $option => $label)\n  <label>\n    <input\n      type=\"radio\"\n      name=\"{{ $handle }}\"\n      value=\"{{ $option }}\"\n      @if (isset($js_driver)) {!! $js_attributes !!} @endif\n      @checked($value == $option)\n      @required(in_array('required', $validate ?? []))\n    >\n    {{ $label === null ? $option : $label }}\n  </label>\n  @unless ($inline) <br> @endunless\n@endforeach\n```\n\n## Select\n\n`resources/views/vendor/statamic/forms/fields/select.blade.php`\n\n```blade\n@php\n  $isMultiple = isset($multiple) && $multiple == true;\n  $inline = isset($inline) && $inline === true;\n  $placeholderText = $placeholder ?? __('Please select...');\n\n  $fieldName = $handle;\n\n  if ($isMultiple) {\n    $fieldName .= '[]';\n  }\n@endphp\n\n<select\n  name=\"{{ $fieldName }}\"\n  @if (isset($js_driver)) {!! $js_attributes !!} @endif\n  @if ($isMultiple) multiple @endif\n  @required(in_array('required', $validate ?? []))\n>\n  @unless ($isMultiple)\n    <option value>{{ $placeholderText  }}</option>\n  @endunless\n  @foreach ($options as $option => $label)\n    @php\n      $selected = false;\n\n      if ($isMultiple) {\n        $selected = in_array($option, $value ?? []);\n      } else {\n        $selected = $option == $value;\n      }\n    @endphp\n    <option\n            value=\"{{ $option }}\"\n            @selected($selected)\n    >{{ $label === null ? $option : $label }}</option>\n  @endforeach\n</select>\n```\n\n## Text\n\n`resources/views/vendor/statamic/forms/fields/text.blade.php`\n\n```blade\n<input\n  type=\"{{ $input_type ?? 'text' }}\"\n  name=\"{{ $handle }}\"\n  value=\"{{ $value ?? '' }}\"\n  @if (isset($placeholder)) placeholder=\"{{ $placeholder }}\" @endif\n  @if (isset($character_limit)) maxlength=\"{{ $character_limit }}\" @endif\n  @if (isset($autocomplete)) autocomplete=\"{{ $autocomplete }}\" @endif\n  @if (isset($js_driver)) {!! $js_attributes !!} @endif\n  @required(in_array('required', $validate ?? []))\n>\n```\n\n## Textarea\n\n`resources/views/vendor/statamic/forms/fields/textarea.blade.php`\n\n```blade\n<textarea\n  name=\"{{ $handle }}\"\n  rows=\"5\"\n  @if (isset($placeholder)) placeholder=\"{{ $placeholder }}\" @endif\n  @if (isset($character_limit)) maxlength=\"{{ $character_limit }}\" @endif\n  @if (isset($js_driver)) {!! $js_attributes !!} @endif\n  @required(in_array('required', $validate ?? []))\n>{{ $value }}</textarea>\n```\n\n## Toggle\n\n`resources/views/vendor/statamic/forms/fields/toggle.blade.php`\n\n```blade\n<label>\n  <input type=\"hidden\" name=\"{{ $handle }}\" value=\"0\">\n  <input\n    type=\"checkbox\"\n    name=\"{{ $handle }}\"\n    value=\"1\"\n    @if (isset($js_driver)) {!! $js_attributes !!} @endif\n    @checked($value && $value !== '0')\n    @required(in_array('required', $validate ?? []))\n  >\n  @if (isset($inline_label)) {{ $inline_label }} @endif\n</label>\n```\n"
  },
  {
    "path": "content/collections/pages/blade.md",
    "content": "---\nid: c7816387-ebc4-4204-b5f2-8e7073a4db8b\nblueprint: page\ntitle: 'Blade Templates'\nintro: '[Antlers](/antlers) is not _always_ the best template engine for the job. If you''re using Statamic as a headless CMS or want to share views with a Laravel application already using [Blade](https://laravel.com/docs/blade) or another engine, you can do that.'\n---\n## Overview\n\nWhile Statamic's [Antlers](/antlers) template language is powerful, tightly integrated, and simple to learn, it's not the only way to build your frontend views.\n\nAntlers combines the responsibilities of Blade Templates _and_ [Controllers](/controllers) all at once. If you choose to **not** use Antlers, you _may_ need to create controllers and routes to fetch content and map them to templates depending on what you're doing. Want to write Antlers in your Blade templates? That's also possible by using the [@antlers](#writing-pure-antlers-in-blade) Blade directive.\n\n## How to render a template with Blade\n\nInstead of naming your views `myview.antlers.html`, use `myview.blade.php` extension.\n\n## View data\n\nYou will have access to the same data as you would in Antlers views.\n\n### Current page\n\nThe current page's data will be available in a `$page` variable, and you can access its values using a syntax similar to Eloquent models.\n\n``` yaml\n---\ntitle: My First Breakdance Competition\nmoves:\n  - Toprock\n  - 6-step\n  - Windmill\n  - L-kick\n  - Headspin\n---\nI did not win but I did have a good time.\n```\n\n``` blade\n<h1>{{ $page->title }}</h1>\n\n<p>First I did\n@foreach ($page->moves as $move)\n    {{ $move }}, then I did\n@endforeach\nand it was sick.</p>\n\n{{ $page->content }}\n```\n\n:::tip\nAntlers outputs **unescaped** values by default, while `{{ $content }}` in Blade will be escaped. If you need to output unescaped HTML, use `{!! $content !!}`\n:::\n\n:::tip\nWhen on a custom route, the `$page` variable **won't** be available in your view. \n:::\n\n### Globals\n\nThere is a variable for each global set, and its fields can be accessed using the same Eloquent style syntax.\n\n```yaml\n# content/globals/settings.yaml\ndata:\n  site_name: Rad City\n```\n\n```blade\n{{ $settings->site_name }}\n```\n\n### System variables\n\nTop level [system variables](/variables#system-variables) like, `environment`, `logged_in`, etc will be available as dedicated variables.\n\n```blade\n{{ $environment }}\n@if ($logged_in) ... @endif\n```\n\n### Relationships / Queries\n\nSome fieldtypes (e.g. `entries`) will supply their data as query builders. These will work similar to Eloquent models, too.\n\nIf you use property access, it will resolve the query builder and get the items.\n\n```blade\n@foreach ($page->related_posts as $post)\n  {{ $post->title }}\n@endforeach\n```\n\nIf you use a method, it will give you a query builder and allow you to chain clauses on it.\n\n```blade\n@foreach ($page->related_posts()->where('title', 'like', '%awesome%')->get() as $post)\n  {{ $post->title }}\n@endforeach\n```\n\n### Loop variables {#loop-variables}\n\nThe helper variables Antlers exposes inside loops — `first`, `last`, `count`, `index`, `total_results` — are an Antlers feature and are **not** available in Blade. Instead, use Laravel's built-in [`$loop` variable](https://laravel.com/docs/blade#the-loop-variable), which is available inside every `@foreach` and gives you the same information (and more).\n\n| Antlers | Blade equivalent |\n| --- | --- |\n| `{{ first }}` | `$loop->first` |\n| `{{ last }}` | `$loop->last` |\n| `{{ count }}` | `$loop->iteration` |\n| `{{ index }}` | `$loop->index` |\n| `{{ total_results }}` | `$loop->count` |\n\n```blade\n<ul>\n<s:collection:blog>\n    <li @class(['featured' => $loop->first])>\n        {{ $loop->iteration }} of {{ $loop->count }} — {{ $title }}\n        @if ($loop->last) (that's all, folks!) @endif\n    </li>\n</s:collection:blog>\n</ul>\n```\n\nFor nested loops, `$loop->parent` gives you access to the outer loop's variable. See the [Laravel docs](https://laravel.com/docs/blade#the-loop-variable) for the full list of properties.\n\n### Cascade directive\n\nWhen using blade components or rendering views loaded by non-Statamic routes/controllers, the cascade data will be not available by default. In these situations you can use the `@cascade` directive to populate the current scope with cascade data.\n\nIt works in exactly the same way as the `@props` directive used in blade components, with the ability to require certain values and provide default fallback values:\n\n```blade\n@cascade([\n    'site', // Will throw an exception if missing from the cascade\n    'my_global',\n    'page' => null, // Will use fallback if missing from the cascade\n    'live_preview' => false,\n])\n\n{{ $site->locale }}\n{{ $page?->title }}\n```\n\nIt is also possible to populate the current scope with all cascade data if needed:\n\n```blade\n@cascade\n```\n\n## Writing pure Antlers in Blade 🆕\n\nBy using the `@antlers` and `@endantlers` Blade directive pair you can write pure Antlers in your Blade templates.\n\n```antlers\n@antlers\n    {{ collection:articles }}\n        {{ title }}\n    {{ /collection:articles }}\n@endantlers\n```\n\nUnder the hood, this is syntactic sugar for creating an Antlers partial and does an on-the-fly `@include('antlers_file_name_here')` for you. This means that variables created _inside_ the Antlers will not be available _outside_ of the `@antlers` directive.\n\n## Using Antlers Blade components\n\nDespite the name, Antlers Blade Components are a Blade-only feature that allows you to use existing tags inside your Blade templates using a custom tag syntax. For example, you can gather all entries from a \"pages\" collection using the [collection](/tags/collection) tag like so:\n\n```blade\n<s:collection:pages>\n  {{ $title }}\n</s:collection:pages>\n```\n\nIn the previous example, you may have noticed the `s:` prefix. To use Antlers Blade Components we must prefix the tag name with either `s:` or `statamic:` (`s-` and `statamic-` also work).\n\nWe can pass multiple parameters to the tag like so:\n\n```blade\n<s:collection\n  from=\"pages\"\n  limit=\"2\"\n  sort=\"title:desc\"\n>\n  {{ $title }}\n</s:collection>\n```\n\nWe can also pass dynamic values to parameters:\n\n```blade\n@php\n  $collection = 'pages';\n@endphp\n\n<s:collection\n  :from=\"$collection\"\n  limit=\"2\"\n  sort=\"title:desc\"\n>\n  {{ $title }}\n</s:collection>\n```\n\n### Antlers Blade components and partials\n\nPartials also work with Antlers Blade components, and are intended to be used when you'd like to include an Antlers partial inside your Blade template:\n\n```blade\n<s:partial:the-partial-name>\n  <s:slot:header>The header content.</s:slot:header>\n\n  Default slot content.\n</s:partial:the-partial-name>\n```\n\n:::tip\nIf you are going all-in on Blade for a new project, you should consider sticking to Blade features such as `@include` or Blade components instead of reaching for the `partial` tag.\n:::\n\n## Using fluent tags with Blade\n\nYou can use [Tags](/tags) in Blade templates with a Laravel-style fluent syntax. Instantiate your tag with the `Statamic::tag()` method and chain parameters as needed.\n\n``` blade\n@foreach(Statamic::tag('collection:pages')->limit(3) as $page)\n    <li>{{ $page->title }}</li>\n@endforeach\n```\n\n```\n• Home\n• Gallery\n• Contact\n```\n\n:::tip\nWhen using multi-word parameters, like `query_scope`, you must use the camelCased version (`queryScope`).\n:::\n\n### Using explicit parameter setters\n\nIf you need to set a parameter containing a colon (ie. a [filter](/tags/collection#filtering) param), you can use the dedicated `param()` setter method:\n\n```php\nStatamic::tag('collection:pages')->param('title:contains', 'pizza')\n```\n\nOr even set multiple parameters at once using the plural `params()` method:\n\n```php\nStatamic::tag('collection:pages')->params([\n    'title:contains' => 'pizza',\n    'description:contains' => 'lasagna',\n])\n```\n\n### Passing contextual data\n\nYou can pass in contextual data to the tag using the `context($data)` method:\n\n```php\nStatamic::tag('collection:pages')->context($context)\n```\n\n### Fetching the output\n\nWhen you loop over a tag or cast it to a string, it will automatically fetch the result for you. In some cases, you may want to explicitly fetch the output. You can do that with the `fetch` method.\n\n```blade\n@php($output = Statamic::tag('collection:pages')->fetch())\n```\n\n### Pagination\n\nFor tags that provide pagination, you can `fetch` the tag's output in a variable, then output the results and links separately:\n\n```blade\n@php($tag = Statamic::tag('collection:pages')->paginate(2)->as('pages')->fetch())\n\n@foreach($tag['pages'] as $page)\n    <li>{{ $page->title }}</li>\n@endforeach\n\n{{ $tag['paginate']['auto_links'] }}\n```\n\n### Directive {#tag-directive}\n\nYou may prefer to use an alternate syntax, available via a `@tags` Blade directive.\n\nPassing a string will give you the camelCased version of the tag as a variable:\n\n```blade\n@tags('collection:blog')\n\n@foreach($collectionBlog as $post) ... @endforeach\n```\n\nPassing an array of tags can provide multiple variables:\n\n```blade\n@tags(['collection:blog', 'collection:items'])\n\n@foreach($collectionBlog as $post) ... @endforeach\n\n@foreach($collectionItems as $item) ... @endforeach\n```\n\nYou may also pass an array of tags, and parameters, with variable names as the keys:\n\n```blade\n@tags([\n    'posts' => ['collection:blog' => ['limit' => 5]], \n    'items' => ['collection:items' => ['limit' => 5]],\n])\n\n@foreach($posts as $post) ... @endforeach\n\n@foreach($items as $item) ... @endforeach\n```\n\n## Using modifiers with Blade\n\nYou can also use [Modifiers](/modifiers) in Blade templates with a Laravel-style fluent syntax. Wrap your value with the `Statamic::modify()` method and chain modifiers as needed. The value will get passed along in sequence like it does in Antlers. Any parameters should be specified like regular PHP parameters. If you use a modifier that can take more than one parameter, pass those in as an array.\n\n``` blade\n{{ Statamic::modify($content)->striptags()->backspace(1)->ensureRight('!!!') }}\n{{ Statamic::modify($content)->stripTags()->safeTruncate([42, '...']) }}\n```\n\n```\nTHIS IS THE FIRST POST, HOW EXCITING!!!\nI wanted to say more but got cut off...\n```\n\n:::tip\nWhen using multi-word modifiers, like `ensure_right`, you must use the camelCased version (`ensureRight`).\n:::\n\nIf you're using a lot of modifiers in your Blade template, you can also include the `modify` helper function in your template:\n\n```blade\n@php\n  use function Statamic\\View\\Blade\\{modify};\n@endphp\n\n{{ modify('test')->stripTags()->backspace(1)->ensureRight('!!!') }}\n{{ modify('test')->stripTags()->safeTruncate([42, '...']) }}\n```\n\n## Conditional logic and values\n\nDepending on where you got a value from, it may be wrapped in a class like `Value`. These can lead to unexpected results in conditional logic if they are not handled correctly (i.e., calling `->value()` in the condition).\n\nIf you'd prefer to not think about that, you can include the `value` helper function into your template, which will take care of this for you:\n\n```blade\n@php\n  use function Statamic\\View\\Blade\\{value};\n@endphp\n\n@if (value($theVariableName))\n  ...\n@endif\n```\n\nThe `value` helper function will handle the following scenarios for you:\n\n* `Statamic\\Fields\\Value` objects (calls `->value()`)\n* `Statamic\\Fields\\Values` objects (calls `->all()`)\n* `Statamic\\Tags\\FluentTag` objects (calls `->fetch()`)\n* `Statamic\\Modifiers\\Modify` objects (calls `->fetch()`)\n\n## Layouts\n\nWhen Statamic attempts to render a URL (eg. an entry), two views are combined. A template gets injected into a layout's `template_content` variable.\n\nWhen the _template_ is **not** an Antlers view, this rule doesn't apply. The layout is ignored, allowing you to use `@extends` the way you would expect.\n\n``` blade\n{{-- mytemplate.blade.php --}}\n\n@extends('layout')\n\n@section('body')\n  The body content\n@endsection\n```\n\n``` blade\n{{-- mylayout.blade.php --}}\n\n<html>\n<body>\n  @yield('body')\n</body>\n</html>\n```\n\n```html\n<html>\n<body>\n  The body content\n</body>\n</html>\n```\n\nThis rule only applies to the _template_. You're free to use a `.antlers.html` template and a `.blade.php` layout. If you want to do this, the contents of the template will be available as in the `template_content` variable instead of `yield`.\n\n```\n{{# mytemplate.antlers.html #}}\n\nThe template content\n```\n\n``` blade\n{{-- mylayout.blade.php --}}\n\n{!! $template_content !!}\n```\n\n```html\n<html>\n<body>\nThe template contents\n</body>\n</html>\n```\n\n### Passing context into components\n\nIf you are using Blade components for your layout rather than Blade directives, you might want to pass the view context into your layout for access by child components. You can do so with the special `$__data` variable in the layout root, and the `@aware` directive in the child. Here's how:\n\nFirst, add a `context` prop to your layout component.\n\n```blade\n{{-- resources/views/components/layout.blade.php --}}\n@props(['context'])\n<html>\n    {{-- whatever you want to put in here... --}}\n</html>\n```\n\nThen, merge the `context` prop with the parent data in your Layout component's `data` method.\n\n```php\n<?php\n\nnamespace App\\View\\Components;\n\nuse Illuminate\\View\\Component;\n\nclass Layout extends Component\n{\n    /**\n     * Create a new component instance.\n     *\n     * @return void\n     */\n    public function __construct(public $context)\n    {\n        $this->context = $context;\n    }\n\n    public function data()\n    {\n        return array_merge(parent::data(), $this->context);\n    }\n\n    /**\n     * Get the view / contents that represent the component.\n     *\n     * @return \\Illuminate\\Contracts\\View\\View|\\Closure|string\n     */\n    public function render()\n    {\n        return view('components.layout');\n    }\n}\n```\n\nNext, pass in the magic `$__data` variable from your template to your layout.\n\n```blade\n{{-- resources/views/default.blade.php --}}\n<x-layout :context=\"$__data\">\n    <x-hero />\n</x-layout>\n```\n\nLast, use the `@aware` directive in any child component of your layout to access the variables from the cascade within your component.\n\n```blade\n{{-- resources/views/components/blade.php --}}\n@aware(['page'])\n<div>\n    {{ $page->hero_headline }}\n</div>\n```\n\n## Routes and controllers\n\nIf you choose to take a more \"traditional\" Laravel application approach to building your Statamic site, you can use routes and controllers much the same way you might with Eloquent models instead of Statamic's native collection routing and data cascade. Here's an example:\n\n### The routes\n\n```php\nuse App\\Http\\Controllers\\BlogController;\n\nRoute::get('/blog', [BlogController::class, 'index']);\nRoute::get('/blog/{slug}', [BlogController::class, 'show']);\n```\n\n### The controller\n\n```php\n<?php\nnamespace App\\Http\\Controllers;\n\nuse App\\Http\\Controllers\\Controller;\nuse Statamic\\Facades\\Entry;\n\nclass BlogController extends Controller\n{\n    public function index()\n    {\n        $entries = Entry::query()\n            ->where('collection', 'blog')\n            ->take(10)\n            ->get();\n\n        return view('blog.index', ['entries' => $entries]);\n    }\n\n    public function show($slug)\n    {\n        $entry = Entry::query()\n            ->where('collection', 'blog')\n            ->where('slug', $slug)\n            ->first();\n\n        return view('blog.show', ['entry' => $entry]);\n    }\n}\n```\n\n### The index view\n\n```blade\n<h1>The Blog</h1>\n\n<div class=\"grid md:grid-cols-3 gap-3\">\n    @foreach($entries as $entry)\n    <a href=\"{!! $entry->url !!}\" class=\"p-2 rounded shadow-sm\">\n        <img src=\"{!! $entry->featured_image !!}\" class=\"w-full\">\n        <h2>{!! $entry->title !!}</h2>\n        <div>{!! $entry->date->format('Y-m-d') !!}</div>\n    </a>\n    @endforeach\n</div>\n```\n\n### The show view\n\n```blade\n<header>\n    <h1>{!! $title !!}</h1>\n    <div>{!! $entry->date->format('Y-m-d') !!}</div>\n</header>\n<div class=\"mt-8 prose\">\n    <img src=\"{!! $entry->featured_image !!}\" class=\"w-full\">\n    {!! $entry->content !!}\n</div>\n```\n"
  },
  {
    "path": "content/collections/pages/blink-cache.md",
    "content": "---\ntitle: 'Blink Cache'\ntemplate: page\nintro: 'The Blink Cache allows you to cache expensive operations for the life of a single request.'\nid: 73010f4f-bb62-4feb-a6ff-88031f488db8\n---\n## Basic Usage\n\n```php\nuse Statamic\\Facades\\Blink;\n\nBlink::put('key', 'value');\n$value = Blink::get('key'); // value\n```\n\nA more powerful method is `once`. It will run a function once and cache the value.\n\n``` php\n$expensiveFunction = function() {\n   return rand();\n});\n$blink->once('random', $expensiveFunction); // returns random number\n$blink->once('random', $expensiveFunction); // returns the same number\n```\n\nUnder the hood, it uses [Spatie's Blink package](https://github.com/spatie/blink). You are able to use any of the methods mentioned there.\n\n## Stores\n\nThe Blink cache is organized into different \"stores\", each of which are a separate Blink instances.\n\nYou can get your own Blink cache by using the `store` method which could be useful to prevent conflicts with other packages, or if you\nplan to use the `flush` methods.\n\nOnce you have the instance, you can call the Blink methods on it.\n\n```php\nBlink::store('mystore')->put(...);\n```\n\nWithout explicitly calling the `store` method, any methods will be targeting the \"default\" store shared across the application.\n\n"
  },
  {
    "path": "content/collections/pages/blueprints.md",
    "content": "---\nid: 54548616-fd6d-44a3-a379-bdf71c492c63\nblueprint: page\ntitle: Blueprints\nintro: 'Blueprints are a key component of the content modeling process. Inside a blueprint you define your fields, which field types they''ll implement, group them into sections if you desire, and define conditions controlling their visibility. The control panel uses blueprints to render publish forms so you can manage content.'\nrelated_entries:\n  - 2940c834-7062-47a1-957c-88a69e790cbb\n  - 9a1d8b88-c600-46f2-8727-1deb56f2e87a\n---\n## Overview\n\nThink of blueprints as stencils for your content. They control what [fields](/fields) users get to work with when publishing content, as well as the schema of the data developers will be tapping into to build the [front-end](/frontend) of your site.\n\nEach blueprint belongs to an item:\n\n- You can define multiple Blueprints for collections, and each entry will have the opportunity to choose from one of them.\n- Same goes for taxonomies and their terms.\n- Global sets, Asset containers, and Forms each get their own Blueprint.\n- Users all share a Blueprint.\n\n<figure>\n    <img src=\"/img/blueprints.webp\" alt=\"The Statamic blueprint configuration screen\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/blueprints-dark.webp\" alt=\"The Statamic blueprint configuration screen\" class=\"u-hide-in-light-mode\">\n    <figcaption>A glimpse at configuring a blueprint.</figcaption>\n</figure>\n\n## Creating Blueprints\n\nThere are 3 ways to create blueprints:\n\n- In the respective areas of the control panel. For instance, the collections area will let you define its blueprints.\n  The forms area will let you define its blueprints, and so on.\n- In the **Blueprints** area of the control panel. This page serves as a hub to jump over to managing blueprints in various areas.\n- Creating a YAML file in the appropriate place within `resources/blueprints/`. More on that in a moment.\n\nOnce created, you can begin to define fields and the sections that hold them. If you have more than one section, each becomes a tab in the publish form.\n\n## Directory Structure\n\nWhether you manually create your blueprint's YAML file, or use the control panel, they will all end up as YAML files in the `resources/blueprints` directory.\n\n<figure>\n    <img src=\"/img/blueprints-folder-structure.png\" alt=\"Statamic Blueprints Folder Structure\" width=\"528\">\n    <figcaption>Here's how Blueprints are organized in the filesystem.</figcaption>\n</figure>\n\n``` files theme:serendipity-light\nresources/\n  blueprints/\n    collections/\n      blog/\n        basic_post.yaml\n        art_directed_post.yaml\n      taxonomies/\n        tags/\n          tag.yaml\n    globals/\n      global.yaml\n      company.yaml\n    assets/\n      main.yaml\n    forms/\n      contact.yaml\n    user.yaml\n```\n\nCollections and Taxonomies have their available blueprints organized in subdirectories named after their collections.\nWhen you create an entry or term, you will be able to choose which blueprint to use (if there are multiple).\n\nGlobals, Asset Containers, and Forms can only have one blueprint per item, so they are organized into their own subdirectories, where each YAML file is the handle of the item.\n\nAll users will share the same blueprint, and it hangs out in the root of the directory.\n\n### Customizing the Path\n\nYou can change where blueprints are stored by setting the `blueprints_path` option in `config/statamic/system.php`:\n\n```php\n'blueprints_path' => resource_path('blueprints'),\n```\n\nYou may also provide an array to use different paths for specific blueprint types. This is especially useful for form blueprints when you want to keep them alongside other editable content — for example, if you exclude the `content/` directory from git and don't want form blueprints tracked:\n\n```php\n'blueprints_path' => [\n    'default' => resource_path('blueprints'),\n    'forms' => base_path('content/forms/blueprints'),\n],\n```\n\nThe `default` key is required when using an array — it's used for any blueprint types you haven't explicitly specified.\n\nWhen a type-specific path is set, the type is _not_ appended to the path. Using the example above, a contact form blueprint lives at `content/forms/blueprints/contact.yaml`, not `content/forms/blueprints/forms/contact.yaml`.\n\n## Conditional Fields\n\nIt’s possible to have fields be displayed only under certain conditions. For example, you may only want to show a caption field if an asset field has an image selected, or a whole block of fields if a toggle switch is enabled.\n\n<figure>\n    <img src=\"/img/field-conditions.webp\" alt=\"Statamic conditional field rule builder\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/field-conditions-dark.webp\" alt=\"Statamic conditional field rule builder\" class=\"u-hide-in-light-mode\">\n    <figcaption>The conditional field rule builder</figcaption>\n</figure>\n\nTo learn what's possible and how to implement the various rules, head over to the article on [conditional fields](/conditional-fields).\n\n## YAML Structure\n\nAt its most basic, a blueprint has an array of sections.\n\n``` yaml\nsections: []\n```\n\nA section has a handle, a display name, and an array of fields:\n\n``` yaml\nsections:\n  main:\n    display: Main\n    fields:\n      -\n        handle: content\n        type: markdown\n  meta:\n    display: SEO Metadata\n    fields:\n      -\n        handle: meta_title\n        type: text\n      -\n        handle: meta_description\n        type: textarea\n```\n\n:::tip\nBlueprint fields are **indexed sequentially** instead of keyed by handle. This format allows maximum flexibility: you can reference fields from other blueprints one or more times, override their settings inline, and even reference existing fields for [Bard](/fieldtypes/bard), [Replicator](/fieldtypes/replicator), and [Grid](/fieldtypes/grid) sets.\n:::\n\n## Reusable Fields\n\nA section's fields can be comprised of references to fields in fieldsets (so you can reuse fields) or inline field definitions.\n\n### Field References\n\nYou will likely want to pre-configure reusable fields to pull into your blueprints.\n\nFor example, you might have a rich text field configured with all your favorite buttons, which you've called `content` and stored in the `common` fieldset.\n\nYou can pull it into your blueprint like so:\n\n``` yaml\nfields:\n  -\n    handle: my_content_field\n    field: common.content\n  -\n    handle: another_content_field\n    field: common.content\n```\n\nThis way, you are free to reuse the same field as many times as you like. Update the field in the fieldset and it will be reflected across all your blueprints.\n\n### Customizing Fields\n\nYou may customize a referenced field by adding a `config` array. Any keys found in this will _override_ whatever was defined in your fieldset.\n\n``` yaml\nfields:\n  -\n    handle: my_content_field\n    field: common.content\n    config:\n      display: My Content Field\n      validate: required|max:200\n```\n\nHere, the `display` and `validate` would replace whatever was defined in the fieldset.\n\n**Note:** This only applies to referenced fields. For inline fields, you can just set everything right there.\n\n### Importing Fieldsets\n\nFieldsets serve to create reusable sets of fields. You may import an entire fieldset at any point by using the `import` key, for example:\n\n``` yaml\n# blueprint\n\nfields:\n  -\n    import: survey\n    prefix: favorite_\n  -\n    import: survey\n    prefix: least_favorite_\n```\n\n``` yaml\n# the survey.yaml fieldset\n\nfields:\n  -\n    handle: food\n    type: text\n  -\n    handle: food_reason\n    type: textarea\n```\n\nDoing the above would result in a blueprint like this:\n\n``` yaml\nfields:\n  -\n    handle: favorite_food\n    field:\n      type: text\n  -\n    handle: favorite_food_reason\n    field:\n      type: textarea\n  -\n    handle: least_favorite_food\n    field:\n      type: text\n  -\n    handle: least_favorite_food_reason\n    field:\n      type: textarea\n```\n\nIt would bring every field inline and prefix each field's handle appropriately.\n\nIf you omit the `prefix` you won't be able to import them more than once at the same level because they would have the same handle and overwrite each other.\n\nIf the fieldset you're importing has [its own sections](/fieldsets#sections), you can control how they're rendered with `section_behavior`. Set it to `preserve` (the default) to keep the fieldset's sections intact in the publish form, or `flatten` to merge everything into the current section.\n\n```yaml\nfields:\n  -\n    import: seo\n    section_behavior: flatten\n```\n\n\n## Validation\n\nFields can have various validation rules applied to them, enforcing the need for content creators to fill them out in a specific way before saving or publishing.\n\nWhile configuring a field, switch to the **Validation** tab where you can choose from [any built in Laravel rule](https://laravel.com/docs/13.x/validation#available-validation-rules).\n\nOn top of any Laravel validation rules, there are some Statamic-specific goodies (like usage with conditional fields, Grids, Bards, or Replicators) that are explained on our [dedicated validation documentation](/validation).\n\n## Grid fieldtype\n\nThe [Grid fieldtype](/fieldtypes/grid) lets you define a set of sub-fields, which it will allow you to repeat as many times as you like.\n\nYou should define its fields using the blueprint field syntax. This will allow you to reference other fields and/or import entire fieldsets.\n\n<figure>\n    <img src=\"/img/grid.webp\" alt=\"An example grid field\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/grid-dark.webp\" alt=\"An example grid field\" class=\"u-hide-in-light-mode\">\n    <figcaption>This is an example Grid field.</figcaption>\n</figure>\n\n``` yaml\nlinks:\n  type: grid\n  fields:\n    -\n      handle: url\n      field: links.url\n    -\n      handle: text\n      field: links.text\n    -\n      handle: external\n      field: links.external\n```\n\n\n## Unlisted fields\n\nWhile [conditional fields](#conditional-fields) allow you to control field visibility on the publish form, you may also customize column visibility on **entry listings** in the control panel.\n\n```yaml\nlistable: false\n```\n\nThis hides the field column from entry listings, which can be useful for toggle fields etc, which may never make sense in the context of an entry listing.\n\n```yaml\nlistable: hidden\n```\n\nThis will hide the field from entry listings by default, but still allows a user to toggle visibility using the column selector, and save those column preferences for his/her preferred workflow.\n"
  },
  {
    "path": "content/collections/pages/build-a-fieldtype.md",
    "content": "---\nid: 83786f60-def6-11e9-aaef-0800200c9a66\nblueprint: page\ntitle: 'Build a Fieldtype'\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1568643872\nintro: \"Fieldtypes determine the user interface and storage format for your [fields](/fields). Statamic includes 40+ fieldtypes to help you tailor the perfect intuitive experience for your authors, but there's always room for _one more_.\"\n---\n\n## Creating\n\nFieldtypes have a PHP component and JS component. You can use a command to generate both pieces:\n\n``` shell\nphp please make:fieldtype Uppercase\n```\n\n``` files theme:serendipity-light\napp/\n    Fieldtypes/\n        Uppercase.php\nresources/\n    js/\n        components/\n            fieldtypes/\n                Uppercase.vue\n```\n\nIf you haven't already [set up Vite](/control-panel/css-javascript) for the Control Panel, the command will do it for you.\n\n## Registering\n\nStatamic will automatically register any fieldtype classes in the `App\\Fieldtypes` namespace. \n\nHowever, if you wish to store them elsewhere, you'll need to manually register it by calling the static `register` method on your fieldtype's class:\n\n``` php\npublic function boot()\n{\n    \\App\\SomewhereElse\\Uppercase::register();\n}\n```\n\n\n## PHP Class\n\nThe PHP class can be very barebones. At the most basic level, it just needs to exist in order to let Statamic know about it.\n\n```php\n<?php\n\nnamespace App\\Fieldtypes;\n\nuse Statamic\\Fields\\Fieldtype;\n\nclass Uppercase extends Fieldtype\n{\n    //\n}\n```\n\nOf course, you may add functionality to it, outlined below.\n\n### Icon\n\nYou can either specify the name of [icon included in Statamic](https://ui.statamic.dev/?path=/docs/components-icon--docs#available-icons) or an SVG string containing a custom icon (be sure to use `fill=\"currentColor\"`) via the `$icon` property.\n\nAlternatively, you may return an SVG string from the `icon()` method:\n\n```php\n<?php\n\nclass Uppercase extends Fieldtype\n{\n    protected $icon = 'tags';\n    // or\n    protected $icon = '<svg> ... </svg>';\n    // or\n    function icon()\n    {\n        return file_get_contents(resource_path('svg/left_shark.svg'));\n    }\n}\n```\n\n### Categories\n\nWhen using the blueprint builder inside the Control Panel, your fieldtype will be listed under the `special` category by default. \n\nTo move your fieldtype into a different category, define the `$categories` property on your class:\n\n```php\n<?php\n\nclass Uppercase extends Fieldtype\n{\n    public $categories = ['text'];\n}\n```\n\nYou can select from any of the keys available in the `FieldtypeSelector`:\n- `text`\n- `controls`\n- `media`\n- `number`\n- `relationship`\n- `structured`\n- `special`\n\n\n### Keywords\n\nYou may specify keywords to be used when searching in the fieldtype selector.\n\nFor example: if you search for \"rich text\" or \"gutenberg\", the [Bard fieldtype](/fieldtypes/bard) will be returned as a result.\n\n```php\n<?php\n\nclass CustomFieldtype extends Fieldtype\n{\n    protected $keywords = ['file', 'files', 'image', 'images', 'video', 'videos', 'audio', 'upload'];\n}\n```\n\n### Config fields\n\nYou can make your fieldtype configurable with configuration fields. These fields are defined by adding a `configFieldItems()` method on your PHP class that returns an array of fields.\n\n``` php\nprotected function configFieldItems(): array\n{\n    return [\n        'mode' => [\n            'display' => 'Mode',\n            'instructions' => 'Choose which mode you want to use',\n            'type' => 'select',\n            'default' => 'regular',\n            'options' => [\n                'regular' => __('Regular'),\n                'enhanced' => __('Enhanced'),\n            ],\n            'width' => 50\n        ],\n        'secret_agent_features' => [\n            'display' => 'Enable super secret agent features',\n            'instructions' => 'Can you even handle these features?',\n            'type' => 'toggle',\n            'default' => false,\n            'width' => 50\n        ],\n    ];\n}\n```\n\nThe configuration values can be accessed in the Vue component using the `config` property.\n\n``` js\nreturn this.config.mode; // regular\n```\n\n#### Options\n\n| Key              | Definition                                                       |\n|------------------|------------------------------------------------------------------|\n| **display**      | The field's display label                                        |\n| **instructions** | Text shown underneath the display label. Supports Markdown.      |\n| **type**         | Name of the fieldtype used to manage the config option.          |\n| **default**      | An optional default value.                                       |\n| **width**        | The field's width.                                               |\n| ***other***      | Some fieldtypes have additional configuration options available. |\n\n:::tip\nA little code diving will reveal all the possible config options for each field type. Look for the `configFieldItems()` method in each class here: <https://github.com/statamic/cms/tree/6.x/src/Fieldtypes>\n:::\n\n\n## Vue Component\nThe Vue component is responsible for the view and data binding. It's what your user will be interacting with.\n\nThe `make:fieldtype` command would have generated a Vue component into `resources/js/components/fieldtypes/Uppercase.vue`.\n\nYou'll need to register this Vue component in your JS entry file (`resources/js/cp.js`):\n\n``` js\nimport UppercaseFieldtype from './components/fieldtypes/Uppercase.vue';\n\nStatamic.booting(() => {\n    // Should be named [snake_case_handle]-fieldtype\n    Statamic.$components.register('uppercase-fieldtype', UppercaseFieldtype);\n});\n```\n\nYour component should use our `Fieldtype` composable for defining props & emits, updating the field value and accessing meta.\n\n``` vue\n<script setup>\nimport { Fieldtype } from '@statamic/cms';\n\nconst emit = defineEmits(Fieldtype.emits);\nconst props = defineProps(Fieldtype.props);\nconst { expose, ... } = Fieldtype.use(emit, props);\ndefineExpose(expose);\n</script>\n\n<template>\n    <!-- -->\n</template>\n```\n\nOther than that, your component can do whatever you like!\n\n### Example\n\nFor this example we will create an input field with a button to make the text uppercase:\n\n<figure>\n    <img src=\"/img/uppercase-fieldtype-example.gif\" alt=\"An example fieldtype with a button to make the text uppercase\" class=\"p-4 bg-white\" width=\"477\">\n    <figcaption>Follow along and you could make this!</figcaption>\n</figure>\n\n``` vue\n<script setup>\nimport { Fieldtype } from '@statamic/cms';\nimport { Input, Button } from '@statamic/cms/ui';\n\nconst emit = defineEmits(Fieldtype.emits);\nconst props = defineProps(Fieldtype.props);\nconst { expose, update } = Fieldtype.use(emit, props);\ndefineExpose(expose);\n\nfunction makeItUppercase() {\n    update(props.value.toUpperCase());\n}\n</script>\n\n<template>\n    <div>\n        <Input :model-value=\"value\" @update:model-value=\"update\" />\n        <Button @click=\"makeItUppercase\">Make it upper case!</Button>\n    </div>\n</template>\n```\n\n#### What's happening?\n\n1. The `Fieldtype` composable is providing the `emits` and `props` we need to define, as well as the `expose, update` and `updateDebounced` methods.\n2. When you type into the text field, an `update` method is called which emits an event. Statamic listens to that event and updates the `value` prop.\n\nThose are the two requirements satisfied. ✅\n\nIn addition to that, when the button is clicked, we're converting the string to uppercase and calling `update` in our function.\n\n### Accessing other fields\n\nIf you find yourself needing to access other form field values, configs, etc., you can reach into the publish form store from within your Vue component:\n\n```js\nimport { injectPublishContext } from '@statamic/cms/ui';\nconst { values } = injectPublishContext();\n\n// Do what you need to with values\nconsole.log(values.value.title)\n```\n\n\n## Processing\n\nYou may need to modify the data going to and from the browser.\n\n* The `preProcess` method allows you to modify the original value into what the Vue component requires.\n* The `process` method does the opposite. It takes the Vue component's value and allows you to modify it for what gets saved.\n\nFor example, the YAML fieldtype stores its value in content as an array but the field needs it as a string in order for it to be editable:\n\n```php\npublic function preProcess($value)\n{\n    return YAML::dump($value); // dump a yaml string from an array\n}\n```\n\nIn the other direction, it takes the YAML string and needs to convert it back to an array when saving:\n\n```php\npublic function process($value)\n{\n    return YAML::parse($value); // parses a yaml string into an array\n}\n```\n\n_(These snippets are simplified for example purposes.)_\n\n## Meta Data\n\nFieldtypes can preload additional \"meta\" data from PHP into JavaScript. This can be anything you want, from settings to eager loaded data.\n\n``` php\npublic function preload()\n{\n    return ['foo' => 'bar'];\n}\n```\n\nThis can be accessed in the Vue component using the `meta` prop.\n\n``` js\nreturn props.meta; // { foo: bar }\n```\n\nIf you have a need to update this meta data on the _JavaScript side_, use the `updateMeta` method. This will persist the value back to Vuex store and communicate the update to the appropriate places.\n\n``` js\nconst props = defineProps(Fieldtype.props);\nconst { updateMeta } = Fieldtype.use(emit, props);\n\nupdateMeta({ foo: 'baz' });\nprops.meta; // { foo: 'baz' }\n```\n\n### Example use cases\n\nHere are some reasons why you might want to use this feature:\n\n- The assets and relationship fieldtypes only store IDs, so they will fetch item data using AJAX requests. If you have many of these fields in one form, you'd have a bunch of AJAX requests fire off when the page loads. Preload the item data to avoid the initial AJAX requests.\n- Grid, Bard, and Replicator fields all preload values for what a new row/set contains, plus the recursive meta values of any nested fields.\n\n\n## Replicator Preview\n\nWhen [Replicator](/fieldtypes/replicator) (or [Bard](/fieldtypes/bard)) sets are collapsed, Statamic will display a preview of the values within it.\n\nBy default, Statamic will do its best to display your fields value. However, if you have a value more complex than a simple string or array, you may want to customize it.\n\nYou may customize the preview text by calling `defineReplicatorPreview` from your Vue component. For example:\n\n``` js\nconst { defineReplicatorPreview } = Fieldtype.use(emit, props);\n\ndefineReplicatorPreview(() => props.value.join('+'));\n```\n\n:::tip\nThis _does_ support returning an HTML string so you could display image tags for a thumbnail, etc. Just be aware of the limited space.\n:::\n\n## Index fieldtypes\n\nIn listings (collection indexes in the Control Panel, for example), string values will be displayed as a truncated string and arrays will be displayed as JSON.\n\nYou can adjust the value before it gets sent to the listing with the `preProcessIndex` method:\n\n``` php\npublic function preProcessIndex($value)\n{\n    return str_repeat('*', strlen($value));\n}\n```\n\nIf you need extra control or functionality, fieldtypes may have an additional \"index\" Vue component.\n\n\n``` js\nimport UppercaseIndexFieldtype from './UppercaseIndexFieldtype.vue';\n\n// Should be named [snake_case_handle]-fieldtype-index\nStatamic.$components.register('toggle_password-fieldtype-index', UppercaseIndexFieldtype);\n```\n\n``` vue\n<script setup>\nimport { IndexFieldtype } from '@statamic/cms';\n\nconst props = defineProps(IndexFieldtype.props);\n\nconst numberOfUppercaseCharacters = computed(() => {\n    return [...props.value].filter(char => char >= 'A' && char <= 'Z').length;\n});\n</script>\n\n<template>\n    <div>String contains {{ numberOfUppercaseCharacters }} uppercase characters.</div>\n</template>\n```\n\nThe `IndexFieldtype` composable will provide you with a `value` prop so you can display it however you'd like. Continuing our example above, we will calculate how many characters of the given string are uppercase.\n\n## Augmentation\n\nBy default, a fieldtype will not perform any augmentation. It will just return the value as-is.\n\nYou can customize how it gets augmented with an augment method:\n\n``` php\npublic function augment($value)\n{\n    return strtoupper($value);\n}\n```\n\n[Read more about augmentation](/extending/augmentation)\n\n## Updating References\n\nIf your fieldtype stores references to assets or taxonomy terms — either directly or nested inside sub-fields — you'll want to keep those references in sync when an asset is renamed, moved, or deleted, or when a term is renamed or deleted. Otherwise you end up with broken references pointing at stale URLs or IDs.\n\nStatamic handles this for all built-in fieldtypes automatically. For custom fieldtypes you can opt in by using the `UpdatesReferences` trait and overriding one or more of its methods.\n\n```php\nuse Statamic\\Fields\\Fieldtype;\nuse Statamic\\Fieldtypes\\UpdatesReferences;\n\nclass MyFieldtype extends Fieldtype\n{\n    use UpdatesReferences;\n}\n```\n\nThe trait provides three no-op methods you can override, depending on what kind of data your fieldtype stores:\n\n| Method | Purpose |\n|--------|---------|\n| `replaceAssetReferences($data, $newValue, $oldValue, $container)` | Replace direct asset references (URLs or IDs). |\n| `replaceTermReferences($data, $newValue, $oldValue, $taxonomy)` | Replace direct term references. |\n| `iterateReferenceFields($data, NestedFieldUpdater $updater)` | Traverse nested sub-fields so Statamic can recurse into them. |\n\nIt also gives you three helper methods for the common cases: `replaceValue()`, `replaceValuesInArray()`, and `replaceStatamicUrls()`.\n\n### Asset references\n\nIf your fieldtype stores a single asset reference, override `replaceAssetReferences` and use the `replaceValue()` helper. Bail early if the container doesn't match your field's configured container.\n\n```php\nuse Statamic\\Fields\\Fieldtype;\nuse Statamic\\Fieldtypes\\UpdatesReferences;\n\nclass MyLinkFieldtype extends Fieldtype\n{\n    use UpdatesReferences;\n\n    public function replaceAssetReferences($data, ?string $newValue, string $oldValue, string $container)\n    {\n        if ($this->config('container') !== $container) {\n            return $data;\n        }\n\n        return $this->replaceValue($data, $newValue, $oldValue);\n    }\n}\n```\n\nWhen `$newValue` is `null`, the asset was deleted — Statamic will remove the reference from your data.\n\n### Term references\n\nIf your fieldtype stores an array of term references, override `replaceTermReferences` and use `replaceValuesInArray()`.\n\n```php\nuse Statamic\\Fields\\Fieldtype;\nuse Statamic\\Fieldtypes\\UpdatesReferences;\n\nclass MyTagsFieldtype extends Fieldtype\n{\n    use UpdatesReferences;\n\n    public function replaceTermReferences($data, ?string $newValue, string $oldValue, string $taxonomy)\n    {\n        return $this->replaceValuesInArray($data, $newValue, $oldValue);\n    }\n}\n```\n\n### Nested sub-fields\n\nIf your fieldtype contains nested fields (like Grid, Replicator, or a custom Columns fieldtype), override `iterateReferenceFields` and hand each set of nested fields off to the `NestedFieldUpdater`. Statamic will recurse into them and run the appropriate reference updates against any fieldtypes within that also use the trait.\n\n```php\nuse Statamic\\Data\\NestedFieldUpdater;\nuse Statamic\\Fields\\Fields;\nuse Statamic\\Fields\\Fieldtype;\nuse Statamic\\Fieldtypes\\UpdatesReferences;\n\nclass ColumnsFieldtype extends Fieldtype\n{\n    use UpdatesReferences;\n\n    public function iterateReferenceFields($data, NestedFieldUpdater $updater): void\n    {\n        if (! is_array($data)) {\n            return;\n        }\n\n        $fields = new Fields($this->config('fields'));\n\n        foreach (array_keys($data) as $idx) {\n            $updater->update($fields, \"{$idx}.\");\n        }\n    }\n}\n```\n\nThe second argument to `$updater->update()` is a key prefix used when walking the data array — use it to tell the updater where the nested field's value lives within your data structure.\n\n## Adding config fields to existing fieldtypes\n\nSometimes you may want to add a config field to another fieldtype rather than creating a completely new one.\n\nYou can do this using the `appendConfigField` or `appendConfigFields` methods on the respective fieldtype.\n\n```php\nuse Statamic\\Fieldtypes\\Text;\n\n// One field...\nText::appendConfigField('group', [\n  'type' => 'text',\n  'display' => 'Group',\n]);\n\n// Multiple fields...\nText::appendConfigFields([\n  'group' => ['type' => 'text', 'display' => '...',],\n  'another' => ['type' => 'text', 'display' => '...',],\n]);\n```\n\nYou can also append a config field to _all_ fieldtypes via the `Fieldtype` class:\n\n```php\nuse Statamic\\Fields\\Fieldtype;\n\nFieldtype::appendConfigField('group', [\n    'type' => 'text',\n    'display' => 'A new group',\n]);\n```\n"
  },
  {
    "path": "content/collections/pages/building-a-tag.md",
    "content": "---\ntitle: Building Tags\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569264076\nintro: Ultimately a Tag is nothing more than a PHP method called from an Antlers or Blade template. This common pattern allows non-PHP developers to take advantage of dynamic features in their site easily without writing any code.\nid: 098cb1c5-94c2-4bc0-add7-9aad39951d67\n---\n## Anatomy of a Tag\n\nA tag consists of several parts, none of which are named the “thorax”. Let’s break a Tag down:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ acme:foo_bar src=\"baz\" }}\n```\n\n- The first part? That’s the tag's handle: `acme`\n- The second bit is the method it maps to: `fooBar` (the method will be camelCased)\n- And lastly, a parameter: `src=\"foo\"`. There can be any number of parameters on a Tag.\n\n::tab blade\n```blade\n<s:acme:foo_bar src=\"baz\" />\n```\n\n- The first part after `<s:`? That’s the tag's handle: `acme`\n- The second bit is the method it maps to: `fooBar` (the method will be camelCased)\n- And lastly, a parameter: `src=\"foo\"`. There can be any number of parameters on a Tag.\n\n::\n\nTags can also come in pairs, much like beer comes in pints. For example:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ entries:listing folder=\"blog\" }}\n  <div>{{ title }}</div>\n{{ /entries:listing }}\n```\n::tab blade\n```blade\n<s:entries:listing folder=\"blog\">\n  <div>{{ $title }}</div>\n</s:entries:listing>\n```\n::\n\nAnything in-between your tag pair is available as `$this->content`. Sometimes you’ll want to use it as input, other times manipulate it, and yet another time leave it be. It’s up to you.\n\n\n## Multiple Tags in one class\n\nWe use the plural \"Tags\" for the class, because one class defines multiple tags. They just all use the same handle.\n\nEvery `public` method in a tag class will become a tag, using the snake_cased version.\n\nFor example:\n\n``` php\n<?php\n\nnamespace Acme\\Example;\n\nclass MyTags extends \\Statamic\\Tags\\Tags\n{\n    public function fooBar()\n    {\n        // Stay awhile and listen.\n    }\n}\n```\n\nWould be called like so in your template:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ my_tags:foo_bar }}\n```\n::tab blade\n```blade\n<s:my_tags:foo_bar />\n```\n::\n\n### Index\n\nA tag without a method will use the `index` method.\n\n``` php\npublic function index()\n{\n    // It's just one of those days.\n}\n```\n\nTemplate:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ my_tags }}\n```\n::tab blade\n```blade\n<s:my:tags />\n```\n::\n\n### Wildcards\n\nA common tag pattern is to have the method be dynamic. For example, the [Collection Tag][collection_tag]'s method is the handle of the collection you want to work with: `collection:blog`.\n\nYou may add a `wildcard` method to your Tag class to catch these.\n\n``` php\npublic function wildcard($tag)\n{\n    // ♠️♥️♦️♣️\n}\n```\n\nthen, in your template:\n\n::tabs\n\n::tab antlers\n```antlers\n{{# Replace the * with anything you'd like! Go wild - play your crazy card! #}}\n{{ tag:* }}\n\n{{# In this case, the `$tag` value would be \"foo\" #}}\n{{ tag:foo }}\n```\n::tab blade\n```blade\n{{-- Replace the * with anything you'd like! Go wild - play your crazy card! --}}\n<s:tag:* />\n\n{{-- In this case, the `$tag` value would be \"foo\" --}}\n<s:tag:foo />\n```\n::\n\nIf you want a tag named literally \"wildcard\", you can adjust the wildcard method that Statamic will call by updating the `wildcardMethod` property.\n\n```php\nprotected $wildcardMethod = 'missing';\n\npublic function wildcard()\n{\n    // tag:wildcard\n}\n\npublic function missing($tag)\n{\n    // tag:*\n}\n```\n\n:::best-practice\nYou may notice that the `wildcard` method seems very similar to the `__call()` magic method. It is! The `wildcard` method\nuses `__call` under the hood, but with additional smarts. Be sure to use `wildcard`!\n:::\n\n## Generating a tag class\n\nYou can generate a Tags class with a console command:\n\n``` shell\nphp please make:tag Foo\n```\n\nThis'll create a class in `app/Tags` which will be automatically registered.\n\nTo create and register a tag inside an addon instead, check out the [addon docs](/extending/addons#registering-components).\n\n## Tag Handle\n\nThe first part of the tag will be the tag's \"handle\". This will be the snake_cased version of the class name by default. In this example, it would be `my_tags`.\n\nYou can override this by setting a static `$handle` property.\n\n``` php\nprotected static $handle = 'example';\n```\n\nThen, using the example above, `my_tags:x` would now be `example:x`\n\n## Aliases\n\nYou may choose to create aliases for your tag too. It will then be usable by its handle, or any of its aliases.\n\n``` php\nprotected static $aliases = ['sample'];\n```\n\n## Parameters\n\nYou may get the values of parameters through the `parameters` property.\n\nAny parameters prefixed with a colon will resolve the values from the context automatically.\n\n``` yaml\nauthor: john\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ mytag\n    greeting=\"hello\"\n    :name=\"author\"\n    do_this=\"true\"\n    do_that=\"false\"\n    limit=\"5\"\n    latitude=\"6\"\n    things=\"foo|bar\"\n}}\n```\n::tab blade\n```blade\n<s:mytag\n  greeting=\"hello\"\n  :name=\"$author\"\n  do_this=\"true\"\n  do_that=\"false\"\n  limit=\"5\"\n  latitude=\"6\"\n  things=\"foo|bar\"\n/>\n```\n::\n\n\n``` php\n$this->params->get('greeting'); // \"hello\"\n$this->params->get('name'); // \"john\"\n$this->params->bool('do_this') // true\n$this->params->bool('do_that') // false\n$this->params->int('limit') // 5\n$this->params->float('latitude') // 6.0\n$this->params->explode('things') // ['foo', 'bar']\n\n// Array access also works:\n$this->params['greeting']; // \"hello\"\n\n// It's a Collection, so all those methods work, too.\n$this->params->only(['greeting', 'name']); // ['hello', 'john']\n```\n\nFor any of these methods, you may provide a second argument as a fallback if the key doesn't exist.\n\n``` php\n$this->params->get('nope', 'fallback'); // \"fallback\"\n```\n\nYou may also provide an array of keys. The first match will be used.\n\n``` php\n$this->params->get(['salutation', 'greeting']); // \"hello\"\n```\n\n## Context\n\nThe context is the array of values being used for rendering the template where your tag happens to be placed.\nTake this block of templating for example:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ title }}\n\n{{ collection:blog }}\n    {{ title }}\n    {{ your_tag }}\n{{ /collection:blog }}\n```\n::tab blade\n```blade\n{{ $title }}\n\n<s:collection:blog>\n  {{ $title }}\n\n  <s:your_tag />\n</s:collection:blog>\n```\n::\n\nThe `title` in the first line uses the page context since it's not nested inside any other tag pairs. This would typically be the title of the entry of the URL you're visiting. You can pretend that the entire template is wrapped in a pair of invisible tags.\n\nThe `collection:blog` tag loops over entries in a collection. The `title` in there will be using the context of the entry in the current iteration of the loop.\n\nLikewise, the `your_tag`'s context will be current iteration of the loop.\n\nYou may get values out of the context similar to [parameters](#parameters):\n\n``` php\n$this->context->get('title');\n$this->context->get('unknown', 'fallback');\n$this->context->get(['first_this', 'then_this']);\n```\n\nIf the item you are retrieving is defined in a Blueprint, it'll be a `Value` object. To avoid you needing to check for Value objects and manually convert them yourself, you may use the `value` and `raw` methods instead:\n\n``` php\n// If it's a Value object, it'll get the augmented value for you.\n$this->context->value('something');\n\n// If it's a Value object, it'll get the raw value for you.\n$this->context->raw('something');\n```\n\n## Rendering Data\n\nRendering your tag data is a little different depending on whether you intend to have a single tag or a tag pair.\n\nGenerally, you should try to have a tag that is either always used as a single, or a pair. If you _need_ a tag to work either way, the `$this->isPair` boolean is available to you.\n\n### Single Tags\n\nA single tag stands alone by itself and does not have a closing tag. Your method must return a string if you to render something. Within a template, your tag will be replaced with that returned string.\n\n``` php\npublic function method()\n{\n    return 'hello';\n}\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ your_tag:method }}\n```\n::tab blade\n```blade\n<s:your_tag:method />\n```\n::\n\n```text\nhello\n```\n\nYou may also return a boolean. This is useful if your tag is designed to be used in conditions.\n\n``` php\npublic function method()\n{\n    return true;\n}\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {your_tag:method} }} yup {{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::tag('your_tag:method')->fetch()) yup @endif\n```\n\n::\n\n```text\nyup\n```\n\nIf your tag doesn’t return anything, your tag won’t render anything. This can be useful if you need to perform some sort of non-HTML rendering task. For example, the redirect tag doesn’t output any HTML, it just performs a redirect.\n\n### Tag Pairs\n\nWhen using your tag in a pair, you can return an array (or Collection) to be used as the new context. Its variables will be available between your tags.\n\n``` php\npublic function method() {\n    return [\n        'tree' => 'maple',\n        'path' => 'dirt',\n        'sky'  => 'blue'\n    ];\n}\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ your_tag:method }}\n    {{ tree }} {{ path }} {{ sky }}\n{{ /your_tag:method }}\n```\n::tab blade\n```blade\n<s:your_tag:method>\n  {{ $tree }} {{ $path }} {{ $sky }}\n</s:your_tag:method>\n```\n::\n\n```text\nmaple dirt blue\n```\n\nReturning a multidimensional array will let you loop over the items.\n\n``` php\npublic function method() {\n    return [\n        [\n            'tree' => 'maple',\n            'path' => 'dirt',\n            'sky'  => 'blue'\n        ],\n        [\n            'tree' => 'oak',\n            'path' => 'asphalt',\n            'sky'  => 'black'\n        ]\n    ];\n}\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ your_tag:method }}\n    {{ tree }} {{ path }} {{ sky }}\n{{ /your_tag:method }}\n```\n::tab blade\n```blade\n<s:your_tag:method>\n  {{ $tree }} {{ $path }} {{ $sky }}\n</s:your_tag:method>\n```\n::\n\n```text\nmaple dirt blue\noak asphalt black\n```\n\n### Empty Results in Antlers\n\nWhen using Antlers, a `no_results` variable will be automatically created when a tag returns an empty array.\n\n``` php\npublic function method() {\n    return [ ];\n}\n```\n```\n{{ your_tag:method }}\n    {{ if no_results }}\n        No results.\n    {{ else }}\n        Some results.\n    {{ /if }}\n{{ /your_tag:method }}\n```\n```\nNo results.\n```\n\n### Empty Results in Blade\n\nThere are many different ways to handle empty results in Blade. When iterating simple tag results without aliasing, there is a special Blade component that takes the place of the `no_results` variable:\n\n``` php\npublic function method() {\n    return [ ];\n}\n```\n\n```blade\n<s:your_tag:method>\n  Some results.\n\n  <s:no_results>\n    No results.\n  </s:no_results>\n</s:your_tag:method>\n```\n\nIn all other scenarios, you will need to use other techniques. Some examples are:\n\n```blade\n{{-- Checking the count. --}}\n<s:your_tag:method as=\"results\">\n\n  @if (count($results) > 0)\n    Some results.\n  @else\n    No results.\n  @endif\n\n</s:your_tag:method>\n\n{{-- Using forelese --}}\n<s:your_tag:method as=\"results\">\n  @forelse ($results as $value)\n    ...\n  @empty\n    No results.\n  @endforelse\n\n</s:your_tag:method>\n```\n\nWhichever method you choose will depend on the situation and your personal preference.\n\n### Passing along context\n\nAs mentioned above, the array returned from a tag pair method is what'll be available between the tags. The parent context is not available. This is to prevent variable collision and confusion.\n\nIf you *want* parent context to be available, you can pass those down manually.\n\n``` php\n// One at a time\nreturn [\n    'local' => 'value',\n    'var' => $this->context->get('var'),\n];\n```\n\n``` php\n// Merging in multiple\nreturn array_merge(\n    $this->context->only('foo', 'bar', 'baz')->all(),\n    ['local' => 'value']\n);\n```\n\n## Considerations for Blade\n\nMost tag implementations will work seamlessly whether a user is writing their templates in Antlers or Blade, but there are a few things to keep in mind.\n\n### Conditionally Included Variables and \"Falsey-ness\"\n\nTag implementations that conditionally inject a variable should consider always including the variable in their output, with a backwards-compatible default. Antlers will treat non-existent variables as `false` in conditions, and skip them entirely in other contexts.\n\nBecause Blade compiles to PHP, if you do not always include the variable, users of your tag will have to resort to adding `isset` (or similar) checks.\n\nIf your tag conditionally injects a variable that template authors rely on to change their output, consider adjusting the logic such that the variable is always available, with a backwards-compatible default.\n\nAn example of this is Statamic's own form tag and the `success` variable.\n\n### Aliased Array Results\n\nThe Antlers engine will automatically alias array results. Consider the following tag:\n\n```php\n<?php\n\nnamespace App\\Tags;\n\nuse Statamic\\Tags\\Tags;\n\nclass YourTag extends Tags\n{\n    public function index()\n    {\n        return ['a', 'b', 'c'];\n    }\n}\n```\n\nWhen writing Antlers, the following will \"just work\" due to how to the engine is implemented:\n\n```antlers\n{{ your_tag as=\"the_array_name\" }}\n\n  {{ the_array_name }}\n    {{ value }}\n  {{ /the_array_name }}\n\n{{ /your_tag }}\n```\n\nThe Blade countepart would be:\n\n```blade\n<s:my_custom_tag as=\"the_array_name\">\n  @foreach ($the_array_name as $value)\n    {{ $value }}\n  @endforeach\n</s:my_custom_tag>\n```\n\nHowever, with the current tag implementation, this would not work and template authors would receive an error stating the `$the_array_name` variable doesn't exist. To make this work, we need to add support for the `as` parameter to our tag directly. Luckily, an `aliasedResult` helper method exists to make this easy for us:\n\n```php\n<?php\n\nnamespace App\\Tags;\n\nuse Statamic\\Tags\\Tags;\n\nclass YourTag extends Tags\n{\n    public function index()\n    {\n        return ['a', 'b', 'c']; // [tl! --]\n        return $this->aliasedResult(['a', 'b', 'c']); // [tl! ++]\n    }\n}\n```\n\n### Don't Assume Tag Content is Always Antlers\n\nIf you are interacting with a tag's content and rendering it manually, you should *not* assume that the tag's content is always Antlers.\n\nFor example, if you have a tag implementation that looks something like this:\n\n```php\n<?php\n\nnamespace App\\Tags;\n\nuse Statamic\\Facades\\Antlers;\nuse Statamic\\Tags\\Tags;\n\nclass MyCustomTag extends Tags\n{\n    public function index()\n    {\n        return Antlers::parse($this->content);\n    }\n}\n\n```\n\nconsider using the `parse()` method instead, which will take the current templating language into consideration:\n\n\n```php\n<?php\n\nnamespace App\\Tags;\n\nuse Statamic\\Facades\\Antlers; // [tl! --]\nuse Statamic\\Tags\\Tags;\n\nclass MyCustomTag extends Tags\n{\n    public function index()\n    {\n        return Antlers::parse($this->content); // [tl! --]\n        return $this->parse(); // [tl! ++]\n    }\n}\n\n```\n\n### Implementing Custom Behavior for Blade vs. Antlers\n\nIf you want to change your tags behavior specifically for Blade, you can check if the current tag instance is being rendered within a Blade template like so:\n\n```php\n<?php\n\nnamespace App\\Tags;\n\nuse Statamic\\Tags\\Tags;\n\nclass YourTag extends Tags\n{\n    public function index()\n    {\n        if ($this->isAntlersBladeComponent()) {\n            return 'Hello, Blade!';\n        }\n\n        return 'Hello, Antlers!';\n    }\n}\n```\n\n## Miscellaneous\n\n- `$this->content` - When using a tag pair, this is what's between them.\n- `$this->isPair` - Boolean for whether a single or tag pair was used.\n- `$this->tag` - The full tag that was used.\n    - For `{{ ron foo=\"bar\" }}` it would be `ron:index`\n    - For `{{ ron:swanson foo=\"bar\" }}`, this would be `ron:swanson`\n    - For `{{ ron:swanson:breakfast foo=\"bar\" }}`, this would be `ron:swanson:breakfast`\n- `$this->method` - The tag method that was used.\n    - For `{{ ron foo=\"bar\" }}`, it would `index`\n    - For `{{ ron:swanson foo=\"bar\" }}`, this would be `swanson`\n    - For `{{ ron:swanson:breakfast foo=\"bar\" }}`, this would be `swanson:breakfast`\n\n[collection_tag]: /tags/collection\n"
  },
  {
    "path": "content/collections/pages/building-a-widget.md",
    "content": "---\ntitle: Building Widgets\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569264107\nid: 5900c99f-89b9-4ee3-834c-cb1b070146e4\n---\n\n## Generating a widget\n\nYou can generate a widget with a console command:\n\n```shell\nphp please make:widget LocalWeather\n```\n\nThis will automagically create a class in `app/Widgets` and a Vue component in `resources/js/components/widgets`.\n\nThe PHP class is responsible for returning the Vue component and any props:\n\n```php\n// app/Widgets/LocalWeather.php\n\n<?php  \n  \nnamespace App\\Widgets;  \n  \nuse Statamic\\Widgets\\VueComponent;\nuse Statamic\\Widgets\\Widget;\n  \nclass LocalWeather extends Widget  \n{\n    public function component()  \n    {  \n        return VueComponent::render('LocalWeather', ['message' => 'Hello World!']);\n    }  \n}\n```\n```blade\n<script setup>\nimport { Widget } from '@statamic/cms/ui';\n\ndefineProps(['message']);\n</script>\n\n<template>\n    <Widget title=\"LocalWeather\">\n        <div class=\"px-4 py-3\">\n            <p>👋 {{ message }}</p>\n        </div>\n    </Widget>\n</template>\n```\n\nThe `<Widget>` component requires a `title` prop, along with optional `icon` and `href` props. You also pass an `actions` slot to render content in the top right of the widget.\n\nIf you'd prefer to create your widget using Blade, simply pass the `--blade` argument to the `make:widget` command.\n\n## Configuring\n\nWidgets can be added to the dashboard by modifying the `widgets` array in the `config/statamic/cp.php` file.\n\n``` php\n// config/statamic/cp.php\n'widgets' => [\n  [ // [tl! focus:start]\n      'type' => 'local_weather',\n      'width' => 100,\n  ], // [tl! focus:end]\n],\n```\n"
  },
  {
    "path": "content/collections/pages/building-an-addon.md",
    "content": "---\nid: 5bd75435-806e-458b-872e-7528f24df7e6\nblueprint: page\ntitle: 'Building an Addon'\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569264134\nintro: 'An addon is a composer package you intend to reuse, distribute, or sell. For simple or private packages, consider implementing directly into your Laravel application.'\n---\n## Creating an Addon\n\nYou can generate an addon with a console command:\n\n``` shell\nphp please make:addon example/my-addon\n```\n\nThis will scaffold out everything you need to get started as a [private addon](#private-addons) within your site's `addons` directory.\n\nEventually, an addon may be available on Packagist and installable through Composer (and therefore live inside your `vendor` directory). During development however, you can keep it on your local filesystem as a path repository.\n\n:::tip\nIf you don't plan on distributing your addon or sharing it between multiple projects, you can take a simpler approach and just [add things to your Laravel application](/extending).\n:::\n\n\n### What's in an addon?\n\nAn addon consists of at least a `composer.json` and a service provider. Your directory may be placed anywhere, but for the sake of this example, we'll put it in `addons/acme/example`\n\n``` files theme:serendipity-light\naddons/\n    acme/\n        example/\n            src/\n                ServiceProvider.php\n            composer.json\napp/\ncontent/\nconfig/\npublic/\n    index.php\nresources\ncomposer.json\n```\n\n### Composer.json\n\nThe composer.json is used by (you guessed it) Composer in order to install your package.\n\nThe `extra.statamic` section is used by Statamic to know that it's an addon and not just a standard Composer package.\nThe `extra.laravel.providers` section what Laravel uses to load your service provider.\n\n``` json\n{\n    \"name\": \"acme/example\",\n    \"description\": \"Example Addon\",\n    \"autoload\": {\n        \"psr-4\": {\n            \"Acme\\\\Example\\\\\": \"src\"\n        }\n    },\n    \"authors\": [\n        {\n            \"name\": \"Jason Varga\"\n        }\n    ],\n    \"support\": {\n        \"email\": \"support@statamic.com\"\n    },\n    \"extra\": {\n        \"statamic\": {\n            \"name\": \"Example\",\n            \"description\": \"Example addon\"\n        },\n        \"laravel\": {\n            \"providers\": [\n                \"Acme\\\\Example\\\\ServiceProvider\"\n            ]\n        }\n    }\n}\n```\n\n### Service Provider\n\nThe service provider is where all the various components of your addon get wired together.\n\nYou should make sure that your service provider extends Statamic's `Statamic\\Providers\\AddonServiceProvider`, and not `Illuminate\\Support\\ServiceProvider`. Statamic's `AddonServiceProvider` includes some bootstrapping and autoloading that isn't included with Laravel's service provider.\n\n``` php\n<?php\n\nnamespace Acme\\Example;\n\nuse Statamic\\Providers\\AddonServiceProvider;\n\nclass ServiceProvider extends AddonServiceProvider\n{\n    //\n}\n```\n\n:::tip\nThe `bootAddon` method should be used instead of `boot`. They are the same except that it\nmakes sure to boot _after_ Statamic has booted.\n:::\n\n### Installing your freshly created addon\n\nIf you ran the `make:addon` command, this would have been taken care of for you.\n\nOtherwise, in your project root's `composer.json`, add your package to the `require` and `repositories` sections, like so:\n\n``` json\n{\n    \"require\": {\n        \"acme/example\": \"*\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"path\",\n            \"url\": \"addons/example\"\n        }\n    ]\n}\n```\n\nRun composer update from your _project root_ (not your addon directory).\n\n``` shell\ncomposer update\n```\n\nIf you've been following correctly, you should see these two lines amongst a bunch of others.\n\n```\nDiscovered Package: acme/example\nDiscovered Addon: acme/example\n```\n\nYour addon is now installed. You should be able to go to `/cp/addons` and see it listed.\n\n\n## Installing an Addon\n\n### Public addons\n\nA public addon is one available as a composer package on packagist.org. Simply require it with composer:\n\n``` shell\ncomposer require vendor/package\n```\n\n### Private addons\n\nA private addon is one _not_ on packagist.org. You will need to use a composer path repository.\n\nDownload the package to a directory of your choosing.\n\nIn your project root's `composer.json`, add the package to the `require` and `repositories` sections, like so:\n\n``` json\n{\n    ...\n\n    \"require\": {\n        ...,\n        \"acme/example\": \"*\"\n    },\n\n    ...\n\n    \"repositories\": [\n        {\n            \"type\": \"path\",\n            \"url\": \"addons/example\"\n        }\n    ]\n}\n```\n\nRun composer update from your project root:\n\n``` shell\ncomposer update\n```\n\nAfter the composer package has been brought in, Statamic will automatically activate it and [publish its assets](#publishing-assets).\n\n### Post-install commands {#post-install-commands}\n\nBy default, the `vendor:publish` command will be run for you after `statamic:install`, letting your assets be automatically published.\n\nHowever, you can run other commands or custom code too using the `afterInstalled` method:\n\n``` php\npublic function bootAddon()\n{\n    Statamic::afterInstalled(function ($command) {\n        $command->call('some:command');\n    });\n}\n```\n\n\n## Registering Components\n\nStatamic will autoload _most_ of your addon's components, as long as they're in the right place and named correctly.\n\nHowever, you can still register them manually in your service provider if you need to:\n\n``` php\nprotected $tags = [\n    \\Acme\\Example\\Tags\\First::class,\n    \\Acme\\Example\\Tags\\Second::class,\n    // etc...\n];\n\nprotected $modifiers = [\n    //\n];\n\nprotected $fieldtypes = [\n    //\n];\n\nprotected $widgets = [\n    //\n];\n\nprotected $commands = [\n    //\n];\n```\n\n## Assets\n\n### CSS and Javascript\nWe recommend using Vite to build CSS and JavaScript for your addon. For full setup instructions, please see our [Vite Tooling](/addons/vite-tooling) docs.\n\n### Publishables\n\nYou may also mark generic assets for publishing by providing a `publishables` array with the full path to the  origin and the destination directory.\n\n``` php\nprotected $publishables = [\n    __DIR__.'/../resources/images' => 'images',\n];\n```\n\n### Publishing assets\n\nWhen using the `$vite`, `$scripts`, `$stylesheets`, and `$publishables` properties, these files will be made available to the `artisan vendor:publish` command.\nThey will all be tagged using your addon's slug.\n\nWhenever the `statamic:install` command is run (i.e. after running `composer update`, etc) the following command will be run:\n\n``` shell\nphp artisan vendor:publish --tag=your-addon-slug --force\n```\n\nYou can prevent these from being automatically published by adding a property to your provider:\n\n``` php\nprotected $publishAfterInstall = false;\n```\n\nThis may be useful if you need more control around groups of assets to be published, or if you're using custom [post-install commands](#post-install-commands).\n\n## Routing\n\n### Registering Routes\n\nAddons can register three types of routes:\n\n* Control Panel routes\n* Action routes\n* Web routes\n\nTo keep things organized, we recommend keeping your routes in separate files.\n\n``` files theme:serendipity-light\n/\n    src/\n    routes/\n        cp.php\n        actions.php\n        web.php\n```\n\nIf you follow this convention, Statamic will automatically register these route files for you. If you prefer to keep them elsewhere, you can register them manually in your service provider:\n\n``` php\nprotected $routes = [\n    'cp' => __DIR__.'/../routes/cp.php',\n    'actions' => __DIR__.'/../routes/actions.php',\n    'web' => __DIR__.'/../routes/web.php',\n];\n```\n\n#### Control Panel Routes\n\nControl Panel routes will be automatically prefixed by `/cp` (or whatever URL the control panel has been configured to use) and will have authorization applied.\n\nWe recommend prefixing routes with your addon's name but we didn't enforce this explicitly to give you a bit more flexibility.\n\n#### Action Routes\n\nAction routes will be prefixed by `/!/addon-name` and are generally intended as front-end \"actions\" your addon may expose without being a prominent section of the website. For example, somewhere to process a form submission.\n\n#### Web Routes\n\nWeb routes have no prefix and no Statamic middleware attached. They will be added at the root level, as if you were adding them to a standard Laravel app's `routes/web.php` file, giving you complete control. However, as a Laravel route, they will have the `web` middleware attached.\n\n### Writing Routes\n\nWhen referencing a controller in a route, it will automatically be namespaced to your addon's root namespace.\n\n``` json\n\"autoload\": {\n    \"psr-4\": {\n        \"Acme\\\\Example\\\\\": \"src\"\n    }\n},\n```\n\n``` php\nRoute::get('/', [ExampleController::class, 'index']); // Acme\\Example\\ExampleController\n```\n\nIf you'd prefer not to have separate route files, you can write routes in your service provider's `bootAddon` method.\n\n``` php\npublic function bootAddon()\n{\n    $this->registerCpRoutes(function () {\n        Route::get(...);\n    });\n\n    $this->registerWebRoutes(function () {\n        Route::get(...);\n    });\n\n    $this->registerActionRoutes(function () {\n        Route::get(...);\n    });\n}\n```\n\nOther than that, you're free to write routes [as per any Laravel application](https://laravel.com/docs/routing).\n\n### Route Model Binding\n\nStatamic uses [route model binding](https://laravel.com/docs/routing#route-model-binding) to automatically convert some route parameters into usable objects.\n\nWords aligning with core Statamic concepts will automatically be converted to their appropriate objects: `collection`, `entry`, `taxonomy`, `term`, `asset_container`, `asset` ,`global`, `site`, `revision`, `form`, and `user`\n\nYou're free to use these words as your route parameters, but be aware they will automatically attempt to convert to the respective objects. For example:\n\n``` php\npublic function example(Request $request, $entry)\n{\n    // Given a route of \"/example/{entry}\", when visiting \"/example/123\"\n    // $entry will be an Entry object with an ID of 123.\n    // There will be a 404 if an entry with an ID of 123 doesn't exist.\n}\n```\n\n## Middleware\n\nYou may push your own middleware onto respective middleware groups using the `$middlewareGroups` property.\nThe keys are the names of the groups, and the values are arrays of middleware classes to be applied.\n\n``` php\nprotected $middlewareGroups = [\n    'statamic.cp.authenticated' => [\n        YourCpMiddleware::class,\n        AnotherCpMiddleware::class\n    ],\n    'web' => [\n        YourWebMiddleware::class\n    ],\n];\n```\n\nAvailable middleware groups are:\n\n| Group | Description |\n|-------|-------------|\n| `web` | Front-end web requests, defined in the project's `App\\Http\\Kernel` class.\n| `statamic.web` | Statamic-specific front-end web requests. This includes routes that correspond to content (like entries), as well as manually defined routes using `Route::statamic()`. These will also have `web` middleware applied.\n| `statamic.cp` | All control panel requests (even ones not protected by authentication, like the login page).\n| `statamic.cp.authenticated` | Control panel routes behind authentication. Anything in there can assume there will be an authenticated user available. These will also have the `statamic.cp` middleware applied.\n\n## Views\n\nAny views located in your `resources/views` directory will automatically be available to use in your code using your package name as the namespace.\n\n``` files theme:serendipity-light\n/\n    src/\n    resources/\n        views/\n            foo.blade.php\n```\n\n``` php\n// assuming your package is named vendor/my-addon\nreturn view('my-addon::foo');\n```\n\nIf you want to customize the namespace, you can set the `$viewNamespace` property on your provider:\n\n``` php\nprotected $viewNamespace = 'custom';\n```\n``` php\nreturn view('custom::foo');\n```\n\n## Inertia\n\nThe Control Panel is powered by [Inertia.js](https://inertiajs.com), which lets Statamic render pages as Vue components while still using Laravel’s server-side routing. Using Inertia for your custom pages is strongly recommended if you want them to match the SPA-like behaviour seen throughout the Control Panel.\n\nTo expose a Vue page component to Statamic, register it in your `cp.js` file:\n\n```js\nimport Foo from './pages/Foo.vue';\n\nStatamic.booting(() => {\n    Statamic.$inertia.register('my-addon::Foo', Foo);\n});\n```\n\nThen return that page from your controller:\n\n```php\nuse Inertia\\Inertia;\n\nreturn Inertia::render('my-addon::Foo', [\n    'message' => 'Hello world!',\n]);\n```\n\nAll data passed to `Inertia::render()` becomes props on the Vue component.\n\nFor proper SPA behaviour, make sure your page uses Inertia’s `<Head>` component to set the document title, and use `<Link>` instead of `<a>` so navigation stays instant and avoids a full refresh:\n\n```vue\n<script setup>\nimport { Head, Link } from '@statamic/cms/inertia';\n</script>\n\n<template>\n    <Head title=\"Foo\" />\n\n    <Link :href=\"cp_url('bar')\">Go to another page</Link>\n</template>\n```\n\n\n\n## Events\n\nStatamic will automatically register any event listeners in the `src/Listeners` directory, as long as the event is type-hinted in the listener's `handle` or `__invoke` method.\n\n``` php\nuse Acme\\Example\\Events\\OrderShipped;\n\nclass SendShipmentNotification\n{\n    public function handle(OrderShipped $event)\n    {\n        //\n    }\n}\n```\n\nSubscribers will also be autoloaded, as long as they live in `src/Subscribers`.\n\nIf your addon's listeners or subscribers live elsewhere, you may register them manually in your service provider:\n\n``` php\nprotected $listen = [\n    \\Acme\\Example\\Events\\OrderShipped::class => [\n        \\Acme\\Example\\Listeners\\SendShipmentNotification::class,\n    ],\n];\n\nprotected $subscribe = [\n    \\Acme\\Example\\Listeners\\UserEventSubscriber::class,\n];\n```\n\nTo learn more about defining events, listeners and subscribers, please consult the [Laravel event documentation](https://laravel.com/docs/events).\n\n\n## Scheduling\n\nTo define a schedule from your addon, you can add a `schedule` method and schedule tasks just like you typically would in a Laravel application's `routes/console.php` file.\n\n``` php\nprotected function schedule($schedule)\n{\n    $schedule->command('something')->daily();\n}\n```\n\nConsult the [Laravel scheduling documentation](https://laravel.com/docs/scheduling#defining-schedules) to learn how to define your schedule.\n\n## Editions\n\nAn addon can have various editions which enable you to limit your features depending on which is selected.\n\nFor example, you could have a free edition with limited features, and an edition with extra features that requires a license.\n\n### Defining Editions\n\nYou can define your editions in your `composer.json`. They should match the edition handles that you set up on the Marketplace.\n\n``` json\n{\n    \"extra\": {\n        \"statamic\": {\n            \"editions\": [\"free\", \"pro\"]\n        }\n    }\n}\n```\n\n:::best-practice\nThe first edition is the default when a user hasn't explicitly selected one. Your editions should be listed from least to most expensive because that's the nice thing to do.\n:::\n\n### Feature Toggles\n\nYou can check for the configured edition in order to toggle features.\n\n``` php\n$addon = Addon::get('vendor/package');\n\nif ($addon->edition() === 'pro') {\n    //\n}\n```\n\n:::tip\nYou don't need to check whether a license is valid, Statamic does that automatically for you.\n:::\n\n## Settings\n\nLaravel config files are great for storing application settings, but they're not ideal for settings you might want users to edit through the Control Panel.\n\nYou can register a settings blueprint in your addon to give users a friendly interface for managing settings. Drop a blueprint file in `resources/blueprints/settings.yaml` or register it in your service provider like this:\n\n```php\npublic function bootAddon()\n{\n    $this->registerSettingsBlueprint([\n        'tabs' => [\n            'main' => [\n                'sections' => [\n                    [\n                        'display' => __('API'),\n                        'fields' => [\n                            [\n                                'handle' => 'api_key',\n                                'field' => ['type' => 'text', 'display' => 'API Key', 'validate' => 'required'],\n                            ],\n                            // ...\n                        ],\n                    ],\n                ],\n            ],\n        ],\n    ]);\n}\n```\n\nYour addon's settings page will show up in the Control Panel under **Tools -> Addons**. Pretty convenient.\n\nYou can even reference config options (and by extension environment variables) in your settings blueprint using Antlers, like so: `{{ config:app:url }}`.\n\nSettings are stored as YAML files in `resources/addons` by default, but can be moved to the database if you prefer. Just run the `php please install:eloquent-driver` command and you're all set.\n\nYou can retrieve the settings using the `Addon` facade:\n\n```php\nuse Statamic\\Facades\\Addon;\n\n$addon = Addon::get('vendor/package');\n\n// Getting settings...\n$addon->settings()->get('api_key');\n\n$addon->settings()->all();\n$addon->settings()->raw(); // Doesn't evaluate Antlers\n\n// Setting values...\n$addon->settings()->set('api_key', '{{ config:services:example:api_key }}');\n\n$addon->settings()->set([\n    'website_name' => 'My Awesome Site',\n    'api_key' => '{{ config:services:example:api_key }}',\n]);\n\n// Saving...\n$addon->settings()->save();\n```\n\n## Update Scripts\n\nYou may register update scripts to help your users migrate data, etc. when new features are added or breaking changes are introduced.\n\nFor example, maybe you've added a new permission and want to automatically give all of your existing form admins that new permission.\n\nTo do this, create a class which extends the `UpdateScript` class and implement the necessary methods:\n\n``` php\nuse Statamic\\UpdateScripts\\UpdateScript;\n\nclass UpdatePermissions extends UpdateScript\n{\n    public function shouldUpdate($newVersion, $oldVersion)\n    {\n        return $this->isUpdatingTo('1.2.0');\n    }\n\n    public function update()\n    {\n        Role::all()->each(function ($role) {\n            if ($role->hasPermission('configure forms')) {\n                $role->addPermission('configure goat-survey-pro')->save();\n            }\n        });\n\n        $this->console()->info('Permissions added successfully!');\n    }\n}\n```\n\nThe `shouldUpdate()` method helps Statamic determine when to run the update script. Feel free to use the `isUpdatingTo()` helper method, or the provided `$newVersion` and `$oldVersion` variables to help you write this logic.\n\nThe `update()` method is where your custom data migration logic happens. Feel free to use the `console()` helper to output to the user's console as well. In the above example, we assign the new `configure goat-survey-pro` permission to all users who have the `configure forms` permission.\n\nThat's it! Statamic should now automatically run your update script as your users update their addons.\n\n## Testing\n\nStatamic automatically scaffolds a PHPUnit test suite when you generate an addon with `php please make:addon`.\n\nTo learn more about writing addon tests, please review our [Testing in Addons](/extending/testing-in-addons) guide.\n\n## Publishing to the Marketplace\n\nOnce your addon is ready to be shared, you can publish it on the [Statamic Marketplace](https://statamic.com/marketplace) where it can be discovered by others.\n\nBefore you can publish your addon, you'll need a couple of things:\n\n- Publish your Composer package on [packagist.org](https://packagist.org).\n- Create a [statamic.com seller account](https://statamic.com/creator/begin)\n- If you're planning to charge for your addons, you'll need to link connect your bank details to your seller account.\n\nIn your seller dashboard, you can create a product. There you'll be able to link your Composer package that you created on Packagist, choose a price, write a description, and so on.\n\nProducts will be marked as drafts that you can preview and tweak until you're ready to go.\n\nOnce published, you'll be able to see your addon on the Marketplace and within the Addons area of the Statamic Control Panel.\n\n\n## Addons vs. Starter Kits\n\nBoth addons and starter kits can be used to extend the Statamic experience, but they have different strengths and use cases:\n\n### Addons\n\n- Addons are installed via `composer`, like any PHP package\n- Addons live within your app's `vendor` folder after they are installed\n- Addons can be updated over time\n- Addon licenses are tied to your site\n\n:::tip\nAn example use case is a custom fieldtype maintained by a third party vendor. Even though the addon is installed into your app, you still rely on the vendor to maintain and update the addon over time.\n:::\n\n### Starters Kits\n\n- Starter kits are installed via `statamic new` or `php please starter-kit:install`\n- Starter kits install pre-configured files and settings into your site\n- Starter kits do not live as updatable packages within your apps\n- Starter kit licenses are not tied to a specific site, and expire after a successful install\n\n:::tip\nAn example use case is a frontend theme with sample content. This is the kind of thing you would install into your app once and modify to fit your own style. You would essentially own and maintain the installed files yourself.\n:::"
  },
  {
    "path": "content/collections/pages/caching.md",
    "content": "---\ntitle: Caching\nintro: Caching is the life-blood, the secret-sauce, and the wizard behind the curtain of Statamic. There are several caching layers, each with its own purpose. Let's explore each one and its specific purpose.\nid: bde2385f-5fee-4cb1-a516-5fe2e2d17e0c\nblueprint: page\n---\n## The Stache\n\n**Purpose:** _Replace the traditional database_\n\nInstead of using a relational database like MySQL as a storage system, Statamic aggregates the data in your content files into an efficient, index-based system and stores it in Laravel's application cache. We call this the \"stache\", and we like to make mustache jokes about it.\n\n<figure class='bg-mint'>\n    <img src=\"/img/tom-selleck-lg.jpg\" alt=\"Tom Selleck as Magnum P.I.\">\n    <figcaption>Behold, the stache of all staches!</figcaption>\n</figure>\n\n**The stache is ephemeral** and can be blown away and rebuilt from scratch at any time without losing data. This is most often done when content or settings change, or when updates are deployed to a production server.\n\nThe [CLI](/cli) has commands to clear, warm, and refresh (clear and then immediately warm) the stache.\n\n``` shell\nphp please stache:clear\nphp please stache:warm\nphp please stache:refresh\n```\n\nThere are settings you can configure to improve the performance of the stache, just like with a relational database. [Learn more about the Stache](/stache) and its various settings.\n\n:::tip\n**You cannot disable the stache** &mdash; it is critical architecture.\n:::\n\n## Application cache\n\n**Purpose:** _Make site faster_\n\nThe application cache is used by your site/application, third-party addons, Laravel Packages, and Statamic itself to store queries, data, and the results of resource intensive operations for pre-defined lengths of time. It uses [Laravel’s Cache API](https://laravel.com/docs/cache).\n\n**For example,** the [Image Transform](/tags/glide) feature uses this cache to store all the manipulated images at their various sizes and transformations. When the arguments that generate those manipulations change, the cached images are blown away and new ones are generated and cached.\n\nEach item inserted into the cache can **optionally and automatically** expire after a specified length of time.\n\nIf you want to clear the entire application cache at once, use the `artisan cache:clear` command.\n\n``` shell\nphp artisan cache:clear\n```\n\n:::tip\nThe Stache is stored **inside** the application cache, so if you clear it, you don't need to _also_ clear the Stache.\n:::\n\n## View fragments\n\n**Purpose:** _Make a view faster_\n\nThere are times when you may want to simply cache a section of an Antlers template to reduce load times for a particularly \"expensive\" bit of logic or content fetching. This is where the [cache tag](/tags/cache) comes in.\n\nWrap your markup in `{{ cache }}` tags, specify a duration, and your site is zippy and, one might say, quite delicious once again.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cache for=\"1 hour\" }}\n  something impressive but slow here\n{{ /cache }}\n```\n::tab blade\n```blade\n<s:cache for=\"1 hour\">\n  something impressive but slow here\n</s:cache>\n```\n::\n\n## Static caching\n\n**Purpose:** _Ultimate speed at the cost of dynamic features_\n\nThere is nothing faster on the web than static pages, except static pages without JavaScript and giant hero images, of course. Statamic can cache static pages and pass routing off to Apache or Nginx through reverse proxying.\n\nStatic Caching can be enabled on a per-page level, allowing you to mix and match dynamic features when needed.\n\n:::tip\nThis should not be confused with [Static Site Generation](https://github.com/statamic/ssg), which is the fastest possible way to run your site, involving generating actual `.html` files used to serve your site, skipping PHP and the Statamic application entirely.\n:::\n\n[Learn more about Static Caching](/static-caching) to make your sites start flying! We're talking `2ms` response times here.\n"
  },
  {
    "path": "content/collections/pages/cli.md",
    "content": "---\ntitle: CLI\nintro: Statamic provides developers a nice long list of scripts available in the command line. They can clear caches, create users, generate addon and extension classes, and perform other time-saving tasks. In short, they make a developer's job easier and more enjoyable.\ntemplate: page\nid: 83145e6c-45d2-4e9c-a412-48a81f144224\nblueprint: page\n---\n## Overview\n\nStatamic's CLI commands are built with [Laravel's Artisan Console package][artisan]. To view the list of Statamic-specific commands, you may use the `please list` command:\n\n``` shell\nphp please list\n```\n\nTo see _all_ commands available, including those provided by Laravel, use the `artisan list` command.\n\n``` shell\nphp artisan list\n````\n\n## Artisan vs Please\n\nThere is no functional difference between Artisan and Please. `please` is merely an alias for `php artisan statamic:`. We just think manners are still important and it feels nice to treat your command line with respect, while saving you a little time typing.\n\n``` shell\n# These are equivalent\nphp please make:user\nphp artisan statamic:make:user\n```\n\nEvery command also includes a help screen which displays and describes the command's available arguments and options. To view a help screen, precede the name of the command with help:\n\n``` shell\nphp please help make:user\n```\n\n## Available commands {#commands}\n\nYou can see the list of available commands in your terminal by running `php please list`. But for those who don't feel like it, haven't installed yet, or are scared of the command line, here they are also.\n\n| Command | Description |\n|---------|-------------|\n| `install`          | Install Statamic |\n| `list`             | List all the Statamic commands |\n| `multisite`        | Converts from a single to multisite installation |\n| `addons:discover`  | Rebuild the cached addon package manifest |\n| `assets:clear-cache` | Clear the `asset_meta` and `asset_container_contents` cache stores |\n| `assets:generate-presets` | Generate asset preset manipulations |\n| `assets:meta`      | Generate asset metadata files |\n| `assets:meta-clean` | Clean orphaned asset metadata files |\n| `auth:migration`   | Generate Auth Migrations |\n| `eloquent:import-groups` | Imports file based groups into the database. |\n| `eloquent:import-roles` | Imports file based roles into the database. |\n| `eloquent:import-users` | Imports file based users into the database. |\n| `flat:camp` | Flat Camp ⛺ |\n| `glide:clear`      | Clear the Glide image cache |\n| `install:collaboration` | Installs the Statamic Collaboration addon and enables broadcasting in Laravel. |\n| `install:eloquent-driver` | Install & configure Statamic's Eloquent Driver package |\n| `install:ssg` | Install & configure Statamic's Static Site Generator package |\n| `license:set` | Set Statamic license key in .env |\n| `make:action`      | Create a new action |\n| `make:addon`       | Create a new addon |\n| `make:dictionary`  | Create a new dictionary |\n| `make:fieldtype`   | Create a new fieldtype |\n| `make:filter`      | Create a new filter |\n| `make:modifier`    | Create a new modifier |\n| `make:scope`       | Create a new query scope |\n| `make:tag`         | Create a new tag |\n| `make:user`        | Create a new user account |\n| `make:user-migration` | Makes the user migration file |\n| `make:widget`      | Create a new widget |\n| `migrate-dates-to-utc` | Migrates dates in your content from your current timezone to UTC. |\n| `nocache:migration` | Generate Nocache Migrations |\n| `pro:enable`      | Enable Statamic Pro in .env |\n| `search:insert`    | Insert an item into its search indexes |\n| `search:update`    | Update a search index |\n| `site:clear`       | Start a fresh site, wiping away all content |\n| `stache:clear`     | Clear the \"Stache\" cache |\n| `stache:doctor`    | Diagnose any problems with the Stache. |\n| `stache:refresh`   | Clear and rebuild the \"Stache\" cache |\n| `stache:warm`      | Build the \"Stache\" cache |\n| `starter-kit:export`  | Export a starter kit package |\n| `starter-kit:init`  | Creates a new starter kit config |\n| `starter-kit:install`  | Install a starter kit |\n| `static:clear`     | Clear the static page cache |\n| `static:warm`      | Warm the static cache by crawling all URLs |\n| `support:details`  | List useful details to help with support |\n| `support:zip-blueprint`  | Create a zip file with a blueprint and all fieldset imports |\n| `updates:run`      | Run update scripts from a specific version |\n\n## Additional reading\n\nRead more about [Artisan][artisan] at Laravel.com.\n\n[artisan]: https://laravel.com/docs/artisan\n"
  },
  {
    "path": "content/collections/pages/code-of-conduct.md",
    "content": "---\nid: 0efc0286-bfb1-4d54-8a94-8589b35adf88\nblueprint: page\ntitle: 'Code of Conduct'\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1632425727\n---\nThis is the Statamic Code of Conduct. By participating here, you are expected to uphold this code like a knight of old. This code of conduct applies to all spaces used by the Statamic community for communication. This includes the Statamic Discord, forums, GitHub, Twitter, Facebook, meetups, conferences, and any other relevant forums. If you believe someone is violating the code of conduct, we ask that you report it by contacting us at [statamic.com/support](https://statamic.com/support). Your identity will remain confidential.\n\n- **Treat others in the way you want to be treated.** Respect each other, we’re all on the same team here so let’s have fun, share what we know, and hopefully learn something new! No bullying, harassment, foul language, racism, sexism, or other negative \"isms\". Generally, just be awesome to each other.\n\n- **There is always another side to every story.** When interpreting the words and actions of others, always assume good intentions. Sometimes it's difficult, but everyone has a bad day now and then. Someday it might be your turn.\n\n- **Don’t be afraid to ask.** There is no such thing as a dumb question. We’re all here to learn and we encourage people to ask questions about anything Statamic-related.\n\n- **Don't bash other platforms** (e.g. WordPress, Craft, ExpressionEngine, etc). They all serve their purposes, markets, and provide livelihoods for thousands of developers. Please be welcoming to those coming from those communities to check out Statamic. Hopefully they'll find something they love, but it won't happen every time. :blush:\n"
  },
  {
    "path": "content/collections/pages/collections.md",
    "content": "---\nid: 7202c698-942a-4dc0-b006-b982784efb03\nblueprint: page\ntitle: Collections\nintro: 'Collections are containers that hold groups of related entries. Each entry in a collection can represent a blog post, product, recipe, or even chapter of your Family Matters fan fiction novel detailing Steve Urkel''s rise to UFC Heavyweight Champion of the world.'\ntemplate: page\nrelated_entries:\n  - 7202c698-942a-4dc0-b006-b982784efb03\n  - 8d9cfb16-36bf-45d0-babb-e501a35ddae6\n  - 6177b316-0eed-4dec-83d1-e5a48a8e00b6\n  - dcf80ee6-209e-45aa-af42-46bbe01996e2\n  - a6a956fd-647d-4503-9a4a-3b24198e6e73\n  - 54548616-fd6d-44a3-a379-bdf71c492c63\n  - cb21fabb-65ba-4869-9acd-f6aa2fb58a01\n  - 420f083d-99be-4d54-9f81-3c09cb1f97b7\n---\n## Overview\n\nNot to be redundant, but Collections are simply containers that hold entries. You can think of them like shoeboxes containing love letters, except they're folders on your server and they're holding text documents. So, not exactly the same thing — or at least, not nearly as romantic.\n\nEach collection holds settings that affect all of its entries. Like URL patterns by way of [routes](/routing), which fields are available with [blueprints](/blueprints), as well as any desired [date behaviors](#dates).\n\nYou can also set default values for system fields like template, blueprint, and published status.\n\nA collection is defined by a YAML file stored in the `content/collections` directory. All accompanying entries will be stored in a sub-directory with a matching name. For example, a `blog` collection looks like this:\n\n``` files theme:serendipity-light\ncontent/collections/\n  blog/\n    hello.md\n    is-it-me.md\n    youre-looking-for.md\n  blog.yaml\n```\n\n:::tip\nCreating a collection in the control panel takes care of all of this for you automatically, so don't stress too hard about memorizing all the details.\n:::\n\n## Entries\n\nEach entry has, at the very least, a title, published status, id, and _usually_ additional content fields. These content fields are determined by one or more [blueprints](/blueprints) set on the collection.\n\nEntries are stored as Markdown files inside their collection's respective directory (`content/collections/{collection}/entry.md`). At any time you can edit any entry in your code editor by popping open these files and doing what comes naturally.\n\n### An example\n\nLet's to pretend it's the summer of '99 and we are journalists covering the Summer X Games. The weather here in San Fransisco is beautiful and 275,000 people are watching Tony Hawk make history.\n\nHere's an entry we might write about the event.\n\n<figure>\n    <img src=\"/img/entry-tony-hawk.webp\" alt=\"An entry being edited in the Statamic control panel\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/entry-tony-hawk-dark.webp\" alt=\"An entry being edited in the Statamic control panel\" class=\"u-hide-in-light-mode\">\n    <figcaption>Entry publishing with only the default content fields.</figcaption>\n</figure>\n\nAnd here's what the Markdown file would look like:\n\n``` markdown\n<!-- content/collections/blog/1999-07-27.tony-hawks-900.md -->\n---\ntitle: Tony Hawk lands the first-ever 900\nid: 3a28f050-f8d2-4a56-ba8a-314a9d46bf38\n---\nIt took skateboarding legend Tony Hawk 11 tries, but he finally landed a 900 at the 1999 Summer X Games in a moment that launched the sport into popular consciousness in a new way.\n```\n\nYou can create, edit, and delete entries in the control panel _or_ filesystem, it's up to you and your preference in the heat of the moment. Let your passion carry you away.\n\n### View data\n\nEach entry has its own unique URL. When you're on that URL in your web browser, all of the entry's data will be available in your views as variables. If an entry is _missing_ data, intentionally or not, it will fall back to a series of defaults. We call this fallback logic [the cascade](/cascade).\n\nIf a value doesn't exist in one place, it'll check the next place, then the next, and so on, in this order:\n\n1. The entry\n2. The origin entry if using localization (the entry it was localized from)\n3. The collection\n\n### Setting default data {#inject}\n\n**Injecting** data into your collection allows you to provide default values for your entries. If entries have these variables set, they will override the collection _defaults_, but not any data set on the entries themselves.\n\nThis is done by adding an `inject` key in your collection's YAML config file.\n\n``` yaml\n# /content/collections/blog.yaml [tl! **]\ntitle: Blog\ndate: true\ndate_behavior:\n  past: public\n  future: private\nroute: 'blog/{slug}'\nsort_dir: desc\ntemplate: blog/show\ninject: #[tl! focus:start]\n  author: jason\n  show_sidebar: true\n  show_newsletter_signup: false #[tl! focus:end]\n```\n\n## Blueprints\n\nEach collection uses blueprints to define the available fields when creating and editing its entries.\n\nWhen you create a new collection, a blueprint of the same name will be createed for you as your default. It contains a very basic set of fields: title, `content`, `slug`, `author`, and `date`, if the collection is configured to store dates. You can customize this blueprint as you wish, as well as create your own additional blueprints.\n\nIf you create _more than_ one blueprint you'll be given the option to choose which one you want when creating a new entry. While this isn't common, it can be a pretty powerful option in the right situations.\n\nYou can hide blueprints from appearing in the new entry menu by activating the _Hidden_ toggle on the blueprint's UI or setting `hide: true` in the blueprint's yaml file.\n\n## Titles\n\nAll entries require a title. Statamic uses titles to display entries in a consistent way throughout the Control Panel.\n\nDepending on the purpose of the collection, a dedicated `title` field might not be useful to you. In this case, you may configure a \"title format\" which would be used to automatically generate titles from other fields so you don't have to invent something every time.\n\nFor example, a \"reviews\" collection might just have `author`, `stars`, and `content` fields. You could configure the titles to be \"5 star rating by John Smith\".\n\n<figure>\n    <img src=\"/img/title-format-setting.webp\" alt=\"Entry Title Format Setting\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/title-format-setting-dark.webp\" alt=\"Entry Title Format Setting\" class=\"u-hide-in-light-mode\">\n    <figcaption>Configuring an automated title</figcaption>\n</figure>\n\nWhen using multiple sites, you may optionally configure the titles on a per site level by using an array:\n\n```yaml\ntitle_format:\n  en: '{stars} star rating by {author:name}'\n  fr: '{stars} étoiles par {author:name}'\n```\n\nIt's worth noting that changes to a collection's title format won't change the titles of existing entries. For it to take effect, you will need to re-save your existing entries.\n\n:::tip\nTo use modifiers in title formats, make sure to use the `{{` Antlers syntax, like this:\n\n```antlers\n{{ headline | ucfirst }}\n```\n:::\n\n## Slugs\n\nSlugs are used in entry URLs. For an entry named `My Entry`, the slug would default to `my-entry` unless you edit it.\n\nSlugs are automatically generated for you based on the title, but if you edit them, that automatic process is switched off. We trust you know what you're doing.\n\n### Disabling Slugs\n\nIf the entries in a specific collection don't need to have dedicated URLs, or if the entries' route only contains other fields, a `slug` field may not be useful for you.\n\nYou may disable the slug requirement by adding a boolean:\n\n```yaml\nslugs: false\n```\n\nThis will prevent collections from automatically adding a slug field.\n\n:::tip\nSince Statamic stores entries as files, it uses the slug for the filename. If you disable slugs, it will use the ID instead. (e.g. `123.md` instead of `my-entry.md`)\n:::\n\n## Dates\n\nIf your collection entries require a date — as they often do — you can decide how Statamic uses it to control visibility. For example, you can choose to have dates set in the future to be private (404), which allows you to schedule their publish date.\n\nAlternatively, you could have _past_ dates be private which would make entries act like \"upcoming events\" that disappear from a list when they're over.\n\n<figure>\n    <img src=\"/img/collection-date-behaviors.webp\" alt=\"Collection Date Behaviors\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/collection-date-behaviors-dark.webp\" alt=\"Collection Date Behaviors\" class=\"u-hide-in-light-mode\">\n    <figcaption>Just imagine! This could be you, configuring date behaviors.</figcaption>\n</figure>\n\n### Available date behaviors\n\nEach of these behaviors is available for future and past dates.\n\n- **public** - Entries will be visible in listings and at their own URLs.\n- **unlisted** - Entries will be hidden in listings but available at their own URLs.\n- **private** - Entries will be hidden in listings, and their own URLs will 404.\n\n:::tip\nDate behaviors are _defaults_. They can be overridden at the [tag level](/tags/collection) in your templates.\n:::\n\n### Date behavior and published status\n\nYou can override [date behavior visibility settings](#available-date-behaviors) by setting the **Publish by Default** option to `false`.\n\nEach entry will automatically be assigned one of four possible computed `status` values, which respects both your collection's date behavior settings, as well as your entry's published setting:\n\n- **published** - Entry is published and visible.\n- **scheduled** - Entry is published, but not yet visible because date is upcoming.\n- **expired** - Entry is published, but not visible anymore because date has expired.\n- **draft** - Entry is explicitly hidden via `published: false`.\n\n:::tip\nWe recommend [filtering](/tags/collection#published-status) and [querying](/repositories/entry-repository#get-all-published-and-scheduled-entries) against your entry's `status` (instead of its `published` boolean) so that you can more easily take advantage of date behavior logic without hassle.\n:::\n\n<figure>\n    <img src=\"/img/collection-published-status-filtering.webp\" alt=\"Collection Published Status Filtering\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/collection-published-status-filtering-dark.webp\" alt=\"Collection Published Status Filtering\" class=\"u-hide-in-light-mode\">\n    <figcaption>Filter by entry status in your collection listings.</figcaption>\n</figure>\n\n## Time\n\n**Time** may be enabled on your [date field](/fieldtypes/date) to have entries publish at a **specific time**, e.g. `11:45am`, or to ensure that multiple entries in the same day are published in chronological order. We recommend leaving the **Time Enabled** setting on.\n\nYou can also enable the **Show Seconds** setting if you need to publish more than one entry per minute.\n\n:::tip\nIf you don't enable the time, _all_ entries on a given day will assume a default time of midnight, or `00:00`.\n:::\n\n## Scheduling\n\nIf you've added a date and/or time to your entries in order to \"schedule\" them, you may need to set up the scheduler in order for Statamic to properly invalidate your cache to display them at the right time.\n\nFor example, you might need things to happen exactly when an entry is scheduled, like refreshing a cached blog listing, or sending a notification.\n\n[Learn how to use the scheduler](/scheduling).\n\n## Ordering\n\nFlick on the \"Orderable\" switch in a collection's settings and you'll have a drag and drop UI in the control panel to order the entries. The collection is now \"structured\". Learn more about [structures](/structures).\n\n<figure>\n    <img src=\"/img/collection-structure.webp\" alt=\"An orderable collection\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/collection-structure-dark.webp\" alt=\"An orderable collection\" class=\"u-hide-in-light-mode\">\n    <figcaption>You can tell these entries are orderable because of the way they are.</figcaption>\n</figure>\n\n:::tip\nOrder will take precedence when sorting. For example, if you make a dated collection **orderable**, date will no longer be the default sort order. You still can sort by date by specifying `sort=\"date\"` on your [collection tag](/tags/collection).\n:::\n\n### Constraining Depth\n\nA structured collection will **not** have a maximum depth unless you set one, allowing you to nest entries as deep as you like. Set the `max_depth` option to limit this behavior. Setting the **Max Depth** option to `1` will replace the tree UI with a flat, table-based UI.\n\n<figure>\n    <img src=\"/img/reorderable-entries.webp\" alt=\"An orderable collection with max depth of 1\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/reorderable-entries-dark.webp\" alt=\"An orderable collection with max depth of 1\" class=\"u-hide-in-light-mode\">\n    <figcaption>These reorderable entries have a max depth of 1.</figcaption>\n</figure>\n\n### Default sort order in listings\n\nFor non-structured collections, you can choose which field and direction to sort the list of entries in the Control Panel by setting the `sort_by` and `sort_dir` variables in your collection.yaml. By default, the Title field will be used.\n\n### Root page\n\nIf you specify that your collection should \"expect a root page\", the first item in the tree UI will be considered the \"root\". This entry will _not_ use a slug in its URI — it will be treated as a `/`.\n\nThe most common usage for this is to define a home page in a pages' collection. In this example, the root page's url would be `/` instead of `/home`. But this would also be true of a sub-section. If you had an ordered `documents` collection that was set up to live at `/documents/`, the \"root\" of that collection in this case would be the `/documents/` URL.\n\n## Routing\n\nEntries receive their URLs from their collection's route setting. You can use standard meta variables in addition to the variables from the collection's blueprint to define your route rule. You can even use [computed values](/computed-values) or Antlers to create more complicated dynamic route logic.\n\n``` yaml\nroute: /blog/{slug}\n```\n\nIf you are building a multi-site and want different routes for different locales:\n\n```yaml\nroute:\n  english: /events/{slug}\n  french: /evenements/{slug}\n```\n\n:::tip\nStatamic does not automatically define route rules. If you want entries in your new collection to have URLs (almost always the case), make sure you define one!\n:::\n\n### Meta variables\n\n| Variable | Available |\n|----------|-----------|\n| `slug` | always |\n| `year` | when in a dated collection |\n| `month` | when in a dated collection |\n| `day` | when in a dated collection |\n| `parent_uri` | when in an [orderable](#ordering) collection and max_depth > 1 |\n| `depth` | when in an [orderable](#ordering) collection and max_depth > 1 |\n| `mount` | when [mounted](#mounting) to an entry |\n\n### Example Routes\n\nHere are a few examples of possible route rules for inspiration. 💡\n\n#### Wordpress style\n\n``` yaml\nroute: /news/{year}/{month}/{day}/{slug}\n# example: /news/2019/01/01/happy-new-year\n```\n\n#### For when you don't care about SEO\n\n``` yaml\nroute: /{id}\n# example: /12345-1234-321-12345\n```\n\n#### For when you care _too much_ about SEO\n\n``` yaml\nroute: /{parent_uri}/{slug}.html\n# example: /details/project.html\n```\n\n#### Organizing sports brackets with structures\n\nHere's how we use the `depth` variable, along with the `team_name` field from the entry's blueprint.\n\n``` yaml\nroute: /tournament/round-{depth}/{team_name}\n# example: /tournament/round-4/chicago-bulls\n```\n\n#### Using fields from related entries\n\nFor example, if you have a `category` field in your Products collection and you'd like for your product URLs to depend on it, you can configure a [computed value](/computed-values) to return the category URL, then use that computed value in your collection's route:\n\n``` php\n// app/Providers/AppServiceProvider.php\n\nuse Statamic\\Facades\\Collection;\n\nCollection::computed('products', 'category_url', function ($entry, $value) {\n    return $entry->category?->url();\n});\n```\n\n``` yaml\nroute: '{{ category_url }}/{{ slug }}'\n# example: /categories/wooden-toys/steam-locomotive\n```\n\n#### Using Antlers to organize gaming articles\n\nYou can even use Antlers to get more complicated. Here we'll include the [mounted](#mounting) entry at the top level.\n\n``` yaml\nmount: 'id-of-games-page'\nroute: '{{ depth == 1 ?= mount }}/{{ parent_uri }}/{{ slug }}'\n# example: /games/zork/how-to-play/controls\n```\n\n:::tip\nIf you're using Antlers in your route, you must use `{{ double curlies }}` when referencing variables.\n:::\n\n### Index route\n\nOnce you've set up a route for your entries (e.g. `/blog/{slug}`) you'll usually want an index page listing all your entries as well. It's important to know that Statamic **doesn't** create this for you automatically. You need to either:\n\n- Create an entry in another collection (typically a \"pages\" collection) that exists as `/blog` and [mount](#mounting) it to your blog collection.\n- Create a [custom route](/routing#statamic-routes) that exists at `/blog`.\n\n## Redirects\n\nAdding a `redirect` variable to one of your entries will cause an HTTP redirect when visiting its URL.\n\n``` yaml\n---\nid: page-book-tickets\ntitle: Book Ticket\nredirect: http://booking.mysite.com\n```\n\nA particularly useful example of when you might want to do this is if you need an external link in your nav but creating a completely separate nav would be overkill.\n\nThe following redirects are supported:\n- external links (starting with `http`)\n- internal links (starting with `/`)\n- other entries or terms (eg. `entry::id-of-entry` or `term::id-of-term`)\n- its first child page (`@child`) - If there are no child pages you will get a 404\n- a `404` response\n\nAny other strings will be assumed to be a relative link. For example: if the page URL is `/my/page` and you have `redirect: is/here` in your entry, you will be redirected to `/my/page/is/here`.\n\nBy default, Statamic will return a 302 status code when redirecting. To return a different status code, make the `redirect` an array with a `status` key:\n\n```yaml\nredirect:\n    url: http://booking.mysite.com\n    status: 301\n```\n\n:::tip\nEntries with redirects will get filtered out of the [collection](/tags/collection) tag by default. You can include them by adding a `redirects=\"true\"` parameter.\n:::\n\n### Entry link blueprint\n\nWhen a Collection is structured and you have set `Entries in this collection may contain links (redirects) to other entries or URLs.` on in the collection settings, you will be presented with the option to create \"Links\" along with any other available blueprints when you try to create an entry.\n\nThis will load a behind-the-scenes blueprint containing `title` and `redirect` fields. You are free to modify what's shown on these pages by creating your own `entry_link` blueprint.\n\n## Taxonomies\n\n[Taxonomies](/taxonomies) are defined on the _collection level_, not the blueprint-level. This enforces a tighter content-model, and reduces complexity when configuring blueprints.\n\nLet's imagine you have a **product** collection. Each entry is a product, and each product _has one or more_ categories. Thus set, no matter what blueprints you configure, each will have a **categories** field in the sidebar. You'll be able to access any categories on your entries with a `{{ categories }}` variable loop.\n\n### Taxonomies setting\n\n``` yaml\ntaxonomies:\n  - categories\n  - tags\n```\n\n## Mounting\n\nYou may \"mount\" a collection onto an entry in your collection config as a way of saying \"all these entries belong to this section\". When you do this, two neat things happen:\n\n- The collection's entries will become subpages of the entry. E.g. `/blog/that-one-time-at-dev-camp`\n- If the entry is in a structured collection with a nav tree, you will see shortcut links to **add or edit** entries in that collection, like the Blog page in the screenshot below.\n\n<figure>\n    <img src=\"/img/mounted-collection.webp\" alt=\"Mounted collections in a structure\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/mounted-collection-dark.webp\" alt=\"Mounted collections in a structure\" class=\"u-hide-in-light-mode\">\n    <figcaption>Look at those add and edit links!</figcaption>\n</figure>\n\n\n## Search indexes\n\nYou can configure search indexes for your collections to improve the efficiency and relevancy of your users' searches. Learn [how to connect indexes](/frontend/search#connecting-indexes).\n\n## Revisions\n\nRevisions allow you to see the history of any given entry over time. Revisions need to be enabled on the site level ([read those docs](/revisions)), and then you can enable them for any collection in your collection config.\n\n\n## Labels\n\nThroughout the control panel you may find buttons that say \"Create Entry\".\n\nIf you would rather them say something more specific (for example, \"Create Article\"), you may customize them per-collection by adding a translation key.\n\nIn `lang/en/messages.php`, you can add `{handle}_collection_create_entry` with the appropriate label.\n\n```php\n<?php\nreturn [\n    'articles_collection_create_entry' => 'Create Article',\n];\n```\n\nYou may add the same key to `messages.php` in other language directories as necessary.\n\n## Localization\n\nWhen running a [multi-site](/multi-site) installation, you can have entries exist in multiple sites with different content, or have entries exclusive to a site.\n\n[Read about localizing entries](/tips/localizing-entries)\n"
  },
  {
    "path": "content/collections/pages/command-palette.md",
    "content": "---\nid: 3482755d-3d20-42d5-8680-301a1cb95965\ntitle: Command Palette\n---\n\nThe command palette provides handy access to many pages and actions in the Control Panel without having to leave your keyboard.\n\n<figure>\n    <img src=\"/img/command-palette.webp\" alt=\"Command Palette\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/command-palette-dark.webp\" alt=\"Command Palette\" class=\"u-hide-in-light-mode\">\n    <figcaption>Make friends with the `⌘K` shortcut 😎</figcaption>\n</figure>\n\nOut of the box, it provides things like:\n\n- Content search\n- Control panel navigation\n- Recently visited pages\n- Intelligent page-specific and contextually relevant actions\n- Links to relevant documentation\n- Access to user preferences, light/dark mode, etc.\n\n\n## Extending the Command Palette\n\nIf you're [extending the CP nav](/extending/cp-navigation), the command palette will automagically populate itself with those nav items 🎉\n\nHowever, you may find yourself in a situation where you need to add more custom items to the command palette. You can do this in a variety of ways...\n\n### PHP\n\nIf you wish to add basic links from PHP, simply add the following to a service provider, or to a controller for page-specific links:\n\n```php\nuse Statamic\\Facades\\CommandPalette;\n\nCommandPalette::add(\n    text: 'Staff Calendar'\n    url: '/custom-laravel-route',\n    icon: 'calendar',\n);\n```\n\n#### Advanced Link Example\n\nYou can also pass an array to `text` if you want to use the same arrow conventions Statamic uses throughout the command palette (ie. `Search » Ancient » Hotbot`).\n\nOr maybe you want to configure whether to `openNewTab`, or even disable `trackRecent` to prevent it from showing up in recent items, etc.\n\n```php\nuse Statamic\\Facades\\CommandPalette;\n\nCommandPalette::add(\n    text: ['Search', 'Ancient', 'Hotbot'],\n    url: 'https://hotbot.com',\n    icon: 'sexy-robot',\n    openNewTab: true,\n    trackRecent: false,\n);\n```\n\n### JavaScript\n\nJavaScript can also be a great place to add page-specific links, or even contextually relevant actions that might require JS logic.\n\nParameter-wise, the JS API mostly mirrors the parameter set of the PHP API, with a few key differences and additions:\n\n1. The `add()` method is available via the global `Statamic.$commandPalette` helper:\n\n    ```js\n    Statamic.$commandPalette.add({\n        text: ['Search', 'Ancient', 'Hotbot'],\n        url: 'https://hotbot.com',\n        icon: 'sexy-robot',\n        openNewTab: true,\n    });\n    ```\n\n2. The JS API allows you to specify custom `action` and `when` functions, for controlling `when` the item is visible in realtime, or for running custom JS `action` logic on selection:\n\n    ```js\n    Statamic.$commandPalette.add({\n        text: 'Celebrate',\n        icon: 'star',\n        when: () => entryIsPublished(),\n        action: () => throwConfetti(),\n    });\n    ```\n\n3. The JS API gives you a bit more control over where the item is displayed in the command palette.\n\n    For example, though items are always fuzzy-searchable, they are normally rendered further down in the `Miscellaneous` section of the command palette. You can increase visibility by moving them to top section of the command palette by putting them into the `Actions` category:\n\n    ```js\n    Statamic.$commandPalette.add({\n        // ...\n        category: Statamic.$commandPalette.category.Actions,\n    });\n    ```\n\n    On busier pages, you can also `prioritize` primary callout style actions to the very very top, since they normally default to alphabetical order within the section:\n\n    ```js\n    Statamic.$commandPalette.add({\n        // ...\n        prioritize: true,\n    });\n    ```\n\n4. The `trackRecent` option for `url` based link items defaults to `false` on the JS side, because things tend to be more context-specific on the JS side. Of course, you can override this:\n\n    ```js\n    Statamic.$commandPalette.add({\n        // ...\n        trackRecent: true,\n    });\n    ```\n\n### Template Component\n\nSometimes you'll find yourself in a situation where you want to use the JS API to wire up a simple link or button in your template to your command palette, and you don't want to have to extract out to a JS component to do so.\n\nFor these situations, you may use the `<ui-command-palette-item>` component, which is a Vue component that wraps the [JS API](#javascript):\n\n```html\n<ui-command-palette-item\n    text=\"Hotbot\"\n    url=\"https://hotbot.com\"\n    open-new-tab\n>\n    <a href=\"https://hotbot.com\" target=\"_blank\">Hotbot</a>\n</ui-command-palette-item>\n```\n\nIf you want to dry up duplication, you may also use the `v-slot` to reuse things like `text`, `icon`, `url`, etc.\n\n```html\n<ui-command-palette-item\n    text=\"{{ __('Hotbot') }}\"\n    url=\"https://hotbot.com\"\n    icon=\"sexy-robot\"\n    open-new-tab\n    v-slot=\"{ text, url, icon }\"\n>\n    <ui-button :text=\"text\" :href=\"url\" :icon=\"icon\"></ui-button>\n</ui-command-palette-item>\n```\n"
  },
  {
    "path": "content/collections/pages/computed-values.md",
    "content": "---\ntitle: 'Computed Values'\nblueprint: page\nintro: 'Define dynamic values on your data and display them as virtual fields in the Control Panel. They''re like accessors on Eloquent models.'\nid: 0327afd5-469b-4119-a75e-2bfe9389eb05\n---\n## Overview\n\nThink of computed values as virtual fields that can be composed from any source. You could be grabbing a value from a secondary local database, a 3rd party API, or even by composing a dynamic value from other fields on the entry itself.\n\n## Setting computed values\n\nInside a service provider's `boot` method, you can configure dynamic computed field data on [Collections](/collections) and [Users](/users) using the provided `computed()` helper on the relevant Facade.\n\n### On user instances\n\nFor example, maybe you wish to return a user's `balance` using a 3rd party invoicing API:\n\n```php\nuse Statamic\\Facades\\User;\n\nUser::computed('balance', function ($user, $value) {\n    return InvoicingService::balance($user->email());\n});\n```\n\n### On entry instances\n\nOr maybe you wish to return a `shares` count on entries within your `articles` collection using a 3rd party social media API:\n\n```php\nuse Statamic\\Facades\\Collection;\n\nCollection::computed('articles', 'shares', function ($entry, $value) {\n    return TooterService::shareCount($entry->permalink);\n});\n```\n\nIf you want to use the same computed value across multiple collections, you may provide an array of collections instead:\n\n```php\nuse Statamic\\Facades\\Collection;\n\nCollection::computed(['articles', 'pages'], 'shares', function ($entry, $value) {\n    return TooterService::shareCount($entry->permalink);\n});\n```\n\nYou can also provide multiple computed values for the same collection using an associative array:\n\n```php\nCollection::computed('articles', [\n    'shares' => function ($entry, $value) {\n        return TooterService::shareCount($entry->permalink);\n    },\n    'likes' => function ($entry, $value) {\n        return TooterService::likeCount($entry->permalink);\n    },\n]);\n```\n\n### Overriding using stored values\n\nThe second `$value` parameter in the `computed()` callback function will return a _stored_ value under the same handle, if one exists, allowing you to override computed values if necessary.\n\nFor example, maybe you wish to display an article's `subtitle` if one is saved on the entry, otherwise fall back to a truncated version of the entry's `description` value:\n\n```php\nuse Statamic\\Facades\\Collection;\nuse Statamic\\Support\\Str;\n\nCollection::computed('articles', 'subtitle', function ($entry, $value) {\n    return $value ?? Str::limit($entry->value('description'), 25);\n});\n```\n\n### Performance\n\nIf you plan on accessing data through a 3rd party API, or even computing values across large data sets locally, it may be beneficial to cache your data.\n\n:::tip\nYou can use Laravel's [Cache](https://laravel.com/docs/cache#cache-usage) facade to store and retrieve cached values within your computed callback function.\n:::\n\n## Getting computed values\n\nOnce configured, you can simply access your computed values as properties on your instances (ie. `$user->balance` or `$entry->shares`).\n\n:::tip\nComputed values are only available for **top-level** fields. You can't use them inside Replicator or Grid fields. Likewise, computed values can't be queried as they're only evaluated after the query has been executed.\n:::\n\n### Showing computed values in the control panel\n\nOr view your computed values in the control panel if you configure your blueprint to allow for it. The first step is to add a field with your computed value's `handle`:\n\n<figure>\n    <img src=\"/img/computed-field-handle.webp\" alt=\"Computed field handle\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/computed-field-handle-dark.webp\" alt=\"Computed field handle\" class=\"u-hide-in-light-mode\">\n</figure>\n\nNext, set your field `Visibility` to `Computed`. This will ensure your field is displayed on your Publish Form as a read-only field [that will not store any data on save](/fields#field-data-flow):\n\n<figure>\n    <img src=\"/img/computed-field-visibility.webp\" alt=\"Computed field visibility config\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/computed-field-visibility-dark.webp\" alt=\"Computed field visibility config\" class=\"u-hide-in-light-mode\">\n</figure>\n\nYou may also show this field as a column on your listings using the `Listable` setting, as shown above:\n\n<figure>\n    <img src=\"/img/computed-field-listing.webp\" alt=\"Computed field visibility config\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/computed-field-listing-dark.webp\" alt=\"Computed field visibility config\" class=\"u-hide-in-light-mode\">\n    <figcaption>One of us didn't win anything, but does he need the money anyway?</figcaption>\n</figure>\n\n## Computed default values\n\nSometimes you want a field's default value to be dynamic — pulled from a config file, an addon setting, the current user, or any other runtime source. You can register a **computed default** closure and reference it from any field's `default` config.\n\nRegister the callback inside a service provider's `boot` method using the `Field` facade:\n\n```php\nuse Statamic\\Facades\\Field;\n\nField::computedDefault('default_timezone', function () {\n    return config('app.timezone');\n});\n```\n\nThen reference it from your blueprint or fieldset using the `computed:` prefix followed by the key you registered:\n\n```yaml\nfields:\n  -\n    handle: timezone\n    field:\n      type: text\n      default: 'computed:default_timezone' # [tl!**]\n```\n\nThe closure will be resolved each time a new entry is created, so the default stays fresh. Stored values on existing entries are untouched.\n\n:::tip\nComputed defaults are great for addon authors — register a default that reads from your addon's settings so users see a sensible initial value without hardcoding it into every blueprint.\n:::\n"
  },
  {
    "path": "content/collections/pages/conditional-fields.md",
    "content": "---\ntitle: 'Conditional Fields'\nintro: Show and hide fields in your publish forms based on conditions and triggers. For example, you may only want to show a caption field if an asset field has an image selected, or a whole block of fields if a toggle switch is enabled.\ntemplate: page\nid: dd52c1f6-661b-4408-83c6-691fa341aaa7\nblueprint: page\nrelated_entries:\n  - aa96fcf1-510c-404b-9b63-cea8942e1bf8\n  - 54548616-fd6d-44a3-a379-bdf71c492c63\n---\n## Overview\n\nField conditions are set on individual field settings in [blueprints](/blueprints). For example, you could create a `meta_description` field that is only shown and submitted when the `content` field is longer than 140 characters.\n\n<figure>\n    <img src=\"/img/field-conditions.webp\" alt=\"Statamic conditional field rule builder\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/field-conditions-dark.webp\" alt=\"Statamic conditional field rule builder\" class=\"u-hide-in-light-mode\">\n    <figcaption>The conditional field rule builder</figcaption>\n</figure>\n\nYou may specify various rules for showing a field under either the `if` / `show_when` keys, or hiding a field under the `unless` / `hide_when` keys.\n\n### Data flow\n\nOnly visible fields are submitted with your form data. This allows you to control data flow, and [conditionally apply validation](#validation) to visible fields when needed.\n\nIf you require conditionally hidden fields to be saved with your data, you may use the `always_save` config option (read more about [field data flow](/fields#field-data-flow)).\n\n:::tip\nIf you want to cosmetically hide a larger set of fields to get them out of the user's way, you can use the [Revealer](/fieldtypes/revealer) fieldtype to hide them until the user needs them without disrupting data flow on form submission.\n:::\n\n## Boolean\n\nA simple example might be to show a field when a toggle is set to 'on':\n\n```yaml\n-\n  handle: has_author\n  field:\n    type: toggle\n-\n  handle: author\n  field:\n    type: text\n    if:\n      has_author: true\n```\n\n## Empty\n\nIf you need to show a field based on whether another field is empty or not, you can use `empty` or `not empty`:\n\n```yaml\n-\n  handle: favorite_food\n  field:\n    type: text\n-\n  handle: second_favorite_food\n  field:\n    type: text\n    if:\n      favorite_food: not empty\n```\n\n## Equality\n\nMaybe you might wish to show various fields based on the value of a select field:\n\n```yaml\n-\n  handle: post_type\n  field:\n    type: select\n    options:\n      - text\n      - video\n-\n  handle: content\n  field:\n    type: text\n    if:\n      post_type: text\n-\n  handle: youtube_id\n  field:\n    type: text\n    if:\n      post_type: video\n```\n\n## Contains\n\nIf you are dealing with an array of options, you can conditionally show fields when an array contains specific value(s) using `contains` or `contains_any`:\n\n```yaml\n-\n  handle: favorite_foods\n  field:\n    type: checkboxes\n    options:\n      - pizza\n      - lasagna\n      - oatmeal\n-\n  handle: favorite_topping\n  field:\n    type: text\n    if:\n      favorite_foods: 'contains pizza'\n-\n  handle: favorite_italian_singer\n  field:\n    type: text\n    if:\n      favorite_foods: 'contains_any pizza, lasagna'\n```\n\nIf you are dealing with a string value, `contains` and `contains_any` will perform sub-string checks instead:\n\n```yaml\n-\n  handle: favorite_food\n  field:\n    type: text\n-\n  handle: favorite_topping\n  field:\n    type: text\n    if:\n      favorite_food: 'contains pizza'\n-\n  handle: favorite_italian_singer\n  field:\n    type: text\n    if:\n      favorite_food: 'contains_any pizza, lasagna'\n```\n\n### Taxonomy Terms\n\nWhen you want to compare against a taxonomy term, the `contains` term needs to include the taxonomy handle, like `taxonomy::slug`:\n\n```yaml\n-\n  handle: favorite_food\n  field:\n    type: text\n-\n  handle: food_groups\n  field:\n    type: terms\n    taxonomies:\n      - food_groups\n    display: Food Groups\n    mode: select\n-\n  handle: favorite_vegetables\n  field:\n    type: text\n    if:\n      favorite_food: 'contains food_group::vegetables'\n```\n\n## Advanced comparisons\n\nFor more advanced comparisons, several operators and right-hand-side literals/options are available to you.  For example, we could show an `email` field if age is greater than or equal to `16`:\n\n```yaml\n-\n  handle: age\n  field:\n    type: text\n-\n  handle: email\n  field:\n    type: text\n    if:\n      age: '>= 16'\n```\n\nAvailable operators include:\n\n| Operator | Description |\n| :--- | :--- |\n| `is` `equals` `==` | Loose equality comparison (inferred if no operator is used). |\n| `not` `isnt` `!=` | Loose inequality comparison. |\n| `===` | Strict equality comparison. |\n| `!==` | Strict inequality comparison. |\n| `>` | Greater than comparison. |\n| `>=` | Greater than or equal to comparison. |\n| `<` | Less than comparison. |\n| `<=` | Less than or equal to comparison. |\n| `contains` `includes` | Check if array contains a value, or if a string contains a sub-string value. |\n| `contains_any` `includes_any` | Check if array contains any of a comma-separated list of values, or if a string contains any of a comma-separated list of sub-strings values. |\n\nAvailable right-hand-side literals/options include:\n\n| Literal / Option | Description |\n| :--- | :--- |\n| `empty` | Will intelligently check if value is empty (ie. `null`, `''`, `[]`, or `{}`). |\n| `null` | Will be evaluated as a **literal** `null`. |\n| `true` | Will be evaluated as a **literal** `true`. |\n| `false` | Will be evaluated as a **literal** `false`. |\n\n## Multiple conditions\n\nIf you define multiple field conditions, all conditions need to pass for the field to be shown (or hidden if you use the `unless` / `hide_when` parent key).  For example, the following will show the field when `this_field` is `bacon` *__AND__* `that_field` is `cheeseburger`:\n\n```yaml\nif:\n  this_field: bacon\n  that_field: cheeseburger\n```\n\nIf you want to show a field when _any_ of the conditions pass, you can append `_any` onto the parent key.  For example, the following will show the field when `this_field` is `bacon` *__OR__* `that_field` is `cheeseburger`:\n\n```yaml\nif_any:\n  this_field: bacon\n  that_field: cheeseburger\n```\n\n## Nested fields\n\nYou may use dot notation to access nested values when necessary.  For example, maybe you would like to show a field when an `array` fieldtype's `country` value is `Canada`:\n\n```yaml\nif:\n  address.country: Canada\n```\n\n## Field context\n\nBy default, conditions are performed against values in the current level of `fields` in your blueprint.  If you need access to values outside of this context (eg. if you are in a replicator, trying to compare against fields outside of the replicator), you can access parent field values by prepending your field with `$parent`:\n\n```yaml\nif:\n  $parent.favorite_foods: includes bacon\n```\n\nYou can also access values at the top-level of your blueprint with `$root`:\n\n```yaml\nif:\n  $root.favorite_foods: includes bacon\n```\n\n## Extra values\n\nIn addition to field values, a handful of \"extra\" values are made available so you can write conditions against native data that isn't part of the blueprint.\n\n### Assets\n\nWhen editing an asset, the following values are available:\n\n| Value | Description |\n| :--- | :--- |\n| `filename` | The filename including extension (e.g. `beach.jpg`). |\n| `basename` | The filename without the extension (e.g. `beach`). |\n| `extension` | The file extension (e.g. `jpg`). |\n| `path` | The full path to the asset within its container. |\n| `mimeType` | The mime type (e.g. `image/jpeg`). |\n| `width` | The width in pixels, for assets with dimensions. |\n| `height` | The height in pixels, for assets with dimensions. |\n| `duration` | The duration in seconds, for audio and video. |\n\nFor example, to only show an `Autoplay` toggle on video assets shorter than one minute:\n\n```yaml\n-\n  handle: autoplay\n  field:\n    type: toggle\n    display: Autoplay\n    if:\n      extension: mp4\n      duration: '<= 60'\n```\n\n### Entries\n\nWhen editing an entry in a structured collection, the following value is available:\n\n| Value | Description |\n| :--- | :--- |\n| `depth` | The depth of the entry in the structure. Top-level entries are `1`. |\n\nFor example, to only show a field on entries nested deeper than two levels:\n\n```yaml\nif:\n  depth: '> 2'\n```\n\n## Custom logic\n\nIf you need something more complex than the YAML syntax provides, you may write your own logic.  In a [JS script](/extending/control-panel) or addon, you can define custom functions using the `$conditions` JS API:\n\n```yaml\nif:\n  quote: custom isCanadian\n```\n\n```javascript\nStatamic.$conditions.add('isCanadian', ({ target }) => {\n    return new RegExp('eh|bud|hoser').test(target);\n});\n```\n\n:::warning\nIt's worth noting that custom conditions only work in the Control Panel, not in the context of frontend forms.\n:::\n\n### Parameters\n\nYou may also pass parameters to your custom functions:\n\n```yaml\nif:\n  hero_video_url: 'custom isFiletype:mp4'\n  hero_image_url: 'custom isFiletype:jpg,png'\n```\n\n```javascript\nStatamic.$conditions.add('isFiletype', ({ target, params }) => {\n    return new RegExp(params.join('|') + '$').test(target);\n});\n```\n\n### Without Target\n\nIf you need to perform a condition against multiple hardcoded values, you can bypass setting a target field in the yaml by referencing your function name at the top of your `if` condition:\n\n```yaml\nif: reallyLovesFood\n```\n\n```javascript\nStatamic.$conditions.add('reallyLovesFood', ({ values }) => {\n    return (values.meals.length + values.desserts.length) > 10;\n});\n\n```\n\n### Field context\n\nFurthermore, if you need access to values outside of the current [field context](#field-context), we also provide a `root` values parameter, as well as access to the Publish Container object via `container`:\n\n```javascript\nStatamic.$conditions.add('...', ({ root, container }) => {\n    //\n});\n```\n\n## Validation\n\nIf you wish to conditionally apply validation to conditionally shown fields, we recommend using the `sometimes` [Laravel validation rule](https://laravel.com/docs/validation#validating-when-present).\n\n```yaml\n-\n  handle: online_event\n  field:\n    type: toggle\n-\n  handle: venue\n  field:\n    type: text\n    if:\n      online_event: false\n    validate:\n      - sometimes\n      - required\n```\n\nThe above example will only _sometimes_ apply the `required` rule to the `venue` field; Only when it exists in the submitted form data (see notes on [data flow](#data-flow) above).\n\n:::tip\nFor more advanced conditional validation, take a look at Laravel's `required_if`, `required_with`, etc. [validation rules](https://laravel.com/docs/validation#rule-required-if).\n:::\n\n## Templating\n\nYou can take advantage of Conditional Fields on your front-end Forms to automatically generate dynamic forms and logic. [Learn more about it](/tags/form-create#conditional-fields).\n"
  },
  {
    "path": "content/collections/pages/conditions.md",
    "content": "---\nid: 9751908a-a10c-4c36-abd3-2251e83fbc65\nblueprint: page\ntitle: 'Tag Conditions'\ntemplate: page\nintro: 'Conditions allow you to filter the results of your content tags (e.g. Collections, Taxonomies) using the data inside them, much like WHERE clauses do with SQL.'\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1632512130\n---\n:::tip\nAre you looking for \"if/else\" conditions? You probably want this page: [Antler's Logic & Conditions](/antlers#conditions)\n:::\n\n## Overview\n\nQuite often you'll find that you don't want to fetch _all_ entries from a collection, or _all_ terms from a taxonomy. Conditions give the ability to fetch only the content that meets the criteria of your choosing.\n\nFor example, you may want to list all entries that _aren't_ the one you're viewing, are after your birthday 🎂 but before Christmas 🎄, or have a custom field like `pinned` 📌 set to `true`. Piece of cake. Piece of crumb cake. 🥮\n\n_Note: These conditions currently apply to the [collections](/tags/collection), [taxonomy](/tags/taxonomy), and [users](/tags/users) tags._\n\n## Syntax\n\nThe conditions syntax has 3 parts: the field name, the condition name, and the value.\n\n<div class=\"c-syntax-explainer\">\n<span class=\"c-syntax-explainer__1\">{field_name}</span>:<span class=\"c-syntax-explainer__2\">{condition}</span><span>=</span>\"<span class=\"c-syntax-explainer__3\">{value}</span>\"\n</div>\n\n### Field name\nThe field name is the name of the field you're filtering on.\n\n\n\n### Using a variable reference\nIf you prefix the field name with a colon, it will use the value of a variable in your view\n\n```\n:author:is=\"author\"\n```\n\n\n### Multiple values\nYou can pass multiple values by separating them with a pipe.\n\n```\ntaxonomy:category=\"happy|radical\"\n```\n\n### Comparisons\n\nFor conditions where you're matching or comparing a value `is` or `starts_with`, you'd do:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:blog title:starts_with=\"Once upon a time...\" }}\n```\n::tab blade\n```blade\n<s:collection:blog\n  title:starts_with=\"Once upon a time...\"\n>\n\n</s:collection:blog>\n```\n::\n\n### Boolean\n\nFor boolean conditions like `exists` or `null`, specify a value of `true`:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:blog hero_image:exists=\"true\" }}\n```\n::tab blade\n```blade\n<s:collection:blog\n  hero_image:exists=\"true\"\n>\n\n</s:collection:blog>\n```\n::\n\nFor _negative_ boolean conditions, _don't_ use `=\"false\"`. Instead, pick the inverse condition, like `:exists` instead of `:doesnt_exist`.\n\n::tabs\n::tab antlers\n```antlers\n // Nope\n{{ collection:articles related_articles:exists=\"false\" }}\n\n// Yup\n{{ collection:articles related_articles:doesnt_exist=\"true\" }}\n```\n::tab blade\n```blade\n// Nope\n<s:collection:articles\n  related_articles:exists=\"false\"\n>\n</s:collection:articles>\n\n// Yup\n<s:collection:articles\n  related_articles:doesnt_exist=\"true\"\n>\n</s:collection:articles>\n```\n::\n\n### Multiple Conditions\n\nNeed multiple conditions? Yeah, we support that.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:drinks type:is=\"tiki\" ingredients:in=\"Orgeat\" }}\n    <a href=\"{{ url }}\">\n        {{ title }}\n    </a>\n{{ /collection:drinks }}\n```\n::tab blade\n```blade\n<s:collection:drinks\n  type:is=\"tiki\"\n  ingredients:in=\"Orgeat\"\n>\n  <a href=\"{{ $url }}\">\n    {{ $title }}\n  </a>\n<s:/collection:drinks>\n```\n::\n\n### Passing multiple values\n\nTo pass multiple _values_ in a condition, separate them with `|` pipes.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:drinks ingredients:in=\"rum|falernum\" }}\n    <a href=\"{{ url }}\">\n        {{ title }}\n    </a>\n{{ /collection:drinks }}\n```\n::tab blade\n```blade\n<s:collection:drinks\n  ingredients:in=\"rum|falernum\"\n>\n  <a href=\"{{ $url }}\">\n    {{ $title }}\n  </a>\n</s:collection:drinks>\n```\n::\n\n### Sub fields\n\nYou can apply conditions to \"sub fields\", like date ranges:\n\n```yaml\nevent_date:\n  start: 2023-12-01\n  end: 2023-12-03\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:events :event_date.start=\"today\" }}\n{{ collection:events :event_date.end=\"today\" }}\n```\n::tab blade\n```blade\n<s:collection:events\n  :event_date.start=\"$today\"\n>\n</s:collection:events>\n\n<s:collection:events\n  :event_date.end=\"$today\"\n>\n</s:collection:events>\n```\n::\n\n## String conditions\n\nThe following conditions apply to fields with data stored as strings.\n\n| Condition | Description |\n| :--- | :--- |\n| `is` / `equals` | Include if field **is equal** to value. |\n| `not` / `isnt` | Include if field is **not equal** to value. |\n| `exists` / `isset` | Include if field **exists**. |\n| `doesnt_exist` / `is_empty` / `null` | Include if field **doesn't exist**. |\n| `contains` | Include if field **contains** value. |\n| `doesnt_contain` | Include if field **doesn't contain** value. |\n| `in` | Include if field value is **in** the provided array. |\n| `not_in` | Include if field value is **not in** the provided array. |\n| `starts_with` | Include if field **starts with** value. |\n| `doesnt_start_with` | Include if field **doesn't start** with value. |\n| `ends_with` | Include if field **ends with** value. |\n| `doesnt_end_with` | Include if field **doesn't end with** value. |\n| `gt` | Include if field is **greater than** value. |\n| `gte` | Include if field is **greater than or equal to** value. |\n| `lt` | Include if field is **less than** value. |\n| `lte` | Include if field is **less than or equal to** value. |\n| `matches` / `regex` | Include if field **matches** case insensitive regex. |\n| `doesnt_match` | Include if field **doesn't match** case insensitive regex. |\n| `is_alpha` | Include if field contains **only alphabetical characters**. |\n| `is_numeric` | Include if field contains **only numeric characters**. |\n| `is_alpha_numeric` | Include if field contains **only alphanumeric characters**. |\n| `is_url` | Include if field **is a valid URL**. |\n| `is_embeddable` | Include if field **is an embeddable video URL**. |\n| `is_email` | Include if field **is valid email address**. |\n| `is_after` | Include if field **is after** date. |\n| `is_before` | Include if field **is before** date. |\n| `is_numberwang` | Include if field **is numberwang**. |\n\n## Array conditions\n\nThe following conditions apply to fields with data stored as an array.\n\n| Condition | Description |\n| :--- | :--- |\n| `overlaps` | Include if any field value **matches** the provided array (has). |\n| `doesnt_overlap` | Include if **no** value **matches** the provided array (has not). |\n\n## Taxonomy conditions\n\n[Taxonomy](/taxonomies) conditions are a little bit different. They start with `taxonomy:`, followed by the taxonomy name, an optional modifier argument, and finally the term you're seeking.\n\n<div class=\"font-mono bg-grey-300 text-purple rounded inline-block p-2 mb-6 text-sm\"><span class=\"bg-pink text-white p-1 rounded-sm\">taxonomy</span>:<span class=\"bg-purple text-white p-1 rounded-sm\">{handle}</span>:<span class=\"bg-black text-white p-1 rounded-sm\">{modifier}</span><span class=\"p-1\">=</span>\"<span class=\"bg-teal text-white p-1 rounded-sm\">{term}</span>\"\n</div>\n\n### Query modifiers {#taxonomy-query-modifiers}\n\nYou may optionally control the behavior of the condition filter by passing the desired modifier into the tag method call. If you don't set a modifier, it will use `any` by default.\n\n#### Any (default) {#taxonomy-any}\n\nFetch all entries that have _any_ of one or more taxonomy terms.\n\n#### Not {#taxonomy-not}\n\nFetch all entries that _don't_ have one or more taxonomy terms.\n\n#### All {#taxonomy-all}\n\nFetch all entries that contain _each_ of one or more taxonomy terms.\n\n### Examples {#taxonomy-examples}\n\n::tabs\n\n::tab antlers\n```antlers\n<!-- Get all featured articles -->\n{{ collection:articles taxonomy:tags:any=\"featured\" }}\n{{ collection:articles taxonomy:tags=\"featured\" }} (shorthand)\n\n<!-- Get all but sports-related articles -->\n{{ collection:articles taxonomy:tags:not=\"sports\" }}\n\n<!-- Get all \"featured\" articles about gaming -->\n{{ collection:articles taxonomy:tags:all=\"gaming|featured\" }}\n```\n::tab blade\n```blade\n<!-- Get all featured articles -->\n<s:collection:articles taxonomy:tags:any=\"featured\">\n</s:collection:articles>\n\n<s:collection:articles taxonomy:tags=\"featured\"> (shorthand)\n</s:collection:articles>\n\n\n<!-- Get all but sports-related articles -->\n<s:collection:articles taxonomy:tags:not=\"sports\">\n</s:collection:articles>\n\n\n<!-- Get all \"featured\" articles about gaming -->\n<s:collection:articles taxonomy:tags:all=\"gaming|featured\">\n</s:collection:articles>\n\n```\n::\n\n## Arguments\n\n| Argument | Description |\n| :--- | :--- |\n| `{handle}` | Handle of the Taxonomy you wish to query. |\n| `{modifier}` | Control the behavior of the condition filtering. Available options: `all`, `not`, and `any`. Default: `any`.  |\n| `{term}` | Term(s) to query. You may pass multiple terms by separating them with `\\|` pipe delimiters. |\n\n## Snippets\n\nHere are some common and useful conditions snippets to grab on your next project.\n\n### Exclude the current entry\n\n```\n:id:not=\"id\"\n```\n\n### Entries with specific \"tags\"\n\nAssuming you have a taxonomy named \"Tags\"...\n```\ntaxonomy:tags=\"review|colorful\"\n```\n\n### Show draft (unpublished) entries\n\n```\nstatus:is=\"draft\"\n```\n\n\n### Published before a specific date\n\nLet's use Y2K as the example date.\n\n```\ndate:is_before=\"2000-01-01\"\n```\n"
  },
  {
    "path": "content/collections/pages/configuration.md",
    "content": "---\ntitle: Configuration\nintro: Statamic uses standard Laravel config files and environment variables for application-level settings.\ntemplate: page\nblueprint: page\nid: 10d236ff-a80b-4d88-afa8-fe882b0f37a2\n---\n## Config files\n\nStatamic's config files are located in `config/statamic/`. They are PHP files named by area of responsibility.\n\n``` files theme:serendipity-light\nconfig/statamic/\n    antlers.php\n    api.php\n    assets.php\n    autosave.php\n    cp.php\n    editions.php\n    forms.php\n    git.php\n    graphql.php\n    live_preview.php\n    markdown.php\n    oauth.php\n    protect.php\n    revisions.php\n    routes.php\n    search.php\n    stache.php\n    static_caching.php\n    system.php\n    templates.php\n    users.php\n    webauthn.php\n```\n\n## Environment variables\n\nIt is often helpful to have different configuration settings based on the environment where the site is running. For example, you may wish to enable debug mode on your local server but not your production server\n\n:::warning\n**Never enable Debug Mode or DebugBar on production.** The error messages — as beautiful as they are — will reveal much about the way your site is configured, where important files are, and possibly even leak data from your `.env` file depending on how you use those variables.\n:::\n\nIn a fresh Statamic installation you'll find an `.env.example` file in the root directory of your site. Rename or copy it to `.env` to enable it. If you install Statamic via Composer or the [CLI tool](https://github.com/statamic/cli), this will be done automatically for you.\n\n### Environment variable types\n\nVariables in your `.env` files are parsed as strings. In order to handle a wider range of types, some specific values are reserved.\n\n| `.env` &nbsp; Value | Parsed Value |\n|--------------|--------------|\n| `true` | `(bool) true` |\n| `(true)` | `(bool) true` |\n| `false` | `(bool) false` |\n| `(false)` | `(bool) false` |\n| `empty` | `(string) ''` |\n| `(empty)` | `(string) ''` |\n| `null` | `(null) null` |\n| `(null)` | `(null) null` |\n\nIf you need to define an environment variable with a value containing a space, you may do so by enclosing the value in double quotes.\n\n``` env\nAPP_NAME=\"Gluten Free Potato Canons\"\n```\n\n### Retrieving environment variables\n\nAll environment variables are available in your config files by using the `env()` helper function. An optional second argument allows you to pass a default value.\n\n``` php\n// config/app.php\n'awesome' => env('ENABLE_AWESOME', true),\n```\n\n::tabs\n\n::tab antlers\n\nOnce passed into a config file, the variable can be used in your views with the `{{ config }}` tag.\n\n``` antlers\n// To retrieve the above 'awesome' value...\n{{ config:app:awesome }}\n```\n\n::tab blade\n\nOnce passed into a config file, the variable can be used in your views with the `config()` helper function.\n\n```blade\n// To retrieve the above 'awesome' value...\n{{ config('app.awesome') }}\n```\n::\n\n:::warning\n**Your `.env` file should never be committed to version control**.\n\nEach developer or server running your application may require a different configuration, not to mention it can be a security risk in the event your version control repository is ever made public. Any sensitive credentials — like API keys and secret tokens — would be visible.\n:::\n\n### Hiding environment variables from debug pages\n\nWhen an exception is uncaught and the `APP_DEBUG` environment variable is `true`, the debug page will show all environment variables and their contents. You may obscure variables by updating the `debug_blacklist` option in your `config/app.php` config file.\n\n``` php\nreturn [\n    // ...\n\n    'debug_blacklist' => [\n        '_ENV' => [\n            'APP_KEY',\n            'MAILCHIMP_API_KEY',\n            'BITCOIN_WALLET_PW',\n        ],\n\n        '_SERVER' => [\n            'APP_KEY',\n            'DB_PASSWORD',\n        ],\n\n        '_POST' => [\n            'password',\n        ],\n    ],\n];\n```\n\n\nLearn more about [environment configuration](https://laravel.com/docs/configuration#environment-configuration) in the Laravel docs.\n"
  },
  {
    "path": "content/collections/pages/content-managers-guide.md",
    "content": "---\nid: e947dc19-9a8a-44c2-911c-171d1f196c91\nblueprint: page\ntitle: 'Content Manager’s Guide to Statamic'\nnav_title: 'Content Manager’s Guide'\nbreadcrumb_title: \"Content Manager's Guide\"\nintro: |-\n  So you've got yourself a brand new Statamic site, inherited an older one, or joined a team that's already using Statamic. Great! Welcome to the wonderful world of Statamic. Perhaps you're wondering what to do next, how to add a tracking code to a landing page, get technical support, or reset your password and get back into the Control Panel.\n\n  You've come to the right place. We'll try to answer the most common end-user questions and topics people have when encountering Statamic for the first time.\n---\n## Four things you need to know.\n\n### We can’t get into your site.\n\nThis is important information, so please read this whole section. All Statamic sites are **self-hosted**. This means that each Statamic site is a unique _copy_ of the Statamic application, and is running on a server that you, your company, or web design/development agency owns or has access to.\n\nAnd since your Statamic site is running on a server that isn't ours, it means we **do not have access to it**. We can't sign in to your control panel and reset a password for you, give a user more permissions or access, make changes to the site, read the code, or kick out annoying users with bad grammar.\n\nIt might seem like that's a big downside if you're running into a problem that you feel our support team should be able to fix from our floating cloud desks — and maybe in a few cases that might be true — but overall this is a very good thing for you and your organization. Nothing we can do can take your site away. **Your site is yours forever**.\n\nToo many people have put their hard work and money building websites, blogs, and companies futures on platforms that end up getting shut down, bought out, or change their mind about who their audience should be — resulting in prices skyrocketing, or sites just disappearing forever with no way to get data out. Things change so fast in the tech world — your site **should not** be subject to these external (and often desperate) forces. Own your site. Own your content. Own your audience.\n\n::: Did you know?\nServers are just computers that run websites and applications accessible to the internet. And **\"the cloud\"** is just another term for \"a bunch of servers out there somewhere\". They might as well be floating around in the sky as far as most people are concerned or care.\n:::\n\n### Every site is unique.\n\nEach Statamic site starts like a bit of a blank slate. We take a \"start simple and add things as needed\" approach to features and settings, in contrast to other platforms that take a \"everything is included and rip out what you don't want\" approach.\n\nThis means that Statamic doesn't do everything automatically, and generally requires a developer to enable, configure, or hook up different features you might assume are part of every site. A few examples of this in practice: blog posts don't automatically have \"tags\" or \"categories\" — in fact there is no  \"blog\" by default, and there is no built-in \"snippet manager\" to paste analytics or tracking codes into.\n\nEach of these things can be \"built\" in a matter of minutes (or even seconds) with Statamic's building-block approach to custom fields and features.\n\nThis might feel like it's backwards if you're used to working in a platform like WordPress or Drupal, but we have found (and our customers agree emphatically!) that it's much better in the long run to _turn on_ the things you need, enable features you plan to use, and name things _the way you want for clarity_, than to spend precious time clicking about the control panel disabling all the things you'll never need, or worse — just leaving behind features, buttons, or sections that simply don't do anything.\n\nYou may find that your site is missing the ability to edit something you consider to be fundamental in a content management system. It might frustrate you. You might feel stuck. But before reaching for WordPress to rebuild your whole site from scratch (please don't do that), just have a quick chat with whoever built your site, because...\n\n### Statamic sites are _very_ easy to change.\n\n**This is Statamic's secret power.** This is why developers use Statamic over the ubiquitous WordPress. **Do not be afraid** to ask the developer or team who built your Statamic site to make a change, make some bit of content editable, or add a new feature. Most often, it will be a very small amount of work.\n\n<blockquote class=\"font-medium pl-8 border-l-4 border-black italic mt-8\"><p>God, do I love working with Statamic. It feels like implementing anything only takes a fraction of the time compared to other content management systems...</p><p>&mdash; Martin Keck, Developer</p></blockquote>\n\n\n### Every Statamic site needs a developer.\n\nBecause of these three facts, it's important to note that every Statamic site needs — at least at one point or another — a web developer. Someone who can write some code and get it up on a server.\n\nStatamic is pretty easy to learn for anyone who knows HTML and has run other PHP applications (like WordPress, for example) before. But if this isn't your skill set, it's okay. Just know that most people who build websites have the skills necessary to work on a Statamic site (assuming they're willing to read some of this documentation).\n\nOnce your site is set up and launched, it's possible you may not need another developer again, or at least for a long time. But, in order to run updates to make sure you're running the fastest and most secure version of Statamic, you'll need that developer every now and then to do that.\n\nStatamic updates aren't a \"click the button and wait\" kind of process. There are too many potential problems with that approach to web software, and in order to protect you and your website from going down, all Statamic updates must be run from the command line — a level of access a regular user doesn't have.\n\n## FAQs\n\n### Where do I login to my Statamic site's Control Panel?\n\n<mark>Most sites have their login screen set up on `example.com/cp`</mark> (where example.com is the URL of your website). This URL is customizable, and some people like to change it to `example.com/admin` or something else entirely for security purposes.\n\nSomeone with access to your site's Statamic Control Panel will need to create your account and invite you first. As part of that process you will _most likely_ have received an email with a link to activate your account. This link will get you to your Control Panel.\n\nWe say _most likely_ because these defaults can be changed. Instead of an email, they could have sent you a link into Slack or Teams, etc.\n\n:::tip\nYour [statamic.com](https://statamic.com) account (if you have one) has nothing to do with the login for your site's control panel. It's just used to buy licenses, addons, and request support. This is because [we can't get into your site](#we-cant-get-into-your-site).\n:::\n\n### How do I reset my password?\n\nNo worries, it happens to the best of us! Assuming you know your login URL (see [Where do I login to my Statamic site's Control Panel?](#where-do-i-login-to-my-statamic-sites-control-panel)), you should have a link to **Reset Your Password** right there on that screen.\n\n<figure>\n    <img src=\"/img/password-reset-link.png\" width=\"423\" alt=\"Statamic Password Reset Link\">\n    <figcaption>👆 See that Forgot password link?</figcaption>\n</figure>\n\nAfter clicking that link and entering your email address, you should receive an email with a link to reset your password and get back into the Control Panel.\n\nIf you don't receive that email, it's possible that whoever built your site (your developer) didn't set up email sending for your site and/or server. In this circumstance, you have three options.\n\n1. Ask your developer to finish setting up your site properly and/or [reset your password manually](/tips/manually-resetting-a-user-password) for you. **This is the best option** because it prevents this problem from happening again.\n2. Ask someone else with permissions to manage users in Control Panel to reset your password for you. They can do that by going to the Users section, then opening the dropdown next to your account and clicking \"Copy Password Reset Link\" and sending that to you in your preferred communication tool of choice.\n\n3. Ask someone else with permissions to manage users in Control Panel to simply change your password for you. They can do that by going to the Users section, then clicking on your user account, and then the \"Change Password\" button next to your name, and following the form that pops up.\n\n### What do I do if my site is broken?\n\nThere are a few reasons a site might \"randomly\" break, but in almost all of those cases you'll need to contact your developer to fix your site and bring it back online. They _should_ know to check the error logs to see what happened, but feel free to suggest that to them. Also mention the exact error code, if there is one, that you see when visiting your site. A 404 error is very different from a 501, for example.\n\nWe'll explain a few of the most common (though this kind of thing is anything but common) scenarios when this might happen.\n\n#### DNS settings are wrong\nIt's possible that there's nothing wrong with your site, but rather with your DNS settings. DNS settings are usually managed with your Domain Registrar — the place you bought your .com, .co.uk, or other domain name. These settings tell browsers where to go when visiting your domain.\n\nThis is most likely the problem if your site is showing the _wrong_ site, wrong page, or displays an error that mentions DNS, redirects, or something similar.\n\nIf someone has recently been making changes to your DNS (maybe setting up a subdomain or changing email providers), it's possible they changed the wrong thing and your DNS is no longer pointing at your server properly.\n\nIf this happens, contact your developer or whoever manages your DNS to have them undo whatever mess they made.\n\n#### Surprise server upgrade\nIt's possible your server had an upgrade that significantly changed the version of PHP or some other bit of software that your version of Statamic doesn't support. This is similar to when you upgrade a Mac or Windows computer — sometimes you need to upgrade your software to compatible with the latest versions of your operating system.\n\nThis is rare, but can happen with cheap \"shared hosting\" services, like GoDaddy, HostGator, BlueHost, and other similar companies that have crazy low prices. You get what you pay for with hosting.\n\nIf this happens, you'll need to have a developer update Statamic to the latest version, and if that doesn't fix it, open a support ticket with your host. And if that doesn't fix it, it might be time to get a better host.\n\n#### Server ran out of storage\nIf your website has lots and lots of images (or really big images) and files, it's possible your server may have run out of storage space while Statamic was creating all thumbnails and any other image sizes needed to render your design.\n\nIf this happens, you'll need to have your developer clear out any image caches and upgrade the storage capacity of your server so it doesn't happen again.\n\nAlternately, your developer can move your images to AWS (Amazon's file management service) or an equivalent, as it can be more cost effective than upgrading your server.\n\n#### Bad code\nNobody's perfect. It's possible your developer had some custom code in your site that has a performance problem and uses up all your memory, thereby crashing the server — or something like that.\n\nIf this happens, you guessed it — talk to your developer. They'll figure it out. And if they don't, [get a better developer](https://statamic.com/partners).\n\n### How do I find a new Statamic developer?\nIf a developer or agency is no longer available to work on your site, or you feel their quality of work is not up to your standards, it may be time to find a new Statamic developer.\n\nIf you start Googling \"where to hire a Statamic developer\" you _may_ not find a ton out there, at least when compared to WordPress developers, but let us assure you, there are lots of talented and professional Statamic folks out there.\n\nThe best place to find one is on our [Partners](https://statamic.com/partners) directory. There you can find a list of freelancers and agencies who have a proven track record of building Statamic sites and addons. Furthermore, the **Certified Partners** have gone through reference vetting and code reviews with our Core Team to make sure they're technically proficient and have good relationships with their clients. We highly recommend working with a Certified Partner.\n\nIf you'd like us to match you with a Partner, we'd be happy to do so. Head on over to our [Matchmaking Service](https://statamic.com/partners/matchmaking).\n\nYou could also post a job or project on [workwithstatamic.com](https://workwithstatamic.com), or jump into our live [Discord Chat](https://statamic.com/discord) and visit the `#jobs` channel to see who might be interested in helping you out."
  },
  {
    "path": "content/collections/pages/content-modeling.md",
    "content": "---\nid: 884507b0-77ac-469e-a99f-f646065d5954\ntitle: 'Content Modeling'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/content-queries.md",
    "content": "---\nid: e7833062-e05c-42c9-ad35-dc5077f1f0b8\nblueprint: page\ntitle: 'Content Queries'\nintro: 'Statamic provides a fluent query builder interacting with your content and data in PHP-land. If you think of them as Laravel Eloquent Models, you should feel right at home.'\n---\n## Overview\n\nEach of the core Statamic data types has its own Facade used to access an underlying repository class so you can query, create, modify, and delete content. Working with data in this manner is usually done in a [Controller](/controllers), with any retrieved data being passed into a view.\n\nThese methods will work no matter which driver you're using — flat files, Eloquent/MySQL, or any other custom repo driver.\n\n[Learn how Statamic can use different storage methods!](/extending/repositories)\n\n:::tip\nWhile Statamic's Query builder is very similar to [Laravel's Query Builder](https://laravel.com/docs/13.x/queries), they are **completely separate implementations**.\n\nWhat follows is complete documentation on all available methods. If you need a method available in Laravel that we don't currently support, feel free to open a [feature request](https://github.com/statamic/ideas) or better yet, a [Pull Request](https://github.com/statamic/cms)!\n:::\n\n## Retrieving data\n\nThere are two different types of classes you'll interact with while querying content: Repositories and Query Builders.\n\n### Repositories\n\nEach Facade interacts with a **repository**, which allows you to get data about the desired data type. For example, you can use the `Entry` Facade to get an entry, or the `GlobalSet` Facade to get all the variables inside of it.\n\n```php\nuse Statamic\\Facades\\Entry;\nuse Statamic\\Facades\\GlobalSet;\n\nEntry::find('abc123');\nGlobalSet::findByHandle('footer')->inDefaultSite()->get('copyright');\n```\n\n### Query builders\n\nSome Facades also have a **Query Builders** that allows you to query, filter, and narrow down the results you desire. The `Entry` Facade's Query Builder allows you to find all the entries in a collection, by a specific author, and so on.\n\n:::tip\nAll Query Builders are part of a Repository, but not all Repositories have a Query Builder. Just like how all donuts are desserts, but not all desserts are donuts. 🍩\n:::\n\n**Query Builders** allow you to assemble a query, chain additional constraints onto it, and then invoke the `get` method to get the results:\n\n```php\nuse Statamic\\Facades\\Entry;\n\n$entries = Entry::query()\n  ->where('collection', 'blog')\n  ->limit(5)\n  ->get();\n```\n\nThis would return a `Collection` of the items. In this particular example, you would have a `Collection` of `Entry` objects.\n\n### Examples {.popout}\n\n#### Getting a single record\n\nIf you only want to get a single record, you may use the `first` method. This method will return a single data object:\n\n```php\nEntry::query()\n    ->where('collection', 'blog')\n    ->first();\n```\n\n#### Getting specific fields\n\nThis method is really only helpful when using a database — it improves query speed by performing column `SELECT`s behind the scenes.\n\n```php\nEntry::query()\n    ->where('collection', 'blog')\n    ->get(['title', 'hero_image', 'content']);\n```\n\n## Basic where clauses\n\n### Where\nYou may use the query builder's `where` method to add \"where\" clauses to the query. The most basic call to the `where` method requires three arguments. The first argument is the name of the field. The second argument is an operator, which can be any of the supported operators. The third argument is the value to compare against the field's value.\n\nFor example, the following query gets entries where the value of a `status` field is `featured`.\n\n```php\nEntry::query()\n    ->where('status', '=', 'featured') // [tl! ~~]\n    ->get();\n```\n\nAs a shorthand for an \"equals\" query, you may pass the value as the second argument to the `where` method. Statamic will assume you would like to use the `=` operator:\n\n```php\nEntry::query()->where('status', 'featured')->get();\n```\n\nYou can chain where clauses, filtering records based on more than one condition with AND:\n\n```php\nEntry::query()\n    ->where('status', '=', 'featured')\n    ->where('status', '!=', 'sticky')\n    ->get();\n```\n\nThis same query can also be written using one where clause:\n\n```php\nEntry::query()\n    ->where([\n      ['status', '=', 'featured'],\n      ['status', '!=', 'sticky']\n    ])\n    ->get();\n```\n\nYou can query entries across multiple conditions using `orWhere()`:\n\n```php\nEntry::query()\n    ->where('status', '=', 'featured')\n    ->orWhere('status', '=', 'sticky') // [tl! ~~]\n    ->get();\n```\n\n### WhereBetween\nThe `whereBetween` method lets you verify that a field's value lies between two values that you pass:\n\n```php\nEntry::query()\n    ->whereBetween('numeric_field', [0, 1000]) // [tl! ~~]\n    ->get();\n```\n\nYou can also use the `whereNotBetween` method to verify that a field's value does not lie between two values that you pass:\n\n```php\nEntry::query()\n    ->whereNotBetween('numeric_field', [0, 1000]) // [tl! ~~]\n    ->get();\n```\n\nNote: `orWhereBetween` and `orWhereNotBetween` are also supported.\n\n\n### WhereColumn\nThe `whereColumn` method lets you compare a field's value to that of another field:\n\n```php\nEntry::query()\n    ->whereColumn('published', '=', 'status') // [tl! ~~]\n    ->get();\n```\n\nNote: `orWhereColumn` is also supported.\n\n\n### WhereDate\n\nThe `whereDate` method may be used to compare a column's value against a date:\n\n```php\n$users = Entry::query()->whereDate('created_at', '2016-12-31')->get();\n```\n\nThe `whereMonth` method may be used to compare a column's value against a specific month:\n\n```php\n$users = Entry::query()->whereMonth('created_at', '12')->get();\n```\n\nThe `whereDay` method may be used to compare a column's value against a specific day of the month:\n\n```php\n$users = Entry::query()->whereDay('created_at', '31')->get();\n```\n\nThe `whereYear` method may be used to compare a column's value against a specific year:\n\n```php\n$users = Entry::query()->whereYear('created_at', '2016')->get();\n```\n\nThe `whereTime` method may be used to compare a column's value against a specific time:\n\n```php\n$users = Entry::query()->whereTime('created_at', '=', '11:20:45')->get();\n```\n\n\n### WhereIn\nThe `whereIn` method lets you check a field against an a given array of values:\n\n```php\nEntry::query()\n    ->whereIn('status', ['featured', 'sticky', 'special']) // [tl! ~~]\n    ->get();\n```\n\nYou can also use the `whereNotIn` method to ensure a given field's value is not contained in a given array of values:\n\n```php\nEntry::query()\n    ->whereNotIn('status', ['draft', 'boring']) // [tl! ~~]\n    ->get();\n```\n\nNote: `orWhereIn` and `orWhereNotIn` are also both supported.\n\n\n### WhereNull\nThe `whereNull` method lets you check whether a field's value is null:\n\n```php\nEntry::query()\n    ->whereNull('published') // [tl! ~~]\n    ->get();\n```\n\nYou can also use the `whereNotNull` method to check if a field's value is not null:\n\n```php\nEntry::query()\n    ->whereNotNull('published') // [tl! ~~]\n    ->get();\n```\n\nNote: `orWhereNull` and `orWhereNotNull` are also both supported.\n\n\n\n## Complex where clauses\nComplex queries can be made by using closure-based wheres containing any of the [basic where clauses](#basic-where-clauses):\n\n```php\nEntry::query()\n    ->where(function ($query) {\n\t\t$query->where('status', 'featured')\n      \t\t->orWhere('status', 'sticky');\n    })\n    ->orWhere(function ($query) {\n\t\t$query->where('title', '!=', 'statamic')\n      \t\t->where('status', 'boring');\n    })  \n    ->get();\n```\n\n\n## Conditional clauses\nConditional clauses can be applied based on another condition, for example the value for an input on the HTTP request. \n\n```php\nEntry::query()\n    ->when($request->input('rad'), function ($query) {\n\t\t$query->where('status', 'featured')\n      \t\t->orWhere('status', 'sticky');\n    })\n    ->get();\n```\n\nYou can also pass a default value which will be applied when the condition fails:\n\n```php\nEntry::query()\n    ->when($request->input('rad'), function ($query) {\n\t\t$query->where('status', 'featured')\n      \t\t->orWhere('status', 'sticky');\n    }, function ($query) {\n\t\t$query->where('status', '!=', 'featured')\n      \t\t->where('status', '!=', 'sticky');\n    })\n    ->get();\n```\n\nIf you want to simply apply a clause when a value fails you can use `unless()`:\n\n```php\nEntry::query()\n    ->unless($request->input('rad'), function ($query) {\n\t\t$query->where('status', 'featured')\n      \t\t->orWhere('status', 'sticky');\n    })\n    ->get();\n```\n\n## JSON where clauses\nJSON values can be queries using the '->' selector:\n\n```php\nEntry::query()\n    ->where('my_field->sub_field', '!=', 'statamic') // [tl! ~~]\n    ->get();\n```\n\nYou can query JSON arrays using `whereJsonContains()`\n\n```php\nEntry::query()\n    ->whereJsonContains('my_array_field->sub_field', 'statamic') // [tl! ~~]\n    ->get();\n```\n\nOr can pass an array of values. This will match if **all** of the values are found in the field.\n\n```php\nEntry::query()\n    ->whereJsonContains('my_array_field->sub_field', ['statamic', 'is', 'rad']) // [tl! ~~]\n    ->get();\n```\n\nIf you want to check for **any** value being present, use `whereJsonOverlaps`.\n\n```php\nEntry::query()\n    ->whereJsonOverlaps('my_array_field->sub_field', ['statamic', 'is', 'rad']) // [tl! ~~]\n    ->get();\n```\n\nYou can use `whereJsonDoesntContain()` and `whereJsonDoesntOverlap()` to query the absence of a value or values in a JSON array:\n\n```php\nEntry::query()\n    ->whereJsonDoesntContain('my_array_field->sub_field', 'statamic') // [tl! ~~]\n    ->get();\n    \nEntry::query()\n    ->whereJsonDoesntOverlap('my_array_field->sub_field', 'statamic') // [tl! ~~]\n    ->get();\n```\n\nYou can use whereJsonLength method to query JSON arrays by their length:\n\n```php\nEntry::query()\n    ->whereJsonLength('my_array_field->sub_field', 1) // [tl! ~~]\n    ->get();\n```\n\n```php\nEntry::query()\n    ->whereJsonLength('my_array_field->sub_field', '>', 1) // [tl! ~~]\n    ->get();\n```\n\nNote: `orWhereJsonContains` and `orWhereJsonLength` are also both supported.\n\n\n\n## Querying relationships\n\nYou can query across [relationship fields](/fieldtypes/entries) (like `entries`, `terms`, and `users`) using `has`, `whereHas`, and `whereRelation` — mirroring Laravel's Eloquent relationship methods.\n\n:::tip\nRelationship querying is supported on the **Entry**, **Term**, and **User** query builders. The field passed as `$relation` must be a relationship field (`entries`, `terms`, or `users`) defined on a blueprint that belongs to the query builder.\n:::\n\n### whereHas\n\nUse `whereHas` to constrain the query to results where a relationship exists and its related records match additional conditions provided via a closure.\n\n```php\nEntry::query()\n    ->whereHas('related_posts', function ($query) {\n        $query->where('title', 'Post 2');\n    })\n    ->get();\n```\n\nWithout a closure, `whereHas` simply checks that the relationship is not empty:\n\n```php\nEntry::query()->whereHas('related_posts')->get();\n```\n\nNote: `orWhereHas`, `whereDoesntHave`, and `orWhereDoesntHave` are also supported.\n\n### whereRelation\n\n`whereRelation` is syntactic sugar for a `whereHas` with a single `where` clause against the related records:\n\n```php\nEntry::query()\n    ->whereRelation('related_posts', 'title', 'Post 2')\n    ->get();\n```\n\nIt accepts the same signature as `where` — an operator and value, or a closure for more complex logic. `orWhereRelation` is also supported.\n\n### has\n\nUse `has` to constrain the query to results where a relationship has any related records. Pair with `doesntHave` for the inverse.\n\n```php\nEntry::query()->has('related_posts')->get();\nEntry::query()->doesntHave('related_posts')->get();\n```\n\nNote: `orHas` and `orDoesntHave` are also supported.\n\n:::warning\nA couple of things to be aware of:\n\n- **Nested relations** (e.g. `author.posts`) are not supported and will throw an `InvalidArgumentException`.\n- **Counting with subqueries** (e.g. `whereHas('posts', fn ($q) => ..., '>=', 10)`) is not supported and will throw an `InvalidArgumentException`.\n:::\n\n\n## Operators\n\nThe following operators are available in [basic where clauses](#basic-where-clauses) when appropriate for a targeted field's datatype, just like SQL.\n\n| Operator | Description |\n| -------- | ----------- |\n| `=` | Equals |\n| `<>` or `!=` | Not Equals |\n| `like` | Like |\n| `not like` | Not Like |\n| `regexp` | Like Regex |\n| `not regexp` | Not Like Regex |\n| `>` | Greater Than |\n| `<` | Less Than |\n| `>=` | Greater Than Or Equal To |\n| `<=` | Less Than Or Equal To |\n\n\n### Like & Not Like\n\nThe `like` operator is used in `where` clause to search for a specified pattern in a field. `not like` is the inverse, ensuring that the results **do not** match a pattern.\n\nThere are two wildcards used in conjunction with the `like` operator:\n\n- The percent sign `%` represents zero, one, or multiple characters\n- The underscore sign `_` represents one, single character\n\n#### Examples {.popout}\n\n#### Get all Users with a gmail email address\n```php\nUser::query()\n    ->where('email', 'like', '%@gmail.com')\n    ->get();\n```\n\n#### Get all Entries where \"wip\" is not in the title\n```php\nEntry::query()\n    ->where('title', 'not like', '% wip %')\n    ->get();\n```\n\n#### Get all Assets with \"thumbnail\" in the filename.\n```php\nAsset::query()\n    ->where('filename', 'like', '%thumbnail%')\n    ->get();\n```\n\n#### Get all Users who are (probably) not doctors\n```php\nUser::query()\n    ->where('name', 'not like', ['Dr.%', '%MD', '%M.D.'])\n    ->get();\n```\n\n### Regex & Not Regex\n\nThe `regex` operator is used in `where` clause to search for records where a field matches a given regular expression, while `not regex` is the inverse — ensuring that results **do not** match a regular expression.\n\n:::tip\nInternally, this rule uses the PHP `preg_match` function. The pattern specified should obey the same formatting required by `preg_match` and therefore also include valid delimiters. For example: `'/^.+$/i'`.\n:::\n\n#### Examples {.popout}\n\n#### Find entries with Antlers expressions in content\n```php\nEntry::query()\n    ->where('content', 'regexp', '/{{/')\n    ->get();\n```\n\n#### Find all Star Trek movie subtitles but not Star Wars\n```php\nEntry::query()\n    ->where('collection', 'movies')\n    ->where('title', 'not regexp', '/m | [tn]|b/')\n    ->get();\n// [tl! collapse:start]\n// Okay, so this regex doesn't work on any of the Star Wars\n// movies after Rogue One but let's not split hairs here.\n// This is a good example and you know it.\n\n// If we can get enough support though we can submit a\n// petition to Disney to rename the last 3 Skywalker sequels\n// so we don't need to change our regex:\n\n// The Force Awakens -> Awakening of the Force\n// The Last Jedi -> Near Extinction of the Jedi\n// The Rise of Skywalker -> Ascent of the Walker in the Sky [tl! collapse:end]\n```\n\n### Greater Than & Less Than (Or Equal To)\n\nThe `greater than` operator is used to compare two values. If the first is greater than the second, the match will be included. The `greater than or equals` operator will include exact matches.\n\nThe `less than` operator is used to compare two values. If the first is less than the second, the match will be included. The `less than or equals` operator will include exact matches.\n\n#### Examples {.popout}\n\n#### Find all Users old enough to enjoy a dram of whisky in the U.S.\n\n```php\nUser::query()\n    ->where('age', '>=', 21)\n    ->get();\n```\n\n#### Find all Pre-Y2K news\n\n```php\nEntry::query()\n    ->where('collection', 'news')\n    ->where('date', '<', '2000')\n    ->get();\n```\n\n## Ordering, Limiting, & Offsetting\n\nThe `orderBy` method allows you to sort by a given field, or in random order:\n\n```php\nEntry::query()->orderBy('date', 'asc')->get();\nEntry::query()->orderByDesc('title')->get(); // the same as ->orderBy('title', 'desc')\nEntry::query()->inRandomOrder()->get();\n```\n\nYou may limit and/or skip results by using the `limit` and `offset` methods:\n\n```php\nEntry::query()->offset(5)->limit(5)->get();\n```\n\n## Count\n\nThe query builder also provides the `count` method for retrieving the number of records returned.\n\n```php\nEntry::query()->count();\n```\n\n## Paginating\n\nPaginate results by invoking the `paginate` method on a query instead of `get`, and specifying the desired number of results per page.\n\n```php\nEntry::query()->paginate(15);\n```\n\nThis will return an instance of `Illuminate\\Pagination\\LengthAwarePaginator` that you can use to assemble the pagination style of your choice.\n\n:::tip\nYou can [learn more about the LengthAwarePaginator](https://laravel.com/docs/13.x/pagination#paginator-instance-methods)in the Laravel docs.\n:::\n\n## Chunking\n\nBy chunking down the results of a query you receive a small chunk of results that you can each pass into a closure for further processing or manipulation.\n\nExpects both a `$count` and `$callback` argument.\n\n```php\nEntry::query()->chunk(25, function($entries) {\n    // do something with each chunk\n});\n```\n\n:::tip\nYou can [learn more about chunking query results](https://laravel.com/docs/13.x/queries#chunking-results) in the Laravel docs.\n:::\n\n## Lazy streaming\n\nLazily streaming query results allows you to define a number of results to be returned from the query, similar to [chunking](#chunking). The difference is that instead of being able to pass each chunk into a callback, you receive a `LazyCollection`. This can help in situations where you're working with large datasets while keeping the memory usage low.\n\nThe chunk size for the lazy query should be at least `1` and defaults to `1000`.\n\n```php\nEntry::query()->lazy(100)\n```\n\n:::tip\nYou can learn more about [lazily streaming query results](https://laravel.com/docs/13.x/queries#streaming-results-lazily) and [LazyCollections](https://laravel.com/docs/13.x/collections#lazy-collections) in the Laravel docs.\n:::\n\n## Repository classes\n\nHead to the [Repositories Reference](reference/repositories) area for the complete list of classes and methods.\n"
  },
  {
    "path": "content/collections/pages/contributing.md",
    "content": "---\nid: 55a99a3b-e40d-4033-9a70-823de8e4255f\nblueprint: page\ntitle: Contributing\noverview: |\n  This is a guideline for contributing to Statamic, its documentation, addons, and starter kits. All of these wonderful things are hosted here in the [Statamic organization](https://github.com/statamic) on GitHub. We welcome your feedback, proposed changes, and updates to these guidelines. We will always welcome thoughtful issues and consider pull requests.\n---\n✨Thank you for taking the time to consider a contribution!✨\n\n## What you should know before contributing\n\n### Statamic isn’t FOSS\n\nStatamic is not \"Free Open Source Software\". It is **proprietary** open source software. The code is open, you can use it for free, but there are limitations to how you can modify or redistribute it. Everything in this and our other repos on Github — including community-contributed code — is the property of Statamic. Here are the limitations:\n\n- You **cannot** redistribute or use Statamic as a dependency in another distributable project — open source or otherwise — without prior permission or licensing. You **can** use it in your **own** commercial or personal projects.\n- You **cannot** alter code or behavior related to licensing, updating, version/edition checking, or anything else that would circumvent the enforcement of our Statamic Pro business model. We want to stay in business so we can support _you_ better.\n- You **cannot** publicly maintain a long-term fork of Statamic. You **can** maintain a private one for your own needs, if you have them.\n\n### How to get support\n\nFor official developer support (and you own an active license), please go to [statamic.com/support](https://statamic.com/support) and we will always do our best to reply in a timely manner. **Github issues are intended for reporting and resolving bugs.**\n\nYou can chat and collaborate with other developers in the community — [Discord](https://statamic.com/discord) and the [discussions area](https://github.com/statamic/cms/discussions) on GitHub are the best places to go. You will likely find many helpful folks who may be willing to help.\n\n## How you can contribute\n\n### Which repo?\n\nStatamic is split into a few Github repositories. Here's a quick summary of each.\n\n- [`statamic/cms`](https://github.com/statamic/cms) is the core package. It doesn't run by itself but is instead a dependency consumed by Laravel apps. **99% of the work goes on here.**\n- [`statamic/statamic`](https://github.com/statamic/statamic) is the starter Laravel app used to build a new site. It's an empty shell.\n- [`statamic/docs`](https://github.com/statamic/docs) is the Statamic documentation site that is currently running on [statamic.dev](https://statamic.dev).\n\n### Bug reports\n\nFirst things first. If the bug is security related refer to our [security disclosures](#security-disclosures) procedures instead of opening an issue.\n\nNext, **please** (pretty pretty please) search through the [open issues](https://github.com/statamic/cms/issues) to see if it has already been opened.\n\nIf you _do_ find a similar issue, **upvote it** by adding a 👍 [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). If you have relevant information to add, do so in a comment. Please don't add a `+1` comment.\n\nIf no one has filed the issue yet, feel free to [submit a new one](https://github.com/statamic/cms/issues/new). Please include a clear description of the issue, follow along with the issue template, and provide as much relevant information as possible.\n\n:::tip\nIf you are able to create a repo demonstrating an issue, we can fix it **5x faster** than if you share a code example, and **1000x faster** than if you say \"it's broken plz fix it k thx byeeeeee\" without even telling us the error message.\n:::\n\n### Feature requests\n\nFeature requests should be created in the [statamic/ideas](https://github.com/statamic/ideas) repository. **Please** (pretty pretty please) search through the [open issues](https://github.com/statamic/cms/issues) to see if the feature request has already been opened.\n\nIf you _do_ find a similar request, **upvote it** by adding a 👍 [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). If you have relevant information to add, do so in a comment. Please don't add a `+1` comment.\n\n### Security disclosures\n\nIf you discover a security vulnerability, please review our [security policy](https://github.com/statamic/cms/security/policy), then report the issue directly to us from [statamic.com/support](https://statamic.com/support). We will review and respond privately via email. We do not respond to cold \"do you pay bounties?\" emails.\n\n### Documentation edits\n\nStatamic's documentation lives in the [https://github.com/statamic/docs](https://github.com/statamic/docs) repository. Improvements or corrections to them can be submitted as a pull request. These usually get merged very quickly unless your grammar is bad.\n\n### Core enhancements\n\nIf you would like to work on a new feature, consider opening an issue first in [the ideas repo](https://github.com/statamic/ideas) so we can discuss it before you spend time on it. While we appreciate community contributions, we do remain selective about what features make it into Statamic itself, so don’t take it the wrong way if we recommend that you pursue the idea as an addon instead.\n\nIf you're ready to start working on your feature, bug fix, or improvement, we have a [more in-depth guide to walk you through the whole thing](/contribution-guide).\n\n### Compiled assets\n\nIf you are submitting a change that will affect a compiled file, such as most of the files in `resources/css` or `resources/js`, do not commit the compiled files. Due to their large size, they cannot realistically be reviewed by our team. This could be exploited as a way to inject malicious code into Statamic. In order to defensively prevent this, all compiled files will be generated and committed by the core Statamic team.\n\n### Code style\n\nWe use [Laravel Pint](https://laravel.com/docs/master/pint#main-content) to enforce a consistent code style across the codebase. You can run it locally with `./vendor/bin/pint`.\n\n### Control Panel translations\n\nWe welcome new translations and updates! Please follow [these instructions](/cp-translations#contributing-a-new-translation) on how to contribute to Statamic's translation files.\n\n### Pull requests\n\nPull requests should clearly describe the problem and solution. Include the relevant issue number if there is one. If the pull request fixes a bug, it should include a new test case that demonstrates the issue, if possible.\n\nStay rad. If you're not already rad, tell us and we will make sure you become rad.\n\n✨\n"
  },
  {
    "path": "content/collections/pages/contribution-guide.md",
    "content": "---\nid: d0e99506-d01d-484c-884f-46dfc4dcf4c5\nblueprint: page\ntitle: 'Contribution Guide'\nintro: 'A guide on how to contribute to the `statamic/cms` repo'\n---\n\n## Fork the repo\nFirst, you need to create a fork of the repo. A fork is a copy of the repo where you can make changes before sending them back with a request to be merged into the original repo.\n\nHead to the [cms repo][cms-repo] and click the \"Fork\" button at the top right.\n\n## Clone it\nOnce you have a fork, you can clone it on your local machine with git. It can go anywhere - you probably already have a folder where your projects live. Most people use `~/Sites/` or `~/Code`.\n\n```shell\ncd Code # [tl! **]\ngit clone https://github.com/your-username/cms.git # [tl! **]\nCloning into 'cms'...\nremote: Enumerating objects: 86396, done.\nremote: Counting objects: 100% (3025/3025), done.\nremote: Compressing objects: 100% (1917/1917), done.\nremote: Total 86396 (delta 1674), reused 2078 (delta 1085), pack-reused 83371\nReceiving objects: 100% (86396/86396), 33.39 MiB | 5.76 MiB/s, done.\nResolving deltas: 100% (67201/67201), done.\n```\n\n## Create a sandbox project\nThe `cms` repo is just the Laravel package — it can't run on its own. It needs to be installed into a Laravel app.\n\nThe easiest way to set this up is to install a Starter Kit. In a separate folder, create your site:\n\n```shell\ncd sites # [tl! **]\nstatamic new sandbox # [tl! **]\nCreating a statamic/statamic project at ./sandbox\n[✔] Statamic has been successfully installed into the sandbox directory.\nBuild something rad!\n```\n\n## Link your fork to the sandbox\nAt this point, your sandbox app is going to be using the \"real\" version of Statamic. You'll need to tell it to use your local fork.\n\nIn your app's `composer.json`, add a `repositories` array with a \"path\" repository pointing to where you cloned your fork earlier:\n\n```json\n{\n    \"name\": \"statamic/statamic\",\n    \"type\": \"project\",\n    \"description\": \"Statamic\",\n    \"keywords\": [\"statamic\", \"cms\", \"flat file\", \"laravel\"],\n    \"require\": { // [tl! collapse:start]\n        \"php\": \"^8.2\",\n        \"laravel/framework\": \"^11\",\n        \"laravel/tinker\": \"^2.9\",\n        \"statamic/cms\": \"^5.0\"\n    }, // [tl! collapse:end]\n    \"require-dev\": { // [tl! collapse:start]\n        \"barryvdh/laravel-debugbar\": \"^3.8.1\",\n        \"fakerphp/faker\": \"^1.23\",\n        \"laravel/pint\": \"^1.13\",\n        \"laravel/sail\": \"^1.26\",\n        \"mockery/mockery\": \"^1.6\",\n        \"nunomaduro/collision\": \"^8.0\",\n        \"phpunit/phpunit\": \"^11.0\",\n        \"spatie/laravel-ignition\": \"^2.4\"\n    }, // [tl! collapse:end]\n    \"autoload\": { // [tl! collapse:start]\n        \"psr-4\": {\n            \"App\\\\\": \"app/\",\n            \"Database\\\\Factories\\\\\": \"database/factories/\",\n            \"Database\\\\Seeders\\\\\": \"database/seeders/\"\n        }\n    }, // [tl! collapse:end]\n    \"autoload-dev\": { // [tl! collapse:start]\n        \"psr-4\": {\n            \"Tests\\\\\": \"tests/\"\n        }\n    }, // [tl! collapse:end]\n    \"scripts\": { // [tl! collapse:start]\n        \"pre-update-cmd\": [\n            \"Statamic\\\\Console\\\\Composer\\\\Scripts::preUpdateCmd\"\n        ],\n        \"post-autoload-dump\": [\n            \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n            \"@php artisan package:discover --ansi\",\n            \"@php artisan statamic:install --ansi\"\n        ],\n        \"post-root-package-install\": [\n            \"@php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n        ],\n        \"post-create-project-cmd\": [\n            \"@php artisan key:generate --ansi\",\n            \"@php -r \\\"file_exists('database/database.sqlite') || touch('database/database.sqlite');\\\"\"\n        ],\n        \"post-update-cmd\": [\n            \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n        ]\n    }, // [tl! collapse:end]\n    \"extra\": { // [tl! collapse:start]\n        \"laravel\": {\n            \"dont-discover\": []\n        }\n    }, // [tl! collapse:end]\n    \"config\": { // [tl! collapse:start]\n        \"optimize-autoloader\": true,\n        \"preferred-install\": \"dist\",\n        \"sort-packages\": true,\n        \"allow-plugins\": {\n            \"pestphp/pest-plugin\": true,\n            \"php-http/discovery\": true,\n            \"pixelfear/composer-dist-plugin\": true\n        }\n    }, // [tl! collapse:end]\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"repositories\": [ // [tl! focus:start]\n        {\n            \"type\": \"path\",\n            \"url\": \"/path/to/cms\"\n        }\n    ] // [tl! focus:end]\n}\n```\n\nNext, require the branch of `cms` you checked out:\n\n```shell\ncomposer require \"statamic/cms 6.x-dev\"\n```\n\n(We'll go into more detail in a moment on what constraint should be used there.)\n\nIn the output, you should see it symlinks the `cms` directory to your fork:\n\n```shell\n./composer.json has been updated\nRunning composer update statamic/cms\n> Statamic\\Console\\Composer\\Scripts::preUpdateCmd\nLoading composer repositories with package information\nUpdating dependencies\nLock file operations: 0 installs, 1 update, 0 removals\n  - Upgrading statamic/cms (v5.7.3 => 6.x-dev) # [tl! focus]\nWriting lock file\nInstalling dependencies from lock file (including require-dev)\nPackage operations: 0 installs, 1 update, 0 removals\n  - Removing statamic/cms (v5.7.3)\n  - Installing statamic/cms (6.x-dev): Symlinking from /path/to/cms  # [tl! focus]\n  - Downloading statamic/cms (dist)\n    Failed to download\nGenerating optimized autoload files\n> Illuminate\\Foundation\\ComposerScripts::postAutoloadDump\n> @php artisan package:discover --ansi\nDiscovered Package: ajthinking/archetype\nDiscovered Package: barryvdh/laravel-debugbar\nDiscovered Package: intervention/image\nDiscovered Package: laravel/sail\nDiscovered Package: laravel/tinker\nDiscovered Package: nesbot/carbon\nDiscovered Package: nunomaduro/collision\nDiscovered Package: nunomaduro/termwind\nDiscovered Package: rebing/graphql-laravel\nDiscovered Package: spatie/laravel-ignition\nDiscovered Package: statamic/cms\nDiscovered Package: wilderborn/partyline\n> @php artisan statamic:install --ansi\nDiscovering addons.\nPublishing [statamic] assets.\nPublishing [statamic-cp] assets.\nPublishing [statamic-frontend] assets.\nCompiled views cleared successfully.\nApplication cache cleared successfully.\n97 packages you are using are looking for funding.\nUse the `composer fund` command to find out more!\n```\n\nYou can confirm it by checking the path to the package:\n\n```shell\ncomposer show statamic/cms --path # [tl! focus]\nstatamic/cms /path/to/cms\n```\n\n## Use an appropriate branch\n\nBe sure to work on a new, dedicated branch for your Pull Request. Among other things, it'll make it easier for the Statamic team to push minor changes if necessary (like fixing typos, code style, tweaks, and so on). We request that you add \"feature\" or \"fix\" in the branch name so it's easier to understand the intent of your PR.\n\n```shell\ngit checkout -b feature/new-thing\ngit checkout -b fix/issue-9999\n```\n\n:::warning Psst!\nWhen requiring the `cms` package, it's important to require the appropriate constraint. If you don't use the right one, Composer may decide to use the _real_ `cms` package, and you'll be left wondering why your code changes aren't appearing.\n:::\n\nIf the branch is numeric then you need to require `BRANCH.x-dev` (e.g. a branch named `6.x` should use a constraint of `6.x-dev`).\n\nOtherwise, you'll need to use `dev-BRANCH` (e.g. a branch named `feature/mybranch` should use a constraint of `dev-feature/mybranch`).\n\n:::tip\nOnce you've done the initial symlink, you can change `cms` branches freely. However once again, be aware if you do a `composer update` or `require`, you may end up with a live version of `cms`.\n:::\n\n## Dealing with assets\nIf your contribution involves Control Panel assets - Stylesheets, JavaScript, or Vue components - you'll need to compile them and have them used by your sandbox app. You can do this with another symlink.\n\nIn your sandbox, delete the `public/vendor/statamic/cp` directory, which should have been created when you initially created the site.\n\nCompile the assets within the `cms` repo.\n\n```shell\ncd cms\nnpm ci\nnpm run dev  # or npm run build\n```\n\nThe assets will be compiled into `cms/resources/dist`. You can now symlink them into your sandbox:\n\n```shell\ncd sandbox\nln -s /path/to/cms/resources/dist public/vendor/statamic/cp\n```\n\n:::tip\n**Do not attempt to commit any compiled code.** They should already be gitignored, and will be automatically recompiled at release time.\n:::\n\n\n## Commit code\nNow you're ready to actually write code.\n\nIf you're writing tests, you can run the test suite inside the `cms` repo.\n\nIf you want to manually test or use the package, you can do it in through your sandbox. Any changes you make to the code in your `cms` repo will be reflected in your sandbox app which you can see in the browser.\n\nOnce you're done, you should push your branch to Github.\n\n```shell\ngit push --set-upstream origin HEAD # [tl! **]\nEnumerating objects: 5, done.\nCounting objects: 100% (5/5), done.\nDelta compression using up to 8 threads\nCompressing objects: 100% (3/3), done.\nWriting objects: 100% (3/3), 309 bytes | 309.00 KiB/s, done.\nTotal 3 (delta 2), reused 0 (delta 0), pack-reused 0\nremote: Resolving deltas: 100% (2/2), completed with 2 local objects.\nremote:\nremote: Create a pull request for 'feature/new-thing' on GitHub by visiting:  # [tl! **]\nremote:      https://github.com/your-username/cms/pull/new/feature/new-thing # [tl! **]\nremote:\nTo https://github.com/your-username/cms.git\n * [new branch]          HEAD -> feature/new-thing\nBranch 'feature/new-thing' set up to track remote branch 'feature/new-thing' from 'origin'.\n```\n\n## Create the Pull Request\n\n:::best-practice\nWhen creating a pull request that introduces a **new feature or changes current behavior**, please open an issue referencing your PR on the [statamic/docs](https://github.com/statamic/docs/issues/new) repo. No need to write the docs yourself. We'll take care of that for you. Any hints or bullet points are appreciated though!\n:::\n\nIn the output from pushing your branch above, it'll give you a link to create the pull request. If you missed it, no problem. Just head over to `statamic/cms` and you should see a banner waiting for you.\n\n![](/img/guides/contribution-guide/pull-request-banner.png)\n\nClick through there and you'll be taken to a form where you can describe what's being contributed.\n\n![](/img/guides/contribution-guide/pull-request-form.png)\n\nPlease be as thorough as possible. Explain what's being added, what it fixes, list any relevant issues or discussions, and explain how we can test out the changes.\n\n## Cleaning Up\nOnce the PR is resolved, either by being merged or closed, you're free to delete the branch or even the entire fork.\n\nIf your PR was merged, you'll be mentioned in the next release's changelog where you will live in infamy. ✨\n\n![](/img/guides/contribution-guide/release.png)\n\n[cms-repo]: https://github.com/statamic/cms\n\n## Extra Credit\nIf you're a frequent contributor, you may consider permanently setting up the Composer path repository.\n\nInstead of adding `repositories` key into your sandbox's `composer.json` every time, you can add it to your global Composer `~/.composer/config.json`.\n\n```json\n{\n    \"repositories\": [\n        {\n            \"type\": \"path\",\n            \"url\": \"/path/to/cms\",\n            \"canonical\": false\n        }\n    ]\n}\n```\n"
  },
  {
    "path": "content/collections/pages/control-panel.md",
    "content": "---\nid: a9acf949-e23d-42ab-8bc2-21032c98df9a\ntitle: 'Control Panel'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/controllers.md",
    "content": "---\ntitle: Controllers\nintro: Controllers group related Laravel request handling logic into single classes stored in the `app/Http/Controllers/` directory. Use them to build frontend areas or full custom apps, the Laravel way!\nid: 5e848460-9bbc-449e-8edd-182d918163ff\nblueprint: page\n---\n## Overview\n\nStatamic is a package sitting _inside_ a standard Laravel application, giving you the freedom to create your own routes, map them to controllers, and build your own custom application and business logic outside of Statamic's feature set.\n\nAnything you can do in Laravel you can do here. Because you're using Laravel. You just _also_ have access to all of Statamic's capabilities and features.\n\n## Routes\n\n[Routes][laravel-routes] are defined in `routes/web.php`.\n\n:::tip\nThese explicitly defined routes will take precedence over Statamic routes and URL patterns. Keep this in mind.\n:::\n\nFor example, you can map a `GET` request to `yoursite.com/example` to the `index` method in the `app/Http/Controllers/ExampleController.php` file like this:\n\n``` php\nuse App\\Http\\Controllers\\ExampleController;\n\nRoute::get('example', [ExampleController::class, 'index']);\n```\n\n## Basic controller\n\nIn your controller, render views like you would in a regular Laravel app:\n\n``` php\n<?php\n\nnamespace App\\Http\\Controllers;\n\nuse App\\Http\\Controllers\\Controller;\n\nclass ExampleController extends Controller\n{\n    public function index()\n    {\n        $data = [\n            'title' => 'Example Title'\n        ];\n\n        return view('myview', $data);  // resources/views/myview.blade.php\n    }\n}\n```\n\n:::tip\nYou can generate a controller with the following [Artisan command](https://laravel.com/docs/artisan):\n```shell\nphp artisan make:controller ExampleController\n```\n:::\n\n## Antlers views\n\nReturning `view('myview')` _will_ render the `myview.antlers.html` view. To take advantage of Statamic's standard template-injected-into-a-layout behavior, return a `Statamic\\View\\View` instance instead of a regular Laravel one.\n\n``` php\npublic function index()\n{\n    return (new \\Statamic\\View\\View)\n        ->template('myview')\n        ->layout('mylayout')\n        ->with(['title' => 'Example Title']);\n}\n```\n\nNow, `myview` will be injected into `mylayout`'s `template_content` variable.\nAnything provided to `with` (eg. `title`) will be available in both views.\n\nIf you want to make an entry's content available in your view, you can use the `cascadeContent` method:\n\n``` php\n// app/Http/Controllers/MySpecialController.php\n\npublic function index()\n{\n    $entry = Entry::whereCollection('pages')\n        ->where('slug', 'special-page')\n        ->where('published', true)\n        ->first();\n\n    return (new \\Statamic\\View\\View)\n        ->template('myview')\n        ->layout('mylayout')\n        ->cascadeContent($entry);\n}\n```\n\n## Related reading\n\n- [Laravel controllers][laravel-controllers]\n- [Laravel routes][laravel-routes]\n- [Antlers](/antlers)\n\n[laravel-controllers]: https://laravel.com/docs/controllers\n[laravel-routes]: https://laravel.com/docs/routing\n"
  },
  {
    "path": "content/collections/pages/core-concepts.md",
    "content": "---\nid: 24a9c9d8-d607-4117-9806-738668c173cd\nblueprint: page\ntitle: 'Core Concepts'\nintro: 'Statamic is opinionated software. Understanding the principles we follow and apply to the way we build features will help you learn Statamic faster.'\ntemplate: page\nbreadcrumb_title: Overview\n---\n## Statamic is opinionated but configurable\n\nStatamic is an **opinionated platform**. We set defaults to match the most common use cases and implement patterns that help speed up your workflow, enforce consistency, and make it easy to share code between projects.\n\nFollowing these conventions will make it easier to switch between different Statamic projects because you'll know right where everything is and what it's called.\n\nSometimes these conventions don't fit your project, or maybe you're perfectly happy with your own way of doing things. That's fine &mdash; our conventions can usually be configured, overridden, or ignored.\n\n:::tip\n**But don't break convention unless you have a really, really good reason.** Like integrating Statamic with an existing Laravel app or when porting a site from another platform.\n:::\n\nA good example of this is the decision on whether to use [Blade](/blade) as the template language over [Antlers](/antlers). Antlers is deeply integrated with Statamic and can handle the responsibilities of both Blade _and_ Controllers right in your template. If you choose to use Blade templates, you will also have access to our Antlers Blade components, or you can use controllers and fetch data more of the traditional MVC way.\n\n:::best-practice\nDo your best to maintain a project `README.md` with anything you do to override Statamic's default behavior just in case you hand the site off to someone else.\n:::\n\n\n## Statamic is flat _first_\n\nStatamic has the ability to adapt to any data storage mechanism, from relational databases like MySQL and Postgres, to NoSQL solutions like MongoDB and Redis, and more. This feature is called [Repositories](/extending/repositories).\n\nHowever, these solutions **add complexity** and should only be used when necessary, most often for scaling for large amounts of data (tens of thousands of records) or high volume traffic.\n\nStatamic operates in flat file mode by default, which reduces complexity compared to many other architectures, and opens up many possibilities, including:\n\n- End-to-end **version control**.\n- The ability to write and manage content, configs, and templates all **right in your code editor**.\n- The ability to copy & paste or share anything between sites.\n- **Ridiculously simple** deployment and load balancing scenarios.\n\nAs your site scales, you can choose to move from the flat file driver to one best suiting your needs. **Deferring this decision prevents premature optimization and technical debt.**\n\n<figure>\n    <img class=\"u-w-full\" src=\"https://imgs.xkcd.com/comics/the_general_problem.png\" alt=\"Premature Optimization comic by XKCD\">\n    <figcaption>Let's be honest. We've all done this.</figcaption>\n</figure>\n\n## The content schema is up to you\n\nIt's completely up to you how to organize your content. You pick the field names, you pick how to organize entries into different collections. You pick what to name your taxonomies, what the URL patterns should be, and so on.\n\nWe believe that forcing every site to use the same content model is nothing short of a crime. With 40+ different built-in fieldtypes, there are many perfectly reasonable ways to structure and manage your content.\n\nIf you like the \"one big field\" approach with all your content and markup in one chunk, build your site around the [Bard](/fieldtypes/bard) fieldtype and add custom Set blocks with other fieldtypes to get fancy.\n\nOr if you prefer to break everything up into small, discrete, optional fields, showing and hiding things as needed, you can do that too (you should check out [conditional fields](/conditional-fields)).\n\n## You bring the HTML\n\nStatamic doesn't start with a design or HTML you're expected to use or hack apart. It doesn't include any CSS or JavaScript either. All of that is up to you (or a [Starter Kit](/starter-kits)) to provide.\n\nEvery Statamic site &mdash; just like every fingerprint and person in the world &mdash; is unique. This is not a platform for the generic web. This is a tool used to build anything you can imagine.\n\nBecause of this, **most Statamic projects need to involve a developer** for at least _part_ of the process. It's not very \"no-code\" friendly solution to assemble. But once the site is built and all the collections and blueprints configured, just about **anyone can handle maintaining the site**.\n\n## Keep it simple\n\nStatamic does its best to take a \"start simple and add things as needed\" approach to features and settings, in contrast to other platforms that take a \"everything is included and rip out what you don't want\" approach.\n\nThis means that Statamic doesn't do everything right out the box. We find it's much better in the long run to turn on the things you need, enable features you plan to use, and name things the way you want, than to spend precious time clicking about the control panel disabling everything you'll never end up needing, or explaining to a client why the button they clicked doesn't do what they expect.\n\n:::tip\nIf many of the sites you build share a common set of features, collections, blueprints, and/or templates, consider turning them into a [Starter Kit](/starter-kits) and make it your boilerplate to kickstart new projects.\n:::\n\n## Statamic is a box of Lego bricks\n\nYou **may** be used to content management systems and platforms that have a long list of explicit pre-built features, or plugins that provide these features, like photo galleries, hero images, and so on.\n\nStatamic takes a different approach, that when combined with our \"Bring Your Own HTML\" core approach, enables you to build _almost anything_, like a box full of LEGO bricks.\n\n**Want to build a photo gallery?** Add an Assets field that lets you select multiple images, and then loop through the selected images and render thumbnails on the fly with the Glide tag, and link to the full resolution image.\n\n**Need an image slider?** Add an Assets field, select multiple images, and pass the list of images into any number of open source image slider components available on Github.\n\n**Got a Hero Image?** Use an Assets and text field and render the text on top of the `background-image` of your choosing.\n\nAnd just like with Lego bricks, it hurts really bad to step on Statamic barefooted.\n\nHopefully you get the idea and see how you can solve almost any challenge with core fieldtypes and some HTML.\n\n## The Control Panel can be optional\n\nYou should be able to do everything (and more) without ever logging into the Control Panel. Granted, it _does_ tend to make some of the more complicated things easier (like creating relationships, discovering all possible options for a given setting, and rearranging pages in a nav tree), but we love efficiency and your editor is a great place to find it.\n\nPlus, you can easily tap into the power of AI in your code editor to manipulate every little bit of your site and content.\n"
  },
  {
    "path": "content/collections/pages/cp-navigation.md",
    "content": "---\ntitle: Extending CP Navigation\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569347202\nintro: The Control Panel navigation is quite customizable. Addons can add their own sections, pages, and subpages, as well as remove and modify existing ones.\nid: 785ffa10-8b63-44b1-9da3-3837250cacbe\n---\n\n:::tip\nThis page refers to the Control Panel's side-bar navigation. Not to be confused with [\"Navs\"](/navigation), where you can create trees to be used for the front-end of your site.\n:::\n\n## Overview\n\n### Customization vs Extension\n\nStatamic offers the ability for end users to [customize their CP nav via a user-friendly preferences GUI](/customizing-the-cp-nav), but this page is focused on Statamic's PHP API for extending the CP nav within an addon.\n\n### Registering Your Extension\n\nEvery nav item is represented by a `NavItem` object, which has a [full API](#the-navitem-class) for [adding](#adding-items), [removing](#removing-items), and [modifying](#modifying-items) items.  You may register your nav extensions in the `boot()` method of a service provider.\n\n## Adding Items\n\nLet's assume we're creating a Store addon, and want to add a `Store` nav item to the `Content` section of the navigation.  To add this item, we'll add the following code to our service provider's `boot()` method:\n\n```php\nuse Statamic\\Facades\\CP\\Nav;\n\npublic function bootAddon()\n{\n    Nav::extend(function ($nav) {\n        $nav->content('Store')\n            ->route('store.index')\n            ->icon('shopping-cart');\n    });\n}\n```\n\nThe `content()` method there is a [magic method](http://php.net/manual/en/language.oop5.magic.php), and the name of method defines the section name that will be used.  If we need to display special characters in our section name, we can `create()` the nav item and explicitly define the section name:\n\n```php\nNav::extend(function ($nav) {\n    $nav->create('Store')\n        ->section('Jack & Sons Inc.')\n        ->route('store.index')\n        ->icon('shopping-cart');\n});\n```\n\nThe `icon()` method accepts the name of an [icon included in Statamic](https://ui.statamic.dev/?path=/docs/components-icon--docs#available-icons), or an SVG string containing a custom icon (be sure to use `fill=\"currentColor\"`):\n\n```php\nNav::extend(function ($nav) {\n    $nav->create('Store')\n        ->section('Jack & Sons Inc.')\n        ->route('store.index')\n        ->icon('<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M6.547 9.674l7.778 7.778a4.363 4.363 0 0 0 .9-4.435l5.965-5.964.177.176a1.25 1.25 0 0 0 1.768-1.767l-4.6-4.6a1.25 1.25 0 0 0-1.765 1.771l.177.177-5.965 5.965a4.366 4.366 0 0 0-4.435.899zM10.436 13.563L.5 23.499\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"/></svg>');\n});\n```\n\n:::tip\nNote that the `Nav` facade is `Statamic\\Facades\\CP\\Nav`.\n\nThere's another Nav facade _without_ the CP namespace, and it's for the front-end [\"Navs\"](/navigation) feature.\n:::\n\n## Adding Children\n\nMaybe we have `Products` and `Orders`, which we want to display as children under the `Store` item.  To do this, we'll add a `children()` call to the parent nav item:\n\n```php\nNav::extend(function ($nav) {\n    $nav->content('Store')\n        ->route('store.index')\n        ->icon('shopping-cart')\n        ->children([\n            'Products' => cp_route('store.products.index'),\n            'Orders' => cp_route('store.orders.index')\n        ]);\n});\n```\n\nIf we need to customize our child items further, we can use object notation.  For example, maybe we would like to authorize whether the user `can()` see these nav items:\n\n```php\nNav::extend(function ($nav) {\n    $nav->content('Store')\n        ->route('store.index')\n        ->icon('shopping-cart')\n        ->can('view store')\n        ->children([\n            $nav->item('Products')->route('store.products.index')->can('view products'),\n            $nav->item('Orders')->route('store.orders.index')->can('view orders')\n        ]);\n});\n```\n\nWe can also defer the creation of children until render time by passing a closure.  For example, if we're dynamically hitting a data store to generate our children, we can use a closure to avoid the performance hit unless the navigation actually needs to render the children:\n\n```php\nNav::extend(function ($nav) {\n    $nav->content('Store')\n        ->url('store')\n        ->icon('shopping-cart')\n        ->children(function () {\n            return ProductType::hasPublished()->get()->map(function ($type) {\n                return Nav::item($type->name)->url($type->url);\n            });\n        });\n});\n```\n\n## Removing Items\n\nTo remove an item, we may specify the section and item name:\n\n```php\nNav::extend(function ($nav) {\n    $nav->remove('Content', 'Store');\n});\n```\n\nTo remove a child of an item, we can pass a third param to specify the child's name:\n\n```php\nNav::extend(function ($nav) {\n    $nav->remove('Content', 'Collections', 'Products');\n});\n```\n\nTo remove an entire section, we only need to specify the section name:\n\n```php\nNav::extend(function ($nav) {\n    $nav->remove('Content');\n});\n```\n\n## Modifying Items\n\nWe can access any existing item using the same syntax as described above [when adding items](#adding-items).  We can even modify native Statamic nav items.  For example, maybe we wish to change the icon for the `Collections` item in the `Content` section of the nav:\n\n```php\nNav::extend(function ($nav) {\n    $nav->content('Collections')\n        ->icon('coins');\n});\n```\n\nThe `content()` method there is a [magic method](http://php.net/manual/en/language.oop5.magic.php), which performs a `findOrCreate()` under the hood.  If the nav item is found, we can then chain on any modifications to be applied to the item.  If our section name contains any special characters, we can perform an explicit `findOrCreate()`:\n\n```php\nNav::extend(function ($nav) {\n    $nav->findOrCreate('Jack & Sons Inc.', 'Store')\n        ->icon('coins');\n});\n```\n\n## The NavItem Class\n\nEach item you see in the navigation is an instance of the `Statamic\\CP\\Navigation\\NavItem` class. Each top level instance within a section may contain its own collection of `NavItem` children.\n\n### Basic API\n\nThe code examples above demonstrate how to [add](#adding-items), [modify](#modifying-items), and [remove](#removing-items) `NavItem` objects.  Once you have a `NavItem` object, the following chainable methods are available to you:\n\n| Method | Parameters | Description |\n| :--- | :--- | :--- |\n| `name()` | `$name` (string) | Define item name. |\n| `section()` | `$section` (string) | Define section name. |\n| `route()` | `$name` (string), `$params` (mixed, optional) | Define a route automatically prefixing with `statamic.cp.` |\n| `url()` | `$url` (string) | Define a URL instead of a route. A string without a leading slash will be relative from the CP. A leading slash will be relative from the root. You may provide an absolute URL. |\n| `icon()` | `$icon` (string) | Define icon. |\n| `children()` | `$children` (array\\|collection\\|closure) | Define child items. |\n| `can()` | `$ability` (string), `$params` (mixed, optional) | Define authorization. |\n| `view()` | `$view` (string) | Define custom view. |\n\n## Breadcrumbs\n\nBreadcrumbs are displayed at the top of the Control Panel, making it easy to understand where you are in the navigation.\n\nStatamic automatically generates these breadcrumbs using the CP navigation. It supports a couple of additional options which can be set using the `extra()` method on a `NavItem`:\n\n```php\nNav::extend(function ($nav) {\n    $nav->content('Store')\n        ->route('store.index')\n        ->icon('shopping-cart')\n        ->extra([ // [tl! focus:start]\n            'breadcrumbs' => [\n                // Create button\n                'create_label' => 'Create Product',\n                'create_url' => cp_route('store.products.create'),\n                \n                // Configure button\n                'configure_url' => cp_route('store.settings'),\n            ],\n        ]); // [tl! focus:end]\n});\n```\n\nYou may also push additional breadcrumbs from your controller, using the `Breadcrumbs` class:\n\n```php\nuse Statamic\\CP\\Breadcrumbs\\Breadcrumb;\nuse Statamic\\CP\\Breadcrumbs\\Breadcrumbs;\n\nBreadcrumbs::push(new Breadcrumb(\n    text: 'Sneakers',\n    url: cp_route('store.products.category', 'sneakers'),\n    icon: 'sneakers',\n    links: [\n        ['text' => 'T-shirts', 'icon' => 't-shirts', 'url' => cp_route('store.products.category', 't-shirts')],\n        ['text' => 'Socks', 'icon' => 'socks', 'url' => cp_route('store.products.category', 'socks')],\n    ],\n    createLabel: 'Create Category',\n    createUrl: cp_route('store.products.category.create'),\n));\n```"
  },
  {
    "path": "content/collections/pages/cp-translations.md",
    "content": "---\ntitle: 'Control Panel Translations'\nnav_title: Translations\nintro: \"Statamic's Control Panel is currently available in 28 languages. We always welcome new translations!\"\nblueprint: page\nid: 79129d32-3f7c-4215-b6b1-21a2fccafa8d\n---\n## Configuration\n\nSet which language you want to use by default in `config/app.php`. You may also choose a fallback locale in case new content and strings are added to the control panel before an accompanying translation has been updated.\n\n``` php\n'locale' => 'es',\n'fallback_locale' => 'en',\n```\n\n### Per-user override\n\nYou can override the translation locale on a per-user basis by setting `locale: {code}` in a given user's preferences (their YAML record).\n\n``` yaml\n#  users/nosmo.king@example.com\nname: Nosmo King\nsuper: true\npreferences:\n  locale: en\n```\n\n### Available Translations\n\n| Language            | Code    |\n|---------------------|---------|\n| Arabic              | `ar`    |\n| Azerbaijani         | `az`    |\n| Czech               | `cs`    |\n| Danish              | `da`    |\n| German              | `de`    |\n| German (Swiss)      | `de_CH` |\n| English             | `en`    |\n| Spanish             | `es`    |\n| Estonian            | `et`    |\n| Persian             | `fa`    |\n| French              | `fr`    |\n| Hungarian           | `hu`    |\n| Indonesian          | `id`    |\n| Italian             | `it`    |\n| Japanese            | `ja`    |\n| Malay               | `ms`    |\n| Norwegian           | `nb`    |\n| Dutch               | `nl`    |\n| Polish              | `pl`    |\n| Portuguese          | `pt`    |\n| Portuguese (Brazil) | `pt_BR` |\n| Russian             | `ru`    |\n| Slovene             | `sl`    |\n| Swedish             | `sv`    |\n| Turkish             | `tr`    |\n| Ukrainian           | `uk`    |\n| Vietnamese          | `vi`    |\n| Chinese             | `zh_CN` |\n| Chinese (Taiwan)    | `zh_TW` |\n\n_Translations are community contributed so you may find them to be incomplete shortly after an update._\n\n## Translations not covered by Statamic\n\nAlthough Statamic's translations cover *most* of the strings in the Control Panel, there are a couple of places where Statamic will fallback to your application's translations.\n\nOne example of this is on Statamic's authentication pages. Since it's using Laravel's built-in authentication under the hood, translations for any validation errors will be pulled from your app's `lang` or `resources/lang` directory.\n\nTo save you manually translating Laravel's strings yourself, you can copy the necessary translations from the community-driven [Laravel-lang](https://github.com/Laravel-Lang/lang/tree/main/locales) repository into your application.\n\n## Contributing a new translation\n\nThere are 4 steps.\n\n1. Clone [`statamic/cms`](https://github.com/statamic/cms) locally\n2. Run `composer install`\n3. Generate a new translation from source files\n4. Translate new message files in `lang`\n5. Add the language to the [array in CorePreferences](https://github.com/statamic/cms/blob/cce7045e3f0ff418ee6e0a982a3830d604c6b64c/src/Preferences/CorePreferences.php#L56-L82) so it's selectable\n6. Commit changes and submit a PR\n\n### Generating translation files\n\nRun the `translator generate` command in the `statamic/cms` project, along with the new language code as an argument. This will generate empty JSON and PHP files in `lang` ready to be translated into the locale of your choice.\n\nYou can specify a short 2 character language code (`es`) or the full 4 character regional code (`es_MX`).\n\n_It is recommended that you comply to the [`language code standard`](https://www.science.co.il/language/Codes.php)._\n\n``` shell\nphp translator generate eo\n```\n\n- The JSON file contains all the \"short strings\" established on the fly with the translation helpers, e.g. `__('Cowabunga')`.\n- The PHP files contain longer strings and are well organized by section of the control panel.\n- Translatable strings can contain a `|` to separate singular and plurals.\n- Translatable strings can contain the `:something` format to indicate a variable.\n\n``` files theme:serendipity-light\nlang/\n|-- eo/\n|   |-- markdown.php\n|   |-- messages.php\n|   |-- permissions.php\n|   |-- validation.php\n|-- eo.json\n```\n\n:::tip\nThis command will also update existing files with any changes from recent Statamic releases.\n:::\n\n#### Using Google translate\n\nYou can get a translation kickstarted with the Google API by passing your API key.\n\n``` shell\nphp translator generate eo --key=abc123\n```\n\n### Using the reviewer\n\nRunning the `translator review` command will loop through all the translations showing you the key, the English phrase, and new translated phrase for proofreading. You can enter new translations during this process. You can also use this command to gather new or changed translatable strings after a Statamic update.\n\n``` shell\nphp translator review eo messages\n```\n"
  },
  {
    "path": "content/collections/pages/creating-a-starter-kit.md",
    "content": "---\nid: 9c703f43-30de-4f65-98bb-2b89f80012b7\ntitle: 'Creating a Starter Kit'\ntemplate: page\nblueprint: page\nintro: 'Thinking of creating your own Statamic Starter Kit? Here''s everything you need to know to get started.'\nnav_title: Creating\n---\n## Overview\n\nStarter Kit development happens within a real instance of Statamic, just like developing any other Statamic site using your normal, preferred workflows.\n\nA released Starter Kit package contains **only** the files relevant to the Kit itself, and not a full Statamic/Laravel instance. Our Import/Export tools will allow you to maintain **only those relevant files**, without having to worry about maintaining the Statamic and underlying Laravel instances as they get updated over time.\n\nThe **Export** command will export all the files and directories you've created or configured to a new location. It's this directory that becomes the package, and is the thing you should version control, not the sandbox instance.\n\nFor example, maybe you are creating a pre-built, theme-style Starter Kit, the high-level workflow might look like this:\n\n1. Create a new Statamic project.\n2. Initialize it as a Starter Kit:\n   ```shell\n   php please starter-kit:init\n   ```\n\n3. Develop the theme as you normally would.\n\n4. Export the theme to a separate repo for redistribution.\n    ``` shell\n    php please starter-kit:export ../kung-fury-theme\n    ```\n\n5. Publish to [Github](https://github.com/), [Gitlab](https://gitlab.com/), or [Bitbucket](https://bitbucket.org/).\n\n6. Install into new Statamic projects.\n    ``` shell\n    php please starter-kit:install the-hoff/kung-fury-theme\n    ```\n\n\n## Creating the Starter Kit project\n\nThe first step is to [create a new Statamic project](/installing#creating-a-new-statamic-project). This is essentially a throwaway sandbox that you will use to develop and test your Starter Kit.\n\nRun the `init` command to generate the appropriate files.\n\n```shell\nphp please starter-kit:init\n```\n\nThis command will create and wire up a `package` directory, which represents the eventual Starter Kit repository's root directory.\n\n## The Starter Kit package\n\nStarter Kits are installed via Composer. You can control the package's contents via the `package` directory, which will be the exported repository's root directory.\n\nAt a minimum, your `package` directory needs a `starter-kit.yaml` and a `composer.json` file.\n\n``` files theme:serendipity-light\napp/\ncontent/\nconfig/\npackage/ #[tl! ++]\n    composer.json #[tl! ++]\n    starter-kit.yaml #[tl! ++]\npublic/\nresources/\ncomposer.json\n```\n\nYou can also include other files like a `README.md`, etc. as well. Everything you put into this `/package` folder will be exported to your repository's root directory.\n\n``` files theme:serendipity-light\npackage/\n    composer.json\n    starter-kit.yaml\n    README.md #[tl! ++]\n```\n\n:::tip\nIf you want a separate `README.md` to be installed when the end user installs your Starter Kit into their app, you can export the `README.md` at the root of your development sandbox by adding it to [export_paths](http://docs.test/starter-kits/creating-a-starter-kit#exporting-paths).\n:::\n\nFinally, if you plan to [make your Starter Kit updatable](#making-starter-kits-updatable), you should require this as a path repository.\n\n```json\n{\n    \"name\": \"statamic/statamic\",\n    \"require\": [\n        \"the-hoff/kung-fury-theme\": \"dev-master\" // [tl! ++]\n    ],\n    \"repositories\": [ // [tl! ++:start]\n        {\n            \"type\": \"path\",\n            \"url\": \"package\"\n        }\n    ] // [tl! ++:end]\n}\n```\n\n:::tip\nThis `package` folder can be automatically scaffolded and wired up in your composer `repositories` by [running the init command](#creating-the-starter-kit-project) to create your Starter Kit project.\n:::\n\n## Exporting\n\nWhen ready to export your Starter Kit, run the following command:\n\n``` shell\nphp please starter-kit:export {export_repo_path}\n```\n\nThis will copy and arrange the appropriate files into the given directory that will be used as a distributable on GitHub, GitLab, Bitbucket, Composer, etc.\n\n:::tip\nThink of the exported directory similar to a compiled assets directory when using a build tool like Vite. You generate files into this directory and shouldn't touch it manually.\n:::\n\n### Exporting Paths\n\nAny files that you modify on your site that you intend to be installed into a Statamic project should be marked as `export_paths` in your `starter-kit.yaml` file.\n\nFor example, the following config would tell Statamic to export sample content, along with related assets, config, blueprints, css, views, and front-end build config out for distribution on the Statamic Marketplace.\n\n``` yaml\nexport_paths:\n  - content\n  - config/filesystems.php\n  - config/statamic/assets.php\n  - resources/blueprints\n  - resources/css/site.css\n  - resources/views\n  - public/assets\n  - public/css\n  - package.json\n  - tailwind.config.js\n  - webpack.mix.js\n```\n\nAnything not configured in your `starter-kit.yaml` **will not be exported**. This way you don't have to maintain a full Statamic site, or any bootstrap code that is unrelated to your Starter Kit.\n\nOnce your export paths are configured, re-run the above `starter-kit:export` command. Your files should now be available at your new export repo path.\n\n#### Clearing stale files\n\nRe-running the export command will overwrite files in your export repo, but it _won't_ remove files that you've since deleted or renamed in your sandbox project. To wipe your configured `export_paths` in the destination before re-exporting, pass the `--clear` flag:\n\n``` shell\nphp please starter-kit:export ../kung-fury-theme --clear\n```\n\nSince this is destructive, it's opt-in. Review the resulting git diff in your export repo before committing — anything outside your `export_paths` (like `.github`, the stubbed `composer.json`, etc.) is left untouched, so you can discard any overzealous changes before pushing.\n\n\n### Exporting dependencies\n\nIf you wish to bundle any of your installed Composer dependencies with your Starter Kit, just `composer require` them in your sandbox project as you would into any app, then add them under a `dependencies` array in your `starter-kit.yaml` config file:\n\n``` yaml\ndependencies:\n  - statamic/ssg\n```\n\nThe exporter will automatically detect the installed versions and whether or not they are installed as dev dependencies, and export accordingly.\n\nWhen [installing the Starter Kit](#installing-a-starter-kit), composer will install with the same version constraints as you had installed in your sandbox project during development.\n\n\n## Optional modules\n\nYou may also present an optional set of Starter Kit files, nested under `modules` in your `starter-kit.yaml` config file.\n\nFor example, here we'll configure an opt-in `seo` module.\n\n```yaml\nmodules:\n  seo:\n    dependencies:\n      - statamic/seo-pro\n```\n\nThis presents a choice to the user, to confirm whether or not to install this module.\n\n<figure>\n    <img src=\"/img/starter-kit-module-confirmation.png\" alt=\"The user can confirm whether or not to install the `seo` module\">\n</figure>\n\nThese modules are compatible with the same config options that you use at the top level of your config file (ie. `export_paths`, `dependencies`, etc.).\n\n```yaml\nmodules:\n  seo:\n    export_paths:\n      - resources/css/seo.css\n    dependencies:\n      - statamic/seo-pro\n```\n\n### Customizing Prompt Text\n\nIf you don't like the default prompt text, you can customize it with custom `prompt` config.\n\n```yaml\nmodules:\n  seo:\n    prompt: 'Would you like some awesome SEO with that!?'\n    dependencies:\n      - statamic/seo-pro\n```\n\n<figure>\n    <img src=\"/img/starter-kit-module-custom-prompt.png\" alt=\"Starter Kit custom prompt text\">\n    <figcaption>Would you also like fries with that?</figcaption>\n</figure>\n\n### Customizing prompt default value\n\nSetting `default: true` will ensure the module is installed by default if the user spams the enter key through the prompt, or the Starter Kit is installed non-interactively.\n\n```yaml\nmodules:\n  seo:\n    default: true\n    dependencies:\n      - statamic/seo-pro\n```\n\n### Skipping confirmation\n\nOr maybe you wish to skip the user prompt and always install a given module, using modules to better organize larger Starter Kit configs. To do this, simply set `prompt` to false.\n\n```yaml\nmodules:\n  seo:\n    prompt: false\n```\n\n### Selecting between modules\n\nYou may find yourself in a situation where you want the user to select only one of multiple module options. To do this, you may nest multiple module configs under an `options` object.\n\n```yaml\nmodules:\n  js:\n    options:\n      vue:\n        export_paths:\n          - resources/js/vue.js\n      react:\n        export_paths:\n          - resources/js/react.js\n      mootools:\n        export_paths:\n          - resources/js/mootools.js\n```\n\n<figure>\n    <img src=\"/img/starter-kit-select-module.png\" alt=\"Starter Kit select module\">\n</figure>\n\n### Customizing select module prompt text\n\nOf course, you can also customize `prompt` text, the first 'No' `skip_option` text, as well as each option `label`, as you see fit.\n\n```yaml\nmodules:\n  js:\n    prompt: 'Would you care for some JS?'\n    skip_option: 'No, thank you!'\n    options:\n      vue:\n        label: 'VueJS'\n        export_paths:\n          - resources/js/vue.js\n      react:\n        label: 'ReactJS'\n        export_paths:\n          - resources/js/react.js\n      mootools:\n        label: 'MooTools (will never die!)'\n        export_paths:\n          - resources/js/mootools.js\n```\n\n<figure>\n    <img src=\"/img/starter-kit-select-module-customization.png\" alt=\"Customizing Starter Kit select module\">\n    <figcaption>🐮🐮🐮</figcaption>\n</figure>\n\n### Customizing select module default value\n\nSetting a `default` value will ensure a specific module option is installed by default if the user spams the enter key through the prompt, or the Starter Kit is installed non-interactively.\n\n```yaml\nmodules:\n  js:\n    prompt: 'Would you care for some JS?'\n    default: vue\n    # ...\n```\n\n### Disabling select module skip option\n\nIf you want to force the user to select a module option, you can set `skip_option: false` to disable the 'No' skip option.\n\n```yaml\nmodules:\n  js:\n    prompt: 'Would you care for some JS?'\n    skip_option: false\n    # ...\n```\n\n<figure>\n    <img src=\"/img/starter-kit-select-module-disable-skip-option.png\" alt=\"Starter kit disable skip option\">\n</figure>\n\n\n### Nesting modules\n\nFinally, you can also nest modules where it makes sense to do so. Simply nest a `modules` object within any module.\n\n```yaml\nmodules:\n  seo:\n    prompt: 'Would you like some awesome SEO with that!?'\n    dependencies:\n      - statamic/seo-pro\n    modules:\n      sitemap:\n        prompt: 'Would you like additional SEO sitemap features as well?'\n        dependencies:\n          - statamic/seo-pro-sitemap\n```\n\nIn this example, the second `sitemap` module prompt will only be presented to the user, if they agree to installing the parent `seo` module.\n\n\n## Post-install hooks\n\nYou may run additional logic after the Starter Kit is installed. For example, maybe you want to output some information.\n\nTo do so, you can create a `StarterKitPostInstall.php` file in the root of your Starter Kit. It should be a simple non-namespaced class with a `handle` method. You will be provided with an instance of the command so you can output lines, get input, and so on.\n\n```php\n<?php\n\nclass StarterKitPostInstall\n{\n    public function handle($console)\n    {\n        $console->line('Thanks for installing!');\n    }\n}\n```\n\n:::tip\nStatamic will automatically export this file if it exists. You don't need to add it to export_paths.\n:::\n\n\n## Publishing a Starter Kit\n\nOnce exported, you will need to update the `name` property in the `composer.json` created at your specified export repo path. It should match your Composer/GitHub {Organization}/{Repo_Name} exactly.\n\n``` json\n{\n    \"name\": \"the-hoff/kung-fury-theme\",\n    \"extra\": {\n        \"statamic\": {\n            \"name\": \"Kung Fury Theme\",\n            \"description\": \"Kung Fury Theme Starter Kit\"\n        }\n    }\n}\n```\n\nNow create a `README.md` file and push to [Github](https://github.com/), [Gitlab](https://gitlab.com/), or [Bitbucket](https://bitbucket.org/), as you would any PHP package. This is all that is required to publish a free Starter Kit!\n\n:::tip\nUnlike addons, you are not required to register on [Packagist](https://packagist.org/).\n:::\n\nIf you would like to share your Starter Kit, receive more exposure, or would like to charge for your Kit, you should [publish it to the Statamic Marketplace](#publishing-to-the-marketplace).\n\n\n## Publishing to the Marketplace\n\nOnce your Starter Kit is ready for the world, you can publish it on the [Statamic Marketplace](https://statamic.com/marketplace) where it can be discovered by others.\n\nBefore you can publish your Starter Kit, you'll need a couple of things:\n\n- A [Statamic Seller Account](https://statamic.com/creator)\n- A connected [Stripe](https://stripe.com) account _only if_ you're planning to sell your Starter Kits.\n\nIn your seller dashboard, you can create a product. There you'll be able to link your Composer package that you created on Packagist, choose a price, write a description, and so on.\n\nProducts will be marked as drafts that you can preview and tweak until you're ready to go.\n\nOnce published, you'll be able to see your Starter Kit on the Marketplace and within the Starter Kits area of the Statamic Control Panel.\n\n\n## Installing from a local repo\n\nTo test install your Starter Kit from your local exported repo, you can add the repo's local path to your global Composer `config.json` file as a repository:\n\n```json\n{\n    \"repositories\": [\n        {\n            \"type\": \"path\",\n            \"url\": \"/Users/hasselhoff/kung-fury-theme\"\n        }\n    ]\n}\n```\n\n:::tip\nIf you are not sure where your `config.json` is located, run `composer config --global home` to see the location of your global Composer config.\n:::\n\nWith your repo's local path added to your `config.json`, you should now be able to install using the `--local` cli option:\n\n```\nstatamic new kung-fury-dev the-hoff/kung-fury-theme --local\n```\n\n\n## Maintaining a Starter Kit\n\nWhen making changes to your Starter Kit, just [re-export](#exporting) from your development repo and push your changes from your exported repo.\n\n### Keeping up-to-date with Statamic and Laravel\n\nRather than maintaining your development repo as new Statamic and Laravel versions are released, you can always install your Starter Kit into a fresh Statamic instance by using the `--with-config` install option.\n\n``` shell\nstatamic new kung-fury-dev the-hoff/kung-fury-theme --with-config\n```\n\nThis will install your Starter Kit into a brand new Statamic project, along with your `starter-kit.yaml` config file for future exports.\n\n## Making starter kits updatable\n\nAs their name implies, starter kits were originally intended to be a way to \"start\" a site. Once installed, the user is on their own and can customize as they see fit.\n\nThe Kit would get installed via Composer, files would get copied to their respective locations, and then the Kit gets removed.\n\nHowever, you may choose to construct your Kit in a way that it can be updated by the end user. To do that, you should instruct Statamic to leave the Kit required as a Composer dependency by adding `updatable: true` in your `starter-kit.yaml` file:\n\n```yaml\nupdatable: true #[tl! ++]\nexport_paths: ...\n```\n\nNow that the Kit package stays around after installation, it can be updated like any other Composer package:\n\n```shell\ncomposer update\n```\n\nThis means that you could do things like:\n- Add a service provider to wire up Laravel or Statamic behavior.\n- Make the service provider extend AddonServiceProvider to make your Starter Kit _also_ an addon to get behavior for free like autoload tags, modifiers, etc.\n- Rather than exporting views, CSS, JS, PHP classes, etc. into the project, you can keep them in the package itself.\n\n\n## Addons vs. Starter Kits\n\nBoth addons and Starter Kits can be used to extend the Statamic experience, but they have different strengths and use cases:\n\n### Addons\n\n- Addons are installed via `composer`, like any PHP package\n- Addons live within your app's `vendor` folder after they are installed\n- Addons can be updated over time\n- Addon licenses are tied to your site\n\n:::tip\nAn example use case is a custom fieldtype maintained by a third party vendor. Though you would install and use the addon within your app, you would still rely on the vendor to maintain and update the addon over time.\n:::\n\n### Starter Kits\n\n- Starter Kits are installed via `statamic new` or `php please starter-kit:install`\n- Starter Kits install pre-configured files and settings into your site\n- Starter Kits do not live as updatable packages within your apps (by default)\n- Starter Kit licenses are not tied to a specific site, and expire after a successful install\n\n:::tip\nAn example use case is a frontend theme with sample content. This is the kind of thing you would install into your app once and modify to fit your own style. You would essentially own and maintain the installed files yourself.\n:::\n\n## Related reading\n\n- [Starter Kit Overview](/starter-kits)\n- [How to Install a Starter Kit](/starter-kits/installing-a-starter-kit)\n- [How to Update a Starter Kit](/starter-kits/updating-a-starter-kit)\n"
  },
  {
    "path": "content/collections/pages/css-javascript.md",
    "content": "---\nid: a92ce050-2c17-4b4d-8a69-c099759c1502\nblueprint: page\ntitle: 'CSS & JavaScript'\nintro: 'Statamic can load custom stylesheets and Javascript files located in the `public/vendor/` directory, or from external sources.'\n---\n:::tip\nThis guide is intended for apps adding CSS & JavaScript to the Control Panel. If you're building an addon, please see our [Vite Tooling](/addons/vite-tooling) guide instead.\n:::\n\n## Setting up Vite {#using-vite}\n[Vite](https://vite.dev) is the recommended frontend build tool in the Statamic and Laravel ecosystems. \n\nTo set up Vite for the Control Panel, run the setup command:\n\n```bash\nphp please setup-cp-vite\n```\n\nIt will install the necessary dependencies, create a `vite-cp.config.js` file, and publish any necessary stubs.\n\nYou can add any CSS to the `resources/css/cp.css` file, and any JavaScript to the `resources/js/cp.js` file. \n\nTo start Vite, run `npm run cp:dev` and to build for production, run `npm run cp:build`.\n\n## HMR and Vue Devtools\n\nTo use Hot Module Reloading (HMR) or the [Vue Devtools](https://devtools.vuejs.org) browser extension, you will need to publish a special \"dev build\" of Statamic.\n\nYou can do this via the `vendor:publish` command:\n\n```\nphp artisan vendor:publish --tag=statamic-cp-dev\n```\n\nAlternatively, it can be symlinked:\n\n```\nln -s /path/to/vendor/statamic/cms/resources/dist-dev public/vendor/statamic/cp-dev\n```\n\nStatamic will use the dev build as long as `APP_DEBUG=true` in your `.env` and the `public/vendor/statamic/cp-dev` directory exists. You **shouldn't** commit these or use this on production.\n\n## Inertia\n\nThe Control Panel is powered by [Inertia.js](https://inertiajs.com), which lets Statamic render pages as Vue components while still using Laravel’s server-side routing. Using Inertia for your custom pages is strongly recommended if you want them to match the SPA-like behaviour seen throughout the Control Panel.\n\nTo expose a Vue page component to Statamic, register it in your `cp.js` file:\n\n```js\nimport Foo from './pages/Foo.vue';\n\nStatamic.booting(() => {\n    Statamic.$inertia.register('app::Foo', Foo);\n});\n```\n\nThen return that page from your controller:\n\n```php\nuse Inertia\\Inertia;\n\nreturn Inertia::render('app::Foo', [\n    'message' => 'Hello world!',\n]);\n```\n\nAll data passed to `Inertia::render()` becomes props on the Vue component.\n\nFor proper SPA behaviour, make sure your page uses Inertia’s `<Head>` component to set the document title, and use `<Link>` instead of `<a>` so navigation stays instant and avoids a full refresh:\n\n```vue\n<script setup>\nimport { Head, Link } from '@statamic/cms/inertia';\n</script>\n\n<template>\n    <Head title=\"Foo\" />\n\n    <Link :href=\"cp_url('bar')\">Go to another page</Link>\n</template>\n```\n\n## Using `<script>` tags in the Control Panel\n\nFor externally-hosted scripts, you may register assets to be loaded in the Control Panel with the `externalScript` method. This method accepts the URL of an external script.\n\n\n```php\nuse Statamic\\Statamic;\n\nclass AppServiceProvider\n{\n    public function boot()\n    {\n        Statamic::externalScript('https://kit.fontawesome.com/5t4t4m1c.js');\n    }\n}\n```\n\nOtherwise, for inline scripts, you may use the `inlineScript` method. You should omit the `<script>` tags.\n\n```php\nuse Statamic\\Statamic;\n\nclass AppServiceProvider\n{\n    public function boot()\n    {\n        Statamic::inlineScript('window.Beacon(\"init\", \"abc123\")');\n    }\n}\n```"
  },
  {
    "path": "content/collections/pages/customizing-the-cp-nav.md",
    "content": "---\nid: 2ce74b48-d3cc-4b8a-a8d4-f514c0b1d6ff\nblueprint: page\ntitle: Customizing the Control Panel Navigation\nintro: The Control Panel (CP) navigation is quite customizable. You can add your own sections, pages, and subpages, as well as hide or modify existing ones.\ntemplate: page\n---\n\n:::tip\nThis page refers to the Control Panel's side-bar navigation. Not to be confused with [\"Navs\"](/navigation), where you can create trees to be used for the front-end of your site.\n:::\n\n## Accessing CP Nav Preferences\n\nUsers can access CP Nav preferences through the sidebar under `Settings > Preferences`.\n\n<figure>\n    <img src=\"/img/cp-nav-preferences.webp\" alt=\"CP Nav Preferences\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/cp-nav-preferences-dark.webp\" alt=\"CP Nav Preferences\" class=\"u-hide-in-light-mode\">\n    <figcaption>Manage your own CP nav!</figcaption>\n</figure>\n\n## Customizing CP Nav Preferences For Other Users\n\nIn order to customize CP nav preferences for other users, you must first enable [Statamic Pro](/tips/how-to-enable-statamic-pro), and you must either be a super user or have permissions to manage preferences.\n\n<figure>\n    <img src=\"/img/manage-preferences-permission.webp\" alt=\"Manage Preferences Permission\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/manage-preferences-permission-dark.webp\" alt=\"Manage Preferences Permission\" class=\"u-hide-in-light-mode\">\n    <figcaption>Are you rad enough to manage global preferences?</figcaption>\n</figure>\n\nThis will allow you to customize the default CP nav for all users, or on a role-by-role basis, though end-users will still have the ability to further customize their own CP nav as they see fit.\n\n<figure>\n    <img src=\"/img/preferences-other-users.webp\" alt=\"CP Nav Preferences for Other Users\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/preferences-other-users-dark.webp\" alt=\"CP Nav Preferences for Other Users\" class=\"u-hide-in-light-mode\">\n    <figcaption>Manage the default CP nav for other users!</figcaption>\n</figure>\n\n## Extending Via Addon\n\nOn top of allowing the end user to customize their CP nav, Statamic also provides PHP helpers for [extending the CP navigation](/extending/cp-navigation) within an addon.\n"
  },
  {
    "path": "content/collections/pages/dashboard.md",
    "content": "---\ntitle: Dashboard\nintro: The dashboard is a user-customizable screen containing widgets. Lots of widgets, few widgets, custom widgets, or prebuilt widgets. All kinds of widgets.\ntemplate: page\nblueprint: page\nid: 249e046f-a9b4-494b-9e4d-084c28e01028\n---\n## Overview\n\nWhen you log in to the control panel, you'll see the dashboard, which is a customizable screen. At first, you'll find a Getting Started message like the one below, but if you're feeling widget-y, you can add widgets to the dashboard.\n\n<figure>\n    <img src=\"/img/dashboard.webp\" alt=\"Statamic Global Set Example\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/dashboard-dark.webp\" alt=\"Statamic Global Set Example\" class=\"u-hide-in-light-mode\">\n    <figcaption>The default dashboard with no widgets</figcaption>\n</figure>\n\n## Widgets\n\nA widget can contain just about anything. _ANYTHING_ From a list of recent entries to an embedded iframe playing [Poolside.fm](https://poolside.fm). However, it probably makes sense to make and use widgets that have _something_ to do with your site. Like seeing draft or scheduled entries, recent form submissions, and if there are any software updates.\n\nStatamic comes bundled with a [handful of widgets](/widgets), and you may also [create your own](/extending/widgets) or use ones created by others.\n\n## Configuration\n\nWidgets can be added to the dashboard by modifying the `widgets` array in `config/statamic/cp.php`.\n\n``` php\n'widgets' => [\n    [\n        'type' => 'collection',\n        'collection' => 'blog',\n        'width' => 50\n    ],\n    [\n        'type' => 'collection',\n        'collection' => 'pages',\n        'width' => 50\n    ],\n],\n```\n\nEach item in the array should specify the widget as `type` along with any widget-specific settings. You can find what values are available on the respective widget's documentation page.\n\nYou may use the same widget multiple times, configured in different ways.\n\nEach widget may have a `width` defined as a percentage.\n`25`, `33`, `50`, `66`, `75`, and `100` (the default).\n\nFor widgets not requiring any configuration you can provide the string instead of an array, like this:\n\n``` php\n'widgets' => [\n    'updater', // [tl! focus]\n    [\n        'type' => 'collection',\n        'collection' => 'blog',\n        'width' => 50\n    ],\n    [\n        'type' => 'collection',\n        'collection' => 'pages',\n        'width' => 50\n    ],\n],\n```"
  },
  {
    "path": "content/collections/pages/data-inheritance.md",
    "content": "---\nid: 3d5efc5c-17b1-480b-bb77-53faf3d9552c\nblueprint: page\ntitle: 'Data Inheritance'\nintro: 'Statamic sets data in a series of scopes that can inherit and override each other in order. We call this data inheritance model **The Cascade**.'\ntemplate: page\n---\n## Overview\n\nStatamic provides a unique approach to data inheritance. The value of any given variable in your views can depend on the URL you're on. If a value of a variable doesn't exist on an entry URL, Statamic will check for a fallback value. If that fallback doesn't exist, it will fall back further, and so on. If it never finds anything, the value is `null`.\n\nWe call this fallback logic \"the cascade\", because the value of any given variable \"cascades\" down from the \"top\" until it finds where it's defined.\n\nThis approach allows you to create views that are less repetitive and are easier to read because a \"missing\" variable will never throw an error, it will only ever be null.\n\n:::tip\nYou can easily set variable fallbacks and \"catch\" the first value that exists without having to write a series of ugly `if/else` conditions.\n\n::tabs\n\n::tab antlers\n```antlers\n<h1>{{ nav_title ?? breadcrumb_title ?? title }}</h1>\n```\n::tab blade\n```blade\n<h1>{{ $nav_title ?? $breadcrumb_title ?? $title }}</h1>\n```\n::\n:::\n\n## Cascade order\n\nHere's the cascading order in which Statamic will look for the value of a given variable:\n\n1. Are we inside a [partial](/tags/partial)? If so, has the variable been explicitly passed in?\n2. If not, has it been set in a [ViewModel](/view-models)?\n3. If not, has it been set on the [entry](/collections)?\n4. If not, and this entry has been translated from another [origin](/multi-site), is it set on the origin entry?\n5. If not, is it set on the collection via [inject](/collections#inject)?\n6. If not, is it a [global variable](/globals)?\n7. If not, is it a [system variable](/variables)?\n8. Well okay then, `null` it is.\n"
  },
  {
    "path": "content/collections/pages/data.md",
    "content": "---\nid: 0267eb5a-f54b-4e3f-bef0-1f16762720b1\ntitle: Data Retrieval and Manipulation\nintro: One of the most crucial aspects of extending a Content Management System is being able to retrieve the data and manipulate it. Statamic has a number of classes to provide you with ways to handle these sorts of situations.\n---\n\nConsider the various aspects of Statamic: Entries, Terms, Globals, and Assets. They are all Data. Data can have variables/fields that you can get, set, etc.\n\n## Facade Primer\n\nIn most cases, the first point of contact with Statamic functionality will be through a Facade.\n\nYou can find more details on which ones to use later, but you will find them all in the `Statamic\\Facades` namespace. Of course there are exceptions, but in most cases you will be looking for a Facade.\n\nEach facade will proxy method calls to another class. You can see which class by looking for the `getFacadeAccessor` method.\n\nSome will be simple, direct class mappings, like the [YAML facade](https://github.com/statamic/cms/blob/master/src/Facades/YAML.php#L17-L20).\n\n``` php\nStatamic\\Facades\\YAML::parse();\n// This calls the `parse` method on an instance of `Statamic\\Yaml\\Yaml`\n```\n\nSome reference a contract, which could change depending on how an application is configured, like the [Entry facade](https://github.com/statamic/cms/blob/master/src/Facades/Entry.php#L27-L30). This class references the `EntryRepository` contract, which by default is bound to the Stache implementation, but could be changed to use databases, etc.\n\n``` php\nStatamic\\Facades\\Entry::make();\n// This calls the `make` method on an instance of `Statamic\\Contracts\\Entries\\EntryRepository`\n// By default it's `Statamic\\Stache\\Repositories\\EntryRepository`, but could change.\n```\n\nThe facades will have a `@see` annotation in their docblock to give you a hint on where to look.\n\n\n## Retrieving Data\n\nYou should retrieve data using Facade methods. If you’ve used Laravel, it should feel similar to Eloquent. If it helps, try thinking of each data type mentioned above as a Model. We have a Facade for each of those.\n\nFor example, this will find an entry with an ID of `f6d5a87`.\n\n``` php\n$entry = \\Statamic\\Facades\\Entry::find('f6d5a87');\n```\n\nEach data type may have more methods for retrieving data. For example, you can find an entry by its URI.\n\n``` php\nEntry::findByUri('/clothing/shoes');\nEntry::findByUri('/vetements/chaussures', 'french'); // For multisite\n```\n\nMost of them also have dedicated query builders which you can use with the `query` method. Then you may craft a query just like Laravel:\n\n```php\nEntry::query()\n    ->where('collection', 'clothing')\n    ->where('slug', 'shoes')\n    ->first();\n```\n\nLike Laravel, if you’re expecting a collection of models, you will receive a collection. However, Statamic will give you a subclass like `EntryCollection` which will do everything `Illuminate\\Support\\Collection` does [(docs)](https://laravel.com/docs/collections), with a few more contextual methods at your disposal should you need them.\n\nIf you’re expecting a single model you’ll get the corresponding class. (In the example above, you'll get a `Statamic\\Entries\\Entry` instance).\n\nOnce you have your objects, you may get data out of them in [a handful of ways](#getting-field-data).\n\n\n## Manipulating Data\n\nOnce you have a data instance, you can go to town on it.\n\n``` php\n$entry->set('foo', 'bar');\n```\n\nThis is like adding `foo: bar` to the front-matter of the entry file.\n\nWant to change nested data? That works too.\n\nSince you can only _set_ the top-level field, you'll need to get the existing top-level value, update the nested part, and then re-set the top-level field again.\n\n```php\n$values = $entry->get('top_level_value');\n$values[0]['nested_value'] = 'foo';\n$entry->set('top_level_value', $values);\n```\n\nOnce you’re done, go ahead and save it.\n\n``` php\n$entry->save();\n```\n\nNow it’ll be written to file. Nice.\n\n### Events\nWhen you are saving or creating your data instance, the `EntrySaving`, `EntryCreated` and the `EntrySaved` events are dispatched.  In some cases, you would rather suppress those events. For example, to prevent causing an infinite loop of `EntrySaved` events.\n``` php\n$entry->saveQuietly();\n```\n\nSimilarly, when deleting data, the `EntryDeleting` and `EntryDeleted` events are dispatched. To surpress those events, use the `deleteQuietly` method:\n```php\n$entry->deleteQuietly();\n```\n\n## Creating Data\n\nOf course, the data had to get there somehow. You can also create data using the corresponding facades.\n\nEach of them has a `make` method that will give you a new instance.\nOnce you have an instance, you can manipulate it using various methods the same way as if it already existed. Most of the time, these are chainable to give you a nice fluent interface:\n\n``` php\nuse Statamic\\Facades\\Entry;\n\n$entry = Entry::make()\n            ->published(true)\n            ->data(['title' => 'About us', 'subtitle' => 'We are awesome'])\n            ->etc(); // and so on...\n\n$entry->save();\n```\n\n:::tip\nMake sure to use the `make` method, rather than simply `new`'ing up a class. For example, if a user has customized their application to store entries in a database, they will have a different Entry class. Using `Entry::make()` will make sure to get the correct class.\n:::\n\n## Getting Field Data\n\nMore often than not, you'll want to use the \"standard\" way of getting data out of items like entries. Other less common ways are explained further down the page.\n\n### Standard\n\nYou can use property access to get a single field's augmented value:\n\n```yaml\nid: 123\ntitle: My post\ncontent: |\n  # Heading\n  The post content.\nrelated_posts: [2, 3, 4]\n```\n\n```php\n$entry->id; // 123\n$entry->title; // \"My post\"\n$entry->content; // \"<h1>Heading</h1><p>The post content.</p>\n$entry->related_posts; // EntryCollection([Entry, Entry, Entry])\n```\n\nThis will be the value of the field, factoring in inheritance, and will perform any required [augmentation](/extending/augmentation).\n\n### Relationships\n\nAny fields with query builders (like the [Entries](/fieldtypes/entries) fieldtype) will be available using the method for the corresponding field. This will allow you to further refine your results.\n\n```php\n$entry->related_posts() // EntryQueryBuilder\n      ->where('published', true)->get(); // EntryCollection([Entry, Entry])\n```\n\nAs shown in the earlier example, if you use the property, it will just give you the results without you needing to manually complete the query.\n\n```php\n$entry->related_posts; // EntryCollection([Entry, Entry, Entry])\n```\n\n### Data\nThis would be the data defined directly on the item, like what you'd find in an entry's YAML front-matter. (Some specific keys may be stripped out, like an entry's `id`).\n\n```yaml\nid: 123\ntitle: My post\ncontent: |\n  # Heading\n  The post content\n```\n\n```php\n$entry->data();\n// Illuminate\\Support\\Collection([\n//    'title' => 'My post',\n//    'content' => \"# Heading\\nThe post content'\n// ])\n```\n\nYou can use the `get` method to get a single field's data.\n\n```php\n$entry->get('title') // 'My post'\n$entry->get('content') // \"# Heading\\nThe post content\"\n```\n\nThis does not factor in inheritance or augmentation.\n\n### Values\nThe \"values\" are similar to data, except they will also inherit from any originating items. For example, if an entry has been localized\nfrom another entry.\n\n```yaml\nid: 123\ntitle: My post\ncontent: The post content\nimage: post.jpg\n```\n\n```yaml\nid: 456\norigin: 123\ntitle: My localized post\n```\n\n```php\n$entry->values();\n// Illuminate\\Support\\Collection([\n//    'title' => 'My localized post',\n//    'content' => 'The post content'\n//    'image' => 'post.jpg',\n// ])\n```\n\nYou can use the `value` method to get a single field's value.\n\n```php\n$entry->value('title'); // 'My localized post'\n$entry->value('image'); // 'post.jpg'\n```\n\n### Augmented Values\n\nIf you want to get the [Value instances](/extending/augmentation#value) for the fields, you may use the following methods.\n\n:::tip\nMost of the time, you probably **don't** need to reach for these. Using property access will get the underlying augmented value.\n\n```php\n$entry->title; // \"Post title\"\n$entry->image; // Asset\n$entry->related_posts; // EntryCollection(Entry, Entry, ...)\n```\n:::\n\nYou can get a single augmented value instance:\n\n```php\n$instance = $entry->augmentedValue('image'); // Statamic\\Fields\\Value({ raw: \"post.jpg\", fieldtype: \"assets\" })\n$instance->value(); // Asset\n```\n\nAll the available augmented values. Each item in the returned collection will be a `Value` instance.\n\n``` php\n$entry->toAugmentedCollection();\n// AugmentedCollection([\n//    'title' => Value('The post title'),\n//    'content' => Value(\"# Heading\\nSome content\"),\n//    'collection' => Value(Statamic\\Entries\\Collection),\n//    'uri' => Value('/posts/my-post'),\n//    ...etc...\n// ])\n```\n\nOr a subset of augmented values:\n\n```php\n$entry->toAugmentedCollection(['title', 'collection']);\n// AugmentedCollection([\n//    'title' => Value('The post title'),\n//    'collection' => Value(Statamic\\Entries\\Collection),\n// ])\n```\n\nThe `toAugmentedArray` method does the same as `toAugmentedCollection`, except that it returns an array.\n\n\n### Checking for data changes\n\nStatamic provides `isDirty`, `isClean`, and `getOriginal` methods to let you see what data has been changed in your item since it was originally retrieved.\n\nThe `isDirty` method checks if any of the item's data has been changed since it was last saved. You may pass a specific attribute name or an array of attributes to the `isDirty` method to determine if any of the attributes are \"dirty\". The `isClean` method will determine if an attribute has remained unchanged since the item was retrieved. This method also accepts an optional attribute argument:\n\n```php\n$entry->title; // \"Post title\"\n$entry->image; // \"/path/to/image.jpg\"\n\n$entry->title = 'New Title';\n\n$entry->isDirty(); // true\n$entry->isDirty('title'); // true\n$entry->isDirty('image'); // false\n$entry->isDirty(['image', 'title']); // true\n\n$entry->isClean(); // false\n$entry->isClean('title'); // false\n$entry->isClean('image'); // true\n$entry->isClean(['image', 'title']); // false\n\n$entry->save();\n\n$entry->isDirty(); // false\n$entry->isClean(); // true\n```\n\nThe `getOriginal` method returns an array containing the original attributes of the item regardless of any changes to the item since it was retrieved. This method also accepts an optional attribute argument:\n\n```php\n$entry->title; // \"Post title\"\n$entry->image; // \"/path/to/image.jpg\"\n\n$entry->title = 'New Title';\n\n$entry->getOriginal('title'); // \"Post title\"\n$entry->getOriginal(); // [\"Post title\", \"/path/to/image.jpg\"]\n```\n"
  },
  {
    "path": "content/collections/pages/debugging.md",
    "content": "---\ntitle: Debugging\nintro: Debugging is the secret art of the experienced developer. The ability to inspect stack traces, rifle through response objects, and dump data to the screen is often the quickest way to get yourself unstuck and back on track. Here are some tools Statamic provides to help you debug.\ntemplate: page\nid: 7fb5f2df-de3e-480f-a613-f38a9109e8d8\nblueprint: page\n---\n## Ignition debug screens\n\n[Ignition][ignition] is an included Laravel package for debugging exceptions. It provides a clean and organized stack trace, code snippets, information about the request, and even the ability to share the error message with others.\n\nTo enable Ignition, set `APP_DEBUG=true` in your [.env](/configuration#environment-variables) file.\n\n<figure>\n    <img src=\"/img/ignition-collection.png\" alt=\"Ignition error screen showing a typo in a collection tag.\">\n    <figcaption>This is Ignition. Isn't it pretty for an error screen?</figcaption>\n</figure>\n\n<div x-data=\"{ showClippy: false }\">\n    <p>Statamic will try to detect why you're receiving a specific exception and provide a <strong>solution</strong> for the problem along with a link to the most relevant documentation if possible. It's like <a href=\"\" x-on:click.prevent=\"showClippy = true\">Clippy</a>, but 80% less annoying.</p>\n    <img src=\"/img/clippy-docs.gif\" class=\"clippy\" x-bind:class=\"{ 'visible': showClippy }\">\n</div>\n\n\n## Fake SQL queries\n\nBy default, Statamic doesn't use a database, so our query builders don't actually execute SQL queries. However, we \"fake\" the queries so that they appear in your preferred debugging tools like [Ray](https://myray.app) or Debugbar (more on that below).\n\nThey are enabled when you're in debug mode, but if you'd like to disable them you can do so in `config/statamic/system.php`:\n\n```php\n'fake_sql_queries' => false,\n```\n\n\n## Debug bar\n\nThe debug bar is a convenient way to explore many of the things happening in any given page request. You can see data Statamic is fetching, which views are being rendered, information on the current route, available variables, user's session, request data, and more.\n\n<figure>\n    <img src=\"/img/debug-bar.png\" alt=\"Debug bar showing available variables\">\n    <figcaption>The debug bar is like X-Ray vision into many of Statamic's inner workings.</figcaption>\n</figure>\n\n### Benchmarking response times\n\nYou can see all of the major operations performed on a given page request in the **Timeline** tab, which can help with fine-tuning and performance optimization.\n\n<figure>\n    <img src=\"/img/debug-bar-timeline.png\" alt=\"Debug bar the timeline\">\n    <figcaption>Slow tags or operations are candidates for caching or indexing tweaks.</figcaption>\n</figure>\n\n### Exploring data\n\nAny variables defined in a [blueprint](/blueprints) will be shown as a `Value` object in the Variables tab. They can be expanded to see their \"raw\" original data, as well what fieldtype they're managed by, and their [augmented](/augmentation) value.\n\n### How to enable the debug bar\n\nYou need to require the package with [Composer][composer].\n\n``` shell\ncomposer require --dev barryvdh/laravel-debugbar\n```\n\nAnd then enable it in your `.env` file:\n\n```env\nAPP_DEBUG=true\nDEBUGBAR_ENABLED=true\n```\n\n:::warning\nThe debug bar injects JavaScript into the page and adds significant overhead server-side work to each request. **Make sure to turn it off when you're testing your site's performance!**\n:::\n\n## Dump modifier\n\nWhen working in [Antlers](/antlers) templates, you can slap the [dump modifier](/modifiers/dump) onto any variable to explore its contents. Here's an example.\n\n``` yaml\nbands:\n  good:\n    - Goo Goo Dolls\n    - Oasis\n    - Third Eye Blind\n  bad:\n    - Creed\n    - Hanson\n    - The Offspring\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ bands | dump }}\n```\n::tab blade\n```blade\n@dd($bands)\n```\n::\n\n``` php\n// Dumped output\narray:6 [▼\n  \"good\" => array:3 [▶],\n  \"bad\" => array:3 [▶]\n]\n```\n\n## Logs\n\nStatamic uses Laravel's logging handling system to log messages and errors to file, debug service, or even Slack. The default behavior logs these messages to files stored in `storage/logs`. Each day gets its own separate file so it can feel special (and make it easier to find what you're looking for).\n\nLearn more about [configuring other logging channels](https://laravel.com/docs/logging#configuration) on the Laravel docs.\n\n### Viewing logs in the debug bar\n\nYou can enable logs in the Debug Bar in its config file. If you haven't already published the config, you can do so from the command line:\n\n```cli\nphp artisan vendor:publish --provider=\"Barryvdh\\Debugbar\\ServiceProvider\"\n```\n\nAnd then enable the \"Logs\" collector in your new `config/debugbar.php` config file.\n\n```php\n'collectors' => [\n        ...\n        'laravel'         => true, // Laravel version and environment\n        'events'          => true, // All events fired\n        'default_request' => true, // Regular or special Symfony request logger\n        'logs'            => true, // Add the latest log messages [tl! **]\n        'files'           => false, // Show the included files\n        'config'          => false, // Display config settings\n        ...\n    ],\n```\n\n## Laravel Nightwatch\n\n[Laravel Nightwatch](https://nightwatch.laravel.com) is a monitoring service that captures requests, queries, cache events, queued jobs, and more. It works with Statamic, but because Statamic leans heavily on the cache layer, a default install can burn through your event quota quickly.\n\nSee [Using Statamic Alongside Laravel Nightwatch](/tips/using-statamic-with-laravel-nightwatch) for recommended filters to keep your event volume under control.\n\n## Laravel Telescope\n\nStatamic supports [Laravel Telescope][telescope], an elegant debug assistant for the Laravel framework. It's most useful when you're building addons or doing custom Laravel things outside the normal Statamic site scope.\n\nFollow along with Laravel's documentation and you'll (probably) be up and running in no time.\n\n<figure>\n    <img src=\"/img/laravel-telescope.png\" alt=\"Laravel Telescope showing nothing particularly interesting\">\n    <figcaption>Laravel Telescope in action. But just barely.</figcaption>\n</figure>\n\n[composer]: https://getcomposer.org/\n[ignition]: https://flareapp.io/docs/ignition-for-laravel/introduction\n[telescope]: https://laravel.com/docs/telescope\n"
  },
  {
    "path": "content/collections/pages/deploying.md",
    "content": "---\ntitle: 'Deploying'\nintro: Statamic is a modern PHP application, built as a [Laravel](https://laravel.com) package, and can be deployed like any standard Laravel application or as a static site. Here are some common deployment solutions and workflow recommendations.\ntemplate: deploying\nblueprint: page\ncontent_width: max-w-4xl\nid: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n## Overview\n\nDeployments are the process of moving the Statamic site you've built — whether on your local computer or distributed across a team — to the production (aka \"live\") server for the whole world (wide web) to see.\n\nAt its most simple level, it's just the act of moving files from your computer to another computer — the web server. There are many ways to do it, from a slow and painfully manual FTP upload (please don't), to a lightning quick git push with auto-deployment.\n\nThe deployment options available to you will depend on the type of hosting scenario you're using.\n\n"
  },
  {
    "path": "content/collections/pages/dictionaries.md",
    "content": "---\nid: d0668b6e-915b-46da-863e-51fec54b02e2\nblueprint: page\ntitle: Dictionaries\ntemplate: page\nintro: 'Dictionaries add options to the [Dictionary](/fieldtypes/dictionary) fieldtype.'\n---\n## Overview\n\nDictionaries come in two \"flavors\" depending on which class you extend from.\n\nWith a `BasicDictionary`, you only really need to define the items. The options, searching, and GraphQL behavior is all handled automatically.\n\nIf you need more control, you can either override methods, or extend the base `Dictionary` class and do it yourself.\n\n:::tip\nYou might not even need a custom dictionary. The native [File dictionary](/fieldtypes/dictionary#file) allows you simply provide a JSON, YAML, or CSV file to use a source of options.\n:::\n\n\n## Basic Dictionaries\n\nYou may create a dictionary using the following command, which will generate a class in the `App\\Dictionaries` namespace.\n\n```shell\nphp please make:dictionary\n```\n\nYou may generate it into an addon using the `--addon=vendor/package` option, which will generate it into your addon's `Dictionaries` namespace.\n\n```php\n<?php\n\nnamespace App\\Dictionaries;\n\nuse Statamic\\Dictionaries\\BasicDictionary;\n\nclass States extends BasicDictionary\n{\n    protected function getItems(): array\n    {\n        return [\n            ['label' => 'Alabama', 'value' => 'AL', 'capital' => 'Montgomery'],\n            ['label' => 'Alaska', 'value' => 'AK', 'capital' => 'Juneau'],\n            ['label' => 'Arizona', 'value' => 'AZ', 'capital' => 'Phoenix'],\n            // ...\n        ];\n    }\n}\n```\n\n### Item data\n\nIn the example above, you can see that each item has a `label` and `value`. These will be used in the dropdown field. Any additional keys will be available within templates.\n\nHere we are returning a hardcoded array. But in reality you may be getting options from somewhere like a file, database, or an API:\n\n```php\nprotected function getItems(): array\n{\n    return Product::all()->toArray();\n}\n```\n\n\n### Values and Labels\n\nBy default, the `value` and `label` keys will be used. However, you may remap them:\n\n```php\nprotected string $valueKey = 'abbr';\nprotected string $labelKey = 'name';\n\nprotected function getItems(): array\n{\n    return [\n        ['name' => 'Alabama', 'abbr' => 'AL', 'capital' => 'Montgomery'],\n        // ...\n    ];\n}\n```\n\n\nIf you require more logic, you can override the `getItemValue` and/or `getItemLabel` methods:\n\n```php\nprotected function getItemLabel(array $item): string\n{\n    return $item['name'] . ' (' . $item['label'] . ')'; // \"Alabama (AL)\"\n}\n```\n\n### Basic Search\n\nBy default, when a user searches the field, a basic search will be performed by checking against each item's values.\n\nYou may use the `searchable` property to narrow down which fields should be searched.\n\n```php\nprotected array $searchable = ['name', 'abbr'];\n```\n\nAlternatively, you may customize how the match is performed by overriding the `matchesSearchQuery` method:\n\n```php\nprotected function matchesSearchQuery(string $query, Item $item): bool\n{\n    return str_contains($item['name'], $query);\n}\n```\n\n## Options\n\nThe `options` method controls what is selectable within the fieldtype. This method should return an array of value/label pairs.  \n\n```php\npublic function options(?string $search = null): array\n{\n    return [\n        'one' => 'Option One',\n        'two' => 'Option Two',\n    ];   \n}\n```\n\nThis array's keys define what will be stored in the content.\n\n### Search\n\nThe `options` method will be passed a `$search` string if the user is searching within the fieldtype. You should filter your options based on this search term.\n\n## Items\n\nThe `get` method accepts a value (one of the option's keys) and should return an `Item` instance.\n\nAn `Item` requires the value, label, and optionally an array of any additional data.\n\nIn the following example we assume a product ID was saved to the content, the product name is the label, and price/sku is extra.\n\n```php\npublic function get(string $key): ?Item\n{\n    $product = Product::find($key);\n\n    return new Item($key, $product->name, [\n        'price' => $product->price,\n        'sku' => $product->sku,\n    ]);\n}\n```\n\n## Config\n\nYou may define config fields in order for the user to customize functionality of your dictionary. For example, if you are providing products, you may want to allow the user to select a category to narrow down the options.\n\n```php\nprotected function fieldItems()\n{\n    return [\n        'category' => [\n           'type' => 'select',\n           'options' => ['clothing', 'accessories']\n        ]\n    ];\n}\n```\n\nThe user's configuration values will be available in your class within the `config` property.\n\n```php\n$this->config['category'];\n```\n\n## GraphQL\n\nA dictionary will automatically get a GraphQL type named `Dictionary_YourClass`. Within it, you're able to query the item's fields, like so:\n\n```graphql\nyour_dictionary_field {\n  id\n  price\n}\n```\n\nBy default, the base `Dictionary` class will provide the GraphQL schema for nested fields automatically. It does this by looking up the first item. You may wish to override this and provide your own schema.\n\n```php\nprotected function getGqlFields(): array\n{\n    return [\n        'id' => [\n            'type' => GraphQL::nonNull(GraphQL::string()),\n            'resolve' => fn (Item $item, $args, $context, $info) => $item['id'];\n        ],\n        'price' => [\n            'type' => GraphQL::nonNull(GraphQL::int()),\n            'resolve' => fn (Item $item, $args, $context, $info) => $item['price'];\n        ],\n        // ...\n    ];\n}\n```\n\n## Full Example\n\nHere is an example dictionary class that will use the [MusicBrainz](https://musicbrainz.org/) API to create a dictionary of musicians/artists.\n\n```php\n<?php\n\nnamespace App\\Dictionaries;\n\nuse Illuminate\\Support\\Facades\\Cache;\nuse Illuminate\\Support\\Facades\\Http;\nuse Statamic\\Dictionaries\\Dictionary;\nuse Statamic\\Dictionaries\\Item;\n\nclass Artists extends Dictionary\n{\n    public function options(?string $search = null): array\n    {\n        if (! $search) {\n            return [];\n        }\n\n        $response = Http::get('https://musicbrainz.org/ws/2/artist/?fmt=json&query=artist:'.urlencode($search))->json();\n\n        return collect($response['artists'])->mapWithKeys(function ($artist) {\n            $label = $artist['name'];\n\n            if ($disambiguation = $artist['disambiguation'] ?? null) {\n                $label .= ' ('.$disambiguation.')';\n            }\n\n            return [$artist['id'] => $label];\n        })->all();\n    }\n\n    public function get(string $key): ?Item\n    {\n        return Cache::rememberForever('artist-'.$key, function () use ($key) {\n            $response = Http::get('https://musicbrainz.org/ws/2/artist/'.$key.'?fmt=json')->json();\n\n            return new Item($key, $response['name'], [\n                'name' => $response['name'],\n                'disambiguation' => $response['disambiguation'] ?? null,\n                'type' => $response['type'],\n                'country' => $response['country'],\n            ]);\n        });\n    }\n}\n```\n"
  },
  {
    "path": "content/collections/pages/digital-ocean.md",
    "content": "---\nid: d4a54957-9863-471a-a188-d06b0e0cd48d\nblueprint: page\ntitle: 'How to Install Statamic on Digital Ocean'\nbreadcrumb_title: 'Digital Ocean'\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\nintro: 'A full walk-through for installing, configuring, and running Statamic on a Digital Ocean Ubuntu virtual private server.'\n---\n## Prerequisites\n\nThere is only one prerequisite for this guide. You must have:\n\n- A Digital Ocean account ([this signup link](https://m.do.co/c/6469827e2269) will give you $100 in free credit)\n\n## Server Setup\n\nFollow the official [How To Set Up an Ubuntu Server on a DigitalOcean Droplet](https://www.digitalocean.com/community/tutorials/how-to-set-up-an-ubuntu-server-on-a-digitalocean-droplet) guide to setup your server.\n\n<a href=\"https://m.do.co/c/6469827e2269\"><img src=\"https://images.prismic.io/digitalocean/0b619d51-a723-4748-997f-39ed5697a540_intro-to-cloud.jpg?auto=compress,format\" class=\"rounded-lg\"></a>\n\n## Secure Your Server\n\nIf you want to take your security to the next level, check out this [official Securing Your Droplet](https://www.digitalocean.com/community/tech_talks/securing-your-droplet) talk from DigitalOcean's Developer Advocate.\n\nIt's always better to be safe than sorry.\n\n## Install Statamic on Ubuntu\n\nNow that you have a secure server with Ubuntu, follow our [Ubuntu instructions](/installing/ubuntu) to install Statamic."
  },
  {
    "path": "content/collections/pages/digitalocean.md",
    "content": "---\nid: 79d022e5-8fb0-4d20-955d-801e0edafa61\npublished: false\nblueprint: page\ntitle: 'Deploying Statamic to Digital Ocean'\nparent: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n## Create a Droplet.\nWe’ll start by spinning up a new droplet – the DigitalOcean lingo for a cloud server. Go ahead and sign into your DigitalOcean account and create a new droplet.\n\n<figure>\n    <img src=\"/img/deploying/digitalocean/create-droplet.png\" alt=\"Create a new Droplet\" width=\"395\">\n</figure>\n\n### 1-Click App\n\nWe’ll be making use of 1-Click Apps to quickly spin up our server. So under **Choose an image**, click on the Marketplace tab and search for `Laravel`, and select it from the result.\n\n<figure>\n    <img src=\"/img/deploying/digitalocean/laravel-droplet.png\" alt=\"Laravel Droplet\">\n</figure>\n\n:::tip\nLearn more about the [Laravel droplet](https://marketplace.digitalocean.com/apps/laravel)!\n:::\n\n### Plan Selection\n\nNext, choose the basic $5/month plan, which is suitable for most sites without large amounts of traffic.\n\n<figure>\n    <img src=\"/img/deploying/digitalocean/choose-plan.png\" alt=\"Choose a Plan\">\n    <figcaption>You could get fancy with a Premium Intel SSD. That's up to you.</figcaption>\n</figure>\n\n### Datacenter Region\n\nFor the datacenter region, choose the location closest to your main audience. When in doubt, go with New York 1 or 3.\n\n### SSH Key\n\nIf you've already added SSH keys to DigitalOcean before, you can choose from those. Otherwise, you'll need to click on the **New SSH Key** Button to add a new SSH key. You can copy your local SSH key to the clipboard by running the following command:\n\n```shell\npbcopy < ~/.ssh/id_rsa.pub && echo Key copied! You look nice today, btw.\n```\n\nPaste the gibberish into to the SSH key content field, give it a name, and click **Add SSH Key**.\n\n<figure>\n    <img src=\"/img/deploying/digitalocean/add-ssh-key.png\" alt=\"Add SSH Key\">\n</figure>\n\n### Hostname\n\nFinally, choose a hostname if you don't want the unimaginative random one provided for you. For example, `pour-some-ubuntu-on-me` is way more fun than `lemp42onubuntu2004-s-1vcpu-1gb-nyc1-01`. When yoy're ready, click the **Create Droplet** button.\n\nYour droplet is now being created. It should take about 2 minutes. Maybe 3 minutes. Could be 4 minutes. They're all very real possibilities.\n\n<!--\n## Creating a Non-Root Admin\n\nIt's generally recommended to carry out tasks on a server as a non-root user with administrative privileges. So let's do that first before we get into the configuration process.\n\nFirst, you'll need to login to the server as `root` via ssh, from your local terminal applicaton. You'll need to know the IP address, which should be visible in the DigitalOcean admin.\n\n```bash\nssh root@IP_ADDRESS\n```\n\nNext, create the new user. You can call it anything you want, you don't have to name everything after 80s–90s icons like we do.\n\n```bash\nadduser hulkhogan\n```\n\nNext, give your user admin privileges. After this step, you'll be able to run `sudo` commands.\n\n```bash\nusermod -aG sudo hulkhogan\n```\n\nNext, set up the SSH key for the new user. Copy that local SSH public key again to your clipboard in a separate terminal window:\n\n```shell\npbcopy < ~/.ssh/id_rsa.pub && echo Key copied! You still look nice, btw.\n```\n\nBack to your remote session, switch to the new user (if you weren't already, automatically).\n\n```bash\nsu - hulkhogan\n```\n\nNow you can create a new directory to store the SSH key, and restrict permissions to it.\n\n```bash\nmkdir ~/.ssh\nchmod 700 ~/.ssh\n```\n\nWithin the `.ssh` directory, create a new file called authorized_keys:\n\n```bash\ntouch ~/.ssh/authorized_keys\n```\n\nOpen the file:\n\n```bash\nvim ~/.ssh/authorized_keys\n```\n\nAnd paste your public key in there. Hit `esc` to stop editing, then `:wq` and `ENTER`. Now you know vim, if you didn't already. Great job!\n\nNext, restrict the permissions of the authorized_keys file with this command:\n\n```bash\nchmod 600 ~/.ssh/authorized_keys\n```\n\nType `exit` command below to return to the root user, and now you can use SSH keys to log in as the new user. Let's make sure it works.\n\n```bash\nssh hulkhogan@IP_ADDRESS\n```\n\nIf you logged in successfully, you're in great shape. If you didn't, start Googling everything you can think of to fix it.\n\nWe'll use this new `hulkhogan` user for the rest of this walk-through.\n-->\n## Log Into the Server\n\nNext, you'll need to login to the server from your local terminal application. You'll need to know the IP address, which is visible in the DigitalOcean admin.\n\n```bash\nssh root@your_droplet_public_ipv4\n```\n\nIf you did not add an SSH key when you created the Droplet, you’ll first be prompted to reset your root password.\n\nThen, the interactive script that runs will first prompt you for your domain or subdomain:\n\n```bash\n--------------------------------------------------\nThis setup requires a domain name.  If you do not have one yet, you may\ncancel this setup, press Ctrl+C.  This script will run again on your next login\n--------------------------------------------------\nEnter the domain name for your new Laravel site.\n(ex. example.org or test.example.org) do not include www or http/s\n--------------------------------------------------\nDomain/Subdomain name:\n```\n\nThe next prompt asks if you want to use SSL for your website via Let’s Encrypt, which we recommend.\n\n:::tip\nThat the DNS for the domain you've specified will need to be configured before being able to issue an SSL cert.\n:::\n\n```bash\nNext, you have the option of configuring LetsEncrypt to secure your new site.  Before doing this, be sure that you have pointed your domain or subdomain to this server's IP address.  You can also run LetsEncrypt certbot later with the command 'certbot --nginx'\n\nWould you like to use LetsEncrypt (certbot) to configure SSL(https) for your new site? (y/n):\n```\n\n\n## Pulling in Statamic\n\nFinally, the fun stuff! Let's pull in your Statamic site using git by cloning it inside `/var/www`.\n\n```bash\ncd /var/www\ngit clone https://github.com/statamic/demo.git\n```\n\nNext, install the Composer dependencies.\n\n```bash\ncd demo\ncomposer install\n```\n\nNow let's set up your `.env` file so you can manage your environment-specific settings.\n\n```bash\ncp .env.example .env\n```\n\nNext, generate an `APP_KEY` env var.\n\n```bash\nphp artisan key:generate\n```\n\nFinally, edit the `.env` file and set the site to production mode by finding and setting the following vars:\n\n```env\nAPP_ENV=production\nAPP_DEBUG=false\n...\nSTATAMIC_FILE_WATCHER=false\n```\n\n## Setting up Nginx\n\nNow it's time to configure Nginx to serve the site. Create a new virtual host configuration file inside `/etc/nginx/sites-available:`\n\n```bash\nsudo vim /etc/nginx/sites-available/demo\n```"
  },
  {
    "path": "content/collections/pages/dirty-state-tracking.md",
    "content": "---\ntitle: Dirty State Tracking\nid: bbf752d3-ffdb-4ac9-a5c5-0b32e3db9d6f\nintro: |\n  Prevent users from accidentally leaving the page and losing their work.\n---\nAccidentally navigating away from a page while you're in the middle of filling out a form could cause you to lose your progress and be really frustrating.\n\nStatamic will pop up a warning if you're in the middle of something and about to leave, to give you a chance to change your mind. You can tap into this feature, too. The component can track the dirty state from multiple places, and will only be considered clean once all of them are clean.\n\n``` js\nimport { dirty } from '@statamic/cms/api';\n\ndirty.add(name); // Mark a thing as dirty\ndirty.remove(name); // Clean it up\n```\n\n:::tip\nIf you're using a [publish container](/extending/publish-forms#container) (which you should if you're making a form), the dirty state will be automatically tracked. Be sure to call `save()` on it when you save and it will mark it as clean.\n:::\n"
  },
  {
    "path": "content/collections/pages/docker.md",
    "content": "---\nid: 18906b4f-be9a-4edb-9bb3-366226863fa2\nblueprint: page\ntitle: 'How to Install Statamic with Docker'\nbreadcrumb_title: Docker\nintro: Docker is an open source project that streamlines the deployment of an application (or OS) inside a Linux Container. Any dockerized image can run on any machine that is running Docker. You can run Statamic with Docker and never have to configure PHP, Nginx, Apache, or anything else on your local machine.\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\n---\n## Overview\n[Laravel Sail](https://laravel.com/docs/13.x/sail) is a light-weight command-line interface for interacting with Laravel's default Docker development environment. Sail provides a great starting point for building Laravel applications without requiring prior Docker experience, and is a perfect fit for Statamic with a few tweaks.\n\nAt its heart, Sail is a `compose.yaml` file and script that is stored at the root of your project. The sail script provides a CLI with convenient methods for interacting with the Docker containers defined by the `compose.yaml` file.\n\nLaravel Sail is supported on macOS, Linux, and Windows (via WSL2).\n\n:::tip\nSince Sail is the starting point for a new Laravel app, we'll be installing Statamic **into** a fresh Laravel app.\n:::\n\n## Installing Docker\n\nIf you don't already have Docker installed, head to [docker.com/get-started](https://www.docker.com/get-started) and download the latest version for your OS.\n\n## Installing Laravel\n\nFollow the install instructions for creating a fresh Laravel app from [their documentation](https://laravel.com/docs/13.x#creating-a-laravel-project).\n\nInstall Laravel Sail into your new Laravel app with no additional services, unless you want to get fancy and use MySQL with Statamic (yes, you can do that).\n\n``` shell\ncomposer require laravel/sail --dev\nphp artisan sail:install --with=none\n```\n\n:::tip\nNeed to add MySQL, Redis, Meilisearch, or another service later? Run `sail artisan sail:add` and pick what you want.\n:::\n\n## Starting and Stopping Sail\n\n:::tip\n**Before starting Sail**, ensure that no other web servers or databases are running on your local computer.\n:::\n\nTo start all of the Docker containers defined in your site's `compose.yaml` file, execute the up command:\n\n``` shell\n./vendor/bin/sail up\n```\n\nTo start all of the Docker containers in the background, start Sail in \"detached\" mode:\n\n``` shell\n./vendor/bin/sail up -d\n```\n\nOnce the application's containers have been started, your project can be accessed in the browser at: http://localhost.\n\nTo stop all of the containers, press `Control` + `C`. Or if the containers are running in the background, use the stop command:\n\n``` shell\n./vendor/bin/sail stop\n```\n\n## Installing Statamic\n\nAt this point you're running Laravel without Statamic in it. Time to change that.\n\nYou can now follow the steps to [install Statamic into Laravel](/installing/laravel#install-statamic).\n\n:::tip\nKeep in mind that  commands need to be run inside Sail.\n\n- Instead of `php artisan`, run `sail artisan`\n- Instead of `composer require`, run `sail composer require`\n- Instead of `php please`, run `sail artisan statamic`\n:::\n\n\n## Learn more about Laravel Sail\n\nThe [Laravel Sail docs](https://laravel.com/docs/13.x/sail) cover a lot more of what you can do with Sail. Check them out!\n"
  },
  {
    "path": "content/collections/pages/elevated-sessions.md",
    "content": "---\nid: 5eab02e3-c76b-4f44-a304-6a78877d099f\ntitle: Elevated Sessions\n---\n\nElevated Sessions allow you to prompt users for their password or a verification code before being able to take certain actions.\n\n<figure>\n    <img src=\"/img/elevated-session.webp\" alt=\"Elevated Session prompt\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/elevated-session-dark.webp\" alt=\"Elevated Session prompt\" class=\"u-hide-in-light-mode\">\n    <figcaption>Make sure you remember your password! 🔑</figcaption>\n</figure>\n\nOnce you've started an elevated session, you won't be prompted for your password again until the session expires. By default, elevated sessions last for 15 minutes.\n\nStatamic uses elevated sessions before allowing you to update your 2FA settings, edit roles or manage other users. It's trivial to integrate elevated sessions into your own code.\n\n## Control Panel\n\n### JavaScript\n\nYou can use the `requireElevatedSession` function to ensure users are who they say they are before continuing.\n\nWhen a user needs to verify themselves, a modal will be shown, prompting them to enter their password or a verification code. Once an elevated session has been established, the promise will be resolved and the code in the `.then()` callback will be run.\n\nIf the user closes the modal, the promise will be rejected.\n\n```php\n<script setup>\nimport { requireElevatedSession } from '@statamic/cms';\n\nfunction submit() {\n    requireElevatedSession()\n        .then(() => {\n            // Your code here. The user has an elevated session.\n        })\n        .catch(() => {});\n}\n</script>\n```\n\nWe also provide a `requireElevatedSessionIf` function allowing you to conditionally require elevated sessions, like this:\n\n```php\n<script setup>\nimport { requireElevatedSessionIf } from '@statamic/cms';\nimport { ref } from 'vue';\n\nconst isEditingOwnProfile = ref(true);\n\nfunction submit() {\n    requireElevatedSessionIf(!isEditingOwnProfile.value)\n        .then(() => {\n            // Your code here. The user has an elevated session.\n        })\n        .catch(() => {});\n}\n```\n\n### Middleware\n\nThe easiest way to require an elevated session in PHP is by adding the `RequireElevatedSession` middleware to your routes.\n\n```php\nuse Statamic\\Http\\Middleware\\CP\\RequireElevatedSession::class; // [tl! add]\n\nRoute::get('profile', [ProfileController::class, 'index'])\n  ->middleware(RequireElevatedSession::class); // [tl! add]\n```\n\nThe middleware will redirect the user to a page where they can confirm their password. After that, they'll be redirected back to your route.\n\n### Controller\n\nYou can also require an elevated session in your controller by calling the `requireElevatedSession()` method.\n\n```php\nuse Statamic\\Http\\Controllers\\CP\\CpController;\n\nclass ProfileController extends CpController\n{\n    public function update() \n    {\n        $isEditingOwnProfile = true;\n        \n        if (! $isEditingOwnProfile) {\n            $this->requireElevatedSession(); // [tl! add]\n        }\n    \n        // ...\n    }\n}\n```\n\nWhen the user doesn't have an elevated session, they'll be redirected to a page where they can confirm their password. After that, they'll be redirected back to your route.\n\nYour controller will need to extend Statamic's `CpController` in order to use the `requireElevatedSession()` method.\n\n## Frontend\n\nElevated sessions can also be used to protect sensitive actions on your frontend. To learn more, visit the [{{ user:elevated_session_form }}](/tags/user-elevated_session_form) docs.\n\n## Disabling Elevated Sessions\n\nIf you're using a third-party authentication provider (such as OAuth or SSO) and password re-confirmation isn't applicable to your setup, you can disable elevated sessions entirely.\n\nSet `STATAMIC_ELEVATED_SESSIONS_ENABLED=false` in your `.env` file, or set the corresponding option in `config/statamic/users.php`:\n\n```php\n'elevated_sessions_enabled' => env('STATAMIC_ELEVATED_SESSIONS_ENABLED', true),\n```\n\nWhen disabled, the `RequireElevatedSession` middleware and `requireElevatedSession()` controller method are bypassed, the related routes are not registered, and users will never be prompted to reauthorize.\n"
  },
  {
    "path": "content/collections/pages/email.md",
    "content": "---\ntitle: 'Sending Email'\nintro: Be sure to configure your email settings if you want to invite new users, send password resets, and receive form submission notifications. There's even an adorable little test utility in the control panel to help you with a double-check.\ntemplate: page\nid: bd1261de-7c4c-4c22-baf5-8a52cdd10c74\nblueprint: page\n---\n## Overview\n\nStatamic taps into Laravel's clean, simple mail API with drivers for SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice.\n\n## Configuring\n\nYour mail settings are located in `config/mail.php` and pre-wired to use [environment variables](/configuration#environment-variables) so you can easily swap out providers and keep credentials safe and out of your project files.\n\n\n## Drivers\n\nThe API based drivers like Mailgun and Postmark are often simpler and faster than SMTP servers. If possible, you should use one of these drivers. All of the API drivers require the Guzzle HTTP library, which may be installed via the Composer package manager:\n\n``` shell\ncomposer require guzzlehttp/guzzle\n```\n\nFor specific driver configuration details, reference the [Laravel Mail Driver documentation](https://laravel.com/docs/mail#driver-prerequisites).\n\n## Testing\n\nThere's an email utility in the control panel to help you easily test your email settings. Enter an email address (preferably your own) and click **Send Test Email** and wait. If you don't get anything before your birthday you know your settings need tweaking.\n\n<figure>\n    <img src=\"/img/email-utility.webp\" alt=\"Email Utility\" width=\"550\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/email-utility-dark.webp\" alt=\"Email Utility\" width=\"550\" class=\"u-hide-in-light-mode\">\n    <figcaption>Is email working? Click to find out!</figcaption>\n</figure>\n\n## Customizing\n\nYou can modify the HTML and plain-text template used by mail notifications by publishing the view files to your project. After running the following command, the mail email notification templates will be located in the `resources/views/vendor/notifications` directory:\n\n``` shell\nphp artisan vendor:publish --tag=laravel-notifications\n```\n"
  },
  {
    "path": "content/collections/pages/events.md",
    "content": "---\ntitle: Events\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569347432\nid: 6843f6e9-ac62-4128-8d50-887560f201ca\nintro: |\n  Events serve as a great way to decouple various aspects of the application, or even modify behavior or output of core functionality. A single event can have multiple listeners that do not depend on each other.\n---\n## Overview {#overview}\n\nStatamic will dispatch a number of events in various locations throughout the codebase.\n\nTo listen for events, simply create an event listener, type the event name, then handle the event.\n\n``` php\nuse Statamic\\Events\\SomeEvent;\n\nclass SomeListener\n{\n    public function handle(SomeEvent $event)\n    {\n        //\n    }\n}\n```\n\nFor applications with a `app/Providers/EventServiceProvider.php` file, you should also register the event listener in the `$listen` array:\n\n``` php\nprotected $listen = [\n    'SomeEvent' => [\n        'SomeListener',\n    ],\n];\n```\n\nFor a more in-depth explanation on events, please consult the [Laravel documentation](https://laravel.com/docs/events).\n\nIf you're creating an addon, you can quickly [register event listeners or subscribers](/extending/addons#events).\n\n## Available Events\n\n### AddonSettingsSaved\n`Statamic\\Events\\AddonSettingsSaved`\n\nDispatched after an addon's settings have been saved.\n\n``` php\npublic function handle(AddonSettingsSaved $event)\n{\n    $event->addonSettings;\n}\n```\n\n### AddonSettingsSaving\n`Statamic\\Events\\AddonSettingsSaving`\n\nDispatched before an addon's settings are saved. You can return `false` to prevent them from being saved.\n\n``` php\npublic function handle(AddonSettingsSaving $event)\n{\n    $event->addonSettings;\n}\n```\n\n### AssetContainerBlueprintFound\n`Statamic\\Events\\AssetContainerBlueprintFound`\n\nDispatched after Statamic finds the blueprint to be used for an asset in an asset container.\n\nYou may modify the blueprint here and it will be reflected in the publish form (and wherever else a blueprint is used).\nAn example of when this would be useful is to add fields to the publish page on the fly.\n\n``` php\npublic function handle(AssetContainerBlueprintFound $event)\n{\n    $event->blueprint;\n    $event->container;\n}\n```\n\n### AssetContainerCreating\n`Statamic\\Events\\AssetContainerCreating`\n\nDispatched before an asset container is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(AssetContainerCreating $event)\n{\n    $event->container;\n}\n```\n\n### AssetContainerDeleted\n`Statamic\\Events\\AssetContainerDeleted`\n\nDispatched after an asset container has been deleted.\n\n``` php\npublic function handle(AssetContainerDeleted $event)\n{\n    $event->container;\n}\n```\n\n### AssetContainerSaved\n`Statamic\\Events\\AssetContainerSaved`\n\nDispatched after an asset container has been saved.\n\n``` php\npublic function handle(AssetContainerSaved $event)\n{\n    $event->container;\n}\n```\n\n### `AssetCreated`\n`Statamic\\Events\\AssetCreated`\n\nDispatched after an asset has been created or uploaded.\n\n``` php\npublic function handle(AssetCreated $event)\n{\n    $event->asset;\n}\n```\n\n### `AssetCreating`\n`Statamic\\Events\\AssetCreating`\n\nDispatched before an asset is created or uploaded. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(AssetCreating $event)\n{\n    $event->asset;\n}\n```\n\n### AssetDeleting\n`Statamic\\Events\\AssetDeleting`\n\nDispatched before an asset is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(AssetDeleting $event)\n{\n    $event->asset;\n}\n```\n\n### AssetDeleted\n`Statamic\\Events\\AssetDeleted`\n\nDispatched after an asset has been deleted.\n\n``` php\npublic function handle(AssetDeleted $event)\n{\n    $event->asset;\n}\n```\n\n### AssetFolderDeleted\n`Statamic\\Events\\AssetFolderDeleted`\n\nDispatched after an asset folder has been deleted.\n\n``` php\npublic function handle(AssetFolderDeleted $event)\n{\n    $event->folder;\n}\n```\n\n### AssetFolderSaved\n`Statamic\\Events\\AssetFolderSaved`\n\nDispatched after an asset folder has been saved.\n\n``` php\npublic function handle(AssetFolderSaved $event)\n{\n    $event->folder;\n}\n```\n\n### AssetSaved\n`Statamic\\Events\\AssetSaved`\n\nDispatched after an asset has been saved.\n\n``` php\npublic function handle(AssetSaved $event)\n{\n    $event->asset;\n}\n```\n\n### AssetSaving\n`Statamic\\Events\\AssetSaving`\n\nDispatched before an asset is saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(AssetSaving $event)\n{\n    $event->asset;\n}\n```\n\n### AssetUploaded\n`Statamic\\Events\\AssetUploaded`\n\nDispatched after an asset has been uploaded.\n\n``` php\npublic function handle(AssetUploaded $event)\n{\n    $event->asset;\n}\n```\n\n### BlueprintCreating\n`Statamic\\Events\\BlueprintCreating`\n\nDispatched before a blueprint is created. You can return `false` to prevent it from being creating.\n\n``` php\npublic function handle(BlueprintCreating $event)\n{\n    $event->blueprint;\n}\n```\n\n### BlueprintDeleting\n`Statamic\\Events\\BlueprintDeleting`\n\nDispatched before a blueprint is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(BlueprintDeleting $event)\n{\n    $event->blueprint;\n}\n```\n\n### BlueprintDeleted\n`Statamic\\Events\\BlueprintDeleted`\n\nDispatched after a blueprint has been deleted.\n\n``` php\npublic function handle(BlueprintDeleted $event)\n{\n    $event->blueprint;\n}\n```\n\n### BlueprintSaved\n`Statamic\\Events\\BlueprintSaved`\n\nDispatched after a blueprint has been saved.\n\n``` php\npublic function handle(BlueprintSaved $event)\n{\n    $event->blueprint;\n}\n```\n\n### CollectionDeleting\n`Statamic\\Events\\CollectionDeleting`\n\nDispatched before a collection is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(CollectionDeleting $event)\n{\n    $event->collection;\n}\n```\n\n### CollectionDeleted\n`Statamic\\Events\\CollectionDeleted`\n\nDispatched after a collection has been deleted.\n\n``` php\npublic function handle(CollectionDeleted $event)\n{\n    $event->collection;\n}\n```\n\n### CollectionCreated\n`Statamic\\Events\\CollectionCreated`\n\nDispatched after a collection has been created.\n\n``` php\npublic function handle(CollectionCreated $event)\n{\n    $event->collection;\n}\n```\n\n### CollectionCreating\n`Statamic\\Events\\CollectionCreating`\n\nDispatched before a collection is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(CollectionCreating $event)\n{\n    $event->collection;\n}\n```\n\n### CollectionSaved\n`Statamic\\Events\\CollectionSaved`\n\nDispatched after a collection has been saved.\n\n``` php\npublic function handle(CollectionSaved $event)\n{\n    $event->collection;\n}\n```\n\n### CollectionSaving\n`Statamic\\Events\\CollectionSaving`\n\nDispatched before a collection is saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(CollectionSaving $event)\n{\n    $event->collection;\n}\n```\n\n\n### CollectionTreeDeleted\n`Statamic\\Events\\CollectionTreeDeleted`\n\nDispatched after a collection tree has been deleted.\n\n``` php\npublic function handle(CollectionTreeDeleted $event)\n{\n    $event->tree;\n}\n```\n\n### CollectionTreeSaved\n`Statamic\\Events\\CollectionTreeSaved`\n\nDispatched after a collection tree has been saved.\n\n``` php\npublic function handle(CollectionTreeSaved $event)\n{\n    $event->tree;\n}\n```\n\n### CollectionTreeSaving\n`Statamic\\Events\\CollectionTreeSaving`\n\nDispatched when a collection tree is being saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(CollectionTreeSaving $event)\n{\n    $event->tree;\n}\n```\n\n### EntryBlueprintFound\n`Statamic\\Events\\EntryBlueprintFound`\n\nDispatched after Statamic finds the blueprint to be used for an entry.\n\nYou may modify the blueprint here and it will be reflected in the publish form (and wherever else a blueprint is used).\nAn example of when this would be useful is to add a section to a blueprint in the publish page on the fly.\n\n``` php\npublic function handle(EntryBlueprintFound $event)\n{\n    $event->blueprint;\n    $event->entry;\n}\n```\n\n### EntryCreated\n`Statamic\\Events\\EntryCreated`\n\nDispatched after an entry has been created.\n\n``` php\npublic function handle(EntryCreated $event)\n{\n    $event->entry;\n}\n```\n\n### EntryCreating\n`Statamic\\Events\\EntryCreating`\n\nDispatched before an entry is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(EntryCreating $event)\n{\n    $event->entry;\n}\n```\n\n### EntryDeleting\n`Statamic\\Events\\EntryDeleting`\n\nDispatched before an entry is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(EntryDeleting $event)\n{\n    $event->entry;\n}\n```\n\n### EntryDeleted\n`Statamic\\Events\\EntryDeleted`\n\nDispatched after an entry has been deleted.\n\n``` php\npublic function handle(EntryDeleted $event)\n{\n    $event->entry;\n}\n```\n\n### EntrySaved\n`Statamic\\Events\\EntrySaved`\n\nDispatched after an entry has been saved.\n\n``` php\npublic function handle(EntrySaved $event)\n{\n    $event->entry;\n}\n```\n\n:::tip Note\nWhen an entry has multiple localizations, the `EntrySaved` event will be fired for each of those localizations. You may use the `$event->isInitial()` method to determine whether the localized entry from the event was the one originally being saved.\n:::\n\n### EntrySaving\n`Statamic\\Events\\EntrySaving`\n\nDispatched before an entry is saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(EntrySaving $event)\n{\n    $event->entry;\n}\n```\n\n### EntryScheduleReached\n`Statamic\\Events\\EntryScheduleReached`\n\nDispatched whenever a scheduled entry reaches its target date. This event is used in multiple places such as updating search indexes and invalidating caches.\n\nThe event will be dispatched on the minute _after_ the scheduled time.\n\n``` php\npublic function handle(EntryScheduleReached $event)\n{\n    $event->entry;\n}\n```\n\n### FieldsetCreating\n`Statamic\\Events\\FieldsetCreating`\n\nDispatched before a fieldset is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(FieldsetCreating $event)\n{\n    $event->fieldset;\n}\n```\n\n### FieldsetDeleting\n`Statamic\\Events\\FieldsetDeleting`\n\nDispatched before a fieldset is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(FieldsetDeleting $event)\n{\n    $event->fieldset;\n}\n```\n\n### FieldsetDeleted\n`Statamic\\Events\\FieldsetDeleted`\n\nDispatched after a fieldset has been deleted.\n\n``` php\npublic function handle(FieldsetDeleted $event)\n{\n    $event->fieldset;\n}\n```\n\n### FieldsetSaved\n`Statamic\\Events\\FieldsetSaved`\n\nDispatched after a fieldset has been saved.\n\n``` php\npublic function handle(FieldsetSaved $event)\n{\n    $event->fieldset;\n}\n```\n\n### FormBlueprintFound\n`Statamic\\Events\\FormBlueprintFound`\n\nDispatched after Statamic finds the blueprint to be used for a form.\n\nYou may modify the blueprint here and it will be reflected in the publish form (and wherever else a blueprint is used).\nAn example of when this would be useful is to add a section to a blueprint in the publish page on the fly.\n\n``` php\npublic function handle(FormBlueprintFound $event)\n{\n    $event->blueprint;\n    $event->form;\n}\n```\n\n### FormCreating\n`Statamic\\Events\\FormCreating`\n\nDispatched before a form is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(FormCreating $event)\n{\n    $event->form;\n}\n```\n\n### FormDeleting\n`Statamic\\Events\\FormDeleting`\n\nDispatched before a form is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(FormDeleting $event)\n{\n    $event->form;\n}\n```\n\n### FormDeleted\n`Statamic\\Events\\FormDeleted`\n\nDispatched after a form has been deleted.\n\n``` php\npublic function handle(FormDeleted $event)\n{\n    $event->form;\n}\n```\n\n### FormSaved\n`Statamic\\Events\\FormSaved`\n\nDispatched after a form has been saved.\n\n``` php\npublic function handle(FormSaved $event)\n{\n    $event->form;\n}\n```\n\n### FormSubmitted\n`Statamic\\Events\\FormSubmitted`\n\nDispatched when a [Form](/forms) is submitted on the front-end, before the Submission is created.\n\n``` php\npublic function handle(FormSubmitted $event)\n{\n    $event->submission; // The Submission object\n}\n```\n\nYou can `return false` to prevent the submission, but appear to the user as though it succeeded.\n\nIf you'd like to show validation errors, you may throw an `Illuminate\\Validation\\ValidationException`:\n\n``` php\nthrow ValidationException::withMessages(['You did something wrong.']);\n```\n\nYou may also just modify the submission object. You do not need to `return` anything.\n\n### GlideAssetCacheCleared\n`Statamic\\Events\\GlideAssetCacheCleared`\n\nDispatched after the Glide cache has been cleared for a specific asset, either via the `php please glide:clear` command or via the Cache Manager utility.\n\n``` php\npublic function handle(GlideAssetCacheCleared $event)\n{\n    //\n}\n```\n\n### GlideCacheCleared\n`Statamic\\Events\\GlideCacheCleared`\n\nDispatched after the Glide cache has been cleared, either via the `php please glide:clear` command or via the Cache Manager utility.\n\n``` php\npublic function handle(GlideCacheCleared $event)\n{\n    //\n}\n```\n\n### GlideImageGenerated\n`Statamic\\Events\\GlideImageGenerated`\n\nDispatched after Glide generates an image.\n\n``` php\npublic function handle(GlideImageGenerated $event)\n{\n    $event->path;\n    $event->params;\n}\n```\n\n### GlobalSetCreating\n`Statamic\\Events\\GlobalSetCreating`\n\nDispatched before a global set is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(GlobalSetCreating $event)\n{\n    $event->globals;\n}\n```\n\n### GlobalSetDeleting\n`Statamic\\Events\\GlobalSetDeleting`\n\nDispatched before a global set is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(GlobalSetDeleting $event)\n{\n    $event->globals;\n}\n```\n\n### GlobalSetDeleted\n`Statamic\\Events\\GlobalSetDeleted`\n\nDispatched after a global set has been deleted.\n\n``` php\npublic function handle(GlobalSetDeleted $event)\n{\n    $event->globals;\n}\n```\n\n### GlobalSetSaved\n`Statamic\\Events\\GlobalSetSaved`\n\nDispatched after a global set has been saved.\n\n``` php\npublic function handle(GlobalSetSaved $event)\n{\n    $event->globals;\n}\n```\n\n### GlobalVariablesCreated\n`Statamic\\Events\\GlobalVariablesCreated`\n\nDispatched after Global Variables have been created.\n\n``` php\npublic function handle(GlobalVariablesCreated $event)\n{\n    $event->variables;\n}\n```\n\n### GlobalVariablesCreating\n`Statamic\\Events\\GlobalVariablesCreating`\n\nDispatched before Global Variables are created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(GlobalVariablesCreating $event)\n{\n    $event->variables;\n}\n```\n\n### GlobalVariablesDeleting\n`Statamic\\Events\\GlobalVariablesDeleting`\n\nDispatched after Global Variables have been deleted.\n\n``` php\npublic function handle(GlobalVariablesDeleting $event)\n{\n    $event->variables;\n}\n```\n\n### GlobalVariablesDeleted\n`Statamic\\Events\\GlobalVariablesDeleted`\n\nDispatched before Global Variables are deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(GlobalVariablesDeleted $event)\n{\n    $event->variables;\n}\n```\n\n### GlobalVariablesSaved\n`Statamic\\Events\\GlobalVariablesSaved`\n\nDispatched after Global Variables have been saved.\n\n``` php\npublic function handle(GlobalVariablesSaved $event)\n{\n    $event->variables;\n}\n```\n\n### GlobalVariablesSaving\n`Statamic\\Events\\GlobalVariablesSaving`\n\nDispatched before Global Variables are saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(GlobalVariablesSaving $event)\n{\n    $event->variables;\n}\n```\n\n### GlobalVariablesBlueprintFound\n`Statamic\\Events\\GlobalVariablesBlueprintFound`\n\nDispatched after Statamic finds the blueprint to be used for a variables in a global set.\n(Variables meaning the globals localized to a particular site)\n\nYou may modify the blueprint here and it will be reflected in the publish form (and wherever else a blueprint is used).\nAn example of when this would be useful is to add a section to a blueprint in the publish page on the fly.\n\n``` php\npublic function handle(GlobalVariablesBlueprintFound $event)\n{\n    $event->blueprint;\n    $event->globals;\n}\n```\n\n### ImpersonationStarted\n`Statamic\\Events\\ImpersonationStarted`\n\nDispatched whenever a user starts impersonating another user.\n\n``` php\npublic function handle(ImpersonationStarted $event)\n{\n    $event->impersonator; // The other who started impersonating the other.\n    $event->impersonated; // The user being impersonated.\n}\n```\n\n### ImpersonationEnded\n`Statamic\\Events\\ImpersonationEnded`\n\nDispatched whenever a user finishes impersonating another user.\n\n``` php\npublic function handle(ImpersonationEnded $event)\n{\n    $event->impersonator; // The other who started impersonating the other.\n    $event->impersonated; // The user being impersonated.\n}\n```\n\n### LicensesRefreshed\n`Statamic\\Events\\LicensesRefreshed`\n\nDispatched when a user manually triggers a \"Sync\" of a site's licenses via the Licenses utility.\n\n``` php\npublic function handle(LicensesRefreshed $event)\n{\n    //\n}\n```\n\n### LicenseSet\n`Statamic\\Events\\LicenseSet`\n\nDispatched after a license key has been set via the `php please license:set` command.\n\n``` php\npublic function handle(LicenseSet $event)\n{\n    //\n}\n```\n\n### LocalizedTermDeleted\n`Statamic\\Events\\LocalizedTermDeleted`\n\nDispatched after a taxonomy term has been deleted.\n\nThis event is *similar* to the [`TermDeleted`](#termdeleted) event, however, instead of `$term` being a `Term` instance, it will be a `LocalizedTerm` instance.\n\n``` php\npublic function handle(LocalizedTermDeleted $event)\n{\n    $event->term;\n}\n```\n\n### LocalizedTermSaved\n`Statamic\\Events\\LocalizedTermSaved`\n\nDispatched after a taxonomy term has been saved.\n\nThis event is *similar* to the [`TermSaved`](#termsaved) event, however, instead of `$term` being a `Term` instance, it will be a `LocalizedTerm` instance.\n\n``` php\npublic function handle(LocalizedTermSaved $event)\n{\n    $event->term;\n}\n```\n\n### NavCreated\n`Statamic\\Events\\NavCreated`\n\nDispatched after a nav has been created.\n\n``` php\npublic function handle(NavCreated $event)\n{\n    $event->nav;\n}\n```\n\n### NavCreating\n`Statamic\\Events\\NavCreating`\n\nDispatched before a nav is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(NavCreating $event)\n{\n    $event->nav;\n}\n```\n\n### NavDeleting\n`Statamic\\Events\\NavDeleting`\n\nDispatched before a nav is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(NavDeleting $event)\n{\n    $event->nav;\n}\n```\n\n### NavDeleted\n`Statamic\\Events\\NavDeleted`\n\nDispatched after a nav has been deleted.\n\n``` php\npublic function handle(NavDeleted $event)\n{\n    $event->nav;\n}\n```\n\n### NavSaved\n`Statamic\\Events\\NavSaved`\n\nDispatched after a nav has been saved.\n\n``` php\npublic function handle(NavSaved $event)\n{\n    $event->nav;\n}\n```\n\n### NavSaving\n`Statamic\\Events\\NavSaving`\n\nDispatched before a nav is saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(NavSaving $event)\n{\n    $event->nav;\n}\n```\n\n### NavTreeSaved\n`Statamic\\Events\\NavTreeSaved`\n\nDispatched after a nav tree has been saved.\n\n``` php\npublic function handle(NavTreeSaved $event)\n{\n    $event->tree;\n}\n```\n\n### NavTreeSaving\n`Statamic\\Events\\NavTreeSaving`\n\nDispatched when a nav tree is being saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(NavTreeSaving $event)\n{\n    $event->tree;\n}\n```\n\n### ResponseCreated\n`Statamic\\Events\\ResponseCreated`\n\nDispatched after Statamic finishes creating the response to send to the front-end.\nYou may wish to modify the response to add headers, etc.\n\n``` php\npublic function handle(ResponseCreated $event)\n{\n    $event->response; // The Response object\n}\n```\n\n### RevisionDeleted\n`Statamic\\Events\\RevisionDeleted`\n\nDispatched after a revision of an entry has been deleted.\n\n``` php\npublic function handle(RevisionDeleted $event)\n{\n    $event->revision;\n}\n```\n\n### RevisionSaving\n`Statamic\\Events\\RevisionSaving`\n\nDispatched before a revision of an entry is saved. You can return `false` to prevent it from being saved.\n\n``` php\npublic function handle(RevisionSaving $event)\n{\n    $event->revision;\n}\n```\n\n### RevisionSaved\n`Statamic\\Events\\RevisionSaved`\n\nDispatched after a revision of an entry has been saved.\n\n``` php\npublic function handle(RevisionSaved $event)\n{\n    $event->revision;\n}\n```\n\n\n### RoleDeleted\n`Statamic\\Events\\RoleDeleted`\n\nDispatched after a role has been deleted.\n\n``` php\npublic function handle(RoleDeleted $event)\n{\n    $event->role;\n}\n```\n\n### RoleSaved\n`Statamic\\Events\\RoleSaved`\n\nDispatched after a role has been saved.\n\n``` php\npublic function handle(RoleSaved $event)\n{\n    $event->role;\n}\n```\n\n### SearchIndexUpdated\n`Statamic\\Events\\SearchIndexUpdated`\n\nDispatched when a search index is updated, either via the `php please search:update` command or via the Search utility.\n\n``` php\npublic function handle(SearchIndexUpdated $event)\n{\n    $event->index;\n}\n```\n\n### SiteCreated\n`Statamic\\Events\\SiteCreated`\n\nDispatched when a site is created via the Control Panel.\n\n``` php\npublic function handle(SiteCreated $event)\n{\n    $event->site;\n}\n```\n\n### SiteDeleted\n`Statamic\\Events\\SiteDeleted`\n\nDispatched when a site is deleted via the Control Panel.\n\n``` php\npublic function handle(SiteDeleted $event)\n{\n    $event->site;\n}\n```\n\n### SiteSaved\n`Statamic\\Events\\SiteSaved`\n\nDispatched when a site is saved via the Control Panel.\n\n``` php\npublic function handle(SiteSaved $event)\n{\n    $event->site;\n}\n```\n\n### StacheCleared\n`Statamic\\Events\\StacheCleared`\n\nDispatched after the Stache cache has been cleared, either via the `php please stache:clear` command or via the Cache Manager utility.\n\n``` php\npublic function handle(StacheCleared $event)\n{\n    //\n}\n```\n\n### StacheWarmed\n`Statamic\\Events\\StacheWarmed`\n\nDispatched after the Stache cache has been warmed, either via the `php please stache:warm` command or via the Cache Manager utility.\n\n``` php\npublic function handle(StacheWarmed $event)\n{\n    //\n}\n```\n\n### StaticCacheCleared\n`Statamic\\Events\\StaticCacheCleared`\n\nDispatched after the Static Cache has been cleared, either via the `php please static:clear` command or via the Cache Manager utility.\n\n``` php\npublic function handle(StaticCacheCleared $event)\n{\n    //\n}\n```\n\n### SubmissionCreated\n`Statamic\\Events\\SubmissionCreated`\n\nDispatched after a form submission has been created. This happens after a form has been submitted on the front-end.\n\n``` php\npublic function handle(SubmissionCreated $event)\n{\n    $event->submission;\n}\n```\n\nIf you're looking to prevent a form being submitted or trigger validation errors, check out the [FormSubmitted](#formsubmitted) event.\n\n### SubmissionCreating\n`Statamic\\Events\\SubmissionCreating`\n\nDispatched before a submission is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(SubmissionCreating $event)\n{\n    $event->submission;\n}\n```\n\n### SubmissionDeleted\n`Statamic\\Events\\SubmissionDeleted`\n\nDispatched after a form submission has been deleted.\n\n``` php\npublic function handle(SubmissionDeleted $event)\n{\n    $event->submission;\n}\n```\n\n### SubmissionSaved\n`Statamic\\Events\\SubmissionSaved`\n\nDispatched after a form submission has been saved.\n\n``` php\npublic function handle(SubmissionSaved $event)\n{\n    $event->submission;\n}\n```\n\n### TaxonomyCreating\n`Statamic\\Events\\TaxonomyCreating`\n\nDispatched before a taxonomy is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(TaxonomyCreating $event)\n{\n    $event->taxonomy;\n}\n```\n\n### TaxonomyDeleting\n`Statamic\\Events\\TaxonomyDeleting`\n\nDispatched before a taxonomy is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(TaxonomyDeleting $event)\n{\n    $event->taxonomy;\n}\n```\n\n### TaxonomyDeleted\n`Statamic\\Events\\TaxonomyDeleted`\n\nDispatched after a taxonomy has been deleted.\n\n``` php\npublic function handle(TaxonomyDeleted $event)\n{\n    $event->taxonomy;\n}\n```\n\n### TaxonomySaved\n`Statamic\\Events\\TaxonomySaved`\n\nDispatched after a taxonomy has been saved.\n\n``` php\npublic function handle(TaxonomySaved $event)\n{\n    $event->taxonomy;\n}\n```\n\n### TermBlueprintFound\n`Statamic\\Events\\TermBlueprintFound`\n\nDispatched after Statamic finds the blueprint to be used for a taxonomy term.\n\nYou may modify the blueprint here and it will be reflected in the publish form (and wherever else a blueprint is used).\nAn example of when this would be useful is to add a section to a blueprint in the publish page on the fly.\n\n``` php\npublic function handle(TermBlueprintFound $event)\n{\n    $event->blueprint;\n    $event->term;\n}\n```\n\n### TermCreating\n`Statamic\\Events\\TermCreating`\n\nDispatched before a taxonomy term is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(TermCreating $event)\n{\n    $event->term;\n}\n```\n\n### TermDeleting\n`Statamic\\Events\\TermDeleting`\n\nDispatched before a taxonomy term is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(TermDeleting $event)\n{\n    $event->term;\n}\n```\n\n### TermDeleted\n`Statamic\\Events\\TermDeleted`\n\nDispatched after a taxonomy term has been deleted.\n\nThis event is *similar* to the [`LocalizedTermDeleted`](#localizedtermdeleted) event, however, instead of `$term` being a `LocalizedTerm` instance, it will be a `Term` instance.\n\n``` php\npublic function handle(TermDeleted $event)\n{\n    $event->term;\n}\n```\n\n### TermSaved\n`Statamic\\Events\\TermSaved`\n\nDispatched after a taxonomy term has been saved.\n\nThis event is *similar* to the [`LocalizedTermSaved`](#localizedtermsaved) event, however, instead of `$term` being a `LocalizedTerm` instance, it will be a `Term` instance.\n\n``` php\npublic function handle(TermSaved $event)\n{\n    $event->term;\n}\n```\n\n### TwoFactorAuthenticationChallenged\n`Statamic\\Events\\TwoFactorAuthenticationChallenged`\n\nDispatched when the two-factor authentication challenge is presented to a user.\n\n``` php\npublic function handle(TwoFactorAuthenticationChallenged $user)\n{\n    $event->user;\n}\n```\n\n### TwoFactorAuthenticationDisabled\n`Statamic\\Events\\TwoFactorAuthenticationDisabled`\n\nDispatched when a user disables two-factor authentication.\n\n``` php\npublic function handle(TwoFactorAuthenticationDisabled $user)\n{\n    $event->user;\n}\n```\n\n### TwoFactorAuthenticationEnabled\n`Statamic\\Events\\TwoFactorAuthenticationEnabled`\n\nDispatched when a user enables two-factor authentication.\n\n``` php\npublic function handle(TwoFactorAuthenticationEnabled $user)\n{\n    $event->user;\n}\n```\n\n### TwoFactorAuthenticationFailed\n`Statamic\\Events\\TwoFactorAuthenticationFailed`\n\nDispatched when a user fails the two-factor authentication challenge.\n\n``` php\npublic function handle(TwoFactorAuthenticationFailed $user)\n{\n    $event->user;\n}\n```\n\n### TwoFactorRecoveryCodeReplaced\n`Statamic\\Events\\TwoFactorRecoveryCodeReplaced`\n\nDispatched when one of a user's two-factor authentication recovery codes is replaced.\n\n``` php\npublic function handle(TwoFactorRecoveryCodeReplaced $user)\n{\n    $event->user;\n}\n```\n\n### UserBlueprintFound\n`Statamic\\Events\\UserBlueprintFound`\n\nDispatched after Statamic finds the blueprint to be used for a user.\n\nYou may modify the blueprint here and it will be reflected in the publish form (and wherever else a blueprint is used).\nAn example of when this would be useful is to add a section to a blueprint in the publish page on the fly.\n\n``` php\npublic function handle(UserBlueprintFound $event)\n{\n    $event->blueprint;\n}\n```\n\n### UserCreating\n`Statamic\\Events\\UserCreating`\n\nDispatched before a user is created. You can return `false` to prevent it from being created.\n\n``` php\npublic function handle(UserCreating $event)\n{\n    $event->user;\n}\n```\n\n### UserDeleting\n`Statamic\\Events\\UserDeleting`\n\nDispatched before a user is deleted. You can return `false` to prevent it from being deleted.\n\n``` php\npublic function handle(UserDeleting $event)\n{\n    $event->user;\n}\n```\n\n### UserDeleted\n`Statamic\\Events\\UserDeleted`\n\nDispatched after a user has been deleted.\n\n``` php\npublic function handle(UserDeleted $event)\n{\n    $event->user;\n}\n```\n\n### UserGroupDeleted\n`Statamic\\Events\\UserGroupDeleted`\n\nDispatched after a user group has been deleted.\n\n``` php\npublic function handle(UserGroupDeleted $event)\n{\n    $event->group;\n}\n```\n\n### UserGroupSaved\n`Statamic\\Events\\UserGroupSaved`\n\nDispatched after a user group has been saved.\n\n``` php\npublic function handle(UserGroupSaved $event)\n{\n    $event->group;\n}\n```\n\n### UserPasswordChanged\n`Statamic\\Events\\UserPasswordChanged`\n\nDispatched when the password of another user has been changed in the Control Panel.\n\n``` php\npublic function handle(UserPasswordChanged $event)\n{\n    $event->user;\n}\n```\n\n### UserRegistering\n`Statamic\\Events\\UserRegistering`\n\nDispatched before a user is saved.\n\nYou can return false to prevent the submission, but appear to the user as though it succeeded.\n\n``` php\npublic function handle(UserRegistering $event)\n{\n    $event->user;\n}\n```\n\n### UserRegistered\n`Statamic\\Events\\UserRegistered`\n\nDispatched after a user is saved.\n\n``` php\npublic function handle(UserRegistered $event)\n{\n    $event->user;\n}\n```\n\n### UserSaved\n`Statamic\\Events\\UserSaved`\n\nDispatched after a user has been saved.\n\n``` php\npublic function handle(UserSaved $event)\n{\n    $event->user;\n}\n```\n\n### UrlInvalidated\n`Statamic\\Events\\UrlInvalidated`\n\nDispatched after a URL has been removed from the static cache.\n\n``` php\npublic function handle(UrlInvalidated $event)\n{\n    $event->url;\n}\n```\n"
  },
  {
    "path": "content/collections/pages/field-actions.md",
    "content": "---\nid: 41e386b3-9df9-4d28-9338-8005399f953c\nblueprint: page\ntitle: 'Field Actions'\ntemplate: page\nintro: 'Field actions allow you perform JavaScript-based tasks on individual fields within a publish form.'\n---\n:::tip\nIf you'd like to perform tasks on entire PHP-based items like Entries, check out [Actions](/extending/actions).\n:::\n\n## Overview\n\nField actions allow you to use JavaScript to modify value for specific fields.\n\nSome examples of what you could do:\n\n- Manipulate the value\n- Make the value uppercase\n- Translate the value\n- Toggle fullscreen mode\n\n## Defining actions\n\nActions can be added to fieldtypes and Bard/Replicator sets.\n\nYou should pass the name of the Vue component and the action object to `Statamic.$fieldActions.add()` method.\n\n```js\nStatamic.$fieldActions.add('text-fieldtype', { /* ... */ });\nStatamic.$fieldActions.add('bard-fieldtype-set', { /* ... */ });\nStatamic.$fieldActions.add('replicator-fieldtype-set', { /* ... */ });\n```\n\nThe action will be accessible by a dropdown in the field header.\n\n### Within a fieldtype\n\nIf you are the fieldtype author, you may choose to define actions internally by adding an `internalFieldActions` computed property to your Vue component.\n\n```js\ncomputed: {\n    internalFieldActions() {\n        return [\n            { ... },  \n            { ... },  \n        ];\n    }    \n}\n```\n\n## Action Definition\n\nEach action needs at a minimum the `title` and `run` properties.\n\n```js\nStatamic.$fieldActions.add('text-fieldtype', {\n    title: 'Reverse',\n    run: (payload) => {\n        //\n    }\n});\n```\n\nThe `run` callback will be provided with a [payload object](#callback-payload) containing variables and functions that will be useful to you.\n\nThe most basic of which will be `value` and `update` which will let you read the value and update it, respectively.\n\n```js\nrun: ({ value, update }) => {\n    const reversed = value.split('').reverse().join('');\n    update(reversed);\n}\n```\n\n### Loading State\n\nIf your action is expected to take a longer amount of time - perhaps you are doing an AJAX request - you may want to provide a loading state.\n\nTo add a loading state, return a Promise from your `run` function. A loading graphic will be automatically applied. When resolved, it will be removed.\n\n```js\nrun: ({ value, update }) => {\n    return new Promise(resolve => {\n        longTask();\n        resolve();\n    });\n}\n```\n\n## Callback Payload\n\nThe payload provided to the `run`, `quick`, `visible`, and `icon` functions will contain the following properties:\n\n| Property          | Type     | Description                                                                                                                                |\n|-------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------|\n| `handle`          | string   | The handle of the field                                                                                                                    |\n| `value`           | mixed    | The value of the field, when used on a field.                                                                                              |\n| `values`          | mixed    | The values of the set, when used on a set.                                                                                                 |\n| `config`          | Object   | The field configuration                                                                                                                    |\n| `meta`            | Object   | The field's meta data                                                                                                                      |\n| `update`          | function | Whatever you pass to this method will update the field's value. When used in a set, this expects a field handle as the first argument.     |\n| `updateMeta`      | function | Whatever you pass to this method will update the field's meta data. When used in a set, this expects a field handle as the first argument. |\n| `fieldPathPrefix` | string   | The path to the field handle, when nested inside another field like a Grid or Replicator.                                                  |\n| `vm`              | Object   | The Vue component                                                                                                                          |\n| `fieldVm`         | Object   | When inside a Bard or Replicator set, this is the Vue component of the Bard/Replicator.                                                    |\n| `isReadOnly`      | bool     | Whether the field is read only.                                                                                                            |\n| `confirmation`    | Object   | When using a [confirmation modal](#confirmation-modals), this will contain the result of the submission.                                   |                                   \n\n## Quick Actions\n\nAn action can be marked as \"quick\" and will be made available through an icon in addition to the dropdown. The `icon` can be a name of an icon included with Statamic, or an `<svg>...</svg>` string.\n\n```js\n{\n    quick: true,\n    icon: 'light/crane'\n}\n```\n\nEither of these may be functions.\n\n```js\n{\n    quick: (payload) => true,\n    icon: (payload) => 'light/crane'\n}\n```\n\n## Visibility\n\nSince actions are applied to a fieldtype, you may not want to have it usable on every field that uses that fieldtype. You can control whether the action is visible using the `visibile` property.\n\n```js\n{\n    visible: true\n}\n```\n\nThis may also be a function:\n\n```js\n{\n    visible: (payload) => true\n}\n```\n\n\n## Read Only Fields\n\nBy default, Statamic will not display an action if the field is read only. However, you can opt into showing it.\n\n```js\n{\n    visibleWhenReadOnly: true\n}\n```\n\nYou may also pair this with the `isReadOnly` property within the payload.\n\n```js\n{\n    visibleWhenReadOnly: true,\n    run: ({ update, value, isReadOnly }) => {\n        doSomething();\n        \n        if (!isReadOnly) update(...);\n    }\n}\n```\n\n\n## Confirmation Modals\n\nWhen running your action, you may use a modal as confirmation and to ask for additional information.\n\n```js\n{\n    confirm: true,\n    run: () => {\n        // do something\n    }\n}\n```\n\nIf the user closes the modal without confirming, the `run` won't be executed. \n\n### Confirmation Modal Options\n\nThe `confirm` option will give a generic \"Are you sure\" prompt if you pass `true`. \n\nYou may pass an options object to the `confirm` property in order to customize it. For example:\n\n```js\n{\n    confirm: {\n        title: 'My Modal',\n        text: 'Are you sure you want to do that?'\n    }\n} \n```\n\n| Option        | Type   | Description                                                                                                      |\n|---------------|--------|------------------------------------------------------------------------------------------------------------------|\n| `title`       | string | The title to displayed in the header of the modal. Defaults to the title of the action.                          |\n| `buttonText`  | string | The text to be displayed in the confirmation button. Default: `Confirm`.                                         |\n| `text`        | string | The body text. Defaults to `Are you sure?` if the modal would otherwise be empty (no fields, warning text, etc). |\n| `warningText` | string | Red warning text. It will be displayed after confirmationText if defined.                                        |\n| `dangerous`   | bool   | Whether the confirmation button should be red.                                                                   | \n| `fields`      | object | An object containing field definitions. See [fields](#confirmation-modal-fields).                                |\n\n### Confirmation Modal Fields\n\nYou may provide blueprint field definitions that will be displayed in the modal. A `confirmation` property will be available within the `run` method payload.\n\n```js\n{\n    confirm: {\n        fields: {\n            name: {\n                type: 'text',\n                display: 'Name',\n                instructions: 'Enter your name',\n                validate: ['required', 'min:2']\n            },\n            color: {\n                type: 'color',\n                instructions: 'Select the color',\n            }\n        }\n    },\n    run: ({ confirmation }) => {\n        console.log(confirmation.values);\n        // { name: 'Bob Down', color: '#aabbcc' }\n    }\n}\n```\n\nThe `confirmation` property is an object containing the following properties:\n\n| Property | Type | Description                                                                       |\n|----------|------|-----------------------------------------------------------------------------------|\n| `values` | object | The values that are used in the modal's fieldtype Vue components. (Pre-processed) |\n| `processed` | object | The PHP-based values. e.g. What would get saved to content when editing an entry. |\n| `meta` | object | The meta data for all the fields in the modal.                                    |\n\n## Accessing Other Fields\n\nIf you find yourself needing to access other form field values, configs, etc., you can reach into the publish form store from within your `run` function: \n\n```js\n{\n    run: ({ store, storeName }) => {\n        const values = store.state.publish[storeName].values;\n    }\n}\n```\n"
  },
  {
    "path": "content/collections/pages/fields.md",
    "content": "---\nid: cb21fabb-65ba-4869-9acd-f6aa2fb58a01\nblueprint: page\ntitle: Fields\ntemplate: page\nintro: 'While in the control panel all content is managed inside fields. They come in many types, from basic text and select boxes, to rich text fields and image pickers. Fields are grouped into blueprints and fieldsets and can be reused in a number of different ways.'\nrelated_entries:\n  - 9a1d8b88-c600-46f2-8727-1deb56f2e87a\n  - 54548616-fd6d-44a3-a379-bdf71c492c63\n  - 2940c834-7062-47a1-957c-88a69e790cbb\n  - 9b2f6f55-5355-4334-b90d-d1236fb58887\n  - dd52c1f6-661b-4408-83c6-691fa341aaa7\n---\n## Configuration\n\n### Common settings\n\nAll fields share the following settings regardless of type:\n\n- **Display** – The field's label shown throughout the Control Panel\n- **Handle** – The field's variable name used in templates\n- **Instructions** – Help text shown to your authors\n- **Listable** – Whether to show the field as a column in the Control Panel's listing table\n- **Sortable** – Whether the field should be sortable in the Control Panel's listing table\n- **Visibility** – Allows you to control [field visibility](#field-visibility) on publish forms\n- **Always Save** – Allows you to override [field data flow](#field-data-flow) on save\n- **Localizable** – Whether the field can be translated in [other sites](#localization)\n\n<figure>\n    <img src=\"/img/field-settings.webp\" alt=\"Textarea field settings\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/field-settings-dark.webp\" alt=\"Textarea field settings\" class=\"u-hide-in-light-mode\">\n    <figcaption>A textarea field's settings screen.</figcaption>\n</figure>\n\n### Field visibility\n\nFields are always visible by default, but you can configure custom visibility to any of the following options:\n\n- **Visible** - Always visible\n- **Read Only** - Prevent editing\n- **Computed** - Show read-only [computed value](/computed-values), and never submit this value on save\n- **Hidden** - Hide field on publish form, but still submit current value on save\n\n:::tip\nYou can also dynamically show and hide your fields using [Conditional Fields](/conditional-fields) and/or [Revealer Fields](/fieldtypes/revealer).\n\n**Note:** Unless you are using a Revealer, hiding a field using conditions will generally prevent its value from being submitted on save. Learn more about [Field Data Flow](#field-data-flow) to get the most out of this feature!\n:::\n\n### Field data flow\n\nFields are always submitted on save, except for in the following situations:\n\n- If the field is configured to show a [computed value](/computed-values)\n- If the field is dynamically hidden using [field conditions](/conditional-fields)\n\nIf you want to override the above-mentioned field condition data flow behaviour, you can either use a [Revealer Field](/fieldtypes/revealer), or set the following to 'Always Save' your field:\n\n<figure>\n    <img src=\"/img/field-always-save.webp\" alt=\"Always save field setting\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/field-always-save-dark.webp\" alt=\"Always save field setting\" class=\"u-hide-in-light-mode\">\n</figure>\n\n## Blueprints & fieldsets\n\n[Blueprints](/blueprints) determine what fields are shown in your publish forms. You can configure the fields order, each field's width, and group them into sections and tabs.\n\nBlueprints are attached to collections, taxonomies, global sets, assets, users, and even forms, all of which help to determine their content schema.\n\n[Fieldsets](/fieldsets) are used to store and organize **reusable fields**. Blueprints can reference fields or entire fieldsets, helping you keep your configurations nice and [DRY][dry].\n\n## Fieldtypes\n\nThe visual UI and storage format for any given field is determined by its [fieldtype](/fieldtypes). There are 40+ included fieldtypes to help you design intuitive content management experiences for your authors.\n\n<figure>\n    <img src=\"/img/quick-start/fieldtypes-v6.webp\" alt=\"Statamic 6 fieldtype picker\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/fieldtypes-v6-dark.webp\" alt=\"Statamic 6 fieldtype picker\" class=\"u-hide-in-light-mode\">\n    <figcaption>The fieldtype picker thingamajig</figcaption>\n</figure>\n\n## Augmentation\n\nEach field type has its own data storage format. Text and Markdown fields store strings (simple text), lists and YAML fields store arrays, Bard stores ProseMirror document objects, and so on.\n\nEach fieldtype has the ability to _augment_ this data when accessed from the frontend of your site, transforming it into whatever format is easiest to work with.  In Statamic v2 this would need to be done manually with [modifiers](/modifiers). For example:\n\n- **Asset** fields will return Asset objects with access to meta data and any additional fields\n- **Bard** fields will transform ProseMirror document objects into an array of structured data and HTML.\n- **Markdown** fields will automatically parse content and return HTML.\n- **Relationship** fields will return the content objects of the entries they refer to.\n\n:::tip\n**Augmentation** is only performed when a field is defined in a blueprint. Data created \"on the fly\" in Front Matter may still require [modifiers](/modifiers) to transform it according to your whims and fancies.\n:::\n\n## Localization\n\nFields can be marked as \"localizable\", allowing you to translate or modify the field's content in a multi-site project.\n\nFor example, you could build the website for a multi-national company with headquarters in the United States and branches in the UK, and Germany.\n\n- 🇺🇸 The \"base\" site the US/English version, and all content is created with that location and audience in mind.\n\n- 🇬🇧 In the UK version you only need to localize a few fields, replacing \"color\" and \"favorite\" with \"colour\" and \"favourite\", and swapping out company phone numbers and addresses.\n\n- 🇩🇪 In the German version of the site, however, all written content would need to be translated.\n\nTo accomplish this you can configure your Statamic install as a multi-site instance, enable localization on all appropriate fields, and switch between sites with the site switcher dropdown in the global nav, or the locale list in the sidebar of your publish forms.\n\nLearn more about configuring Statamic for [multi-site](/multi-site) projects.\n\n## Translating UI\n\nYou may provide translations for the field UI (such as the display text, instructions, select option labels, etc). This allows content editors to display the Control Panel in their preferred language, regardless of whether it's used in a multi-site setup.\n\nField UI strings are run through [Laravel's translations](https://laravel.com/docs/13.x/localization) feature.\n\nFor example, you may have a field defined like this:\n\n```yaml\nfields:\n  -\n    handle: favorite_food\n    field:\n      type: text\n      display: Favorite Food # [tl!**]\n      instructions: Please provide your food preference. # [tl!**]\n```\n\nTo define the French translations, you can create a `lang/fr.json`:\n\n```json\n{\n    \"Favorite Food\": \"Nourriture favorite\",\n    \"Please provide your food preference.\": \"Veuillez indiquer votre préférence alimentaire.\"\n}\n```\n\nAlternatively, you can use translation keys. The keys can be whatever you want. The first part denotes the filename, and everything else is the array key. If you do this, you'll need to provide the Default/English strings too.\n\n```yaml\nfields:\n  -\n    handle: favorite_food\n    field:\n      type: text\n      display: fields.favorite_food.display # [tl!**]\n      instructions: fields.favorite_food.instructions # [tl!**]\n```\n\n```php\n// lang/en/fields.php\nreturn [\n    'favorite_food.display' => 'Favorite Food',\n    'favorite_food.instructions' => 'Please provide your food preference.',\n];\n```\n```php\n// lang/fr/fields.php\nreturn [\n    'favorite_food.display' => 'Nourriture favorite',\n    'favorite_food.instructions' => 'Veuillez indiquer votre préférence alimentaire.',\n];\n```\n\n[dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself\n"
  },
  {
    "path": "content/collections/pages/fieldsets.md",
    "content": "---\nid: 2940c834-7062-47a1-957c-88a69e790cbb\nblueprint: page\ntitle: Fieldsets\ntemplate: page\nintro: 'Fieldsets are used to store and organize reusable fields.'\nrelated_entries:\n  - 54548616-fd6d-44a3-a379-bdf71c492c63\n  - 9a1d8b88-c600-46f2-8727-1deb56f2e87a\n---\n## Overview\n\nFieldsets are almost functionally equivalent to [Blueprints](/blueprints), except that they exist for you to be able to create reusable fields.\n\nWhile Blueprints attach directly to content like collections or forms, Fieldsets are not directly attached to anything.\n\nFieldsets contain [fields](/fields), just like Blueprints, but the benefit of using a Fieldset is that you can import their fields (or the whole thing) into other Blueprints. You can even import fieldsets into certain fieldtypes, like [Grid](/fieldtypes/grid), [Replicator](/fieldtypes/replicator), or [Bard](/fieldtypes/bard).\n\nFieldsets can be a simple flat list of fields, or organized into [sections](#sections) when you want to group related fields together for reuse across blueprints.\n\n## Creating fieldsets\n\nThere are 2 ways to create fieldsets:\n\n- In the **Fieldsets** area of the control panel.\n- Creating a YAML file in the appropriate place within `resources/fieldsets/`. More on that in a moment.\n\nOnce created, you can begin to define its fields.\n\n## Directory structure\n\nWhether you manually create your fieldsets's YAML file, or use the control panel, they will all end up as YAML files in the `resources/fieldsets` directory.\n\n``` files theme:serendipity-light\nresources/\n  fieldsets/\n    bard-image.yaml\n    bard-quote.yaml\n    common.yaml\n```\n\nYou can even nest fieldsets in subdirectories to further organize them. Use `.` in the handle to indicate a subdirectory.\n\n### Customizing the path\n\nYou can change where fieldsets are stored by setting the `fieldsets_path` option in `config/statamic/system.php`:\n\n```php\n'fieldsets_path' => resource_path('fieldsets'),\n```\n\n## YAML structure\n\nAt its most basic, a fieldset has an array of fields.\n\n```yaml\nfields:\n  -\n    handle: content\n    type: markdown\n  -\n    handle: featured\n    type: toggle\n```\n\n## Sections\n\nFieldsets can optionally organize their fields into sections, just like blueprints. This is useful when you're building a reusable set of fields that you want grouped visually (like SEO metadata, Open Graph tags, or address fields) wherever they're imported.\n\nA fieldset uses either a flat `fields` array _or_ a `sections` array. If you save a fieldset with a single, unnamed section, Statamic collapses it back down to the flat `fields` structure automatically.\n\n```yaml\ntitle: SEO\nsections:\n  -\n    display: Metadata\n    fields:\n      -\n        handle: meta_title\n        field:\n          type: text\n      -\n        handle: meta_description\n        field:\n          type: textarea\n  -\n    display: Open Graph\n    instructions: Controls how this page appears when shared on social media.\n    collapsible: true\n    collapsed: true\n    fields:\n      -\n        handle: og_title\n        field:\n          type: text\n      -\n        handle: og_image\n        field:\n          type: assets\n          max_files: 1\n```\n\nEach section supports the following keys:\n\n| Key | Description |\n|-----|-------------|\n| `display` | The section's display name. |\n| `instructions` | Optional helper text shown under the section heading. |\n| `collapsible` | Set to `true` to let users collapse the section on publish forms. |\n| `collapsed` | When combined with `collapsible`, renders the section collapsed by default. |\n| `fields` | The array of fields in the section. |\n\n## Using fields\n\nAs mentioned earlier, a Fieldset is not inherently attached to anything. In order to use a field (or fields) in a fieldset, you'll need to approach it from the Blueprint side.\n\nSee the [Reusable Fields](/blueprints#reusable-fields) section of the Blueprint docs for more details.\n\n### Importing a sectioned fieldset\n\nWhen a blueprint imports a fieldset that has sections, you can control how those sections appear in the final publish form using `section_behavior`.\n\n```yaml\n# blueprint\nsections:\n  main:\n    display: Main\n    fields:\n      -\n        handle: content\n        field:\n          type: markdown\n      -\n        import: seo\n        section_behavior: preserve\n```\n\n| Behavior | Description |\n|----------|-------------|\n| `preserve` _(default)_ | The imported fieldset's sections are kept intact. They are split out of the current section and rendered as their own publish sections. Any fields that appear _after_ the import stay in their own section below the imported sections. |\n| `flatten` | All fields from the imported fieldset are merged directly into the current section as if it were a flat fieldset. |\n\nThe control panel surfaces this automatically — when you link a fieldset that contains sections, the \"Section Behavior\" control appears in the import settings, and a badge on the field indicates whether sections are being preserved or ignored.\n"
  },
  {
    "path": "content/collections/pages/fieldtypes.1.md",
    "content": "---\nid: 79cc18f6-d8c2-4249-97f7-d03febce051a\nblueprint: link\ntitle: Fieldtypes\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/formatters.md",
    "content": "---\ntitle: Formatters\nintro: |\n  Locale-aware formatters for dates and numbers in the Control Panel, built on top of the browser's `Intl` APIs.\nid: 4bd202d5-6ef2-4fb3-9e90-aed0d0183071\n---\nStatamic ships two JavaScript formatters for rendering dates and numbers in the user's locale. They're thin wrappers around the browser's [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) and [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) APIs with a few Statamic-flavored presets and a shared locale.\n\n```js\nimport { dateFormatter, numberFormatter } from '@statamic/cms/api';\n```\n\n:::tip\nBoth formatters share the same default locale. Calling `setDefaultLocale()` on one is reflected in the other.\n:::\n\n## Date Formatter\n\nThe `dateFormatter` renders dates using the browser's `Intl.DateTimeFormat` with a handful of built-in presets.\n\n### Presets\n\n| Preset | Description |\n|---|---|\n| `datetime` | Year, month, day, hour, minute (default) |\n| `date` | Year, month, day |\n| `time` | Short time style |\n\n### Formatting dates\n\n```js\nimport { dateFormatter } from '@statamic/cms/api';\n\ndateFormatter.format('2026-03-26T20:24:21');\n// en-us: 3/26/2026, 8:24 PM\n// de:    26.3.2026, 20:24\n\ndateFormatter.format('2026-03-26', 'date');\n// en-us: 3/26/2026\n\ndateFormatter.format('2026-03-26T20:24:21', 'time');\n// en-us: 8:24 PM\n```\n\nYou can pass any [`Intl.DateTimeFormat` options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options) object instead of a preset name:\n\n```js\ndateFormatter.format('2026-03-26', {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n});\n// March 26, 2026\n```\n\n### Overriding preset options\n\nTo start from a preset and tweak only a few fields, pass an options object with a `preset` key alongside any other `Intl.DateTimeFormat` options. The overrides are merged on top of the preset.\n\n```js\ndateFormatter.format('2026-03-26T20:24:21', {\n    preset: 'datetime',\n    timeZone: 'Australia/Sydney',\n});\n\ndateFormatter.format('2026-03-26T20:24:21', {\n    preset: 'datetime',\n    month: 'short',\n});\n```\n\n### Relative dates\n\nPass `relative: true` (or a specificity) to output a humanized relative string powered by `Intl.RelativeTimeFormat`.\n\n```js\ndateFormatter.format(someDate, { relative: true });         // \"3 months ago\"\ndateFormatter.format(someDate, { relative: 'day' });        // caps out at days\ndateFormatter.format(someDate, { relative: 'hour' });       // caps out at hours\n```\n\nValid specificities are `minute`, `hour`, `day`, `week`, `month`, and `year` (the default when `true`). When the date falls outside your specificity, it falls back to `datetime` — pass `fallback` to override:\n\n```js\ndateFormatter.format(someDate, {\n    relative: 'day',\n    fallback: 'date',\n});\n```\n\n### Static usage\n\n```js\nimport DateFormatter from '@statamic/cms/components/DateFormatter.js';\n\nDateFormatter.format('2026-03-26', 'date');\n```\n\n## Number Formatter\n\nThe `numberFormatter` renders numbers and numeric ranges using `Intl.NumberFormat`.\n\n### Presets\n\n| Preset | Description |\n|---|---|\n| `decimal` | Standard decimal formatting (default) |\n| `percent` | Multiplies by 100 and appends `%` |\n\n### Formatting numbers\n\n```js\nimport { numberFormatter } from '@statamic/cms/api';\n\nnumberFormatter.format(123456789.10);\n// en: 123,456,789.1\n// de: 123.456.789,1\n\nnumberFormatter.format(0.25, 'percent');\n// en: 25%\n\nnumberFormatter.format(1234.5, {\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n});\n// en: 1,234.50\n```\n\nAny [`Intl.NumberFormat` options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options) are accepted.\n\n### Formatting ranges\n\n```js\nnumberFormatter.formatRange(1234, 5678);\n// en:    1,234–5,678\n// ja-jp: 1,234～5,678\n\nnumberFormatter.formatRange(0.1, 0.2, 'percent');\n// en: 10%–20%\n// de: 10–20 %\n```\n\n### Static usage\n\n```js\nimport NumberFormatter from '@statamic/cms/components/NumberFormatter.js';\n\nNumberFormatter.format(1234.5, 'decimal');\nNumberFormatter.formatRange(1, 10);\n```\n\n## Locale\n\nBoth formatters default to the user's browser locale (via `Intl.DateTimeFormat().resolvedOptions().locale`). The default locale is shared between them, so changing it on one instance affects the other.\n\n### Reading the current locale\n\n```js\ndateFormatter.locale;    // e.g. \"en-us\"\nnumberFormatter.locale;  // same value\n```\n\n### Setting the default locale\n\n```js\nimport { dateFormatter, numberFormatter } from '@statamic/cms/api';\n\nnumberFormatter.setDefaultLocale('de');\n\ndateFormatter.format('2026-03-26');   // 26.3.2026, ...\nnumberFormatter.format(1234.5);       // 1.234,5\n```\n\nOr using the static accessor:\n\n```js\nimport DateFormatter from '@statamic/cms/components/DateFormatter.js';\n\nDateFormatter.defaultLocale = 'de';\nDateFormatter.defaultLocale;  // \"de\"\n```\n\n### Temporarily switching locales\n\nUse `withLocale` to run a callback under a different locale without leaking the change. The previous locale is always restored, even if the callback throws.\n\n```js\nnumberFormatter.withLocale('de', (formatter) => {\n    return formatter.format(1234.567, 'decimal'); // 1.234,567\n});\n\n// Default locale is unchanged here.\n```\n"
  },
  {
    "path": "content/collections/pages/forms.md",
    "content": "---\nid: fdb45b84-3568-437d-84f7-e3c93b6da3e6\nblueprint: page\ntitle: Forms\ntemplate: page\nintro: 'Forms are a natural part of the internet experience and a core component of most websites. From a basic \"Contact Me\" form to a multi-page job application, Statamic can help manage your forms, submissions, and thereby make your life a little bit easier.'\nrelated_entries:\n  - e4f4f91e-a442-4e15-9e16-3b9880a25522\n---\n## Overview\n\nStatamic forms collect submissions, provide reports on them on aggregate, and display user submitted data on the [frontend](/frontend). The end-to-end solution includes tags, settings, and a dedicated area of the Control Panel.\n\n## Your first form\n\nLet's pretend you're a famous celebrity with a large following of dedicated fans. If this is true, why are you building your own website? Who's going to sail your yacht?\n\nOkay, let's just pretend you're a famous celebrity's _web developer_. You've been tasked with collecting electronic fan mail (we'll call it EF-Mail). You want to collect the following bits of info from <del>crazed</del> enthusiastic fans:\n\n- name\n- age\n- level of adoration (appreciation, fixation, or obsession)\n- message\n\n### Create the form\n\nFirst, head to `/cp/forms` in the Tools area of the Control Panel and click the **Create Form** button. Alternately you can create a `.yaml` file in `resources/forms` which will contain all the form's settings.\n\nEach form should contain a title. Optionally it may also have an email configuration.\n\n```yaml\ntitle: Super Fans\nemail: []\n```\n\n### The blueprint\n\nThe [blueprint](blueprints) is where you define your form's `fields` and validation rules to be used on form submission.\n\nThe blueprint is located in `resources/blueprints/forms/{handle}.yaml`\n\n```yaml\nfields:\n  -\n    handle: name\n    field:\n      display: Name\n      type: text\n      validate: required\n  -\n    handle: age\n    field:\n      display: Age\n      type: text\n      validate: required|integer\n  -\n    handle: adoration\n    field:\n      display: Level of Adoration\n      type: text\n      validate: required\n  -\n    handle: comment\n    field:\n      display: Comment\n      type: textarea\n      validate: required\n```\n\n:::warning\nThe `message` variable is a Laravel reserved word within this email context, so you should avoid using that as a field handle if you intend on using the email feature.\n:::\n\nIf you use the Control Panel to build your blueprint, you will find that there's only a subset of fieldtypes available to you.\nThese are the fields that have corresponding views ready to be used on the front-end.\n\nIf you'd like to include more fieldtypes, you can opt into each one by calling `makeSelectableInForms` on the respective class within a service provider:\n\n```php\nStatamic\\Fieldtypes\\Section::makeSelectableInForms();\n```\n\nYou can also do the opposite and prevent a fieldtype from being used in forms by calling `makeUnselectableInForms` on the respective class within a service provider:\n\n```php\nStatamic\\Fieldtypes\\Dictionary::makeUnselectableInForms();\n```\n\n### The template\n\nSeveral [tags](tags/form) are provided to help you manage your form. You can explore these at your leisure, but for now here's a look at a basic form template.\n\nThis example dynamically renders each input's HTML. You could alternatively write the HTML yourself, perform conditions on the field's `type`, or even [customize the automatic HTML](/tags/form-create#dynamic-rendering).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:super_fans }}\n\n    // First let's check if this is after a submission, and if so, was it successful.\n    // If it was, just show the success message. After all, we don't want them submitting again once they've gotten in touch!\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}\n        </div>\n    {{ else }}\n        // If we weren't successful, show any errors. If a fresh page load, there's no errors, so do nothing.\n        {{ if errors }}\n            <div class=\"bg-red-300 text-white p-2\">\n                {{ errors }}\n                    {{ value }}<br>\n                {{ /errors }}\n            </div>\n        {{ /if }}\n\n        // Loop through and render the form inputs\n        {{ fields }}\n            <div class=\"p-2\">\n                <label>{{ display }}</label>\n                <div class=\"p-1\">{{ field }}</div>\n                {{ if error }}\n                    <p class=\"text-gray-500\">{{ error }}</p>\n                {{ /if }}\n            </div>\n        {{ /fields }}\n\n        // Add the honeypot field\n        <input type=\"text\" class=\"hidden\" name=\"{{ honeypot ?? 'honeypot' }}\">\n\n        // This is just a submit button.\n        <button type=\"submit\">Submit</button>\n    {{ /if }}\n\n{{ /form:super_fans }}\n```\n::tab blade\n```blade\n<s:form:super_fans>\n\n  // First let's check if this is after a submission, and if so, was it successful.\n  // If it was, just show the success message. After all, we don't want them submitting again once they've gotten in touch!\n  @if ($success)\n    <div class=\"bg-green-300 text-white p-2\">\n      {{ $success }}\n    </div>\n  @else\n    // If we weren't successful, show any errors. If a fresh page load, there's no errors, so do nothing.\n    @if (count($errors) > 0)\n      <div class=\"bg-red-300 text-white p-2\">\n        @foreach ($errors as $error)\n          {{ $error }}<br>\n        @endforeach\n      </div>\n    @endif\n\n    // Loop through and render the form inputs\n    @foreach ($fields as $field)\n      <div class=\"p-2\">\n        <label>{{ $field['display'] }}</label>\n        <div class=\"p-1\">{!! $field['field'] !!}</div>\n        @if ($field['error'])\n          <p class=\"text-gray-500\">{{ $field['error'] }}</p>\n        @endif\n      </div>\n    @endforeach\n\n    // Add the honeypot field\n    <input type=\"text\" class=\"hidden\" name=\"{{ isset($honeypot) ? $honeypot : 'honeypot' }}\" />\n\n    // This is just a submit button.\n    <button type=\"submit\">Submit</button>\n  @endif\n</s:form:super_fans>\n```\n::\n\n## Viewing submissions\n\nIn the Forms area of the control panel you can explore the collected responses, configure dashboards and export the data to CSV or JSON formats.\n\n<figure>\n  <img src=\"/img/cp-forms.webp\" alt=\"List of form submissions in the control panel\" class=\"u-hide-in-dark-mode\">\n  <img src=\"/img/cp-forms-dark.webp\" alt=\"List of form submissions in the control panel\" class=\"u-hide-in-light-mode\">\n  <figcaption>Forms. Submissions. Features.</figcaption>\n</figure>\n\n## Displaying submission data\n\nYou can display any or all of the submissions of your forms on the front-end of your site using the [form submissions][submissions] Tag.\n\n::tabs\n\n::tab antlers\n```antlers\n<h2>My fans have said some things you can't forget...</h2>\n{{ form:submissions in=\"superfans\" }}\n  {{ message | markdown }}\n{{ /form:submissions }}\n```\n::tab blade\n```blade\n<h2>My fans have said some things you can't forget...</h2>\n<s:form:submissions in=\"superfans\">\n  {!! Statamic::modify($message)->markdown() !!}\n</s:form:submissions>\n```\n::\n\n## Exporting your data\n\nExporting your data is just a click of the **Export** button away. You have the choice between CSV and JSON. Choose wisely, or choose both, it doesn't matter to us.\n\n### Configuring exporters\n\nOut of the box, Statamic gives you two exporters: a CSV exporter and a JSON exporter.\n\n```php\n// config/statamic/forms.php\n\n'exporters' => [\n    'csv' => [\n        'class' => Statamic\\Forms\\Exporters\\CsvExporter::class,\n    ],\n    'json' => [\n        'class' => Statamic\\Forms\\Exporters\\JsonExporter::class,\n    ],\n],\n```\n\nIf you want to customize the labels of the exporters, you may add a `title` key to the exporter's config. You can also add a `forms` key to the exporter config to limit it to certain forms:\n\n```php\n// config/statamic/forms.php\n\n'exporters' => [\n    'csv' => [\n        'class' => Statamic\\Forms\\Exporters\\CsvExporter::class,\n        'title' => 'CSV (Works in Excel)',\n        'forms' => ['contact_form', 'event_registrations'],\n    ],\n],\n```\n\n### CSV Exporter\n\nThe CSV exporter supports two configuration options:\n\n#### `csv_delimiter`\n\nThis allows you to configure the delimiter used for CSV exports. This defaults to `,`.\n\n```php\n// config/statamic/forms.php\n\n'csv_delimiter' => ',',\n```\n\n#### `csv_headers`\n\nThis allows you to configure whether the field handle or the field display text is used for the CSV's heading row. This defaults to `handle`.\n\n```php\n// config/statamic/forms.php\n\n'csv_headers' => 'handle',\n```\n\n### Custom exporter\n\nIf you need to export form submissions in a different file format or need more flexibility around how the CSV/JSON files are created, you may build your own custom exporter.\n\nTo build a custom exporter, simply create a class which extends Statamic's `Exporter` class and implement the `export` and `extension` methods:\n\n```php\n<?php\n\nnamespace App\\Forms\\Exporters;\n\nuse Statamic\\Forms\\Exporters\\Exporter;\n\nclass SpecialExporter extends Exporter\n{\n    public function export(): string\n    {\n        return '';\n    }\n\n    public function extension(): string\n    {\n        return 'csv';\n    }\n}\n```\n\nThe `export` method should return the file contents and the `extension` method should return the file extension.\n\nThen, to make the exporter available on your forms, simply add it to your forms config:\n\n```php\n// config/statamic/forms.php\n\n'exporters' => [\n    'csv' => [\n        'class' => Statamic\\Forms\\Exporters\\CsvExporter::class,\n    ],\n    'json' => [\n        'class' => Statamic\\Forms\\Exporters\\JsonExporter::class,\n    ],\n    'special_exporter' => [ // [tl! focus]\n        'class' => App\\Forms\\Exporters\\SpecialExporter::class, // [tl! focus]\n    ], // [tl! focus]\n],\n```\n\n## Emails\n\nAllowing your fans to send their comments is all well and good, but at this point you will only know about it when you head back into the Control Panel to view the submissions. Wouldn't it be better to get notified? Let's hook that up next.\n\nYou can add any number of emails to your formset.\n\n```yaml\nemail:\n  -\n    to: hello@celebrity.com\n    from: website@celebrity.com\n    subject: You've got fan mail!\n    html: fan-mail\n    text: fan-mail-text\n  -\n    to: agent@celebrity.com\n    subject: Someone still likes your client\n```\n\nHere we'll send two emails for every submission of this form. One will go to the celebrity, and one to the agent. The first one uses custom html and text views while the other doesn't, so it'll get an \"automagic\" email. The automagic email will be a simple text email with a list of all fields and values in the submission.\n\n### Email variables\n\nInside your email view, you have a number of variables available:\n\n- `date`, `now`, `today` - The current date/time\n- `site_url` - The site home page.\n- `site`, `locale` - The handle of the site\n- `config` - Any app configuration values\n- `email_config` - The email's config (the current item from your `email:` array)\n- `form_config` - Any extra config values appended to the form's blueprint (e.g. via addons using `Form::appendBlueprintTab()`)\n- Any data from [Global Sets](/globals#global-sets)\n- All of the submitted form values\n- A `fields` array\n\nThe submitted form values will be augmented for you. For instance, if you have an `assets` field, you will get a collection of Asset objects rather than just an array of paths. Or, a `select` field will be an array with `label` and `value` rather than just the value.\n\n::tabs\n\n::tab antlers\n```antlers\n<b>Name:</b> {{ name }}\n<b>Email:</b> {{ email }}\n```\n::tab blade\n```blade\n<b>Name:</b> {{ $name }}\n<b>Email:</b> {{ $email }}\n```\n::\n\nThe `fields` variable is an array available for you for if you'd rather loop over your values in a dynamic way:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ fields }}\n    <b>{{ display }}</b> {{ value }}\n{{ /fields }}\n```\n::tab blade\n```blade\n@foreach ($fields as $field)\n  <b>{{ $field['display'] }}</b> {{ $field['value'] }}\n@endforeach\n```\n::\n\nIn each iteration of the `fields` array, you have access to:\n\n- `display` - The display name of the field. (e.g. \"Name\")\n- `handle` - The handle of the field (e.g. \"name\")\n- `value` - The augmented value (same as explained above)\n- `fieldtype` - The handle of the fieldtype (e.g. \"assets\")\n- `config` - The configuration of the blueprint field\n\n\n### Setting the from and reply-to name\n\nYou can set a full \"From\" and \"Reply-To\" name in addition to the email address using the following syntax:\n\n```\nfrom: 'Jack Black <jack@jackblack.com>'\nreply_to: 'Jack Black <jack@jackblack.com>'\n```\n\n\n### Setting the recipient dynamically\n\nYou can set the recipient to an address submitted in the form by using the variable in your config block. Assuming you have a form input with `name=\"email\"`:\n\n```yaml\nemail:\n  -\n    to: \"{{ email }}\"\n    # other settings here\n```\n\n### Setting the \"Reply to\" dynamically\n\nYou can set the \"reply to\" to an address submitted in the form by using the variable in your config block. Assuming you have a form input with `name=\"email\"`:\n\n```yaml\nemail:\n  -\n    reply_to: \"{{ email }}\"\n    # other settings here\n```\n\n### Setting the \"Subject\" dynamically\n\nYou can set the email \"subject\" to a value in your form by using the variable in your config block. Assuming you have a form input with `name=\"subject\"`:\n\n```yaml\nemail:\n  -\n    subject: '{{ subject ?? \"Email Form Submission\" }}'\n    # other settings here\n```\n\n[Learn how to create your emails](/email)\n\n### Attachments\n\nWhen using [file uploads](#file-uploads) in your form, you may choose to have those attached to the email. By adding `attachments: true` to the email config, any `assets` fields will be automatically attached.\n\n```yaml\nemail:\n  -\n    attachments: true\n    # other settings here\n```\n\nIf you don't want the attachments to be kept around on your server, you should pick the `files` fieldtype option explained in the [File Uploads](#file-uploads) section.\n\n### Using Markdown Mailable Templates\n\nLaravel allows you to create email templates [using Markdown](https://laravel.com/docs/mail#markdown-mailables). It's pretty simple to wire these up with your form emails:\n\n1. Enable Markdown parsing in your email config:\n\n```yaml\nemail:\n  -\n    # other settings here\n    markdown: true # [tl! add]\n```\n\n2. Next, create a **Blade** view for your email template and start using Laravel's Markdown Mailable components:\n\n```yaml\nemail:\n  -\n    # other settings here\n    markdown: true\n    html: 'contact-us' # [tl! add]\n```\n\n```blade\n{{-- contact-us.blade.php --}}\n<x-mail::message>\n# New form submission\n\nSomeone has taken the time to fill out a form on your website. Here are the details:\n\n<x-mail::panel>\n@foreach ($fields as $item)\n<strong>{{ $item['display'] }}:</strong> {{ $item['value'] }}<br>\n@endforeach\n</x-mail::panel>\n</x-mail::message>\n```\n\n:::warning\nMake sure you don't use indentation in your Markdown view. Laravel's markdown parser will render it as code.\n:::\n\nYou can customize the components further by reviewing the [Laravel documentation](https://laravel.com/docs/13.x/mail#customizing-the-components).\n\n## File uploads\n\nSometimes your fans want to show you things they've created, like scissor-cut love letters and innocent selfies with cats. No problem! File input types to the rescue. Inform Statamic you intend to collect files, specify where you'd like the uploads to go, and whether you'd like them to simply be placed in a directory somewhere, or become reusable Assets.\n\nFirst, add a file upload field to your blueprint:\n- Add an `assets` field if you want the uploaded files to be stored in one of your asset containers.\n- Add a `files` field if you're only wanting to attach the uploads to the email. Anything uploaded using this fieldtype will be attached and then deleted after the emails are sent.\n\nThen decide if you need single or multiple files to be uploaded.\n\n### Single files\n\nOn your field, add a `max_files` setting of `1`:\n\n```\n<input type=\"file\" name=\"cat_selfie\" />\n```\n\n```yaml\nfields:\n  -\n    handle: cat_selfie\n    field:\n      type: assets\n      container: main\n      max_files: 1\n```\n\n### Multiple files\n\nYou have two methods available to you:\n\nFirst, you can create separate fields for each upload. This is useful if each has a separate purpose, like Resume, Cover Letter, and Headshot. You'll need to explicitly create each and every one in your formset.\n\nOr, you can enable multiple files on one field by dropping the `max_files` setting on your field, and using array syntax on your input by adding a set of square brackets to the `name` attribute:\n\n```\n<input type=\"file\" name=\"selfies[]\" multiple />\n```\n\n```yaml\nfields:\n  -\n    handle: selfies\n    field:\n      type: assets\n      container: main\n```\n\n## Honeypot\n\nSimple and effective spam prevention.\n\nThe honeypot technique is simple. Add a field to your forms, that when filled in will cause the submission to fail, but appear successful. Nothing will be saved and no emails are sent.\n\nHide this field by a method of your choosing (ie. CSS), so your users won't see it but spam bots will just think it’s another field.\n\nFor example:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:create }}\n    ...\n    <input type=\"text\" name=\"honeypot\" class=\"honeypot\" />\n{{ /form:create }}\n```\n::tab blade\n```blade\n<s:form:create>\n  ...\n  <input type=\"text\" name=\"honeypot\" class=\"honeypot\" />\n</s:form:create>\n```\n::\n\n```css\n.honeypot { display: none; }\n```\n\n:::tip\nIn order to fool smarter spam bots, you should customize the name of the field by changing the `name=\"\"` attribute to something common, but not used by your particular form. Like `username` or `address`. Then, add `honeypot: your_field_name` to your formset config.\n:::\n\n## Using AJAX\n\nTo submit the form with AJAX, be sure to pass all the form inputs in with the submission, as Statamic sets `_token` and `_params`, both of which are required.\n\nYou'll also need to set your ajax library's `X-Requested-With` header to `XMLHttpRequest`.\n\nThe URL endpoint to send the request to is `/!/forms/{form-handle}`. You can configure the action route prefix which defaults to `!` in `config/statamic/routes.php`.\n\n## Rate limiting\n\nForm submissions are rate limited by IP address to help protect against abuse. By default, the `statamic.forms` limiter allows 10 submissions per minute across all forms.\n\nYou can customize the limit by redefining the named rate limiter in your `AppServiceProvider`'s `boot` method:\n\n```php\nuse Illuminate\\Cache\\RateLimiting\\Limit;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\RateLimiter;\n\npublic function boot()\n{\n    RateLimiter::for('statamic.forms', function (Request $request) {\n        return Limit::perMinute(20)->by($request->ip());\n    });\n}\n```\n\nConsult the [Laravel documentation](https://laravel.com/docs/13.x/routing#rate-limiting) to learn more about defining rate limiters.\n\n## Caching\n\nIf you are static caching the URL containing a form, return responses like 'success' and 'errors' will not be available after submitting unless you [exclude this page from caching](/static-caching#excluding-pages) or wrap the form in {{ nocache }} tags.\n\n**Wrapping the form in {{ nocache }}**\n\n::tabs\n\n::tab antlers\n```antlers\n{{ nocache }}\n    {{ form:create formset=\"contact\" }}\n        ...\n    {{ /form:create }}\n{{ /nocache }}\n```\n::tab blade\n```blade\n<s:nocache>\n  <s:form:create formset=\"contact\">\n    ...\n  </s:form:create>\n</s:nocache>\n```\n::\n\n### Axios example\n\n``` javascript\nwindow.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';\nform = document.getElementById('form');\n\n// On submit...\naxios.post(form.action, new FormData(form))\n  .then(response => {\n      console.log(response.data)\n  });\n```\n\n## Precognition\n\nStatamic supports using [Laravel Precognition](https://laravel.com/docs/13.x/precognition) in forms.\n\nHere is a basic example that uses Alpine.js for the Precognition validation, and a regular form submission. This is a starting point that you may customize as needed. For instance, you might prefer to use AJAX to submit the form.\n\nNote that `js=\"alpine_precognition\"` is used rather than just `alpine`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact js=\"alpine_precognition\" }}\n    {{ if success }}\n        Success!\n    {{ /if }}\n\n    <template x-if=\"form.hasErrors\">\n        <div>\n            Errors!\n            <ul>\n                <template x-for=\"error in form.errors\">\n                    <li x-text=\"error\"></li>\n                </template>\n            </ul>\n        </div>\n    </template>\n\n    {{ fields }}\n        <label>{{ display }}</label>\n        {{ field }}\n        <small x-show=\"form.invalid('{{ handle }}')\" x-text=\"form.errors.{{ handle }}\"></small>\n    {{ /fields }}\n\n    <button :disabled=\"form.processing\">Submit</button>\n{{ /form:contact }}\n```\n\n::tab blade\n```blade\n<s:form:contact js=\"alpine_precognition\">\n    @if ($success) Success! @endif\n\n    <template x-if=\"form.hasErrors\">\n      <div>\n        Errors!\n        <ul>\n          <template x-for=\"error in form.errors\">\n            <li x-text=\"error\"></li>\n          </template>\n        </ul>\n      </div>\n    </template>\n\n    @foreach ($fields as $field)\n      <label>{{ $field['display'] }}</label>\n      {!! $field['field'] !!}\n\n      <small\n        x-show=\"form.invalid('{{ $field['handle'] }}')\"\n        x-text=\"form.errors.{{ $field['handle'] }}\"\n      ></small>\n    @endforeach\n\n    <button :disabled=\"form.processing\">Submit</button>\n</s:form:contact>\n```\n::\n\nTo build on the regular form submission example above, here's an example for AJAX submission.\n- The third argument of the `js` parameter defines the Alpine component.\n- The native form's submit event is listened for, prevented, and the component's `submit` method is called instead.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact\n    js=\"alpine_precognition:form:contact\" \n    @submit.prevent=\"submit\"\n}}\n```\n::tab blade\n```blade\n<s:form:contact \n    js=\"alpine_precognition:form:contact\" \n    @submit.prevent=\"submit\"\n>\n```\n::\n\n```html\n<script>\ndocument.addEventListener('alpine:init', () => {\n    Alpine.data('contact', (data) => ({\n        ...data,\n        submit() {\n            this.form.submit().then(response => {\n                this.form.reset();\n                console.log(\"Success\")\n            }).catch(error => {\n                console.log(error);\n            });\n        }\n    }));\n});\n</script>\n```\n\n### User Forms\n\nThe user form tags ([login][login_form], [register][register_form], [profile][profile_form], and [password][password_form]) also support Precognition — but at the request level only. The tags themselves don't accept a `js=\"alpine_precognition\"` parameter, so you wire it up manually against the form's action URL.\n\nInstall the Alpine adapter:\n\n```shell\nnpm install laravel-precognition-alpine\n```\n\nRegister it before Alpine starts:\n\n```js\nimport Alpine from 'alpinejs'\nimport precognition from 'laravel-precognition-alpine'\n\nAlpine.plugin(precognition)\nAlpine.start()\n```\n\nThen bind a `$form` to the appropriate endpoint inside the user form tag:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:login_form\n    x-data=\"{ form: $form('post', '/!/auth/login', { email: '', password: '' }) }\"\n    @submit.prevent=\"form.submit().then(() => window.location = '/dashboard')\"\n}}\n    <label>Email</label>\n    <input\n        type=\"email\"\n        name=\"email\"\n        x-model=\"form.email\"\n        @change=\"form.validate('email')\"\n    >\n    <small x-show=\"form.invalid('email')\" x-text=\"form.errors.email\"></small>\n\n    <label>Password</label>\n    <input\n        type=\"password\"\n        name=\"password\"\n        x-model=\"form.password\"\n        @change=\"form.validate('password')\"\n    >\n    <small x-show=\"form.invalid('password')\" x-text=\"form.errors.password\"></small>\n\n    <button type=\"submit\" :disabled=\"form.processing\">Log in</button>\n{{ /user:login_form }}\n```\n::tab blade\n```blade\n<s:user:login_form\n    x-data=\"{ form: $form('post', '/!/auth/login', { email: '', password: '' }) }\"\n    @submit.prevent=\"form.submit().then(() => window.location = '/dashboard')\"\n>\n    <label>Email</label>\n    <input\n        type=\"email\"\n        name=\"email\"\n        x-model=\"form.email\"\n        @change=\"form.validate('email')\"\n    >\n    <small x-show=\"form.invalid('email')\" x-text=\"form.errors.email\"></small>\n\n    <label>Password</label>\n    <input\n        type=\"password\"\n        name=\"password\"\n        x-model=\"form.password\"\n        @change=\"form.validate('password')\"\n    >\n    <small x-show=\"form.invalid('password')\" x-text=\"form.errors.password\"></small>\n\n    <button type=\"submit\" :disabled=\"form.processing\">Log in</button>\n</s:user:login_form>\n```\n::\n\nThe same pattern applies to the other user forms — just swap the action URL and the data fields:\n\n| Tag | Endpoint |\n|---|---|\n| `{{ user:login_form }}` | `/!/auth/login` |\n| `{{ user:register_form }}` | `/!/auth/register` |\n| `{{ user:profile_form }}` | `/!/auth/profile` |\n| `{{ user:password_form }}` | `/!/auth/password` |\n\nIf you'd rather submit normally (full page reload) and only use Precognition for live validation, drop the `@submit.prevent` handler.\n\n[tags]: /tags/form\n[submissions]: /tags/form-submissions\n[login_form]: /tags/user-login_form\n[register_form]: /tags/user-register_form\n[profile_form]: /tags/user-profile_form\n[password_form]: /tags/user-password_form\n"
  },
  {
    "path": "content/collections/pages/fortrabbit.md",
    "content": "---\nid: 94c521e3-bacb-45e3-b385-00bad3cac401\nblueprint: page\ntitle: Deploying Statamic with fortrabbit\nintro: fortrabbit is a managed PHP app hosting solution running on AWS. Well known since 2012.\nparent: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n\n[fortrabbit](https://www.fortrabbit.com) is a full service provider, no account with another hosting provider required. Just sign up at fortrabbit.\n\n## Creating a New App\n\nfortrabbit has a 'try before buy' model. Create your first free trial App with the fortrabbit Dashboard. The free trial is limited in time, but you can ask friendly human support to extend the trial time.\n\nWhile creating an App, choose Laravel as the framework for pre-configuration. Note that this will not install software. It is anticipated that you have a local development environment running Statamic ([see here](/installing)) to be deployed to the fortrabbit platform.\n\n## Choosing a Deployment Method\n\nThe fortrabbit hosting platform offers integrated Git deployment as well as classical SSH/SFTP access. Depending on your use case and skills pick what fit's you the most. In the following the most popular workflow is shown. Replace variables in curly braces with your settings as provided with the fortrabbit Dashboard:\n\n## Deploying Statamic with Git + rsync\n\n* Content changes are synced up and down via rsync\n* Template and theme code is deployed via Git\n* Composer dependencies are automatically installed during Git deployment\n\n### Configuring\n\nExclude contents from Git in your `.gitignore` file:\n\n```.gitignore\n# Exclude stuff you are creating from Git in .gitignore\n/content\n/users\n/resources/blueprints\n/resources/fieldsets\n/resources/forms\n/resources/users\n/storage/forms\n/public/assets\n```\n\n### Deploying Code with Git\n\nIn your local terminal, with the root folder of your Statamic project execute:\n\n```shell\n# 1. Initialize Git\ngit init\n\n# 2. Add your Apps Git remote to your local repo\ngit remote add fortrabbit {{appname}}@deploy.{{region}}.frbit.com:{{appname}}.git\n\n# 4. Add changes to Git\ngit add -A\n\n# 5. Commit changes\ngit commit -m 'My first commit'\n\n# 6. Initial push and upstream\ngit push -u fortrabbit main\n\n# From there on only\ngit push\n```\n\n### Deploying Content with rsync\n\nAgain, in your local terminal, with the root folder of your Statamic project execute:\n\n```shell\n# SYNC UP: from local to remote\n$ rsync -avR ./content ./users ./resources/blueprints ./resources/fieldsets ./resources/forms ./resources/users ./storage/forms ./public/build ./storage/app  ./public/assets {{appname}}@deploy.{{region}}.frbit.com:~/\n```\n\nIt also works down and for specific folders only as shown here:\n\n```shell\n# SYNC DOWN: from remote to local one by one examples\nrsync -av {{appname}}@deploy.{{region}}.frbit.com:~/content ./\nrsync -av {{appname}}@deploy.{{region}}.frbit.com:~/users ./\n…\n```\n\n## Advanced\n\nfortrabbit also offers MySQL resources, so you can run Statamic in database mode. The fortrabbit team really cares about Statamic. See [their extensive Statamic guides section](https://help.fortrabbit.com/#statamic) with multiple deployment articles or contact human support if you are hanging somewhere.\n"
  },
  {
    "path": "content/collections/pages/from-wordpress-to-statamic.md",
    "content": "---\nid: 550e7bf1-de6e-40ba-9b06-b32d9119e436\nblueprint: page\ntitle: 'Switching From WordPress to Statamic'\nnav_title: 'WordPress to Statamic'\nintro: |-\n  Thinking about moving from WordPress to Statamic? You wouldn't be the first. If you’ve been in the WordPress world for a while, you’re pretty familiar with one of its biggest strengths — the massive plugin ecosystem. If you're evaluating Statamic against this catalogue you might think our community's few hundred addons aren't nearly enough.\n\n  Hopefully this guide will open your eyes to a different approach to building sites. One that doesn't require so many addons and taps into a more flexible way of building bigger features out of smaller ones. And we'll show you Statamic's answers to WordPress plugins you're probably most familiar with.\n---\n\n## ACF and Custom Fields\n\nArguably one of the best ways to build modern content-driven WordPress sites — especially those with a proper separation of content and style — is with Advanced Custom Fields (ACF) or Pods Framework.\n\nInstead of this custom field approach being an afterthought, Statamic was built from the ground up with this approach with 40 different [fieldtypes](/reference/fieldtypes) that you can organize into [blueprints](/blueprints) and reusable fieldsets.\n\n<figure>\n    <img src=\"/img/blueprints.webp\" alt=\"The Statamic blueprint configuration screen\">\n    <figcaption>A glimpse at configuring a blueprint.</figcaption>\n</figure>\n\nYour fields are organized into Blueprints, which support sections and tabs for better organization. You have control over field order and width, validation rules, and can even configure conditions that show and hide fields based on your content, making your authoring experience as streamlined and uncluttered as possible for your content team.\n\nIf you have groups of fields you want to use in multiple Blueprints, you can create a reusable Fieldset that can be imported into any Blueprint, saving you time duplicating configs.\n\nIt’s super intuitive to manage through the control panel. It feels like ACF, but it’s baked right into the core CMS.\n\n## Gutenberg and block/page builders\n\nIf you've been working with a Gutenberg or Page Builder approach, take a look at the [Bard](/fieldtypes/bard) and [Replicator](/replicator) fieldtypes — they allow you to create blocks (we call them \"sets\") out of any _other_ native fieldtypes, giving you virtually unlimited ways to configure your content.\n\nThese can be used to create numerous components that can be combined as a \"page builder\" allowing your content team to create and rearrange pages without ever worrying about what it looks like.\n\n<figure>\n    <img src=\"/img/fieldtypes/screenshots/v6/bard-with-sets.webp\" width=\"597\" alt=\"Bard Fieldtype UI\">\n    <figcaption>The Bard Fieldtype in action.</figcaption>\n</figure>\n\n### Block to set examples\n\nHere is how you could create some common \"blocks\" with Bard and Replicator sets using our native fieldtypes.\n\n#### Hero\n\n- [Assets](/fieldtypes/assets) field for a background image\n- [Color](/fieldtypes/color) field to control a background or overlay color\n- [Text](/fieldtypes/text) field to edit the `<h1>` text\n- [Group](/fieldtypes/group) field with a Text and [Link](/fieldtypes/link) field to create a call to action button with a destination URL\n\n#### Slideshow\n\nA single [Assets](/fieldtypes/assets) field is all you need, as Assets themselves can have their own custom fields for alt text, description, caption, credit, etc.\n\n#### Blockquote\n\nA [Markdown](/fieldtypes/markdown) or [Bard](/fieldtypes/bard) field to hold the quote, and a [Text](/fieldtypes/text) field for the author `<cite>`.\n\n#### Newsletter signup\n\nAn empty set works, or a single [HTML](/fieldtypes/html) field letting you insert a display message in your editor saying \"Newsletter shown here\", and then on the frontend have it render whatever [partial](/tags/partial) you need for the form.\n\n#### Video embed\n\nA single [video](/fieldtypes/video) fieldtype to paste in the URL of a YouTube or Vimeo video would be enough, but you could add a [Select](/fieldtypes/select) or [Button Group](fieldtypes/button_group) field with some options to control the size of the embed (inline vs oversized, for example).\n\n::: tip\nThese fields store **structured content**, but don't explicitly give control over your _layout_ because they don't write their own HTML. You always have full control of your markup, which in the end makes for a better long-term experience, allowing you to redesign sites without ever having to clean up or rewrite content again.\n:::\n\n## Themes\n\nStatamic uses [Starter Kits](/starter-kits) instead of traditional themes. These kits go beyond just styling – they can include plugins, custom code, and entire workflows. \n\nYou can [get Starter Kits from the Marketplace](https://statamic.com/starter-kits), where there are free and commercially available options. For example, the first-party [Cool Writings](https://statamic.com/starter-kits/statamic/cool-writings) starter kit is an excellent choice to use as the basis for a simple blog.\n\nWe've even made it easy for you to [create your own starter kit](/starter-kits/creating-a-starter-kit). So once you've migrated your WordPress site, why not submit it to the marketplace?\n\n## SEO\n\nYoast SEO is probably the biggest go-to SEO plugin for the WordPress world. It's massive and does many things. But it's also pretty complicated and often overkill, especially for smaller sites.\n\nIn Statamic, you don’t really _need_ a big plugin to have good SEO. You can get a lot of mileage out of managing all your metadata using our native fields and templates, and then tap into one of the bigger reporting tools like [Moz Seo](https://moz.com/) or [Ahrefs](https://ahrefs.com) to get valuable insight about your site's content.\n\nIf you want to take it to the next level, you can check out our first-party addon — [SEO Pro](https://statamic.com/addons/statamic/seo-pro). It includes a number of useful features. It:\n\n- Sets up all your meta data fields for you, including Open Graph and Twitter data, images, and cards.\n- Provides a reporting tool to scan your site and make sure all your pages have meta titles, descriptions, and other important SEO factors\n- Generates sitemaps automatically\n- Manages Google and Bing site verifications\n- Generates a [humans.txt](https://humanstxt.org/) file to show who's _behind_ your websites\n\nBut SEO Pro isn't the only option in the Statamic Ecosystem. You can explore some of the other popular addons:\n\n- [Advanced SEO](https://statamic.com/addons/aerni/advanced-seo)\n- [Aardvark SEO](https://statamic.com/addons/candour/aardvark-seo)\n- [SEOtamic](https://statamic.com/addons/cnj/seotamic)\n- [SEO Checker](https://statamic.com/addons/luckymedia/seochecker)\n\n\n## E-Commerce\n\nWhile there is no do-it-all-and-then-some solution like WooCommerce in the Statamic world, there are still quite a few options that provide a lot of flexibility depending on your specific needs.\n\n[Cargo](https://statamic.com/addons/duncanmcclean/cargo) developed by a core team member, provides essential features like product catalogs, shopping carts, and order management. It can handle digital and physical products, tax calculations, and shipping.\n\nThe [Shopify addon](https://statamic.com/addons/rad-pack/shopify) helps you integrate with Shopify's powerful platform — controlling the frontend of your site with Statamic and leaving the heavy cart, checkout flow, and product management to Shopify.\n\n[Donation Checkout](https://statamic.com/addons/ghijk/donation-checkout) lets you accept Stripe payments of arbitrary amounts via Stripe Checkout.\n\nThere are integrations for [Lemon Squeezy](https://statamic.com/addons/rias/lemon-squeezy) and [Snipcart](https://statamic.com/addons/aerni/snipcart) as well.\n\nAdditionally, Statamic benefits from Laravel's extensive ecosystem, which includes tools like [Laravel Cashier](https://laravel.com/docs/13.x/billing) for subscription billing, and integrations with payment processors such as Stripe and Paddle. This flexibility allows developers to create fully custom e-commerce solutions tailored to specific needs.\n\n## Forms\n\nIn WordPress, forms are usually handled by plugins like Contact Form 7, WooForms, or WPForms.\n\nStatamic has a built-in [forms feature](/forms) that enable you to manage form fields, collect submissions, provide reports on them on aggregate, and even display user submitted data on the frontend.\n\nAnd if you need more customization, addons like [Flexible Forms](https://statamic.com/addons/addon-foundry/flexible-forms) or [Livewire Forms](https://statamic.com/addons/aerni/livewire-forms) can level it up further.\n\n\n## Security\n\n95.5% of the content-managed websites hacked are running WordPress ([source](https://sucuri.net/reports/2023-hacked-website-report/)). Also, last year there were almost 6000 vulnerabilities found in themes and plugins ([source](https://patchstack.com/whitepaper/state-of-wordpress-security-in-2024/)). It is the most targeted CMS on the market, which makes plugins like Wordfence, Patchstack, or WPScan critical to your security solution. Here are a few reasons Statamic is more secure than WordPress:\n\n- Around 5% of website hacks are done through SQL Injection. Out of the box, Statamic doesn't use a database, thus eliminating most forms of automated attacks.\n- Statamic's developer team maintains all of the fundamental features most websites need. You will not need 30 plugins by 30 authors on different update schedules. This is one of the reasons why WordPress is so vulnerable.\n- Statamic is built on [Laravel](https://laravel.com), widely regarded as the most secure and well-maintained PHP framework today.\n\n## Performance\n\nWordPress is notoriously slow out of the box, which is generally alleviated by plugins like WP Rocket and Redis caching.\n\nWe've considered and optimized for performance in every area of Statamic. Built-in smart caching is often enough for most sites to fly <strong>right out of the gate</strong>, and for those more complex sites that have more heavy lifting or higher traffic — [static caching](/static-caching), Redis caching, or even [static site generation](https://github.com/statamic/ssg) are all native tools at your disposal.\n\n## Spam protection\n\nIf you’re used to using Akismet to keep spam out of your forms, you can [continue doing so](https://statamic.com/addons/silentz/akismet).\n\n## Redirection\n\nNeed to manage redirects? In WordPress, you’d likely use the Redirection plugin. In Statamic, there’s an addon for that — [Redirect](https://statamic.com/addons/rias/redirect). You can redirect legacy urls, manage 301 and 302 redirects right from the control panel without any performance impacts. Super simple.\n\n## Backups\n\nIn WordPress, you might use UpdraftPlus to handle backups, but in Statamic — as long as you're running on flat files — git becomes your backup, version controlling all your changes to content, templates, and configs along the way.\n\nIf you’re using Statamic Pro, it can even automate your Git commits and pushes. No more worrying about backups, they’re just an invisible part of your workflow.\n\n## Importing content\n\nStatamic has a [native Importer](https://github.com/statamic/importer) with support for WordPress's XML or CSV export formats. It supports importing entries, taxonomy terms, and users, and can handle converting Gutenberg content to Bard sets. It even has hooks you can use to customize the import process at any step of the way.\n\n## Everything else\n\n- **Slider Revolution:** Build custom sliders with Statamic’s [Replicator field](/fieldtypes/replicator) and plug it into frontend libraries like [Slick](https://kenwheeler.github.io/slick/) or [Flickity](https://flickity.metafizzy.co/).\n- **MonsterInsights:** You can drop Google Analytics right into Statamic or use the [Ginsights Analytics](https://statamic.com/addons/vijay-software/ginsights-analytics) addon if you want a more integrated feel.\n- **WPML:** Statamic’s built-in [multi-site](/multi-site) feature helps you  manage different languages.\n- **Mailchimp for WordPress:** Use the [Mailchimp](https://statamic.com/addons/rad-pack/mailchimp) addon to connect directly with your audience.\n- **Smush, Imagify:** Statamic has you covered with [Glide](/tags/glide), an image manipulation tag that compresses and optimizes images on the fly.\n- **Comments:** Check out [Meerkat](https://statamic.com/addons/stillat/meerkat-statamic-3).\n\n## Glossary\n\nWhen migrating from WordPress to Statamic, one of the initial challenges is adapting to new terminology. While both systems share many similar concepts, they often use different names for comparable features. Hopefully this table helps point you in the right direction.\n\n\n| WordPress Term | Statamic Equivalent | Notes |\n|---------------|-------------------|-------|\n| Post/Page | Entry | Basic content unit in Statamic |\n| Custom Post Type | Collection | Groups of similar entries |\n| Category/Tag | Taxonomy | Both systems use taxonomies for classification |\n| Template | Template/View | Antlers or Blade templates in Statamic |\n| Theme | Starter Kit | Starter Kits are a starting place, but are not generally interchangeable |\n| Plugin | Addon | Extends core functionality |\n| Meta Fields | Fields/Blueprints | Statamic uses YAML for field definitions |\n| Featured Image | Asset | Part of Statamic's Asset system |\n| Menu | Navigation | We call menu structures \"Navigations\" |\n| Custom Fields/ACF | Fieldtypes | Various content input types and controls |\n| Gutenberg Block | Bard/Replicator | Rich content editing tools |\n| Shortcode | Tag | Template tags for dynamic content |\n| Media Library | Assets | Asset management system |\n| User Role | User Role/Group | Similar permission systems |\n| Options/Settings | Globals | Site-wide variables and settings |\n| Post Status | Status | Published, Draft, etc. |\n| Author | User | Content creators |\n| wp-config.php | `.env` or `config/statamic/` | Site configuration |\n| functions.php | ServiceProvider | For adding functionality |\n| hooks/filters | Events/Listeners | For modifying core behavior |\n| Child Theme | - | Statamic uses bespoke themes |\n| Featured Image | Asset | Hero/main images |\n"
  },
  {
    "path": "content/collections/pages/frontend.md",
    "content": "---\nid: 7e0dd8f1-9988-4173-8453-3ccee12ff976\ntitle: Frontend\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/getting-started.md",
    "content": "---\nid: 9157e598-81f9-4669-b25a-d9356d8b1c78\ntitle: 'Getting Started'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/git-automation.md",
    "content": "---\nid: c095fb87-4c02-462c-9e6f-dfe0b6889248\nblueprint: page\ntitle: 'Git Automation'\nintro: \"Statamic can automate your version control workflow with Git. It can automatically commit and push content as it's changed, schedule commits, or allow users to commit and push changes from the control panel without having to understand how git works.\"\npro: true\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1632512218\nrelated_entries:\n  - b46adc3b-c4de-4148-a388-c8ff498ae9c9\n---\n## Overview\n\nEnabling Statamic's Git integration is like having Spock in your enterprise, listening for content changes with those large handsome ears. You won't find anyone more committed. 🖖\n\n<figure>\n    <img src=\"/img/git-utility.webp\" alt=\"Git utility allowing user to manually trigger commits from control panel\" class=\"u-hide-in-dark-mode\" width=\"600\">\n    <img src=\"/img/git-utility-dark.webp\" alt=\"Git utility allowing user to manually trigger commits from control panel\" class=\"u-hide-in-light-mode\" width=\"600\">\n</figure>\n\n## Enabling\n\nTo enable in a specific environment, add the following to your `.env` file:\n\n```env\nSTATAMIC_GIT_ENABLED=true\n```\n\nBy default, content will be committed automatically as it's changed, but you can customize your git workflow using the provided [configuration options](#configuration).\n\n## Configuration\n\nGit workflow can be configured in your `config/statamic/git.php` file, or per environment in your `.env` file.\n\n## Git user\n\nBy default, Statamic will attempt to use the authenticated user's name and email when committing changes. If you prefer to always use hardcoded git user info, you can disable this by setting `use_authenticated` to `false` in your [configuration](#configuration):\n\n```php\n'use_authenticated' => true,\n\n'user' => [\n    'name' => env('STATAMIC_GIT_USER_NAME', 'Spock'),\n    'email' => env('STATAMIC_GIT_USER_EMAIL', 'spock@example.com'),\n],\n```\n\n_Note: Depending on how you configure your commit workflow, an authenticated user may not always be available. In these cases, Statamic will fall back to the above configured user._\n\n## Tracked paths\n\nYou are free to define the tracked paths to be considered when staging and committing changes. Default stache and file locations are already set up for you, but feel free to modify these paths in your [configuration](#configuration) to suit your storage config.\n\n```php\n'paths' => [\n    base_path('content'),\n    base_path('users'),\n    resource_path('addons'),\n    resource_path('blueprints'),\n    resource_path('fieldsets'),\n    resource_path('forms'),\n    resource_path('users'),\n    resource_path('preferences.yaml'),\n    resource_path('sites.yaml'),\n    storage_path('forms'),\n    public_path('assets'),\n],\n```\n\n:::tip\nYou may also reference absolute paths to external repositories! If Statamic detects an external repository path, changes will be staged and committed relative to your external repository.\n:::\n\n## Committing changes\n\nBy default, Statamic listens to various `Saved` and `Deleted` data events to determine when your content is changed, and will automatically commit your changes. If you prefer users to manually trigger commits using the Git utility interface, you may set this to `false` in your [configuration](#configuration):\n\n```php\n'automatic' => env('STATAMIC_GIT_AUTOMATIC', false),\n```\n\nOr in a specific environment's `.env` file:\n\n```env\nSTATAMIC_GIT_AUTOMATIC=false\n```\n\n### From the command line\n\nManually trigger commits via the command line with the following command:\n\n``` shell\nphp please git:commit\n```\n\n## Pushing changes\n\nStatamic can also `git push` your changes after committing. Enable this behavior in your [configuration](#configuration):\n\n```php\n'push' => env('STATAMIC_GIT_PUSH', true)\n```\n\nOr in a specific environment's `.env` file:\n\n```env\nSTATAMIC_GIT_PUSH=true\n```\n\n### Remote setup\n\nWhen pushing, Statamic assumes you have a [Git remote](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) with an upstream branch set, and are authenticated to push to your remote [via SSH](https://docs.github.com/en/github/using-git/which-remote-url-should-i-use).\n\n:::tip\nIf you use [Laravel Forge](https://forge.laravel.com/) or [Ploi](https://ploi.io/) to deploy your site, a git remote and upstream branch will automatically be configured for you.\n:::\n\nIf a remote and upstream is not already configured for your deployment, you may need to set this up manually on your server. For example, the following commands could be run from your deployment folder to add an `origin` remote and track the `master` branch:\n\n```shell\ngit init\ngit remote add origin git@github.com:your/remote-repository.git\ngit fetch\ngit branch --track master origin/master\ngit reset HEAD\n```\n\n\n## Queueing commits\n\nWhen automatic [committing](#committing-changes) is enabled, commits are automatically pushed onto a [queue](https://laravel.com/docs/queues) for processing. By default, your Statamic app is configured to use the `sync` queue driver, which will run the job immediately after your content is saved during the web request. You have the option to set a dedicated queue connection using the `STATAMIC_GIT_QUEUE_CONNECTION` environment variable.\n\n```env\nSTATAMIC_GIT_QUEUE_CONNECTION=redis\n```\n\n### Queueing for performance\n\nIf you are experiencing slow-down when saving or deleting content in the control panel, we recommended configuring another queue driver so that commits can be run by a background process, which will help keep the experience fast for your users.\n\n:::tip\nA popular choice is to use a [Redis](https://laravel.com/docs/redis) store and [queue driver](https://laravel.com/docs/queues#driver-prerequisites), along with [Laravel Horizon](https://laravel.com/docs/horizon) for managing your Redis queues.\n:::\n\n_Note: When commits are run by a queue's background process, there will be no authenticated user. In this case, Statamic will use the hardcoded git user in your [configuration](#configuration)._\n\n## Delaying Commits\n\nWhen [queueing commits](#queueing-commits), you can also [configure](#configuration) a dispatch delay for your commits:\n\n```php\n'dispatch_delay' => env('STATAMIC_GIT_DISPATCH_DELAY', 10),\n```\n\nOr in a specific environment's `.env` file:\n\n```env\nSTATAMIC_GIT_DISPATCH_DELAY=10\n```\n\nIn this example, we queue a delayed commit to run 10 minutes after a user makes a content change. If at that time the repository status is clean, the commit will be cancelled. Please note that the default `sync` queue driver does not support this. Use another queue driver like `redis` instead.\n\n:::tip\nSince all tracked paths are committed at once, this can allow for more consolidated commits when you have multiple users making simultaneous content changes to your repository.\n:::\n\n## Scheduling commits\n\nYou can also [schedule](https://laravel.com/docs/scheduling) commits to run via cron job at regular intervals within your `routes/console.php` file:\n\n```php\n<?php\n\nuse Illuminate\\Support\\Facades\\Schedule;\n\nSchedule::command('statamic:git:commit')->everyTenMinutes();\n```\n\nIn this example, we schedule a commit to run 10 minutes after a user makes a content change. If at that time the repository status is clean, the commit will be cancelled.\n\n_Note: If you have never used Laravel's scheduler, be sure to also [configure a cron job on your server](https://laravel.com/docs/scheduling#running-the-scheduler) to run all scheduled jobs. This only needs to be done once per server._\n\n```\n* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1\n```\n\n### Scheduling for performance\n\nScheduling commits can be a great alternative to [queueing](#queueing-commits), for when you don't have a proper queue setup. If you choose to schedule commits, just be sure to [disable automatic commit functionality](#committing-changes) as well.\n\n:::tip\nSince all tracked paths are committed at once, this can allow for more consolidated commits when you have multiple users making simultaneous content changes to your repository.\n:::\n\n_Note: When commits are scheduled to run via cron, there will be no authenticated user. In this case, Statamic will use the hardcoded git user in your [configuration](#configuration)._\n\n## Customizing commits\n\nTo customize the commit messages themselves, modify the `commands` array in the [configuration](#configuration) file.\n\nFor example, you can append `[BOT]` to the commit message so that you can selectively disable automatic site deployments when a commit is [automatically pushed](#pushing-changes) back to your repository:\n\n```php\n'commands' => [\n    'git add {{ paths }}',\n    'git -c \"user.name={{ name }}\" -c \"user.email={{ email }}\" commit -m \"{{ message }} [BOT]\"',\n],\n```\n\nIn your deploy scripts on [Forge](https://forge.laravel.com), [Ploi](https://ploi.io), or [Cleavr](https://cleavr.io) you could then add the following:\n\n### Forge\n\n``` shell\nif [[ $FORGE_DEPLOY_MESSAGE =~ \"[BOT]\" ]]; then\n    echo \"AUTO-COMMITTED ON PRODUCTION. NOTHING TO DEPLOY.\"\n    exit 0\nfi\n```\n\n### Ploi\n\n``` shell\nif [[ {COMMIT_MESSAGE} =~ \"[BOT]\" ]]; then\n    echo \"AUTO-COMMITTED ON PRODUCTION. NOTHING TO DEPLOY.\"\n    exit 0\nfi\n```\n\n### Cleavr\n\n``` shell\nif [[ \"{{ commitMessage }}\" =~ \"[BOT]\" ]]; then\n    echo 'AUTO-COMMITTED ON PRODUCTION. NOTHING TO DEPLOY.'\n    exit 1\nfi\n```\n\n## Ignoring Events\n\nWhen [automatically committing](#committing-changes), Statamic will listen on all `Saved` and `Deleted` events, as well as any events registered by installed addons. To ignore specific events, add them to the `ignored_events` array in the [configuration](#configuration) file.\n\nFor example, if you're [storing users in a database](/tips/storing-users-in-a-database), you may wish to ignore user-based events:\n\n```php\n'ignored_events' => [\n    \\Statamic\\Events\\Data\\UserSaved::class,\n    \\Statamic\\Events\\Data\\UserDeleted::class,\n],\n```\n\n:::tip\nWhen ignoring events, you may also wish to remove any related [tracked paths](#tracked-paths) from your configuration.\n:::\n\n## Addon events\n\nWhen building an addon that provides its own content saved events, you should register those events with our git listener in your addon service provider:\n\n```php\n\\Statamic\\Facades\\Git::listen(PunSaved::class);\n```\n\nThis will allow your end users to track addon-related content in their [tracked paths](#tracked-paths), if they decide to opt-in for automatic commit and push when saving.\n\n### Providing a default commit message\n\nYou can also provide a default commit message by implementing the `ProvidesCommitMessage` interface with a `commitMessage()` method definition:\n\n```php\n<?php\n\nnamespace Punner\\Events;\n\nuse Statamic\\Contracts\\Git\\ProvidesCommitMessage;\nuse Statamic\\Events\\Event;\n\nclass PunSaved extends Event implements ProvidesCommitMessage\n{\n    public $item;\n\n    public function __construct($item)\n    {\n        $this->item = $item;\n    }\n\n    public function commitMessage()\n    {\n        return __('Pun saved');\n    }\n}\n```\n"
  },
  {
    "path": "content/collections/pages/globals.md",
    "content": "---\ntitle: Globals\nintro: Global variables store content that belongs to the **whole site**, not just a single page or URL. Globals are available everywhere, in all of your views, all of the time. Just like the memory of eating your first hot pepper. 🌶\ntemplate: page\nid: 1e91dd54-c452-4e3b-8972-dba83c048d3d\nblueprint: page\n---\n## Overview\n\nGlobals are intended to be used for **reusable content** or content that **belongs to the site** and not just one page.\n\n- Company phone number, address, and logo\n- Footer content\n- Customer quotes or testimonials\n- Site settings (e.g. on/off toggles for various features)\n- Success and error message text\n\n## Global sets\n\nGlobals are organized into \"sets\", each containing [fields](/fields). This convention helps you keep groups of globals together and stay organized. Each set also acts as a \"scope\" for templating purposes.\n\n<figure>\n    <img src=\"/img/global-set-footer.webp\" alt=\"Statamic Global Set Example\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/global-set-footer-dark.webp\" alt=\"Statamic Global Set Example\" class=\"u-hide-in-light-mode\">\n    <figcaption>Global Set</figcaption>\n</figure>\n\n## Storage\n\nGlobals are stored in the `content/globals` directory.\n\n``` files theme:serendipity-light\ncontent/\n  globals/\n    global.yaml\n    footer.yaml\n    default/\n      global.yaml\n      footer.yaml\n```\n\nThe `content/globals/{handle}.yaml` file contains metadata about the global set, like its title. While, the actual data for the global set is stored in `content/globals/{site}/{handle}.yaml`.\n\n``` yaml\n# content/globals/footer.yaml\ntitle: Footer\n```\n\n```yaml\n# content/globals/default/footer.yaml\ncopyright: 2021 Neat Fake Company, LLC\nflair: Made with ❤️ by humans\n```\n\n## Frontend templating\n\n::tabs\n\n::tab antlers\n\nIn this example all of the variables inside a `footer` global set will be accessed through `footer:<var_name>`.\n\n```antlers\n<footer class=\"site-footer\">\n    <p>{{ footer:copyright }}</p>\n    <p class=\"text-sm\">{{ footer:flair }}</p>\n</footer>\n```\n::tab blade\n\nIn this example all of the variables inside a `footer` global set will be accessed through `$footer->{$var_name}`.\n\n```blade\n<footer class=\"site-footer\">\n\t<p>{{ $footer->copyright }}</p>\n\t<p class=\"text-sm\">{{ $footer->flair }}</p>\n</footer>\n```\n::\n\nIf you only have one default global set (which we named \"Globals\" because it cannot get any simpler), _the scope is optional_. You can access them with either `{{ var_name }}` or `{{ global:var_name }}`.\n\n## Blueprints are optional\n\nIf you don't explicitly create a [Blueprint](/blueprints) for your global set, Statamic will treat each key in the YAML file as a text variable. Blueprints only become necessary when you need more control over which fieldtype you want used, wish to create fields before you have the content to put in them, or want to work with [GraphQL](/graphql)\n\nIf you _do_ want a blueprint, you can configure it in the Control Panel's Global Settings. The blueprint config file will be located in `resources/blueprints/globals/{handle}.yaml`.\n\nUnrelated, \"Lorem Ipsum\" is an adorable name for a little girl.\n\n## Localization\n\nWhen running a [multi-site](/multi-site) installation, you can have globals existing in multiple sites with different content.\n\n[Read about localizing globals](/tips/localizing-globals)\n\n## Ideas on how to use globals\n\nHere are a few more ideas what you can use globals for:\n\n- **Theme or design settings**, with [assets fields](/fieldtypes/assets) for logo, and favicon and [colors fields](/fieldtypes/color) to set brand colors.\n- **JavaScript embed codes**, using a [replicator field](/fieldtypes/replicator) to add any number of [textarea fields](/fieldtypes/textarea) for analytics, pixel trackers, and other \"copy and paste this before the `</body>` tag\" type things\n- **Interactive text-adventure games**. Not really sure how you'd do it honestly, but we'd like to see someone try.\n"
  },
  {
    "path": "content/collections/pages/graphql.md",
    "content": "---\ntitle: 'GraphQL API'\nintro: 'The GraphQL API is a **read-only** API for delivering content from Statamic to your frontend, external apps, SPAs, and numerous other possible sources. Content is delivered as JSON data.'\npro: true\nblueprint: page\nid: fc564ddf-80c1-4d87-8675-4a41f13c7774\n---\n(If you're interested in a [REST API](/content-api), we have one of those too.)\n\n## Enable GraphQL\n\nTo enable the GraphQL API, add the following to your `.env` file:\n\n```env\nSTATAMIC_GRAPHQL_ENABLED=true\n```\n\nOr you can enable it for all environments in `config/statamic/graphql.php`:\n\n```php\n'enabled' => true,\n```\n\nYou will also need to [enable the resources](#enable-resources) you want to be available. For security, they're all disabled by default.\n\n:::tip\nWhen GraphQL is enabled, [GraphiQL](https://github.com/graphql/graphiql) is available in the Control Panel. This allows you to explore and test available queries and fields.\n:::\n\n:::tip Heads up\nIf you publish the underlying [package's](#laravel-package) config, the query routes will be enabled regardless of whether you've disabled it in the Statamic config.\n:::\n\n### Enable resources\n\nYou can enable resources (ie. Collections, Taxonomies, etc.) in your `config/statamic/graphql.php` config:\n\n```php\n'resources' => [\n    'collections' => true,\n    'taxonomies' => true,\n    // etc.\n]\n```\n\n### Enable specific sub-resources\n\nIf you want more granular control over which sub-resources are enabled within a resource type (ie. enabling specific Collection queries only), you can use array syntax:\n\n```php\n'resources' => [\n    'collections' => [\n        'articles' => true,\n        'pages' => true,\n        // 'events' => false, // Sub-resources are disabled by default\n    ],\n    'taxonomies' => true,\n    // etc.\n]\n```\n\n\n## Interfaces\n\nStatamic will provide \"interface\" types, which describe more generic items. For instance, an `EntryInterface` exists for all\nentries, which would provide fields like `id`, `slug`, `status`, `title`, and so on.\n\nIn addition to the interfaces, Statamic will provide implementations of them, which would come from the blueprints.\n\nFor example, if you had a collection named `pages`, and it had blueprints of `page` and `home`, you would find `Entry_Pages_Page`\nand `Entry_Pages_Home` types. These implementations would provide fields specific to the blueprint, like `subtitle`, `content`, etc.\n\n```graphql\n{\n    entries {\n        id\n        title\n        data {\n            ... on Entry_Pages_Page {\n                subtitle\n                content\n            }\n            ... on Entry_Pages_Home {\n                hero_intro\n                hero_image\n            }\n        }\n    }\n}\n```\n\n## Queries\n\nStatamic has a number of root level queries you can perform to get data.\n\nYou can read about the [available queries](#available-queries) further down the page,\nbut know that you can perform more than one query at a time. They just need to be at the top level of your GraphQL query body.\n\nFor example, the following would perform both `entries` and `collections` queries\n\n```graphql\n{\n    entries {\n        # ...\n    }\n    collections {\n        # ...\n    }\n}\n```\n\nThe response will contain the results of both queries:\n\n```json\n{\n    \"entries\": { /* ... */ },\n    \"collections\": { /* ... */ },\n}\n```\n\nNote that you can even perform the same query multiple times. If you want to do this, you should use aliases:\n\n```graphql\n{\n    home: entry(id: \"home\") {\n        title\n    }\n    contact: entry(id: \"contact\") {\n        title\n    }\n}\n```\n\n```json\n{\n    \"home\": { /* ... */ },\n    \"contact\": { /* ... */ },\n}\n```\n\n## Available queries\n\n- [Ping](#ping-query)\n- [Collections](#collections-query)\n- [Collection](#collection-query)\n- [Entries](#entries-query)\n- [Entry](#entry-query)\n- [Asset Containers](#asset-containers-query)\n- [Asset Container](#asset-container-query)\n- [Assets](#assets-query)\n- [Asset](#asset-query)\n- [Taxonomies](#taxonomies-query)\n- [Taxonomy](#taxonomy-query)\n- [Terms](#terms-query)\n- [Term](#term-query)\n- [Global Sets](#global-sets-query)\n- [Global Set](#global-set-query)\n- [Navs](#navs-query)\n- [Nav](#nav-query)\n\n### Ping {#ping-query}\n\nUsed for testing that your connection works. If you send a query of `{ping}`, you should receive `{\"data\": {\"ping\": \"pong\"}}`.\n\n```graphql\n{\n    ping\n}\n```\n\n```json\n{\n    \"data\": {\n        \"ping\": \"pong\"\n    }\n}\n```\n\n### Collections {#collections-query}\n\nUsed for querying collections.\n\nReturns a list of [Collection](#collection-type) types.\n\n```graphql\n{\n    collections {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"collections\": [\n        { \"handle\": \"blog\", \"title\": \"Blog Posts\" },\n        { \"handle\": \"events\", \"title\": \"Events\" },\n    ]\n}\n```\n\n### Collection {#collection-query}\n\nUsed for querying a single collection.\n\nReturns a [Collection](#collection-type) type.\n\n```graphql\n{\n    collection(handle: \"blog\") {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"collections\": {\n        \"handle\": \"blog\",\n        \"title\": \"Blog Posts\"\n    }\n}\n```\n\n### Entries {#entries-query}\n\nUsed for querying multiple entries.\n\nReturns a [paginated](#pagination) list of [EntryInterface](#entry-interface) types.\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `collection` | `[String]` | Narrows down the results by entries in one or more collections.\n| `limit` | `Int` | The number of results to be shown per paginated page.\n| `page` | `Int` | The paginated page to be shown. Defaults to `1`.\n| `filter` | `JsonArgument` | Narrows down the results based on [filters](#filtering).\n| `sort` | `[String]` | [Sorts](#sorting) the results based on one or more fields and directions.\n\nExample query and response:\n\n```graphql\n{\n    entries {\n        current_page\n        data {\n            id\n            title\n        }\n    }\n}\n```\n\n```json\n{\n    \"entries\": {\n        \"current_page\": 1,\n        \"data\": [\n            { \"id\": 1, \"title\": \"First Entry\" },\n            { \"id\": 2, \"title\": \"Second Entry\" }\n        ]\n    }\n}\n```\n\n### Entry {#entry-query}\n\nUsed for querying a single entry.\n\n```graphql\n{\n    entry(id: 1) {\n        id\n        title\n    }\n}\n```\n\n```json\n{\n    \"entry\": {\n        \"id\": 1,\n        \"title\": \"First Entry\"\n    }\n}\n```\n\n### Asset containers {#asset-containers-query}\n\nUsed for querying asset containers.\n\n```graphql\n{\n    assetContainers {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"assetContainers\": [\n        { \"handle\": \"images\", \"title\": \"Images\" },\n        { \"handle\": \"documents\", \"title\": \"Documents\" },\n    ]\n}\n```\n\n### Asset container {#asset-container-query}\n\nUsed for querying a single asset container.\n\nReturns an [AssetContainer](#asset-container-type) type.\n\n```graphql\n{\n    assetContainer(handle: \"images\") {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"assetContainer\": {\n        \"handle\": \"images\",\n        \"title\": \"Images\"\n    }\n}\n```\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `handle` | `String!` | Specifies which asset container to retrieve.\n\n### Assets {#assets-query}\n\nUsed for querying multiple assets of an asset container.\n\nReturns a [paginated](#pagination) list of [AssetInterface](#asset-interface) types.\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `container` | `String!` | Specifies which asset container to query.\n| `limit` | `Int` | The number of results to be shown per paginated page.\n| `page` | `Int` | The paginated page to be shown. Defaults to `1`.\n| `filter` | `JsonArgument` | Narrows down the results based on [filters](#filtering).\n| `sort` | `[String]` | [Sorts](#sorting) the results based on one or more fields and directions.\n\nExample query and response:\n\n```graphql\n{\n    assets(container: \"images\") {\n        current_page\n        data {\n            url\n        }\n    }\n}\n```\n\n```json\n{\n    \"entries\": {\n        \"current_page\": 1,\n        \"data\": [\n            { \"url\": \"/assets/images/001.jpg\" },\n            { \"url\": \"/assets/images/002.jpg\" },\n        ]\n    }\n}\n```\n\n### Asset {#asset-query}\n\nUsed for querying a single asset.\n\n```graphql\n{\n    asset(id: 1) {\n        id\n        title\n    }\n}\n```\n\n```json\n{\n    \"asset\": {\n        \"id\": 1,\n        \"title\": \"First Entry\"\n    }\n}\n```\n\nYou can either query by `id`, or by `container` and `path` together.\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `id` | `String` | The ID of the asset. If you use this, you don't need `container` or `path`.\n| `container` | `String` | The container to look for the asset. You must also provide the `path`.\n| `path` | `String` | The path to the asset, relative to the container. You must also provide the `container`.\n\n### Taxonomies {#taxonomies-query}\n\nUsed for querying taxonomies.\n\n```graphql\n{\n    taxonomies {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"taxonomies\": [\n        { \"handle\": \"tags\", \"title\": \"Tags\" },\n        { \"handle\": \"categories\", \"title\": \"Categories\" },\n    ]\n}\n```\n\n### Taxonomy {#taxonomy-query}\n\nUsed for querying a single taxonomy.\n\n```graphql\n{\n    taxonomy(handle: \"tags\") {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"taxonomy\": {\n        \"handle\": \"tags\",\n        \"title\": \"Tags\"\n    }\n}\n```\n\n### Terms {#terms-query}\n\nUsed for querying multiple taxonomy terms.\n\nReturns a [paginated](#pagination) list of [TermInterface](#term-interface) types.\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `taxonomy` | `[String]` | Narrows down the results by terms in one or more taxonomies.\n| `limit` | `Int` | The number of results to be shown per paginated page.\n| `page` | `Int` | The paginated page to be shown. Defaults to `1`.\n| `filter` | `JsonArgument` | Narrows down the results based on [filters](#filtering).\n| `sort` | `[String]` | [Sorts](#sorting) the results based on one or more fields and directions.\n\nExample query and response:\n\n```graphql\n{\n    terms {\n        current_page\n        data {\n            id\n            title\n        }\n    }\n}\n```\n\n```json\n{\n    \"terms\": {\n        \"current_page\": 1,\n        \"data\": [\n            { \"id\": \"tags::one\", \"title\": \"Tag One\" },\n            { \"id\": \"tags::two\", \"title\": \"Tag Two\" }\n        ]\n    }\n}\n```\n\n### Term {#term-query}\n\nUsed for querying a single taxonomy term.\n\n```graphql\n{\n    term(id: \"tags::one\") {\n        id\n        title\n    }\n}\n```\n\n```json\n{\n    \"term\": {\n        \"id\": \"tags::one\",\n        \"title\": \"Tag One\"\n    }\n}\n```\n\n### Global sets {#global-sets-query}\n\nUsed for querying multiple global sets.\n\nReturns a list of [GlobalSetInterface](#global-set-interface) types.\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `taxonomy` | `[String]` | Narrows down the results by terms in one or more taxonomies.\n| `limit` | `Int` | The number of results to be shown per paginated page.\n| `page` | `Int` | The paginated page to be shown. Defaults to `1`.\n| `sort` | `[String]` | [Sorts](#sorting) the results based on one or more fields and directions.\n\nExample query and response:\n\n```graphql\n{\n    globalSets {\n        title\n        handle\n        ... on GlobalSet_Social {\n            twitter\n        }\n        ... on GlobalSet_Company {\n            company_name\n        }\n    }\n}\n```\n\n```json\n{\n    \"globalSets\": [\n        { \"handle\": \"social\", \"twitter\": \"@statamic\" },\n        { \"handle\": \"company\", \"company_name\": \"Statamic\" },\n    ]\n}\n```\n\n### Global set {#global-set-query}\n\nUsed for querying a single global set.\n\n```graphql\n{\n    globalSet(handle: \"social\") {\n        title\n        handle\n        ... on GlobalSet_Social {\n            twitter\n        }\n    }\n}\n```\n\n```json\n{\n    \"globalSet\": {\n        \"title\": \"Social\",\n        \"handle\": \"social\",\n        \"twitter\": \"@statamic\",\n    }\n}\n```\n\n### Forms {#forms-query}\n\nUsed for querying multiple forms.\n\n```graphql\n{\n    forms {\n        handle\n        title\n        fields {\n            handle\n            display\n        }\n    }\n}\n```\n\n```json\n{\n    \"forms\": [\n        {\n            \"handle\": \"contact\",\n            \"title\": \"Contact\",\n            \"fields\": [\n                { \"handle\": \"name\", \"display\": \"Name\" },\n                { \"handle\": \"email\", \"display\": \"Email\" },\n                { \"handle\": \"inquiry\", \"display\": \"Inquiry\" }\n            ]\n        }\n    ]\n}\n```\n\n### Form {#form-query}\n\nUsed for querying a single form.\n\n```graphql\n{\n    form(handle: \"contact\") {\n        handle\n        title\n        fields {\n            handle\n            display\n        }\n    }\n}\n```\n\n```json\n{\n    \"form\": {\n        \"handle\": \"contact\",\n        \"title\": \"Contact\",\n        \"fields\": [\n            { \"handle\": \"name\", \"display\": \"Name\" },\n            { \"handle\": \"email\", \"display\": \"Email\" },\n            { \"handle\": \"inquiry\", \"display\": \"Inquiry\" }\n        ]\n    }\n}\n```\n\n### Navs {#navs-query}\n\nUsed for querying Navs.\n\n```graphql\n{\n    navs {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"navs\": [\n        { \"handle\": \"header_links\", \"title\": \"Header Links\" },\n        { \"handle\": \"footer_links\", \"title\": \"Footer Links\" },\n    ]\n}\n```\n\n### Nav {#nav-query}\n\nUsed for querying a single Nav.\n\n```graphql\n{\n    nav(handle: \"footer\") {\n        handle\n        title\n    }\n}\n```\n\n```json\n{\n    \"nav\": {\n        \"handle\": \"footer\",\n        \"title\": \"Footer Links\"\n    }\n}\n```\n\n### Users {#users-query}\n\nUsed for querying multiple users.\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `limit` | `Int` | The number of results to be shown per paginated page.\n| `page` | `Int` | The paginated page to be shown. Defaults to `1`.\n| `filter` | `JsonArgument` | Narrows down the results based on [filters](#filtering).\n| `sort` | `[String]` | [Sorts](#sorting) the results based on one or more fields and directions.\n\nExample query and response:\n\n```graphql\n{\n    users {\n        current_page\n        data {\n            name\n            email\n        }\n    }\n}\n```\n\n```json\n{\n    \"users\": {\n        \"current_page\": 1,\n        \"data\": [\n            { \"name\": \"David Hasselhoff\", \"email\": \"thehoff@statamic.com\" },\n            { \"name\": \"Chuck Norris\", \"email\": \"norris@statamic.com\" },\n        ]\n    }\n}\n```\n\n### User {#user-query}\n\nUsed for querying a single user.\n\n```graphql\n{\n    user(email: \"thehoff@statamic.com\") {\n        name\n        email\n    }\n}\n```\n\n```json\n{\n    \"user\": {\n        \"name\": \"David Hasselhoff\",\n        \"email\": \"thehoff@statamic.com\"\n    }\n}\n```\n\nYou can query by either `id` or `email`.\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `id` | `String` | The ID of the user. If you use this, you don't `email`.\n| `email` | `String` | The email address of the user. If you use this, you don't `id`.\n\n## Custom queries\n\nHere's an example of a basic query class. It has the name attribute which is the key the user needs to put in the request, any number of middleware, the type(s) that will be returned, any arguments, and how the data should be resolved.\n\n```php\nuse Statamic\\Facades\\GraphQL;\nuse Statamic\\GraphQL\\Queries\\Query;\n\nclass Products extends Query\n{\n    protected $attributes = [\n        'name' => 'products',\n    ];\n\n    protected $middleware = [\n        MyMiddleware::class,\n    ];\n\n    public function type(): Type\n    {\n        return GraphQL::paginate(GraphQL::type(ProductType::NAME));\n    }\n\n    public function args(): array\n    {\n        return [\n            'limit' => GraphQL::int(),\n        ];\n    }\n\n    public function resolve($root, $args)\n    {\n        return Product::paginate($args['limit']);\n    }\n}\n```\n\n```graphql\n{\n    products {\n        name\n        price\n    }\n}\n```\n\nYou may add your own queries to Statamic's default schema.\n\nYou can add them to the config file, which makes sense for app specific queries:\n\n```php\n// config/statamic/graphql.php\n'queries' => [\n    MyCustomQuery::class\n]\n```\n\nOr, you may use the `addQuery` method on the facade, which would be useful for addons.\n\n```php\nGraphQL::addQuery(MyCustomQuery::class);\n```\n\n## Types\n\n- [EntryInterface](#entry-interface)\n- [Collection](#collection-type)\n- [CollectionStructure](#collection-structure-type)\n- [CollectionTreeBranch](#collection-tree-branch-type)\n- [NavTreeBranch](#nav-tree-branch-type)\n- [PageInterface](#page-interface)\n- [TermInterface](#term-interface)\n- [AssetInterface](#asset-interface)\n- [GlobalSetInterface](#global-set-interface)\n- [Code](#code-type)\n\n### EntryInterface {#entry-interface}\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | `ID!` |\n| `title` | `String!` |\n\nEach `EntryInterface` will also have implementations for each collection/blueprint combination.\n\nYou will need to query the implementations using fragments in order to get blueprint-specific fields.\n\n```graphql\n{\n    entries {\n        id\n        title\n        data {\n            ... on Entry_Blog_Post {\n                intro\n                content\n            }\n            ... on Entry_Blog_ArtDirected_Post {\n                hero_image\n                content\n            }\n        }\n    }\n}\n```\n\nThe fieldtypes will define their types. For instance, a text field will be a `String`, a [grid](#grid-fieldtype) field will expose a list of `GridItem` types.\n\n### Collection {#collection-type}\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `handle` | `String!` |\n| `title` | `String!` |\n| `structure` | [`CollectionStructure`](#collection-structure-type) | If the collection is structured (e.g. a \"pages\" collection), you can use this to query its tree.\n\n### CollectionStructure {#collection-structure-type}\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `handle` | `String!` |\n| `title` | `String!` |\n| `tree` | [[`CollectionTreeBranch`](#collection-tree-branch-type)] | A list of tree branches.\n\n### CollectionTreeBranch {#collection-tree-branch-type}\n\nRepresents a branch within a structured collection's tree.\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `depth` | `Int!` | The nesting level of the current branch.\n| `entry` (or `page`) | [`EntryInterface`](#entry-interface) | Contains the entry's fields.\n| `children` | [[`CollectionTreeBranch`](#collection-tree-branch-type)] | A list of tree branches.\n\n:::tip\nIt's not possible to perform recursive queries in GraphQL. If you want to retrieve multiple levels of child branches, take a look at a workaround in [recursive tree branches](#recursive-tree-branches) below.\n:::\n\n### NavTreeBranch {#nav-tree-branch-type}\n\nRepresents a branch within a nav's tree.\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `depth` | `Int!` | The nesting level of the current branch.\n| `page` | [`PageInterface`](#page-interface) | Contains the page's fields.\n| `children` | [[`NavTreeBranch`](#nav-tree-branch-type)] | A list of tree branches.\n\n:::tip\nIt's not possible to perform recursive queries in GraphQL. If you want to retrieve multiple levels of child branches, take a look at a workaround in [recursive tree branches](#recursive-tree-branches) below.\n:::\n\n\n### PageInterface {#page-interface}\n\nA \"page\" within a nav's tree.\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | `ID!` | The ID of the page.\n| `entry_id` | `ID` | The `entry` ID.\n| `title` | `String` | For entry pages, it's the entry's `title` unless overridden on the branch. For basic pages, it's the `title`.\n| `url` | `String` | For entry pages, it's the entry's `url`. For basic pages, it's the `url`. For text-only pages it'll be null.\n| `permalink` | `String` | The absolute version of `url`.\n\nIf you want to query any fields that you've added to the nav's blueprint, you have 4 different options available to you that you can use as inline fragments.\nYou can use more than one at a time:\n\n- `EntryInterface` for all entry pages.\n- `NavEntryPage_{NavHandle}_{Collection}_{Blueprint}` for a specific entry/blueprint combination on entry pages.\n- `NavBasicPage_{NavHandle}` for basic non-entry pages.\n- `NavPage_{NavHandle}` for either basic or entry pages.\n\n```graphql\npage {\n    title\n    url\n    ... on EntryInterface {\n        # ...\n    }\n    ... on NavPage_HeaderLinks {\n        # ...\n    }\n    ... on NavBasicPage_HeaderLinks {\n        # ...\n    }\n    ... on NavEntryPage_HeaderLinks_Blog_ArtDirected {\n        # ...\n    }\n}\n```\n\n### TermInterface {#term-interface}\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | `ID!` |\n| `title` | `String!` |\n| `slug` | `String!` |\n\nEach `TermInterface` will also have implementations for each taxonomy/blueprint combination.\n\nYou will need to query the implementations using fragments in order to get blueprint-specific fields.\n\n```graphql\n{\n    terms {\n        id\n        title\n        ... on Term_Tags_RegularTag {\n            content\n        }\n        ... on Term_Tags_SpecialTag {\n            how_special\n            content\n        }\n    }\n}\n```\n\nThe fieldtypes will define their types. For instance, a text field will be a `String`, a [grid](#grid-fieldtype) field will expose a list of `GridItem` types.\n\n### AssetInterface {#asset-interface}\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `path` | `String!` | The path to the asset.\n\nEach `AssetInterface` will also have an implementation for each asset container's blueprint.\n\nYou will need to query the implementations using fragments in order to get blueprint-specific fields.\n\n```graphql\n{\n    entries {\n        path\n        ... on Asset_Images {\n            alt\n        }\n    }\n}\n```\n\nThe fieldtypes will define their types. For instance, a text field will be a `String`, a [grid](#grid-fieldtype) field will expose a list of `GridItem` types.\n\n### GlobalSetInterface {#global-set-interface}\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `handle` | `String!` | The handle of the set.\n| `title` | `String!` | The title of the set.\n\nEach `GlobalSetInterface` will also have an implementation for each set's blueprint.\n\n:::tip\nWhile Statamic doesn't enforce a blueprint for globals (see [Blueprint is Optional](/globals#blueprints-are-optional)), it _is_ required within the GraphQL context. Fields that haven't been explicitly added to a blueprint will not be available.\n:::\n\nYou will need to query the implementations using fragments in order to get blueprint-specific fields.\n\n```graphql\n{\n    globalSets {\n        handle\n        ... on GlobalSet_Social {\n            twitter\n        }\n    }\n}\n```\n\nThe fieldtypes will define their types. For instance, a text field will be a `String`, a [grid](#grid-fieldtype) field will expose a list of `GridItem` types.\n\n### Code {#code-type}\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `code` | `String!` | The actual code value.\n| `mode` | `String!` | The language \"mode\".\n\nThe [code fieldtype](/fieldtypes/code) will return this type when `mode_selectable` is enabled. Otherwise, it'll just be a string.\n\n```graphql\n{\n    snippet {\n        code\n        mode\n    }\n}\n```\n\n\n## Filtering\n\n### Enabling filters\n\nFor security, [filtering](#filtering) is disabled by default. To enable, you'll need to opt in by defining a list of `allowed_filters` for each sub-resource in your `config/statamic/graphql.php` config:\n\n```php\n'resources' => [\n    'collections' => [\n        'articles' => [\n            'allowed_filters' => ['title', 'status'],\n        ],\n        'pages' => [\n            'allowed_filters' => ['title'],\n        ],\n        'events' => true, // Enable this collection without filters\n        'products' => true, // Enable this collection without filters\n    ],\n    'taxonomies' => [\n        'topics' => [\n            'allowed_filters' => ['slug'],\n        ],\n        'tags' => true, // Enable this taxonomy without filters\n    ],\n    // etc.\n],\n```\n\nFor queries that don't have sub-resources (ie. users), you can define `allowed_filters` at the top level of that resource config:\n\n```php\n'resources' => [\n    'users' => [\n        'allowed_filters' => ['name', 'email'],\n    ],\n],\n```\n\n### Using filters\n\nYou can filter the results of listing queries (like `entries`) using the `filter` argument. This argument accepts a JSON object containing different\n[conditions](/conditions).\n\n```graphql\n{\n    entries(filter: {\n        title: { contains: \"rad\", ends_with: \"!\" }\n    }) {\n        data {\n            title\n        }\n    }\n}\n```\n\n```json\n{\n    \"data\": [\n        { \"title\": \"That was so rad!\" },\n        { \"title\": \"I wish I was as cool as Daniel Radcliffe!\" },\n    ]\n}\n```\n\nIf you only need to do a simple \"equals\" condition, then you can use a string and omit the condition name, like the `rating` here:\n\n```graphql\n{\n    entries(filter: {\n        title: { contains: \"rad\" }\n        rating: 5\n    }) {\n        # ...\n    }\n```\n\nIf you need to use the same condition on the same field more than once, you can use the array syntax:\n\n```graphql\n{\n    entries(filter: {\n        title: [\n            { contains: \"rad\" },\n            { contains: \"awesome\" },\n        ]\n    }) {\n        # ...\n    }\n```\n\n### Advanced filtering config\n\nYou can also allow filters on all enabled sub-resources using a `*` wildcard config. For example, here we'll enable only the `articles`, `pages`, and `products` collections, with `title` filtering enabled on each, in addition to `status` filtering on the `articles` collection specifically: \n\n```php\n'resources' => [\n    'collections' => [\n        '*' => [\n            'allowed_filters' => ['title'], // Enabled for all collections\n        ],\n        'articles' => [\n            'allowed_filters' => ['status'], // Also enable on articles\n        ],\n        'pages' => true,\n        'products' => true,\n    ],\n],\n```\n\nIf you've enabled filters using the `*` wildcard config, you can disable filters on a specific sub-resource by setting `allowed_filters` to `false`:\n\n```php\n'resources' => [\n    'collections' => [\n        '*' => [\n            'allowed_filters' => ['title'], // Enabled for all collections\n        ],\n        'articles' => [\n            'allowed_filters' => false, // Disable filters on articles\n        ],\n        'pages' => true,\n        'products' => true,\n    ],\n],\n```\n\nOr you can enable queries and filters on all sub-resources at once by setting both `enabled` and `allowed_filters` within your `*` wildcard config:\n\n```php\n'resources' => [\n    'collections' => [\n        '*' => [\n            'enabled' => true, // All collection queries enabled\n            'allowed_filters' => ['title'], // With filters enabled for all\n        ],\n    ],\n],\n```\n\n\n## Sorting\n\nYou can sort the results of listing queries (like `entries`) on one or multiple fields, in any direction.\n\n```graphql\n{\n    entries(sort: \"title\") {\n        # ...\n    }\n```\n\n```graphql\n{\n    entries(sort: \"title desc\") {\n        # ...\n    }\n```\n\n```graphql\n{\n    entries(sort: [\"price desc\", \"title asc\"]) {\n        # ...\n    }\n```\n\n## Pagination\n\nSome queries (like [entries](#entries-query)) will provide their results using pagination.\n\nIn a paginated response, you will find the actual items within a `data` key.\n\nBy default there will be `1000` per page. You can change this using a `limit` argument.\nYou can specify the current paginated page using the `page` argument.\n\n```graphql\n{\n    entries(limit: 15, page: 2) {\n        current_page\n        has_more_pages\n        data {\n            # ...\n        }\n    }\n}\n```\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `data` | [mixed] | A list of items on the current page. In an `entries` query, there will be `EntryInterface` types, etc.\n| `total` | `Int!` | Number of total items selected by the query.\n| `per_page` | `Int!` | Number of items returned per page.\n| `current_page` | `Int!` | Current page of the cursor.\n| `from` | `Int` | Number of the first item returned.\n| `to` | `Int` | Number of the last item returned.\n| `last_page` | `Int!` | The last page (number of pages).\n| `has_more_pages` | `Boolean!` | Determines if cursor has more pages after the current page.\n\n\n## Fieldtypes\n\n### Replicator\n\nReplicator fields require that you query each set using a separate fragment.\n\nThe fragments are named after your configured sets using StudlyCased field and set handles. e.g. `Set_{ReplicatorFieldName}_{SetHandle}`\n\n```yaml\nfields:\n  -\n    handle: content_blocks\n    field:\n      type: replicator\n      sets:\n        image:\n          fields:\n            -\n              handle: image\n              type: assets\n              max_files: 1\n        pull_quote:\n          fields:\n            -\n              handle: quote\n              field:\n                type: textarea\n            -\n              handle: author\n              field:\n                type: text\n```\n\n```graphql\n{\n    content_blocks {\n        ... on Set_ContentBlocks_Image {\n            type\n            image\n        }\n        ... on Set_ContentBlocks_PullQuote {\n            type\n            quote\n            author\n        }\n    }\n}\n```\n\n:::tip\nIf you have nested fields, include each parent's handle, (and grandparent's, great grandparent's etc), like so: `Set_TopLevelReplicator_NestedReplicator_DeeplyNestedReplicator_SetHandle`\n:::\n\n### Bard\n\nBard fields work the same as Replicator, except that you also have an additional `BardText` for the text fragment.\n\n```graphql\n{\n    content_blocks {\n        ... on BardText {\n            type\n            text\n        }\n        ... on Set_ContentBlocks_Image {\n            type\n            image\n        }\n        ... on Set_ContentBlocks_PullQuote {\n            type\n            quote\n            author\n        }\n    }\n}\n```\n\n### Grid\n\nGrid fields can be queried with no extra requirements. You can just use the nested field handles.\n\n```graphql\n{\n    cars {\n        make\n        model\n    }\n}\n```\n\n### Select, radio, checkboxes, and button group\n\nThese fieldtypes provide you with labels and values. You'll need to use a sub selection.\n\n```graphql\nmy_select_field {\n    value\n    label\n}\n```\n\n```json\n\"my_single_select_field\": {\n    \"value\": \"potato\",\n    \"label\": \"Potato\"\n}\n```\n\nThe same syntax is used when multiple values are expected. e.g. a select field with multiple values enabled, or a checkboxes field. You'll just get a nested array returned.\n\n```json\n\"my_multi_select_field\": [\n    {\n        \"value\": \"potato\",\n        \"label\": \"Potato\"\n    },\n    {\n        \"value\": \"tomato\",\n        \"label\": \"Tomato\",\n    }\n]\n```\n\n## Recursive tree branches\n\nOften, when dealing with navs, you need to recursively output all the child branches. For example, when using the `nav` tag in Antlers, you might do something like this:\n\n```\n<ul>\n{{ nav }}\n    <li>\n        <a href=\"{{ url }}\">{{ title }}</a>\n        {{ if children }}\n            <ul>{{ *recursive children* }}</ul>\n        {{ /if }}\n    </li>\n{{ /nav }}\n</ul>\n```\n\nIn GraphQL, it's not possible to perform recursive queries like that. You'll need to explicitly query each level:\n\n```graphql\n{\n    nav(handle: \"links\") {\n        tree {\n            page {\n                title\n                url\n            }\n            children {\n                page {\n                    title\n                    url\n                }\n                children {\n                    page {\n                        title\n                        url\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\nIn this example, if you wanted anything more than `title` and `url`, you'd need to add them to each level.\n\nThis can quickly become tedious and is very repetitive, so here's a workaround using fragments.\n\nIf you wanted to add more fields, you only need to do it one spot - the `Fields` fragment. If you want to query more levels, you can just increase the nesting level of the `RecursiveChildren` fragment.\n\n```graphql\n{\n    nav(handle: \"links\") {\n        tree {\n            ...Fields\n            ...RecursiveChildren\n        }\n    }\n}\n\nfragment Fields on NavTreeBranch {\n    depth\n    page {\n        title\n        url\n        # any other fields you want for each branch\n    }\n}\n\nfragment RecursiveChildren on NavTreeBranch {\n    children {\n        ...Fields\n        children {\n            ...Fields\n            children {\n                ...Fields\n                # just keep repeating this as deep as necessary\n            }\n        }\n    }\n}\n```\n\nHat tip to Hash Interactive for their [blog post](https://hashinteractive.com/blog/graphql-recursive-query-with-fragments/) on this technique.\n\n## Custom fieldtypes\n\nA fieldtype can define what GraphQL type will be used. By default, all fieldtypes will return strings.\n\n```php\nuse GraphQL\\Type\\Definition\\Type;\n\npublic function toGqlType()\n{\n    return GraphQL::string();\n}\n```\n\nYou're free to return an array with a more complicated structure in order to provide arguments, etc.\n\n```php\nuse GraphQL\\Type\\Definition\\Type;\n\npublic function toGqlType()\n{\n    return [\n        'type' => GraphQL::string(),\n        'args' => [\n            //\n        ]\n    ];\n}\n```\n\nIf you need to register any types, the fieldtype can do that in the `addGqlTypes` method:\n\n```php\npublic function addGqlTypes()\n{\n    // A class that extends Rebing\\GraphQL\\Support\\Type\n    $type = MyType::class; // or `new MyType;`\n\n    GraphQL::addType($type);\n}\n```\n\n## Laravel package\n\nUnder the hood, Statamic uses the [rebing/graphql-laravel](https://github.com/rebing/graphql-laravel) package.\n\nBy default, the integration should feel seamless and you won't even know another package is being used. Statamic will perform the following automatic configuration of this package:\n\n- Setting up the `default` schema to Statamic's.\n- Disabling the `/graphiql` route (since we have our own inside the Control Panel)\n\nHowever, you're free to use this package on its own, as if you've installed it into a standalone Laravel application.\n\nIf Statamic detects that you've published the package's config file (located at `config/graphql.php`), it will assume you're trying to use it manually and will\navoid doing the automatic setup steps mentioned above.\n\nIf you'd like to use Statamic's GraphQL schema within the config file (maybe you want a different default, and want Statamic's one at `/graphql/statamic`) you can use the `DefaultSchema` class.\n\n```php\n[\n    'schemas' => [\n        'statamic' => \\Statamic\\GraphQL\\DefaultSchema::class\n    ]\n]\n```\n\n## Authorization\n\nBy default, all queries are allowed by anyone. We plan to add native features in the future.\n\nYou can define custom authorization logic for any query by providing a closure to the static `auth` method.\n\n```php\nEntriesQuery::auth(function () {\n    return true; // true authorizes, false denies.\n});\n```\n\n:::warning\nPer-request authorization logic is **not safe to cache**. Statamic's response cache keys on the query and variables only — not on the user or request — so the first response is served to everyone after it. If you use `::auth()` closures or per-user authorization, [disable the cache](#disabling-caching).\n:::\n\n## Authentication\n\nOut of the box, the GraphQL API is publicly accessible.\n\nYou can restrict access to the API by adding the `STATAMIC_GRAPHQL_AUTH_TOKEN` key to your `.env` file. It should be set to a long, random string.\n\n```php\nSTATAMIC_GRAPHQL_AUTH_TOKEN=a-long-random-string\n```\n\nThen, when you make requests to the GraphQL API, you'll need to include the token in the `Authorization` header, like this:\n\n```curl\ncurl -X GET \"https://example.com/graphql\" \\\n  -H \"Authorization: Bearer a-long-random-string\" \\\n  -H \"Accept: application/json\"\n  -d '{\"query\": \"{ping}\"}'\n```\n\n### Authenticating users\n\nIf you want to authenticate based on users, we recommend using [Laravel Sanctum](https://laravel.com/docs/master/sanctum) instead.\n\nTo use Sanctum, you'll need to [store users in the database](/tips/storing-users-in-a-database) and add the `auth:sanctum` middleware in the `graphql.php` config.\n\n```php\n// config/statamic/graphql.php\n\n'middleware' => [\n    'auth:sanctum',\n],\n```\n\n:::warning\nWhen responses vary per authenticated user, you must [disable the response cache](#disabling-caching). The default cache is shared across all clients and does not account for the request's user, so one user's data can be served to another.\n:::\n\n## Custom fields\n\nYou can add fields to certain types by using the `addField` method on the facade.\n\nThe method expects the [type](#types) name, the field name, and a closure that returns a GraphQL field definition array.\n\nFor example, if you wanted to include a thumbnail from an asset field named `image`, you could do that here. You can even have arguments. In this example, we'll expect the width of the thumbnail to be passed in.\n\n```php\nuse GraphQL\\Type\\Definition\\Type;\nuse Statamic\\Facades\\GraphQL;\nuse Statamic\\Facades\\Image;\nuse Statamic\\Facades\\URL;\n\nGraphQL::addField('EntryInterface', 'thumbnail', function () {\n    return [\n        'type' => GraphQL::string(),\n        'args' => [\n            'width' => [\n                'type' => GraphQL::int(),\n            ]\n        ],\n        'resolve' => function ($entry, $args) {\n            $asset = $entry->image;\n            $url = Image::manipulate($asset)->width($args['width'])->build();\n            return URL::makeAbsolute($url);\n        }\n    ];\n});\n```\n\n```graphql\n{\n    entry(id: 1) {\n        thumbnail(width: 100)\n    }\n}\n```\n\n```json\n\n{\n    \"entry\": {\n        \"thumbnail\": \"http://yoursite.com/img/asset/abc123?w=100\"\n    }\n}\n```\n\nThe closure you pass to the method should return a GraphQL field definition array.\n\nYou may add custom fields to the following types and any of their implementations:\n\n- `EntryInterface`\n- `PageInterface`\n- `TermInterface`\n- `AssetInterface`\n- `GlobalSetInterface`\n\n## Caching\n\nGraphQL uses a basic whole-response cache by default. Each query/variables combination's response will be cached for an hour. You may customize the cache expiry in `config/statamic/graphql.php`.\n\n```php\n'cache' => [\n    'expiry' => 60,\n],\n```\n\n:::warning\nThe cache key is based on the **query and variables only** — not the authenticated user or request context. This means any per-request authorization (via [`::auth()`](#authorization) closures or [per-user authentication](#authenticating-users) like Sanctum) is **not safe to cache**, because the first response will be served to every subsequent client regardless of who they are.\n\nA global [auth token](#authentication) is safe — the request is rejected before it ever reaches the cache, so everyone who gets through has identical access. Per-user auth is not. If any of your queries return data that depends on who's asking, [disable caching](#disabling-caching).\n:::\n\n### Cache invalidation\n\nCached responses are automatically invalidated when content is changed. Depending on your GraphQL usage and blueprint schema, you may also wish to ignore specific events when invalidating.\n\n```php\n'cache' => [\n    'expiry' => 60,\n    'ignored_events' => [\n        \\Statamic\\Events\\UserSaved::class,\n        \\Statamic\\Events\\UserDeleted::class,\n    ],\n],\n```\n\n### Disabling caching\n\nIf you wish to disable caching altogether, set `cache` to `false`.\n\n```php\n'cache' => false,\n```\n\n## Custom middleware\n\nYou may add custom middleware, which are identical to any other Laravel middleware class. They will be executed on all GraphQL requests (unless another middleware, e.g. caching, prevents it).\n\nUse the `handle` method to perform some action, and pass the request on.\n\n```php\nuse Closure;\n\nclass MyMiddleware\n{\n    public function handle($request, Closure $next)\n    {\n        // do something\n\n        return $next($request);\n    }\n}\n```\n\nYou may add your own middleware to Statamic's default schema.\n\nYou can add them to the config file, which makes sense for app specific middleware:\n\n```php\n// config/statamic/graphql.php\n'middleware' => [\n    MyMiddleware::class\n]\n```\n\nOr, you may use the `addMiddleware` method on the facade, which would be useful for addons.\n\n```php\nGraphQL::addMiddleware(MyMiddleware::class);\n```\n\n## Troubleshooting\n\n### \"Cannot query field\" error\n\nIf you see an error like `Cannot query field \"entries\" on type \"Query\"`, this likely means you haven't enabled that query. See [Enable GraphQL](#enable-graphql).\nAfter enabling it, you may need to clear your cache as the request would probably have been cached.\n"
  },
  {
    "path": "content/collections/pages/home.md",
    "content": "---\nid: 5ee53e1b-8933-4b2e-a72d-af09f2b32600\nblueprint: home\ntitle: Home\nmeta_title: 'Learn Statamic'\nintro: 'This is where the learning begins and the veterans return for their references.'\ntemplate: home\ncontent_width: flex-1\nbreadcrumb_title: 'Learn Statamic'\ntiles:\n  -\n    id: m8fsi7v5\n    tile_image: tiles/floppy-disk.png\n    tile_title: 'Installing Statamic'\n    tile_description: 'Let’s get Statamic installed and start tinkering around.'\n    type: tile\n    enabled: true\n    flush_image: false\n    hue_rotate: false\n    tile_link: 'entry::ab08f409-8bbe-4ede-b421-d05777d292f7'\n  -\n    id: m8fsmcva\n    tile_image: tiles/cat.png\n    tile_title: 'Learn by Watching'\n    tile_description: 'Learn Statamic with Jack’s free Laravel Creator Series.'\n    type: tile\n    enabled: true\n    flush_image: false\n    hue_rotate: hue_rotate_1\n    tile_link: 'https://learnstatamic.com'\n  -\n    id: m8fszcgf\n    tile_image: tiles/modern-people.png\n    tile_title: 'Discord Community'\n    tile_description: 'Hang out with exceptional devs & designers 24/7.'\n    type: tile\n    enabled: true\n    flush_image: false\n    hue_rotate: false\n    tile_link: 'https://statamic.com/discord'\n  -\n    id: m8ft268a\n    tile_image: tiles/computer.png\n    tile_title: 'Antler Templating Docs'\n    tile_description: 'Start unlocking the mighty flexible powers of Antlers.'\n    type: tile\n    enabled: true\n    flush_image: false\n    hue_rotate: false\n    tile_link: 'entry::d37b2af2-f2bf-493a-9345-7087fb5929ce'\n  -\n    id: m8ft2vpz\n    tile_image: tiles/cd.png\n    tile_title: 'UI Component Library'\n    tile_description: 'Level up your custom addons and control panel extensions'\n    type: tile\n    enabled: true\n    flush_image: false\n    hue_rotate: false\n    tile_link: 'https://ui.statamic.dev'\n  -\n    id: m8ft8fb1\n    tile_image: tiles/stereo.png\n    tile_title: 'Recent Doc Updates'\n    tile_description: 'Help keep your Statamic superpowers up-to-date.'\n    type: tile\n    enabled: true\n    flush_image: false\n    hue_rotate: false\n    tile_link: 'entry::a77eb282-e050-4288-b83d-789840e245fd'\nadvert_override: 972d7159-e76a-4817-a441-65d965d8c794\n---\n"
  },
  {
    "path": "content/collections/pages/hooks.md",
    "content": "---\nid: 900414a8-f73a-46f0-82bd-b607767f5d5d\nblueprint: page\ntitle: 'Hooks'\nintro: 'Statamic allows you to hook into specific points in PHP logic and perform operations using Pipelines.'\n---\n:::warning\nThis page is about PHP-based hooks. We also have [JavaScript-based hooks](/extending/js-hooks), which work differently.\n:::\n\n## About\n\nClosures may be registered allowing you to \"hook\" into a specific point in PHP's lifecycle. These closures are added to a pipeline.\n\nHooks may be located in tags, fieldtypes, and so on.\n\nAt some point, a payload is send through the pipeline, allowing any registered closures to inspect or modify the payload, then finally gets sent back to the origin.\n\n## How to use hooks\n\nFor example, the `collection` tag will query for entries, then run the `fetched-entries` hook, passing all the entries along. Your hook may modify these entries.\n\n```php\n// app/Providers/AppServiceProvider.php\n\nuse Statamic\\Tags\\Collection;\n\nCollection::hook('fetched-entries', function ($entries, $next) {\n    // Modify the entries...\n    $modified = $entries->take(3);\n\n    // Pass them along to the next registered closure.\n    return $next($modified);\n});\n```\n\nIt's also possible to wait until all the other closures in the pipeline have completed. To do that, pass it along to the next closure _first_.\n\nFor example, maybe you need to get all the ids of the entries that will be output. By passing along to the other closures first, it will give them a chance to manipulate it. In the example above, it would take the first 3 entries. Now in this hook we'll be getting 3 ids rather than the full amount the tag was originally going to output.\n\n```php\n// app/Providers/AppServiceProvider.php\n\nuse Statamic\\Tags\\Collection;\n\nCollection::addHook('fetched-entries', function ($entries, $next) {\n    // Pass the payload along to the next registered closures.\n    $entries = $next($entries);\n\n    $ids = $entries->pluck('id');\n\n    // You'll still need to return it!\n    return $entries;\n});\n```\n\n### Scope\n\nThe closure is scoped to the class where the hook was triggered. The `$this` variable will be the class itself, and will act as if you're in the class so you can call protected methods, as well as any macroed methods.\n\n```php\nTag::addHook('name', function ($payload, $next) {\n    // {{ tag foo=\"bar\" }}\n    $this->params->get('foo'); // bar\n});\n```\n\n\n## Available hooks\n\n### All tags: `init`\nTriggered after the tag has been initialized. The payload is `null`.\n\n### Collection tag: `fetched-entries`\nTriggered just after completing the query.\nThe payload will either be an `EntryCollection` or a `Paginator`, depending on whether the `paginate` parameter was used.\n\n### Form tag: `attrs`\nTriggered when building the opening form tag. The payload is an array containing two properties:\n- 'attrs' - an array containing the currently calculated list of attributes for the opening &lt;form&gt; tag. Modifications to this array will affect the rendered form tag - e.g. it can be used to add attributes to the form tag.\n- 'data' - the data assembled about the form (config, blueprint, sections etc.)\n\n### Form tag: `after-open`\nTriggered immediately after the opening form tag. The payload is an array containing two properties:\n- 'html' - A string containing the rendered markup of the form so far. Modifications to this string will affect the final rendered markup.\n- 'data' - the data assembled about the form (config, blueprint, sections etc.)\n\n### Form tag: `before-close`\nTriggered immediately before the closing form tag. The payload is an array containing two properties:\n- 'html' - A string containing the rendered markup of the form so far. Modifications to this string will affect the final rendered markup.\n- 'data' - the data assembled about the form (config, blueprint, sections etc.)\n\n### Augmentation: `augmented`\nTriggered when a new augmented instance is made.\nThe payload will be the object being augmented (eg. `Entry` / `Term`).\n\n### Asset Thumbnails: `asset`\nTriggered when generating a thumbnail for an asset in the Control Panel.\n\n```php\nuse Statamic\\Http\\Resources\\CP\\Assets\\FolderAsset as FolderAssetResource;\n\nFolderAssetResource::hook('asset', function ($payload, $next) {\n    $payload->data->thumbnail ??= \"https://custom-thumbnail-cdn.com/{$this->resource->id()}\";\n\n    return $next($payload);\n});\n```\n\n### Entry Creation Values: `creating-entry`\nTriggered when showing the entry creation form in the Control Panel.\nThe payload will be an object with `entry` and `values` properties. You can modify `values` to change the default values on screen.\n\n```php\nuse Statamic\\Http\\Controllers\\CP\\Collections\\EntriesController;\n        \nEntriesController::hook('creating-entry', function ($payload, $next) {\n    if ($payload->entry->collection()->handle() == 'my-collection') {\n        $payload->values = [...$payload->values, 'title' => 'testing 123'];\n    }\n\n    return $next($payload);\n});\n```\n\n### Entry Index Query: `query`\nTriggered before the index query for the Entries listing table is executed.\nThe payload will be an object with `query` and `collection` properties.\n\n```php\nuse Statamic\\Hooks\\CP\\EntriesIndexQuery;\n\nEntriesIndexQuery::hook('query', function ($payload, $next) {\n    $payload->query; // a QueryBuilder instance\n    $payload->collection; // a Collection instance\n\n    return $next($payload);\n});\n```\n\n### Bard: `augment`\nTriggered while the Bard fieldtype is being augmented.\nThe payload will be an array of the Bard's content.\n\n### Bard: `process`\nTriggered when the `process` method is called on the Bard fieldtype (when saving a Bard field in the Control Panel).\nThe payload will be an array of the Bard's content.\n\n### Bard: `pre-process`\nTriggered when the `preProcess` method is called on the Bard fieldtype (when preparing the Bard field for the publish form).\nThe payload will be an array of the Bard's content.\n\n### Bard: `pre-process-index`\nTriggered when the `preProcessIndex` method is called on the Bard fieldtype (when preparing the Bard field for a listing column).\nThe payload will be an array of the Bard's content.\n\n### Bard: `pre-process-validatable`\nTriggered when the `preProcessValidatable` method is called on the Bard fieldtype (when preparing the field for validation).\nThe payload will be an array of the Bard's content.\n\n### Bard: `preload`\nTriggered when the `preload` method is called on the Bard fieldtype (when preparing the `meta` prop for the publish form).\nThe payload will be an array of the Bard's content.\n\n### Bard: `extra-rules`\nTriggered when the `extraRules` method is called on the Bard fieldtype (when gathering validation rules).\nThe payload will be an array of the Bard's content.\n\n### Bard: `extra-validation-attributes`\nTriggered when the `extraValidationAttributes` method is called on the Bard fieldtype (when gathering validation attributes).\nThe payload will be an array of the Bard's content.\n\n### Static Cache Warming: `additional`\nTriggered when the `static:warm` command is run. This hook allows you to warm additional URIs during the static warming process.\nFor more information about this hook, see the docs on [Static Caching](/static-caching#warming-additional-urls).\n\n### Multisite Command: `after`\nTriggered at the end of the `multisite` command. This hook allows you to run code when an app is being converted from a single-site to a multi-site.\nThe payload is `null`.\n\n### GetItemsContainingData: `additional`\nTriggered when updating asset and term references. This hook allows you to return additional content to be updated. You should return a [`LazyCollection`](https://laravel.com/docs/13.x/collections#lazy-collections).\n\n## Triggering your own hooks\n\nYou may want to trigger your own hook pipeline so that others may use it.\n\nTo do this, you may use the `runHooks` method from the `Hookable` trait, passing the hook name and a payload.\nOnce any hook closures have finished running, the payload will be returned back from it.\n\n```php\nuse Statamic\\Support\\Traits\\Hookable;\n\nclass YourClass\n{\n    use Hookable;\n\n    public function something()\n    {\n        $result = $this->runHooks('hook-name', $payload);\n    }\n}\n```\n\nNow others will be able to call `hook` on your class to register their hook:\n\n```php\nYourClass::hook('hook-name', function ($payload, $next) {\n    // ...\n    return $next($payload);\n});\n```\n\n:::tip\nTag classes already `use Hookable` so you can simply use `$this->runHooks()` without importing anything.\n:::\n"
  },
  {
    "path": "content/collections/pages/image-manipulation.md",
    "content": "---\nid: 245068a1-1900-4774-a3ba-29192dc9acff\nblueprint: page\ntitle: 'Image Manipulation (Glide)'\nintro: Statamic uses [Glide](https://glide.thephpleague.com) to manipulate images – from resizing and cropping to adjustments (like sharpness and contrast) and image effects (like pixelate and sepia).\n---\n## Route\n\nThe route controls where your Glide images will be served.\n\n```php\n'image_manipulation' => [\n    'route' => 'img'\n]\n```\n\nBy default your Glide images will be served from `'/img/...'` but you are free to change that. Perhaps if you intend to have some actual images stored in the `img` directory.\n\n:::tip\nThis route setting may become irrelevant when using customized [caching options](#caching) explained further down this page.\n:::\n\n## Presets\n\nPresets are pre-configured sets of manipulations that can be referenced at a later time. They are managed in `config/statamic/assets.php` as an array that holds a list of named presets and their desired parameters.\n\n```php\n'image_manipulation' => [\n    'presets' => [\n        'thumbnail' => [ 'w' => 300,  'h' => 300, 'q' => 75 ],\n        'hero'      => [ 'w' => 1440, 'h' => 600, 'q' => 90 ],\n    ],\n],\n```\n\nAll standard [Glide API parameters](https://glide.thephpleague.com/3.0/api/quick-reference/) are available for use in presets.\n\n## Drivers\n\nOut of the box, Glide will use the [GD](https://www.php.net/manual/en/book.image.php) library for manipulating images. However, you can also use [ImageMagick](https://imagemagick.org) (which requires the `imagick` PHP extension) or [libvips](https://github.com/libvips/php-vips). \n\nYou can change the driver in your `config/statamic/assets.php` file:\n\n```php\n'driver' => 'gd', // or 'imagick', or a custom driver class name\n```\n\nTo learn more about the available drivers, please refer to the [Glide documentation](https://glide.thephpleague.com/3.0/config/image-driver/).\n\n## Glide tag\n\nEach named preset can be referenced with the `preset` parameter on the [Glide tag][glide-tag]:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide:thumbnail preset=\"thumbnail\" }}\n<!-- width: 300px, height: 300px, quality: 75% -->\n\n{{ glide:hero_image preset=\"hero\" }}\n<!-- width: 1440px, height: 600px, quality: 90% -->\n```\n::tab blade\n```blade\n<s:glide:thumbnail preset=\"thumbnail\" />\n<!-- width: 300px, height: 300px, quality: 75% -->\n\n<s:glide:hero_image preset=\"hero\" />\n<!-- width: 1440px, height: 600px, quality: 90% -->\n```\n::\n\n### Generate on upload\n\nWhen uploading an image asset, any configured presets will be generated so they're ready when you need to reference them, e.g. in the Glide tag.\n\nBy default, all presets are generated, however you can [customize this per-container](#customize-glide-preset-warming).\n\nYou may also choose to disable image generation on upload completely:\n\n```php\n'image_manipulation' => [\n    'generate_presets_on_upload' => false,\n],\n```\n\n### Generate manually\n\nYou may want to generate the presets manually (for example after you changed the config, and you already uploaded the images, or if you've disabled generation on upload) on the command line:\n\n```bash\nphp please assets:generate-presets\n```\n\n### Process source images\n\nSometimes you may wish to process your actual source images on upload. For example, maybe you need to enforce maximum dimensions on extremely large images in order to save on disk space.\n\nTo do this, first configure an image manipulation preset in `config/statamic/assets.php` for this purpose:\n\n```php\n'image_manipulation' => [\n    'presets' => [\n        'max_upload_size' => ['w' => 2000, 'h' => 2000, 'fit' => 'max'],\n    ],\n],\n```\n\nThen in your asset container settings, you can configure uploads to use this preset:\n\n<figure class=\"mt-0\">\n    <img src=\"/img/glide-process-source-images.webp\" alt=\"Glide Process Source Images\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/glide-process-source-images-dark.webp\" alt=\"Glide Process Source Images\" class=\"u-hide-in-light-mode\">\n</figure>\n\n:::tip\nThe `fit` is the important part for this one. Using `max` will ensure images smaller than those dimensions will not be upscaled - only larger images will be resized.\n:::\n\n### Customize preset warming\n\nAs mentioned [above](#presets), Statamic will generate images for all of your configured presets on upload. (i.e. \"warming\" the generated images).\n\nBy default, Statamic will do this \"intelligently\", which means it'll generate all presets except for the one used for source processing:\n\n<figure>\n    <img src=\"/img/glide-intelligently-warm.webp\" alt=\"Glide Intelligently Warm Presets\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/glide-intelligently-warm-dark.webp\" alt=\"Glide Intelligently Warm Presets\" class=\"u-hide-in-light-mode\">\n</figure>\n\nHowever, you may wish to configure which presets are warmed in your asset container settings (or leave this option blank to disable warming altogether):\n\n<figure class=\"mt-0\">\n    <img src=\"/img/glide-warm-specific-presets.webp\" alt=\"Glide Warm Specific Presets\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/glide-warm-specific-presets-dark.webp\" alt=\"Glide Warm Specific Presets\" class=\"u-hide-in-light-mode\">\n</figure>\n\n:::tip\nIf you have a preset that's only going to be used with images in one particular container, you should customize which ones are used so your server doesn't have to waste resources on generating and storing images that won't get used.\n:::\n\n## Caching\n\nOut of the box, Glide will \"just work\". However, you may want to adjust its caching methods for better performance.\n\nIn the context of Glide, the \"source\" is the filesystem where the original images are kept, and the \"cache\" is the filesystem where it saves the manipulated images.\n\n### Default (Dynamic)\n\nThe default behavior is for the cache to be \"disabled\" or \"dynamic\".\n\n```php\n// config/statamic/assets.php\n'image_manipulation' => [\n    'route' => 'img',\n    'cache' => false,\n]\n```\n\nFrom a user's point of view, the \"cache\" is disabled, however technically it's just located at `storage/statamic/glide`.\n\nThe [Glide tag][glide-tag] will output URLs to the configured Glide [route](#route). When one of these URLs are visited, Statamic will use Glide to perform the transformation.\n\n:::tip\nWhen using this method, since the Glide tag only needs to generate URLs, the load time of the page will be faster, but the initial load time of each image request will be slower.\n:::\n\n:::tip\nBe sure to set `STATAMIC_STACHE_WATCHER=false` in your `.env`.\n:::\n\n### Custom path (static)\n\nThe next level of caching would be to specify a custom, publicly accessible location for the images to be generated.\n\n``` php\n// config/statamic/assets.php\n\n'image_manipulation' => [\n    'route' => 'img',\n    'cache' => true,\n    'cache_path' => public_path('img'),\n]\n```\n\nWhen using this setting, the [Glide tag][glide-tag] will _actually generate_ the images instead of just outputting a URL.\n\nSince the images are generated to a publicly accessible location, the next time a user visits the image URL, the static image would be served directly by the server, and would not need to be touched by PHP or Statamic.\n\n:::tip\nWhen using this method, since the Glide tag has to generate the images, the initial load time of the page will be slower.\n:::\n\n### Custom disk (CDN)\n\nYou may choose to save your cached Glide images to somewhere CDN based, like Amazon S3 or DigitalOcean Spaces. Instead of specifying `true` as mentioned above, you can point to a filesystem disk.\n\n```php\n// config/statamic/assets.php\n\n'image_manipulation' => [\n    'cache' => 'glide',\n],\n```\n\n```php\n// config/filesystems.php  [tl! **]\n\n'disks' => [  // [tl! **]\n    'glide' => [  // [tl! **]\n        'driver' => 's3',  // [tl! **]\n        'key' => env('AWS_ACCESS_KEY_ID'),\n        'secret' => env('AWS_SECRET_ACCESS_KEY'),\n        'region' => env('AWS_DEFAULT_REGION'),\n        'bucket' => env('AWS_BUCKET_GLIDE'), // [tl! **]\n        'url' => env('AWS_URL_GLIDE'),  // [tl! **]\n        'endpoint' => env('AWS_ENDPOINT'),\n        'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),\n        'visibility' => 'public',  // [tl! **]\n    ],\n]\n```\n\n:::tip\nMake sure that the `visibility` is `public` and that the `url` points to the correct location.\n:::\n\n:::warning\nDon't use the same disk, bucket or url as your source images. If you were to clear your Glide cache (e.g. when using the `glide:clear` command) the whole disk will be emptied.\n:::\n\n## Path cache store\n\nBefore Glide tries to generate an image, it will look into the filesystem to determine whether the image has already been generated. This will prevent the need for the image to be needlessly re-generated.\n\nHowever, when using the [Custom Disk CDN](#custom-disk-cdn) caching option with a service like Amazon S3 for example, Glide will need to make an API call just to be able to check if a file exists. This would cause a slowdown.\n\nTo alleviate this problem, Statamic will keep track of whether the images have already been generated in its own separate cache.\n\nThis cache is separate from your application cache. Running `php artisan cache:clear` will **not** clear this Glide cache. This allows the Glide cache to persist through deployments or other scenarios where you might clear your application cache. It will be cleared when running `php please glide:clear`.\n\nBy default, this cache will be located in your filesystem with the storage directory.\n\nIf you would like to customize it, you can create a new store named `glide` in your `config/cache.php` configuration file. For example:\n\n```php\n'stores' => [\n    'glide' => [\n        'driver' => 'redis',\n        'connection' => 'glide',\n    ],\n]\n```\n\nIn this example, you would also need to create a Redis database named `glide` in your `config/database.php` configuration file:\n\n```php\n'redis' => [\n    'glide' => [\n        'url' => env('REDIS_URL'),\n        'host' => env('REDIS_HOST', '127.0.0.1'),\n        'password' => env('REDIS_PASSWORD'),\n        'port' => env('REDIS_PORT', '6379'),\n        'database' => env('REDIS_GLIDE_DB', '2'),\n    ],\n],\n```\n\nWhen using the [database driver](https://laravel.com/docs/13.x/cache#prerequisites-database), make sure to specify the connection and table, like so:\n\n```php\n'glide' => [\n    'driver' => 'database',\n    'connection' => 'mysql',\n    'table' => 'cache',\n],\n```\n\n## Custom hash\n\nBy default, Glide-generated images are saved into a directory named after an md5 hash of the manipulation parameters, resulting in a path like this:\n\n```\ncontainers/assets/path/to/image.jpg/0638baede3a7fc1a91f605e095ab74cc/image.jpg\n```\n\nYou may customize how that hash is generated — for example, to produce human-readable paths for debugging, QA, or aesthetics. Register a closure in a service provider's `boot` (or `register`) method:\n\n```php\nuse Statamic\\Facades\\Glide;\n\npublic function boot()\n{\n    Glide::generateHashUsing(function (string $source, array $params) {\n        return collect($params)\n            ->sortKeys()\n            ->map(fn ($value, $param) => \"$param-$value\")\n            ->join('-');\n    });\n}\n```\n\nWith the closure above, this tag:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide:myimage width=\"100\" height=\"50\" fit=\"crop\" quality=\"50\" }}\n```\n::tab blade\n```blade\n<s:glide:myimage width=\"100\" height=\"50\" fit=\"crop\" quality=\"50\" />\n```\n::\n\n...would generate to a path like this:\n\n```\ncontainers/assets/path/to/image.jpg/fit-crop-h-50-q-50-w-100/image.jpg\n```\n\n:::warning\nYour closure must return a string that is unique per combination of parameters. Collisions will cause the wrong image to be served.\n:::\n\n## Clearing the cache\n\nYou may manually clear the Glide cache by running the following command:\n\n```\nphp please glide:clear\n```\n\nThis will **delete all the files** within your Glide cache filesystem location, as well as clearing the [path cache](#path-cache-store).\n\n\n[glide-tag]: /tags/glide\n"
  },
  {
    "path": "content/collections/pages/installing-a-starter-kit.md",
    "content": "---\nid: c51a5de8-4b02-4240-8195-3ff7987c43cf\ntitle: 'Installing a Starter Kit'\nintro: Installing a Starter Kit is a pretty simple thing, but like with many things in life, there are a few different ways you can do it. Let's cover them all.\ntemplate: page\nblueprint: page\nnav_title: Installing\n---\n## Read this first\n\nMost (but not all) Starter Kits are intended to be used in a brand new, empty site. Be sure to read each Kit's documentation before installing into an existing site so you know what to expect and how to get the most out of it.\n\n## Installing from the command line\n\nYou can spin up a **new** install of Statamic along with a Starter Kit at the all in one command by using the [Statamic CLI Tool](https://github.com/statamic/cli):\n\n``` shell\nstatamic new my-site vendor/starter-kit\n```\n\nYou can alternatively install a Starter Kit into an _existing site_ by running the following command while inside that install's root directory:\n\n``` shell\nphp please starter-kit:install vendor-name/starter-kit-name\n```\n\n### Installing a paid starter kit {#paid}\n\nIf you are installing a paid starter kit, you will be prompted to purchase and/or validate a single-use license. Once successfully installed, this license will be marked as used and cannot be used on future Statamic sites.\n\n### When to clear your site first\n\nIf you are installing into a fresh Statamic installation (a pre-built site, for example), you may wish to clear your site of any sample or placeholder content first.\n\nIf you are installing a more _modular_ or functionality-driven type Starter Kit into an existing Statamic site (like an icon set or e-commerce checkout), you will want to skip this step!\n\nThe installer will ask you if you wish to clear your site first, but you can also force clear by running with the `--clear-site` install option.\n\n### Installing without dependencies\n\nIf you wish to install without bundled dependencies, you can run with the `--without-dependencies` install option.\n"
  },
  {
    "path": "content/collections/pages/installing.md",
    "content": "---\ntitle: How to install Statamic\nbreadcrumb_title: Install\nintro: Because Statamic is a **self-hosted platform**, there are many different ways to get started. We recommend using whichever approach you're most comfortable with.\ntemplate: installing\nid: ab08f409-8bbe-4ede-b421-d05777d292f7\nblueprint: page\ncontent_width: max-w-4xl\nhide_toc: true\n---\n"
  },
  {
    "path": "content/collections/pages/javascript-frameworks.md",
    "content": "---\nid: 131259a5-2072-49d8-9ea4-2099e0338e2f\nblueprint: page\ntitle: 'JavaScript Frameworks'\nintro: 'There are many different approaches you could take to pass data to JavaScriptLand. Here are some suggestions on how to fetch, format, and hydrate (inject data) typical JavaScript components.'\ntemplate: page\nnav_title: 'Front-End Frameworks'\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1632512027\n---\nThe examples below use [Vue.js](https://vuejs.org/) as the framework of choice, but these techniques will apply to most JavaScript frameworks.\n\n## Pass all page data directly to a component\nThis is probably the simplest possible method. You can encode all the page data into JSON and inject it directly into your component. The downside is that you'll be exposing all that data to the client-side, if that's a concern for your particular site.\n\n::tabs\n\n::tab antlers\n```antlers\n<home-page\n  :page-data=\"{{ page | to_json | entities }}\">\n</home-page>\n```\n::tab blade\n```blade\n<home-page\n  :page-data=\"{{ Statamic::modify($page)->toJson() }}\">\n</home-page>\n```\n::\n\n\n## Assemble selective JSON inside Antlers/Blade and pass to components via props\nThis method is simple, best used for one-off situations. It provides you control over exactly what data you want to pass to your components, but is too messy to be used at a larger scale.\n\n::tabs\n\n::tab antlers\n```antlers\n<home-page\n  :navigation=\"[\n   {{ nav:main_navigation }}\n      {\n        title: '{{ title }}',\n        slug: '{{ url }}',\n        id: '{{ id }}'\n      },\n   {{ /nav:main_navigation }}\n ]\"\n></home-page>\n```\n::tab blade\n```blade\n<home-page\n  :navigation=\"[\n   @foreach (Statamic::tag('nav:main_navigation') as $navPage)\n      {\n        title: '{{ $navPage['title'] }}',\n        slug: '{{ $navPage['url'] }}',\n        id: '{{ $navPage['id'] }}'\n      },\n   @endforeach\n ]\"\n></home-page>\n```\n::\n\n## Fetching data from a collection\nThis method is used to fetch _any_ entry-based data, not just that available on the current page.\n\n```vue\n<home-page\n  :footer-data=\"{{ footer as='entry' }}{{ entry | to_json | entities }}{{ /footer }}\">\n</home-page>\n```\n\n:::tip\n[Live Preview](/live-preview) only has the current page's data available to it. Trying to query collection data **will not work**.\n:::\n\n## The Content API\n[The Content REST API](/rest-api) can be used on its own, or in conjunction with the above methods.\n\nHere is a simple example component that fetches data using the asynchronous `created()` function. This data can then be used in the component or passed down to child components. The example uses the standard `Fetch` method but you can use any AJAX library (Axios, etc).\n\n```vue\n<script setup>\nimport { ref, onMounted } from 'vue';\n\nconst pageData = ref(null);\n\nconst fetchPageData = async () => {\n    try {\n        const res = await fetch('/api/collections/pages/entries/home'); // Get the data from the API\n        const { data } = await res.json(); // Convert it to JSON\n        pageData.value = data; // Assign the data to the reactive reference\n    } catch (e) {\n        // Handle your errors\n    }\n}\n\nonMounted(() => fetchPageData());\n</script>\n\n<template>\n\t<section v-if=\"pageData\">\n\t\t<div>\n\t\t\t{{ pageData.title }}\n\t\t\t{{ pageData.content }}\n\t\t</div>\n\t</section>\n</template>\n```\n\n## Custom view models\nIt is also possible to create [a view model](/view-models) which will return only the data you require. However, this requires PHP knowledge.\n"
  },
  {
    "path": "content/collections/pages/js-events.md",
    "content": "---\ntitle: Event Bus\nid: b7519137-73b6-46c7-8432-da7725b1d9b4\n---\nFor situations where emitting an event to the parent component doesn't make sense, Statamic has a global event bus. You can emit and listen to events directly on this which will be available to all Vue components.\n\n``` js\nimport { events } from '@statamic/cms/api';\n\n// Emit from some component...\nevents.$emit('event.name');\n\n// Listen for it in another component...\nevents.$on('event.name');\n```\n\n:::tip\nThe event bus is intended to be used for Vue component communication. If you want to listen for Statamic driven \"events\", check out [Hooks](/extending/hooks).\n:::\n"
  },
  {
    "path": "content/collections/pages/js-hooks.md",
    "content": "---\ntitle: JavaScript Hooks\nnav_title: Hooks\nintro: |\n  Statamic allows you to hook into specific points in JavaScript and perform asyncronous operations using [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\nid: fc136da3-ba46-46e1-8443-e345d5b548ac\n---\n:::warning\nThis page is about JavaScript-based hooks. We also have [PHP-based hooks](/extending/hooks), which work differently.\n:::\n\n## About\n\nHooks are programmable interfaces which you can use to modify or extend already existing JavaScript code inside the Control Panel.\n\nNormally they work like this:\n\n1. Statamic code is running\n2. Statamic code checks for existing Hooks\n3. Your custom hook was found\n4. The code inside your custom hook will be run\n5. Statamic code continues to run (with modfied data etc.)\n\n## Prerequisites\n\nBefore you start using hooks, you need to register the corresponding javascript file.\nPlease follow the guide on [Adding CSS and JS assets](/extending/control-panel#adding-css-and-js-assets).\n\n## How to use hooks\n\nFor example, the `entry.saving` hook allows you to pause saving to perform an action:\n\n```js\nStatamic.$hooks.on('entry.saving', (resolve, reject) => {\n    if (confirm('Are you sure you want to save this entry?')) {\n        // Continue with the save action.\n        resolve();\n    } else {\n        // Cancel the save action. You can provide the error message.\n        reject('You chose not to publish.');\n    }\n});\n```\n\nIf you intend to work with another promise, make to resolve or reject once it's done:\n\n```js\nStatamic.$hooks.on('entry.saving', (resolve, reject) => {\n    return axios.get('/something')\n        .then(resolve)\n        .catch(() => reject('It broke'));\n});\n```\n\nSome hooks may provide you with a payload containing additional data. For example, the `entry.saving` hook provides you with the collection handle and form values.\n\n```js\nStatamic.$hooks.on('entry.saving', (resolve, reject, payload) => {\n    console.log(payload.collection); // blog\n    console.log(payload.values); // { title: \"My Post\", content: \"Post Content\" }\n});\n```\n\n:::best-practice\nWhen you `reject` a hook, any other code using that hook will not be executed.\nUnless your intention is to stop the execution chain, you should always `resolve`, even when your code does nothing.\n\n``` js\nStatamic.$hooks.on('example', (resolve, reject) => {\n    if (somethingShouldHappen) {\n        doSomething();\n    }\n    resolve();\n});\n```\n:::\n\n## Available hooks\n\n### `entry.saving`\n\nTriggered when you click save on the publish form.\n\nYou can use `reject()` to prevent the request. Payload contains the collection handle, entry reference and form values.\n\n### `entry.saved`\n\nTriggered when you click save, but after the request has finished.\n\nPayload contains the collection handle, entry reference and the Axios response.\n\n### `entry.publishing`\n\nTriggered when revisions are enabled, and you click publish in the publish action stack.\n\nYou can use `reject()` to stop the request. Payload contains the collection handle and revision message.\n\n### `entry.published`\n\nTriggered when revisions are enabled, but after the request has finished.\n\nPayload contains collection name, revision message, and the Axios response.\n\n### `global-set.saving`\n\nTriggered when you click save on the publish form.\n\nYou can use `reject()` to prevent the request. Payload contains the global set handle and form values.\n\n### `global-set.saved`\n\nTriggered when you click save, but after the request has finished.\n\nPayload contains the global set handle and the Axios response.\n\n### `term.saving`\n\nTriggered when you click save on the publish form.\n\nYou can use `reject()` to prevent the request. Payload contains the taxonomy handle, term reference and form values.\n\n### `term.saved`\n\nTriggered when you click save, but after the request has finished.\n\nPayload contains the taxonomy handle, term reference, and the Axios response.\n\n### `user.saving`\n\nTriggered when you click save on the publish form.\n\nYou can use `reject()` to prevent the request. Payload contains the user reference and form values.\n\n### `user.saved`\n\nTriggered when you click save, but after the request has finished.\n\nPayload contains the user reference and the Axios response.\n\n"
  },
  {
    "path": "content/collections/pages/keyboard-shortcuts.md",
    "content": "---\ntitle: 'Keyboard Shortcuts'\nintro: 'Improve usability by adding keyboard shortcuts.'\nid: efcca509-5690-4201-88d7-74c542bb9900\n---\nYou may add keyboard shortcuts with a simple syntax, based on the [Mousetrap](https://craig.is/killing/mice) library.\n\n## Basic usage\n\nBind a keyboard shortcut in a similar way to how you would with Mousetrap. You will get a reference to a Binding object. To unbind, destroy it.\n\n``` js\nimport { ref, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue';\n\nconst binding = ref(null);\n\nconst save = () => {\n  //\n}\n\nonMounted(() => {\n  binding.value = Statamic.$keys.bind('mod+s', save);\n});\n\nonBeforeUnmount(() => {\n  binding.value?.destroy();\n});\n```\n\n## Unbinding\n\nIf you are binding a keyboard shortcut in a component that may disappear - perhaps it's in a stack or modal - you should destroy it once you're done.\n\nWhen destroying the binding, it will revert back to the previous binding if one existed.\n\nFor example: If you're on a form which already uses mod+s to save it, and you open your component which re-binds mod+s, when you destroy your binding, the previous form's binding will kick back into gear.\n\n## Available methods\n\n``` js\nthis.$keys.bind(keys, fn);\n```\n\nCreates a keyboard shortcut binding.\nFirst argument is a [key sequence](#key-sequences), or array of key sequences. Second argument is a function to be executed.\nThe `Binding` object is returned.\n\n``` js\nthis.$keys.bindGlobal(keys, fn);\n```\n\nCreates a global keyboard shortcut binding.\nWorks the same as `bind`, except the shortcut will work inside text fields.\n\n## Key sequences\n\nA sequence can be:\n\n- a single key. eg. `/`\n- multiple keys together: eg. `shift+/`\n- an actual sequence of keys: eg. `up up down down`\n\n## Available keys\n\nFor modifier keys you can use `shift`, `ctrl`, `alt`, or `meta`.\n\nYou can substitute `option` for `alt` and `command` for `meta`.\n\nOther special keys are `backspace`, `tab`, `enter`, `return`, `capslock`, `esc`, `escape`, `space`, `pageup`, `pagedown`, `end`, `home`, `left`, `up`, `right`, `down`, `ins`, `del`, and `plus`.\n\nAny other key you should be able to reference by name like `a`, `/,` `$,` `*,` or `=.`\n\n:::tip\nYou can use `mod` to mean both `ctrl` on Windows and `cmd` on Mac. This saves you from having to define two separate sequences.\n:::\n"
  },
  {
    "path": "content/collections/pages/knowledge-base.md",
    "content": "---\nid: 2d6381b8-dc0a-4a5d-b750-1a9dd7c0bb14\ntitle: 'Knowledge Base'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/laravel-7-to-8.md",
    "content": "---\nid: ec130472-4f44-4e7e-8dce-71d0c93e8fef\nblueprint: page\ntitle: 'Upgrade from Laravel 7 to 8'\nintro: 'A quick guide for upgrading from Laravel 7 to 8.'\ntemplate: page\n---\nOn a fundamental level, a Statamic website is a Laravel application with Statamic installed into it. When you upgrade Statamic, you're _just_ upgrading Statamic — Laravel is not automatically upgraded at the same time.\n\nMajor Statamic releases may require newer versions of Laravel, which result in the need to upgrade the Laravel side of the application. There are many benefits to staying up to date with the latest versions of Laravel — security, performance, and compatibility with a wider range of Composer packages to name a few.\n\n:::warning Disclaimer\n**This guide is intended for \"stock\" Statamic sites.**\n\nThat is, a site where you installed Statamic and haven't added or customized functionality on the Laravel side of the application (typically the `/app/` directory).\n\nIf you've added custom functionality, this guide will still be helpful, just be mindful of anything you may have added or modified and be sure to account for it.\n:::\n\n:::warning Another Disclaimer\nYou should be performing your updates **locally**. Never update directly on production.\n:::\n\n1. First, your site should be version controlled with Git. These steps assume you'll be able to look at a git diff to make sure you don't remove important changes, dependencies, or routes.\n\n1. Make sure all Composer dependencies are up to date by running `composer update`.\n   ```bash\n   composer update\n   ```\n\n1. Run a `git commit` to start a clean slate.\n   ```bash\n   git commit -am \"Updated Composer dependencies\"\n   ```\n\n1. Download a zip of a fresh `statamic/statamic` site.\n   Head to the [3.2.7 GitHub release](https://github.com/statamic/statamic/releases/tag/v3.2.7), download `Source code (zip)`, and unzip it somewhere on your computer.\n\n1. Delete (or move to another folder, or the trash) `app`, `bootstrap`, `config`, `routes`, `composer.json`, and `artisan`.\n\n1. Copy `app`, `bootstrap`, `config`, `routes`, `composer.json`, and `artisan` from the zip into your project.\n\n1. Look through the Git changes to see if anything was removed that you wanted to keep. For example:\n\n   - Anything custom, like a Modifier or Controller, you may have added to the `app` directory\n   - Configuration values you may have changed in the `config` directory\n   - Additional dependencies in `composer.json`\n   - Custom routes you may have configured in `routes/*`\n\n1. Update your dependencies again now that you have an updated `composer.json`.\n   ```bash\n   composer update\n   ```\n\n1. Give your site a thorough test. For most sites, that'll do it!\n\nIf your site is more complex or is part of a larger Laravel Application, you can follow the [Laravel Upgrade guide](https://laravel.com/docs/8.x/upgrade) or use [Laravel Shift](https://laravelshift.com) to automate the upgrade for you."
  },
  {
    "path": "content/collections/pages/laravel-cloud.md",
    "content": "---\nid: 68d936b0-b1b0-431d-bbe0-a8356decf251\nblueprint: page\ntitle: 'Deploying Statamic with Laravel Cloud'\nintro: |-\n  Laravel Cloud is a fully managed infrastructure platform. It's relentlessly optimized for Laravel and PHP. It's our favorite way to deploy Statamic sites that need to scale.\nparent: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n\n:::warning\nCurrently, Statamic's [Git automation](/git-automation) doesn't work on Laravel Cloud. \n\nThis may change in the future as Laravel Cloud continues to evolve. For now, we recommend moving [content](/tips/storing-content-in-a-database) and [users](/tips/storing-users-in-a-database) into the database, and moving assets [onto Laravel Cloud's object storage](#creating-an-object-storage-bucket) service. \n\nAlternatively, if you prefer to keep everything in flat files, you can disable the Control Panel and manually push any content changes from your local environment.\n:::\n\n\n## Creating your application\n\nOnce you've created your [Laravel Cloud](https://app.laravel.cloud) account, click \"New application\" to get started.\n\nIf it's your first application, you'll be asked to connect your Git provider of choice (GitHub, GitLab or Bitbucket).\n\nFrom there, select the repository you want to deploy, give it a name and pick the region where you want your application deployed.\n\n<figure>\n    <img src=\"/img/deployment-cloud-new-application.png\" alt=\"New application modal\">\n</figure>\n\nUpon creation, you can setup any \"resources\" needed for your application, like a database or object storage bucket.\n\n<figure>\n    <img src=\"/img/deployment-cloud-project-overview.png\" alt=\"Application overview\">\n</figure>\n\nMake sure to click \"Save\" after making changes to your application's resources.\n\n\n### Creating a database\n\nIf you're storing content and users in the database (which), you'll need to create a database cluster in Laravel Cloud. You can do this from the environment overview page:\n\n<figure>\n    <img src=\"/img/deployment-cloud-new-database-cluster.png\" alt=\"New database cluster modal\">\n</figure>\n\nOnce you've created your database cluster, you'll probably need to import data from an existing database you might have, whether that be locally or from a staging server.\n\nYou can use a database GUI, like [TablePlus](https://tableplus.com/) to do this.\n\n1. Open your existing database, select all of the tables, and right click \"Export\". Make sure to save your export as a `.sql` file.\n2. Connect to your new Laravel Cloud database using the \"View credentials\" button.\n\n\t<figure>\n    \t<img src=\"/img/deployment-cloud-db-credentials.png\" alt=\"Database credentials modal\">\n\t</figure>\n\n\tIf you're using TablePlus (or another GUI that supports it), you can open the database directly from the \"Deeplink\" tab.    \n3. Finally, import your database by right-clicking the tables list and selecting \"Import -> From SQL File\". From here, you can choose the `.sql` file you just exported.\n\n\n### Creating an object storage bucket\n\nSince the [Git Automation](/git-automation) does not work on Laravel Cloud, we recommend moving assets to Laravel Cloud's object storage service.\n\nLaravel Cloud provides an S3-compatible filesystem, so you will need to install the S3 Flysystem driver in your project:\n\n```\ncomposer require league/flysystem-aws-s3-v3 \"^3.0\" --with-all-dependencies\n```\n\nThen, in Laravel Cloud, you can create a new bucket from the environment overview page:\n\n<figure>\n    <img src=\"/img/deployment-cloud-new-bucket.png\" alt=\"\">\n</figure>\n\nYou will then be prompted to select a filesystem disk for the bucket, it should match a disk in your `config/filesystems.php` config. \n\nOnce the bucket is created, you can upload existing assets using a tool like [Transmit](https://panic.com/transmit/), [Cyberduck](https://cyberduck.io/), or any similar app that supports S3-compatible filesystems.\n\n## Deploying your application\n\nNow that you've got everything set up, all thats left to do is trigger your first deployment. 🚀\n\nIf you're using Vite to build CSS/JavaScript, make sure to uncomment the `npm` commands in your application's deployment settings.\n\nFor more information about Laravel Cloud, please see its [documentation](https://cloud.laravel.com/docs).\n\n\n## Static Caching\n\nYou can't use full-measure static caching with Laravel Cloud, as there's no way to edit the underlying Nginx config.\n\nHowever, you can use [half-measure static caching](/static-caching#application-driver), which stores the cached HTML pages in your application's cache. In order for the static cache to persist between deployments, you should use a persistent cache driver like [`database` or `redis`](https://laravel.com/docs/master/cache#configuration).\n\n## Troubleshooting\n\n### Upstream sent too big header while reading response header from upstream\n\nYou may encounter this error when submitting a form on the frontend, or updating content in the Control Panel.\n\nYou can fix it by changing your application's session driver from `cookie` to another driver, like `database` or `redis`. \n\nYou can find more information about session drivers on the [Laravel documentation](https://laravel.com/docs/13.x/session#introduction)."
  },
  {
    "path": "content/collections/pages/laravel-forge-1-click.md",
    "content": "---\nid: 48c60d99-04e7-47f6-9576-aee1401fcb50\nblueprint: page\ntitle: 'How to Install Statamic on Laravel Forge'\nnav_title: 'Laravel Forge'\nintro: \"A full tutorial on how to install Statamic with Forge's 1-Click Installer. For this walk-through, we'll assume you have a [Forge](https://forge.laravel.com) account with a server provisioned.\"\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\n---\n\nThe Laravel team have made installing Statamic exceedingly simple. Follow these ... steps, and you'll have a Statamic site running that you can log right into.\n\n:::tip\nIf you _already have_ a Statamic site built, you should switch over to the [Deploying Statamic on Laravel Forge](/deploying/laravel-forge) guide.\n:::\n\n### 1. Create a new site\n\nMake sure to select \"Statamic\" from the \"New site\" dropdown. Then click on the \"Use a starter kit\" tab.\n\n<figure>\n    <img src=\"/img/installing-forge-new-site.png\" alt=\"Create site using a starter kit\">\n</figure>\n\nYou'll first be asked to configure a domain. If you don't have one yet, you can use a `.on-forge.com` subdomain.\n\nThen, you can pick which Starter Kit you'd like to use. Only free/open-source Starter Kits are available through this workflow, so if you'd prefer one of the paid/commercial kits, you'll need to follow the [local install](/installing/laravel-herd) and [Deploy on Laravel Forge](/deploying/laravel-forge) guides.\n\n<figure>\n    <img src=\"/img/installing-forge-starter-kits.png\" alt=\"Create site using a starter kit\">\n</figure>\n\nFinally, set up an email and password and click \"Create site\".\n\nAfter creating your site, Forge will take a few seconds to configure the necessary services, like Nginx and PHP-FPM, then you'll be able to visit your new site.\n\n### 2. Sign in to your new Statamic site\n\nAssuming you've pointed your DNS to this server, all that's left is to head to `yourdomain.com/cp` and sign in to the Statamic Control Panel. The site is yours.\n\n<figure>\n    <img src=\"/img/quick-start/login.webp\" alt=\"Statamic Login Screen\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/login-dark.webp\" alt=\"Statamic Login Screen\" class=\"u-hide-in-light-mode\">\n    <figcaption>If you see this screen at <code>/cp</code> you've just earned 200 XP!</figcaption>\n</figure>"
  },
  {
    "path": "content/collections/pages/laravel-forge.md",
    "content": "---\nid: 8fd95af9-f635-45bb-a3d1-1fa1db7be4a2\nblueprint: page\ntitle: 'Deploying Statamic with Laravel Forge'\nintro: Laravel Forge provisions and deploys PHP applications on DigitalOcean, Vultr, Akamai, AWS Hetzner and other hosting platforms. It's our favorite way to deploy Statamic.\nparent: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n\nAssuming you already have a [Laravel Forge](https://forge.laravel.com) account, the first thing to do is create a server.\n\n## Creating a New Server\n\n<figure>\n    <img src=\"/img/deploying/forge/create-server-01.png\" alt=\"Create new server\">\n</figure>\n\nYou can host your site on Laravel VPS (which is billed on top of your Forge subscription), [DigitalOcean](https://m.do.co/c/6469827e2269), AWS, Hetzner, or even your own fresh Ubuntu server.\n\n<figure>\n    <img src=\"/img/deploying/forge/create-server-02.png\" alt=\"Configure new server\">\n</figure>\n\nOn the next screen, you'll be asked to configure your server. \n\nFor most Statamic sites, you'll want to leave the type as \"App server\". You should pick the region closest to your users and select a server size suitable for your project.\n\nOnce you've created your server, Forge will give you your server's sudo and database passwords. Keep these safe as you won't be able to retrieve them later.\n\n## Creating a New Site\n\nThe next step is to create a new site. Make sure to select \"Statamic\" from the \"New site\" dropdown to take advantage of some Statamic-specific optimizations.\n\n<figure>\n    <img src=\"/img/deploying/forge/create-site.png\" alt=\"Create site page\">\n</figure>\n\nSelect your Git repository, the branch you want to deploy, and configure a domain. If you don't have a domain yet, you can use a `.on-forge.com` subdomain.\n\n:::tip Note\nZero downtime deployments are disabled by default for Statamic sites, due to the additional configuration required.\n\nBefore enabling, please review our [Zero Downtime Deployments](/tips/zero-downtime-deployments) guide.\n:::\n\n## Deploying\n\nAfter creating your site, Forge will take a few seconds to configure the necessary services, like Nginx and PHP-FPM, then you'll be able to trigger your first deploy.\n\n<figure>\n    <img src=\"/img/deploying/forge/deployment.png\" alt=\"Deployment logs\">\n</figure>\n\n## Configuring Deployments\n\nYou can customize your deployment script under `Settings -> Deployments`. Your deploy script will look something like this:\n\n```shell\ncd /home/forge/forge-demo-znqnhr0d.on-forge.com\n\ngit pull origin $FORGE_SITE_BRANCH\n$FORGE_COMPOSER install --no-dev --no-interaction --prefer-dist --optimize-autoloader\n\n# Prevent concurrent php-fpm reloads...\ntouch /tmp/fpmlock 2>/dev/null || true\n( flock -w 10 9 || exit 1\n  echo 'Reloading PHP FPM...'; sudo -S service $FORGE_PHP_FPM reload ) 9</tmp/fpmlock\n\nnpm ci && npm run build\n\nif [ -f artisan ]; then\n    $FORGE_PHP artisan optimize\n    $FORGE_PHP please stache:warm\n    $FORGE_PHP please search:update --all\n    # $FORGE_PHP please static:clear\n    # $FORGE_PHP please static:warm --queue\nfi\n```\n\nIf you're using [Static Caching](/static-caching), you may want to uncomment the `static:clear` and `static:warm` commands.\n\nIf you're using the [Git Automation](/git-automation), you may want to [add this snippet](/git-automation#customizing-commits) to the very top of your deploy script to prevent Control Panel content changes triggering full deployments.\n\nIf you're using the [Eloquent Driver](https://github.com/statamic/eloquent-driver), you may want to comment out the `$FORGE_PHP please stache:warm` command and replace it with `$FORGE_PHP please cache:clear`.\n\nIf you want code changes to be deployed automatically when pushing to the site's branch, enable **push to deploy**.\n\n## Configuring your environment\n\nUnder `Settings -> Environment`, you may configure your site's environment variables. You'll find Statamic's variables near the bottom, prefixed with `STATAMIC_`.\n\n<figure>\n    <img src=\"/img/deploying/forge/environment.png\" alt=\".env editor\">\n</figure>\n"
  },
  {
    "path": "content/collections/pages/laravel-herd.md",
    "content": "---\nid: 61c8db2d-f7bf-4829-bafd-f8a4db5a9a57\nblueprint: page\ntitle: 'Install Statamic Locally Using Laravel Herd'\nnav_title: Herd\nintro: 'Using Laravel Herd to run Statamic locally is the **easiest and fastest way** to get started with the best CMS out there. It is also very **beginner-friendly**.'\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\n---\n## Overview\n\nLaravel Herd offers a straightforward approach to setting up your machine with all the necessary dependencies for PHP development, like a web server and Composer, and of course PHP itself. It's easier compared to using Homebrew, less error-prone, and blazingly fast. It's our preferred and recommended way to get started with Statamic and Laravel development on a Mac and Windows.\n\n:::watch https://www.youtube-nocookie.com/embed/MZZYTXSysrQ?si=7sMSYltGFHLndWcf\nWatch this guide as a video 🐘\n:::\n\n## Prerequisites\n\nTo install Herd and run Statamic locally you will need the following:\n\n- A Macintosh running macOS 12.0+\n- A personal computer running Windows 10+\n- That's it.\n\n## Install Laravel Herd\n\nInstalling Herd is super easy and the same process as with any other application. Just download the latest version on [herd.laravel.com](https://herd.laravel.com).\n\n<figure>\n    <img src=\"/img/herd-dmg-finder.jpg\" alt=\"Screenshot showing the macOS finder with the Herd app icon\">\n    <figcaption>Drag'n'Drop It Like It's Hot </figcaption>\n</figure>\n\nAfter you open it for the first time, you will get prompted to type in your admin password so all the necessary files can be put in the right places.\n\n<figure>\n    <img src=\"/img/herd-password-prompt.jpg\" alt=\"Screenshot showing macOS prompting for the user password to install Laravel Herd\">\n</figure>\n\nIf you previously used Laravel Valet on Mac you can migrate to Herd and all settings, like linked projects and installed PHP versions, will be synced. Didn't use Valet before? Then you won't see this screen.\n\n<figure>\n    <img src=\"/img/herd-valet-detected.jpg\" alt=\"Screenshot showing that Laravel Herd detected Valet being used before\">\n</figure>\n\nNext, you're presented with a screen saying that you can now use Herd, Composer, and more. The default location to place your projects in is `~/Herd` which you can change in the settings, if you like. You can also choose to automatically launch Herd on startup which we would recommend.\n\n<figure>\n    <img src=\"/img/herd-installation-success.jpg\" alt=\"Screenshot showing the welcome screen of Herd after a successful installation\">\n</figure>\n\nAfter that's done, Herd has been successfully installed and you now have a local PHP development environment on your machine.\n\n<figure>\n    <img src=\"/img/herd-menu-bar-item.jpg\" alt=\"Screenshot showing the macOS menubar item of Laravel Herd with Hide the Pain Harold giving thumbs up and several elephant emojis\">\n    <figcaption>Harold congratulates you from Hungary 🇭🇺</figcaption>\n</figure>\n\nIf you want to install additional PHP versions or make your local sites available through [Expose](http://expose.dev/) via secure tunnels, open the Herd settings to get access to a range of different options.\n\n<figure>\n    <img src=\"/img/herd-settings-php-versions.jpg\" alt=\"Screenshot showing Herd's settings pane\">\n</figure>\n\n## Install Statamic CLI\n\nNext up on your journey to greatness is installing the Statamic CLI. To do this, run the following command in your terminal:\n\n``` shell\ncomposer global require statamic/cli\n```\n\nUpon installation, you can now use the `statamic new` command to spin up fresh Statamic sites with a CLI setup wizard 🧙‍♂️ to guide you through a variety of settings and options.\n\nOur CLI is essentially a super fancy wrapper around the `composer create-project` command. You can choose to not install it. Though it offers a wide range of really neat features and we recommend it.\n\n## Create a new site\n\nLet's take the last big step toward you enjoying all of Statamic's radness™.\n\nIn your terminal, run `statamic new your-project-name` and follow the prompts to create a new site. The command will create the site **in the current directory** you're in. So with Herd on Mac, you should run this command from the `~/Herd` directory.\n\nIf you encounter any issues running `statamic new`, like a `Command not found` error, have a look at [our tips on troubleshooting this](/troubleshooting/command-not-found-statamic).\n\n<figure>\n    <img src=\"/img/herd-statamic-cli.webp\" alt=\"Screenshot showing the Kitty terminal emulator running the statamic new command with ASCII art\">\n    <figcaption>Lime green and zesty spirit 🍋</figcaption>\n</figure>\n\n## Access the site\n\nSweet, if you did all the previous steps you should now be able to open your site at  `http://your-project-name.test`.\n\n<figure>\n    <img src=\"/img/herd-fresh-statamic-site.webp\" alt=\"Screenshot showing a browser window with the welcome page of a fresh Statamic site\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/herd-fresh-statamic-site-dark.webp\" alt=\"Screenshot showing a browser window with the welcome page of a fresh Statamic site\" class=\"u-hide-in-light-mode\">\n</figure>\n\nThe Control Panel, Statamic's admin area, can be accessed at `/cp` where you can log in with the user you created during the CLI's setup wizard.\n\n## What's Next\n\nWell done, you have turned your computer into a local PHP development environment, installed the Statamic CLI, and set up your first Statamic site! 🎉\n\nGet creative, explore everything, and build something rad.\n\nWant to learn more about Statamic? Watch [our series on Laracasts](https://laracasts.com/series/learn-statamic-with-jack) or just continue to browse through the docs.\n\nFor additional information on Laravel Herd, have a look at its [documentation](https://herd.laravel.com/docs).\n\n:::tip\nUse all of Statamic's Pro features while in development (like unlimited users, permissions, GraphQL, REST API, and more), by setting `'pro' => true` in `config/statamic/editions.php`.\n:::\n"
  },
  {
    "path": "content/collections/pages/laravel.md",
    "content": "---\nid: e48bde09-8957-401a-a2b4-ba7a4fd26d67\nblueprint: page\ntitle: 'How to Install into an Existing Laravel Application'\nbreadcrumb_title: Laravel\nintro: Statamic can be installed **into** an existing Laravel application and used to add new sections — like a blog or press release section — function as a headless CMS, or even manage existing data.\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\n---\n## Overview\n\nThere are many reasons why you might want to install Statamic into an existing Laravel application. You could use Statamic to:\n\n- handle all the marketing and \"logged out\" content for a SaaS app\n- add an easy-to-manage blog the whole team can update\n- manage existing data kinda like [Laravel Nova](https://nova.laravel.com/) (yes, [you can do that](/extending/repositories))\n- run as a headless CMS and provide a REST API to your data\n- be a special comfort package for those tough projects even when you don't need it\n\n:::tip\nIf you're starting a brand new project, it's much easier to use the [standard Statamic installation method](/installing/local).\n\nYou'll get a bunch of things automatically set up for you, like a pages collection, views, etc. If you install Statamic _into_ Laravel, you're going to have to do those things manually.\n:::\n\n## Supported Versions of Laravel\n\n**Statamic 6 supports Laravel 12 and 13**. If you are on an earlier version of Laravel you can still use Statamic 5 or previous versions. Keep in mind that these version might not be supported by us anymore. For more details please have a look at our [release and support schedule](/knowledge-base/release-schedule-support-policy).\n\n:::warning\nInertia 3 is not currently supported.\n\nIf you've used Laravel's React, Vue or Svelte starter kits recently, it would be using Inertia 3. If you want to use Statamic 6 you will need to downgrade to Inertia 2. We plan to add support for Inertia 3 in Statamic 7.\n:::\n\n## Install Statamic\n\nThere are 3 steps to follow to install Statamic into your Laravel app.\n\n1. Run `php artisan config:clear` to make sure your config isn't cached.\n\n2. In `composer.json`, add the following items:\n\n    ``` json\n    \"scripts\": { // [tl! **]\n        \"pre-update-cmd\": [ // [tl! ++ **]\n            \"Statamic\\\\Console\\\\Composer\\\\Scripts::preUpdateCmd\" // [tl! ++ **]\n        ],  // [tl! ++ **]\n        \"post-autoload-dump\": [ // [tl! **]\n            \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n            \"@php artisan package:discover --ansi\",\n            \"@php artisan statamic:install --ansi\" // [tl! ++ **]\n        ], // [tl! **]\n    } // [tl! **]\n    ```\n   \n3. Install `statamic/cms` with Composer.\n\n    ``` shell\n    composer require statamic/cms --with-dependencies\n    ```\n\n4. Depending on how you set up users in your app, you might need to run a command to publish Statamic's auth migrations.\n\n   ``` shell\n   php please auth:migration\n   ```\n\n## Adding Content\n\nWhen you install Statamic into Laravel this way, **no content or views are included**.\n\nYou'll probably want to create a collection and some entries, as well as views and a layout in order to see things appear on the front-end of your site.\n\n### Pages\nA common \"catch-all\" content scenario is to create a Pages collection which allows you to create a home page as well as any other pages in a tree structure. You get this when you install Statamic from scratch, but it's easy to set up yourself.\n\nThe easiest way is to copy the `pages.yaml` file and the `pages` directory [from the `statamic/statamic` repository](https://github.com/statamic/statamic/tree/6.x/content/collections).\n\nOr, if you wanted to do it through the Control Panel:\n\n1. Create a collection named `pages`.\n    - Set the route to `{parent_uri}/{slug}`\n    - Enable the \"Orderable\" toggle\n    - Enabled the \"Expect a root page\" toggle\n2. Create an entry. The first one will be your home page.\n\n### Views\n\nStatamic routed URLs will expect views named `default` and `layout`. You will need to add those manually too.\n\n[Read more about how Statamic views and layouts work](/views)\n\n## Database\n\n### Content\n\nWhen you install Statamic into an existing Laravel application, content will be stored as flat files. \n\nIf you'd prefer to store content in a database instead, please follow the [\"Storing Content in a Database\"](/tips/storing-content-in-a-database) guide.\n\n### Users\n\nIf you want to continue to keep users in a database, head over to [Storing Users in a Database in an Existing Laravel App](/tips/storing-users-in-a-database#in-an-existing-laravel-app) and follow those steps.\n\nOtherwise, the [Storing User Records](/users#storage) page should have instructions for the most common scenarios.\n\n## New Statamic Directories\n\nAfter Statamic is installed, you'll have 3 new directories in your project:\n- `content/`,\n- `resources/users/`\n- `config/statamic/`\n\n:::tip\n**Statamic relies on a \"catch-all\" wildcard route to handle its URLs.** Any explicitly defined routes in your application will take precedence over those handled by Statamic. Be sure that you don't have _your own_ catch-all route or else nothing will ever be passed off to Statamic.\n:::\n"
  },
  {
    "path": "content/collections/pages/licensing.md",
    "content": "---\ntitle: Licensing\nintro: 'Statamic is available in two distinct flavors, but one splendid codebase. Statamic Core is **free and open source** and can be used for any type of project, while **Statamic Pro** is powerful commercial software designed for team and more robust use.'\nblueprint: page\nid: 56fadb93-b846-4867-ad73-4f721cc940c2\n---\n## Core vs. Pro\n\nStatamic Core carries a few limitations you'll need to upgrade to Pro to remove:\n\n- One admin [user account](/users)\n- One [content form](/forms)\n\nAdditionally, **Statamic Pro** also includes the following, exclusive features:\n\n- [Roles & Permissions](/control-panel/users#permissions)\n- [Revisions, Drafts, and Content History](/revisions)\n- Headless mode via [REST API](/rest-api) and [GraphQL](/graphql)\n- [Multi-site, multilingual, and multi-user-editing](/multi-site)\n- [White Label customization](/white-labeling)\n- [Git integration and automation](/git-automation)\n- Developer Support\n\n:::tip\nYou can use Statamic Pro for as long you'd like in development. We call this \"Trial Mode\".\n:::\n\n## Enabling Pro\n\nWhen you install Statamic you will be asked if you want to enable **Pro**. If you decide skip it to start with Core, you can still opt into Pro at any time by running `php please pro:enable` via command line or updating your editions config file.\n\n``` php\n// config/statamic/editions.php\n'pro' => true,\n```\n\n## Using Pro in Production\n\nOnce it’s time to launch your Pro site on a public domain, there are a few things you need to do:\n\n- [Create a Site](#sites) on statamic.com and enter the appropriate domain(s).\n- Purchase any required licenses (e.g. Statamic Pro and/or any paid addons) and attach them to your Site.\n- Add your Site's license key to your environment file (preferred solution) or Statamic system config.\n\n::tabs\n::tab env\n```env\nSTATAMIC_LICENSE_KEY=your-site-license-key\n```\n::tab config\n```php\n// config/statamic/system.php\n'license_key' => 'your-site-license-key',\n```\n::\n\n:::tip\nIf you're using the free version of Statamic and you don't have any commercial addons installed, you don't need to create and link a site. But you can if you want! Being organized is a nice thing.\n:::\n\n## Sites\n\nIn your [statamic.com account](https://statamic.com/account/sites), you can create \"Sites\" that are used to organize your licenses (and in the future may provide some other nice features).\n\nEach Site has one unique license key that any and all commercial products are attached to and validated through. No more juggling a fist full of keys like a bunch of quarters at the arcade.\n\nEach license entitles you to run one production installation. You will need to specify the domains you plan to use from the \"Sites\" area of your Statamic Account. Domains are treated as wildcards so you can use subdomains for locales, testing, and other purposes.\n\nIf you attempt to use the site from a domain not listed in your Site settings, you will get a notification inside the Control Panel informing you thusly to make the necessary changes. You may change the domain associated with a license at any time on [statamic.com](https://statamic.com/account/sites).\n\n### Sites API\n\nYou can programmatically create sites using our [Sites API](/sites-api). This is most useful while using our [Platform subscription plan](https://statamic.com/pricing/platform).\n\n## License validation\n\nIf you want to know about the legal terms you can [read those here](https://statamic.com/license). The rest of this article covers the more _technical_ aspects of the call-home features, domain restrictions, and so forth.\n\n### Phoning Home\n\nStatamic pings The Outpost (our validation web service) on a regular basis. The Outpost collects the license key, public domain info (domain name, IP address, etc), and PHP version so we can validate them against your account.\n\nThis happens once per hour, and only when logged into the control panel. Changing your license key setting will trigger an immediate ping to The Outpost. Tampering with outgoing API call will cause Statamic to consider your license invalid. If that happens, you'll need to open a [support request][support] to reinstate your license.\n\nIf you need to run Statamic in an environment without an internet connection, please [contact support](https://statamic.com/support).\n\n### Public Domains\n\nWhen Statamic calls home we use a series of rules to determine if the domain it’s running on is considered “public”.\n\nIf any of the following rules match, the domain is considered _not public_ (letting you stay in Trial Mode)\n\n- Is it a single segment? eg. `localhost`\n- Is it an IP address?\n- Does it use a port other than `80` or `443`?\n- Does it have a dev-related subdomain? `test.`, `testing.`, `sandbox.`,  `local.`, `dev.`, `stage.`, `staging.`, or `statamic.`\n- Does it use a dev-related TLD? `.local`, `.localhost`, `.test`, `.invalid`, `.example`, or `.wip`\n\n## Special Circumstances\n\n[Contact us][support] if you have one and we'll see what we can do.\n\n[support]: https://statamic.com/support\n"
  },
  {
    "path": "content/collections/pages/lifecycle.md",
    "content": "---\ntitle: Lifecycle\nid: fd34ca35-57d1-4d28-97f9-ba6801656b39\n---\n\n## Laravel boots\n\nRequest comes in to the server, and is handled by Laravel.\n\nLaravel will spin through all service provider classes and run each one's `register` method. You should put any container bindings in here.\n\n``` php\npublic function register()\n{\n    $this->app->bind(SomeInterface::class, function () {\n        return new SomeClass;\n    })\n}\n```\n\nIt'll then loop through them again, this time calling the `boot` method. Here's where you can run any bootstrapping logic.\n\n``` php\npublic function boot()\n{\n    //\n}\n```\n\n## Auth service provider boots\n\nAs part of the boot process, Statamic will set up its permissions. If you'd like to do anything permission or auth\nrelated, (like adding custom [permissions](/extending/permissions)) you should wrap your provider code in a booted\ncallback to ensure it happens _after_ Statamic has done its thing.\n\n``` php\npublic function boot()\n{\n    $this->app->booted(function () {\n        Permission::register(...);\n    });\n}\n```\n\n## View loads\n\nIf you're using any JavaScript in the Control Panel, you can pass configuration variables to it.\nYou can do this in a service provider or a view composer.\n\n``` php\nView::composer('statamic::layout', function ($view) {\n    Statamic::provideToScript(['foo' => 'bar']);\n});\n```\n\n``` js\nStatamic.$config.get('foo'); // 'bar'\n```\n\n## Vue boots\n\nIf you've ever built a Vue application, you'll know that any global components need to be registered _before_ the root Vue instance is created. Statamic provides a `booting` callback for that.\n\n``` js\nStatamic.booting(() => {\n    Statamic.$components.register(...);\n})\n```\n\nOnce the Vue app has been created but _before_ it's mounted, you'll have a chance to configure it inside a `configuring` callback. This is where you'd register Vue plugins or add global properties — anything that needs to happen on the app instance prior to mounting.\n\n``` js\nStatamic.configuring(() => {\n    Statamic.$app.use(SomePlugin);\n\n    Object.assign(Statamic.$app.config.globalProperties, {\n        $something: something,\n    });\n});\n```\n\nThen, the Vue app will mount and you'll have a chance to do other JavaScript work within a `booted` callback. This is almost equivalent to putting things in a `created` hook of a Vue component.\n\nThis is where you'd do things like adding [Bard extensions](/fieldtypes/bard#extending-bard) and wiring up [Hooks](/backend-apis/hooks) or [events](/vue-components/js-events).\n\n``` js\nStatamic.booted(() => {\n    Statamic.$bard.extend(...);\n    Statamic.$hooks.on(...);\n});\n```\n\n:::tip\nThe Vue part of the lifecycle only applies to Control Panel requests. Since you have 100% control over the front-end of your site, you can do whatever you want there.\n:::\n"
  },
  {
    "path": "content/collections/pages/linode.md",
    "content": "---\nid: f8bac6fc-401c-4f0e-b338-386e332c91b8\nblueprint: page\ntitle: 'How to Install Statamic on Linode'\nbreadcrumb_title: Linode\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\nintro: A full walk-through for installing, configuring, and running Statamic on a Linode Ubuntu virtual private server.\n---\n## Prerequisites\n\nThere is only one prerequisite for this guide. You must have:\n\n- A [Linode](https://www.linode.com/) account (Use this referral code for )\n- A Linode account ([this signup link](https://linode.gvw92c.net/9WzZX5) will give you $100 in free credit)\n\n## Server Setup\n\nFollow the [official “Getting Started with Linode”](https://www.linode.com/docs/guides/getting-started/) guide to setup your server. Be sure to choose the Ubuntu image when creating your server — preferably the Ubuntu version 20.04 LTS. You can still use Ubuntu 18.04 or 16.04 if you want. To use old software.\n\nWe recommend at least 1GB of memory on your VPS.\n\n## Secure Your Server\n\nFollow the [official “How to Secure Your Server” Linode guide](https://www.linode.com/docs/guides/securing-your-server/) to secure your server. This guide covers web users, SSH hardening, trimming down network-facing services, configuring a firewall, and a lot of other stuff.\n\n## Install Statamic on Ubuntu\n\nNow that you have a secure server with Ubuntu, follow our [Ubuntu instructions](/installing/ubuntu) to install Statamic.\n"
  },
  {
    "path": "content/collections/pages/live-preview.md",
    "content": "---\ntitle: 'Live Preview'\nintro: 'Live Preview gives you the ability to see what your entry will look like in real time as you write and edit. You can configure and switch the preview screen size or pop it out into a new window.'\ntemplate: page\nblueprint: page\nid: cdffd2c9-cf42-495d-a8f1-f416ddfddc29\n---\n## Overview\n\nThe ability to preview what your content looks like in real-time is practically a super power. You can be confident you won't have any layout surprises.\n\nLive Preview will render your work-in-progress content with whichever template you have currently loaded. You can even switch between templates while previewing.\n\n:::tip\nKeep in mind: Live Preview does not work using the `array` cache driver.\n:::\n\n<figure>\n    <img src=\"/img/live-preview.webp\" alt=\"Statamic Live Preview\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/live-preview-dark.webp\" alt=\"Statamic Live Preview\" class=\"u-hide-in-light-mode\">\n    <figcaption>And he's still touring, ladies and gentlemen.</figcaption>\n</figure>\n\n## Device sizes\n\nYou can customize the list of device sizes in `config/statamic/live_preview.php`.\n\n``` php\n'devices' => [\n\t'Laptop' => ['width' => 1440, 'height' => 900],\n    'Tablet' => ['width' => 1024, 'height' => 786],\n    'Mobile' => ['width' => 375, 'height' => 812],\n],\n```\n\n<figure>\n    <img src=\"/img/device-sizes.webp\" alt=\"Device Size Switcher\" class=\"u-hide-in-dark-mode\" width=\"500\">\n    <img src=\"/img/device-sizes-dark.webp\" alt=\"Device Size Switcher\" class=\"u-hide-in-light-mode\" width=\"500\">\n    <figcaption>This dropdown will obey you better than any puppy will, guaranteed.</figcaption>\n</figure>\n\n## Customizing the toolbar\n\nYou may add extra input fields to Live Preview's header toolbar using custom Vue components. The values of these fields will be available in the data injected into the template.\n\nDefine these inputs using an array of key/value pairs of field handles and vue component names in `config/statamic/live_preview.php`:\n\n``` php\n'inputs' => [\n    'show_ads' => 'live-preview-ads',\n]\n```\n\nSimilar to fieldtypes, this component will be given a value prop and expect any changes to emit an input event.\n\n``` javascript\nStatamic.$components.register('live-preview-ads', require('./LivePreviewAds.vue'));\n```\n\n``` vue\n<script setup>\ndefineProps(['value']);\n</script>\n\n<template>\n    <div>\n        <label>\n            <input \n                type=\"checkbox\"\n                :value=\"value\"\n                @input=\"$emit('input', $event.target.checked)\" \n            />\n            Show Advertisements\n        </label>\n    </div>\n</template>\n```\n\nThese values are available in your views, scoped into the `live_preview` array:\n\n```\n{{ if live_preview:show_ads }}\n    <div class=\"ad\"> ... </div>\n{{ /if }}\n```\n\n## Preview targets\n\nOn a Collection, you may define one or more preview targets which lets you choose which page should be viewed in the Live Preview window.\n\nFor example, you may want to preview how an entry looks on the entry's page itself, as well as a listing page.\n\n```yaml\n# content/collections/blog.yaml\npreview_targets:\n  -\n    label: Entry\n    url: /blog/{slug}\n  -\n    label: Index\n    url: /blog\n```\n\nYou may use the entry's variables in the URL, just like defining a route.\n\nIf you don't define any targets, it will use the entry's URL.\n\n### Auto-refreshing\n\n:::tip\nIf you're using Statamic in a headless environment, please refer to the [Auto-refreshing](#auto-refreshing-1) section below.\n:::\n\nWhen the `refresh` option is enabled, a full refresh will occur whenever a change is made.\n\nWhen its disabled, Statamic will attempt to update the iframe's HTML automatically. If you're using Alpine or Livewire, its morphing function will be used.\n\nIf you need to override how the iframe is updated, you may define a `StatamicLivePreviewMorph` closure:\n\n```js\nwindow.StatamicLivePreviewMorph = (from, to) => {\n    // Whatever you need to do...\n};\n```\n\nYou may opt-out of this behavior if you wish:\n\n```php\n// config/statamic/live_preview.php\n\n'hot_reload_contents' => true,\n```\n\n## Headless / front-end frameworks\n\nTo use Live Preview with a front-end framework, you may use a [preview target](#preview-targets) that points to a custom URL.\n\nFor example, [Nuxt.js's Preview Mode](https://nuxtjs.org/docs/features/live-preview#preview-mode) requires that you point to a URL with a `preview=true` query parameter.\n\n```yaml\npreview_targets:\n  -\n    label: Entry\n    url: https://your-nuxt-app.com/blog/{slug}?preview=true\n```\n\nA `token` query parameter will be appended to the URL automatically, which you can then pass back to Statamic in a GraphQL query, where it will know to replace the entry with the Live Preview version.\n\n```js\n$http.post('/graphql?token='+params.token, ...);\n```\n\n### Auto-refreshing\n\nOn a preview target, you may disable the behavior that causes a full refresh of the iframe when you make changes. By disabling the refresh, [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) will be used instead to send a payload to the front-end. You can listen for this event and perform your own live updating behavior.\n\nFirst, set the preview target refresh setting to `false`:\n\n```yaml\npreview_targets:\n  -\n    label: Entry\n    url: https://your-nuxt-app.com/blog/{slug}?preview=true\n    refresh: false\n```\n\nThen, the `event.data` will be an object containing the event name, reference of what you're editing, and the Live Preview token.\n\n```js\nwindow.onmessage = function (e) {\n    if (e.data.name === 'statamic.preview.updated') {\n        updatePage(e)\n    }\n}\n\n// A silly example where you update parts of the page by\n// querying the REST API with the provided token.\nconst updatePage = function (e) {\n    const id = e.data.reference.split('::')[1];\n    const url = `https://site.com/api/collections/articles/entries/${id}?token=${e.data.token}`;\n\n    fetch(url)\n        .then(response => response.json())\n        .then(response => {\n            document.querySelector('#title').innerText = response.data.title;\n            document.querySelector('#excerpt').innerText = response.data.excerpt;\n        });\n}\n\n// A more realistic example, using a front-end framework like Nuxt.\nconst updatePage = function (e) {\n    window.$nuxt.refresh();\n}\n```\n\n## Custom rendering\n\nIf you need even more control, you may create your own route that retrieves the Live Preview entry through the token manually. Whatever you return from the route will be displayed within Live Preview.\n\n```yaml\npreview_targets:\n  -\n    label: Entry\n    url: /render-live-preview\n```\n\n```php\nuse Facades\\Statamic\\CP\\LivePreview;\nuse Illuminate\\Http\\Request;\n\nRoute::get('/render-live-preview', function (Request $request) {\n  $entry = LivePreview::item($request->statamicToken());\n  $entry->title;        // The edited title\n  $entry->foo;          // The edited foo field, etc.\n  $entry->live_preview; // All the \"extra\" data from the custom toolbar fields are in here.\n\n  return view('whatever', ['entry' => $entry]);\n});\n```\n"
  },
  {
    "path": "content/collections/pages/local.md",
    "content": "---\nid: 2093f557-8d4a-4baf-bf5c-cbbf584acd3b\nblueprint: page\ntitle: 'How to Install Statamic Locally'\nnav_title: Local\nintro: 'Fast-track local install for getting Statamic running on your computer or development machine.'\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\n---\n## Overview\n\nRunning Statamic locally is the preferred method for building and maintaining your sites. With version control (we recommend git), it's quite simple to deploy changes almost instantly from your local computer to a live site with a single command.\n\n:::watch https://youtube.com/embed/zuKZQNUYSf8\nFeel free to watch instead of read!\n:::\n\n:::tip Heads up!\nThis video assumes you're serving your local sites using [Laravel Valet][valet].\n:::\n\n## Prerequisites\n\nTo install Statamic locally you will need the following:\n\n- A computer running MacOS, Windows, or Linux\n- A supported version of [PHP](https://php.net) (we recommend PHP 8)\n- [Composer](https://getcomposer.org) to manage PHP packages\n\n## Install Statamic CLI\n\nStatamic CLI is a commandline tool to help you get Statamic installed quickly and easily. The package can be installed on your machine using Composer:\n\n``` shell\ncomposer global require statamic/cli\n```\n\nOnce installed, you can run the command `statamic list` to see a list of available commands.\n\n:::tip\nIf you run into any issues or errors, check out this [helpful article](/troubleshooting/fixing-issues-with-global-composer-packages) on what to do next.\n:::\n\n## Install Statamic\nIn your terminal, `cd` to the parent directory you want to start a new Statamic project in and run the install command.\n\n``` shell\nstatamic new project_name\n```\n\nYou'll be asked if you want to install a blank site or a [Starter Kit](/starter-kits). If this is your first time, we usually recommend starting with a blank site. Keep it simple.\n\nNext, you'll be prompted to set up your first super admin user. Do it.\n\n_After that_, everything is finished!\n\n## Accessing the site\n\nThe address where you access the site will be different depending on your development environment.\n\nFor example, if you're using [Valet][valet] then your site would be at `http://$project_name.test` and the Control Panel at `/cp`.\n\nIf you don't have Valet or some other server set up, you can run `php artisan serve` to use the built-in server, then use the URL it provides, (which is typically `http://127.0.0.1:8000`).\n\n```cli\n$ php artisan serve\nStarting Laravel development server: http://127.0.0.1:8000\n```\n\n## Troubleshooting\n\nIf your local environment is reasonably \"up to date\", everything should have gone smoothly. But let's face it, tech doesn't always work the way it's supposed to on the [first try](https://www.youtube.com/watch?v=3KDnrGdpNZY).\n\nCheck out the [troubleshooting section](/troubleshooting) to get help about common error messages.\n\n## What's Next\n\nYou're now (probably) running the latest and greatest version of Statamic! Well done! 🎉 You can now get on with the fun parts.\n\nThe [Quick Start Guide](/quick-start-guide) is a great place to head next if you're just kicking the tires (or tyres — if you're not from our neck of the woods).\n\n:::tip\nYou can use Pro features while in development (like users, permissions revisions, REST API, and so on), by setting `'pro' => true` in `config/statamic/editions.php`.\n:::\n\n:::tip Another Hot Tip\nThe default install and all first-party Starter Kits use [TailwindCSS](https://tailwindcss.com/docs/just-in-time-mode) in Just In Time mode, so anytime you change classes in your HTML you'll need to recompile your CSS.\n\nThis is super easy and happens automatically when you run `npm run dev` from the terminal in your project directory (as long as you've run `npm install` first).\n:::\n\n[valet]: https://laravel.com/docs/valet\n"
  },
  {
    "path": "content/collections/pages/markdown.md",
    "content": "---\ntitle: Customizing Markdown\nid: be292d2b-dc0e-48dc-bce4-0058df27ccc6\n---\n\n## Basic Usage\n\nYou may parse Markdown in Statamic by using the `Markdown` facade.\n\n``` php\nStatamic\\Facades\\Markdown::parse('# Hello World!');\n```\n```html\n<h1>Hello World!</h1>\n```\n\nBy default, Statamic follows the [CommonMark spec](https://spec.commonmark.org/current/) with a few extra features:\n\n- GFM Tables\n- HTML Attributes (eg. `# heading {.someclass #someid}`)\n- Strikethrough (eg. `~~strikethrough~~`)\n- Description Lists\n- Footnotes\n- Task Lists\n\nA few other extensions are available, but disabled by default:\n\n- Autolinking\n- HTML escaping\n- Automatic line breaks\n- Heading Permalinks\n- Table of Contents\n\n## Customizing Markdown behavior\n\nUnder the hood, we're using the [league/commonmark](https://commonmark.thephpleague.com/) package which supports all sorts of customization using extensions.\n\n### Configuration\n\nYou may customize the behavior of the markdown parser by providing a CommonMark config array in `config/statamic/markdown.php`. All the available options are outlined in the CommonMark docs. You can override the [base options](https://commonmark.thephpleague.com/2.4/configuration/) as well as any [extension](https://commonmark.thephpleague.com/2.4/extensions/overview/)'s options.\n\nYou only need to provide the specific options you want to override. For example:\n\n```php\n'configs' => [\n    'default' => [\n        'allow_unsafe_links' => false, // [tl! ++:start]\n        'heading_permalink' => [\n            'symbol' => '#',\n        ], // [tl! ++:end]\n    ]\n]\n```\n\n### Adding extensions\n\nYou may add an extension with the `addExtension` or `addExtensions` methods. For example, in the `boot` method of your `AppServiceProvider`, return an extension instance, or an array of them.\n\n``` php\n<?php\nnamespace App\\Providers;\n\nuse Illuminate\\Support\\ServiceProvider;\nuse Statamic\\Facades\\Markdown;\nuse Ueberdosis\\CommonMark\\HintExtension;\nuse League\\CommonMark\\Extension\\TableOfContents\\TableOfContentsExtension;\n\nclass AppServiceProvider extends ServiceProvider\n{\n    public function boot()\n    {\n        // Add one extension... [tl! focus:start]\n        Markdown::addExtension(function () {\n            return new HintExtension;\n        });\n\n        // or multiple.\n        Markdown::addExtensions(function () {\n            return [new HintExtension, new TableOfContentsExtension];\n        }); // [tl! focus:end]\n    }\n}\n```\n\n:::tip\nYou can find a long list of Markdown Extensions [on the CommonMark site](https://commonmark.thephpleague.com/2.4/extensions/overview/), or around on GitHub. We love this [Hint Extension](https://github.com/ueberdosis/commonmark-hint-extension) by Ueberdosis – you're seeing it in action, powering this \"Hot Tip\" box.\n:::\n\nStatamic 5.0+ uses CommonMark 2.4, while previous versions used either CommonMark 2.2 or CommonMark 1.6. Keep this in mind when reading docs and looking for extension packages.\n\n### Adding renderers\n\nYou may add a custom renderer for a specific node type with the `addRenderer` or `addRenderers` method. For example, in the `boot` method of your `AppServiceProvider`, return an array containing the node type and your custom renderer.\n\n```php\n<?php\nnamespace App\\Providers;\n\nuse Illuminate\\Support\\ServiceProvider;\nuse Statamic\\Facades\\Markdown;\nuse League\\CommonMark\\Extension\\CommonMark\\Node\\Block\\Heading;\nuse League\\CommonMark\\Extension\\CommonMark\\Node\\Inline\\Link;\n\nclass AppServiceProvider extends ServiceProvider\n{\n    public function boot()\n    {\n        // Add one renderer... [tl! focus:start]\n        Markdown::addRenderer(function () {\n            return [Link::class, new \\App\\Markdown\\LinkRenderer];\n        });\n\n        // or multiple.\n        Markdown::addRenderers(function () {\n            return [\n                [Link::class, new \\App\\Markdown\\LinkRenderer],\n                [Heading::class, new \\App\\Markdown\\HeadingRenderer, 100],\n            ];\n        }); // [tl! focus:end]\n    }\n}\n```\n\nYou may also [specify a priority](https://commonmark.thephpleague.com/2.7/customization/rendering/#designating-renderers) for renderers, which CommonMark uses to determine which result is rendered when multiple renderers are available for the same node type:\n\n```php\nMarkdown::addRenderers(function () {\n    return [\n        [FencedCode::class, new CodeRenderer, 10],\n        [IndentedCode::class, new CodeRenderer, 20]\n    ];\n});\n```\n\n### Helper Methods\n\nIn addition to manually defining and configuring extensions, some frequently used behaviors are wrapped up in methods for you to use.\n\n| Method | Description |\n|--------|-------------|\n| `withAutoLinks()` | Convert URLs and email addresses into links. (eg. `http://example.com` becomes `<a href=\"http://example.com\">http://example.com</a>`) |\n| `withSmartPunctuation()` | Convert plain quotes, dashes, ellipsis, etc into their unicode equivalents. Commonly referred to as \"smartypants\". (eg. `\"CommonMark is the PHP League's Markdown parser\"` becomes `“CommonMark is the PHP League’s Markdown parser”`) |\n| `withMarkupEscaping()` | Converts HTML to entities. Useful for securing input from untrusted users. (eg. `<div>` becomes `&lt;div/&gt;`) |\n| `withAutoLineBreaks()` | Converts newlines into `<br>` tags. Without this, you need to end a line with a `\\` or two spaces. |\n| `withStatamicDefaults()` | Enable the default set of extensions that Statamic uses (tables, strikethrough, etc). Without this, you will get a plain parser. |\n| `newInstance($config)` | Gives you a new parser instance using an existing one as a starting point. It will inherit all the extensions and config. Accepts an array that will be merged into the config. |\n\n\n## Custom Parsers\n\nAny methods on the `Markdown` facade are forwarded onto the `default` Parser. This includes the `addExtension` methods described above.\n\nYou are free to create additional parsers that are configured independently, with their own configuration and extensions.\n\n``` php\nMarkdown::makeParser($config) // Accepts an optional league/commonmark config array.\n    ->withAutoLinks()\n    ->addExtensions(...)\n    ->parse('# Heading');\n```\n\nIf you intend to reuse a parser, you may prefer to create it in one place (like a service provider), and then reference it elsewhere.\n\n``` php\nMarkdown::extend('special', function ($parser) {\n    return $parser\n        ->withStatamicDefaults()\n        ->addExtensions(...);\n});\n```\n``` php\nMarkdown::parser('special')->parse('# Heading');\n```\n\nThe closure provides you with a fresh `Parser` instance which you can customize as needed.\n\n:::tip\nIf you need to provide a config to your custom parser, you can either [define it in the config file](#configuration) or pass an array as the second option.\n\n```php\nMarkdown::extend('special', $config, function ($parser) {\n    //\n});\n```\n:::\n\n### Extending the Parser class\n\nIf you need more control than configuration and extensions allow – for example, manipulating the raw Markdown string before it hits the parser, or adding your own helper methods – you can extend the `Statamic\\Markdown\\Parser` class directly.\n\n```php\n<?php\n\nnamespace App\\Markdown;\n\nuse Statamic\\Markdown\\Parser;\n\nclass CustomParser extends Parser\n{\n    public function parse(string $markdown): string\n    {\n        $markdown = str_replace(':sparkles:', '✨', $markdown);\n\n        return parent::parse($markdown);\n    }\n}\n```\n\nThen register an instance of your custom parser using `Markdown::extend()`:\n\n```php\nuse Statamic\\Facades\\Markdown;\nuse App\\Markdown\\CustomParser;\n\nMarkdown::extend('custom', fn () => new CustomParser);\n```\n\nBecause the underlying `newInstance()` method returns `new static`, chained helpers like `withAutoLinks()`, `withStatamicDefaults()`, and `newInstance()` will return instances of your subclass – so you keep access to your custom methods even after chaining.\n\n### Using a custom parser in a modifier\n\nThe `markdown` modifier accepts an optional argument to choose which parser to use.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ text | markdown:special }}\n```\n::tab blade\n```blade\n{!! Statamic::modify($text)->markdown('special') !!}\n```\n::\n\n### Using a custom parser in a fieldtype\n\nThe `markdown` fieldtype allows you define the `parser`. This will be used when it augments the value, so you don't need the markdown modifier.\n\n``` yaml\n-\n  handle: content\n  field:\n    type: markdown\n    parser: special\n```\n"
  },
  {
    "path": "content/collections/pages/modifiers.1.md",
    "content": "---\nid: 998ad905-6988-457f-b26a-78bc64de6d3f\ntitle: Modifiers\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/modifiers.md",
    "content": "---\ntitle: Building Modifiers\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569264085\nintro: Modifiers give you the ability to manipulate the data of your variables on the fly. They can manipulate strings, filter arrays and lists, help you compare things, do basic math, simplify your markup, play Numberwang, and even help you debug.\nid: e052ecb8-60d9-4afa-980e-ce128c301d70\n---\n## Anatomy of a Modifier\n\nA modifier consists of a few parts. Let’s break it down.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ variable | repeat:2 }}\n```\n\n- The first part? A regular old variable: `variable`.\n- Next up, the modifier's [handle](#handle): `repeat`\n- And finally, a parameter: `2`\n\n::tab blade\n```blade\n{{ Statamic::modify($variable)->repeat(2) }}\n```\n- First, we call `Statamic::modify`, supplying the variable name we want to modify (`$variable`)\n- Next up, we call a method with the modifier's [handle](#handle) (`->repeat()`)\n- If our modifier accepts parameters, we supply them to the modifier's method call (`->repeat(2)`)\n::\n\nParameters are used to modify the behavior of a modifier. They could be anything from an integer or boolean to a variable reference. It’s up to you.\n\n## Creating a Modifier\n\nYou can generate a Modifier class with a console command:\n\n``` shell\nphp please make:modifier Repeat\n```\n\nThis'll create a class in `app/Modifiers` which will be automatically registered.\n\nTo create and register a modifier inside an addon instead, check out the [addon docs](/extending/addons#registering-components).\n\n## Modifying a value\n\nThe modifier class expects one `index` method which should return a modified `$value`.\n\n``` php\n<?php\n\nnamespace App\\Modifiers;\n\nuse Statamic\\Modifiers\\Modifier;\n\nclass Repeat extends Modifier\n{\n    public function index($value, $params, $context)\n    {\n        // Something awesome awaits.\n    }\n}\n```\n\nWe'd be able to use our new `repeat` modifier in our template like so:\n\n::tabs\n::tab antlers\n```antlers\n{{ variable | repeat }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($variable)->repeat() }}\n```\n::\n\nThe first and only required argument passed into `index` will be the `$value` that needs modifying. We can do anything to this value as long as we return it when we’re done. Once returned, the template will either render it, or pass it along the next modifier in the chain.\n\nThe other two arguments are optional:\n\n- `$params` will be an array of any parameters.\n- `$context` will be an array of contextual data available at that position in the template.\n\n## Handle\n\nThe modifier's handle is how it will be referenced in templates. By default, this will be the snake_cased version of the class name. In this example above, it would be `repeat`.\n\nYou can override this by setting a static `$handle` property.\n\n``` php\nprotected static $handle = 'repeatrepeat';\n```\n\n::tabs\n\n::tab antlers\n\nThen, using the example above, `variable | repeat` would now be `variable | repeatrepeat`.\n\n::tab blade\n\nThen, using the example above, `Statamic::modify($variable)->repeat()` would now be `Statamic::modify($variable)->repeatrepeat()`.\n\n::\n\n## Aliases\n\nYou may choose to create aliases for your modifier too. It will then be usable by its handle, or any of its aliases.\n\n``` php\nprotected static $aliases = ['duplicate'];\n```\n\n\n## Example\n\nLet’s say we need a modifier that repeats things. Maybe even delicious things.\n\n``` php\n<?php\n\nnamespace App\\Modifiers;\n\nuse Statamic\\Modifiers\\Modifier;\n\nclass Repeat extends Modifier\n{\n    public function index($value, $params, $context)\n    {\n        // Repeat twice by default\n        $repeat = 2;\n\n        // Get the parameter, if there is one\n        if ($param = Arr::get($params, 0)) {\n            // Either get the variable from the context, or if it doesn't exist,\n            // use the parameter itself - we'll assume its a number.\n            $repeat = Arr::get($context, $param, $param);\n        }\n\n        // Repeat!\n        return str_repeat($value, $repeat);\n    }\n}\n```\n\nGiven the following data:\n\n``` yaml\ntimes: 5\nthing: Pizza\n```\n\nAnd template:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ thing | repeat }}\n{{ thing | repeat:3 }}\n{{ thing | repeat:times }}\n```\n::tab blade\n```blade\n{{ Statamic::modify($thing)->repeat() }}\n{{ Statamic::modify($thing)->repeat(3) }}\n{{ Statamic::modify($thing)->repeat($times) }}\n```\n::\n\nYou would find yourself with varying amounts of pizza.\n\n```html\nPizzaPizza\nPizzaPizzaPizza\nPizzaPizzaPizzaPizzaPizza\n```\n"
  },
  {
    "path": "content/collections/pages/multi-site.md",
    "content": "---\ntitle: Multi-Site\nintro: |\n    Statamic's multi-site capabilities are designed to manage variations of a **single site**, and/or different sections of a single site running on one or more domains or subdomains. It can be used to manage translations, country-specific versions of a company site, put an area such as `support` or `resources` on a subdomain, and other similar use cases. _It is not intended to be used for multi-tenant applications_ running completely separate sites.\ntemplate: page\nid: fb20f2e0-3881-43e6-8507-3308a18c54b0\nblueprint: page\npro: true\n---\n## Overview\n\nStatamic can be configured to handle multiple \"sites\". A site is a way of managing a localized version of your content - whether another language, region, or even company/brand (think Proctor & Gamble).\n\nEach site can have different base URLs:\n\n- domains: `hello.com` and `bonjour.com`\n- subdomains: `example.com` and `fr.example.com`\n- subdirectories: `example.com` and `example.com/fr/`\n\nIf you're looking to run many independent websites from a shared codebase, multi-site is not the right tool. We are intentionally opinionated here, and you should explore our [Platform Pricing](https://statamic.com/pricing/platform) model\n\n::: tip\nEvery Statamic install needs at least one site. Building zero sites is a bad way to build a website and clients will probably challenge your invoices.\n:::\n\n### Converting existing content to multi-site\n\nThe simplest way to convert existing content to a multi-site friendly structure is to run the automated command:\n\n``` shell\nphp please multisite\n```\n\nRead more on [converting to a multi-site setup](/tips/converting-from-single-to-multi-site).\n\n## Configuration\n\n### Enabling multi-site\n\nFirst, enable `multisite` in your `config/statamic/system.php`:\n\n``` php\n'multisite' => true,\n```\n\n### Adding new sites\n\nNext, you can add new sites through the control panel by clicking the Site menu item in the sidebar:\n\n<figure>\n    <img src=\"/img/configure-sites.webp\" alt=\"Configure sites page in control panel\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/configure-sites-dark.webp\" alt=\"Configure sites page in control panel\" class=\"u-hide-in-light-mode\">\n</figure>\n\nOr directly in your `resources/sites.yaml` file:\n\n``` yaml\ndefault:\n  name: First Site\n  url: /\n  locale: en_US\nsecond:\n  name: Second Site\n  url: /second/\n  locale: en_US\n```\n\n## Available options\n\nLet's look at a full site configuration, and then we'll explore all of its options.\n\n``` yaml\n# resources/sites.yaml\n\nen:\n  name: English\n  url: /\n  locale: en_US\n  lang: en\n  attributes:\n    theme: standard\n```\n\n### Handle\n\nEach site is keyed by its `handle`, which is important for directory structure, as well as referencing sites in collection configs, etc. throughout your site. Changing this is non-trivial, and you should be careful if you already have established content in this site. Read more about [renaming sites](#renaming-sites).\n\n``` yaml\nen: # <- This is your site handle\n  name: English\n\n```\n### Name\n\nEach site has a `name`, which is a display-friendly representation of your site's name mostly seen within control panel UI. Changing this does not affect content relations.\n\n``` yaml\nen:\n  name: English\n```\n\n::: tip\nYou'll notice the default site dynamically references a [config variable](/variables/config), but feel free to change this!\n\n``` yaml\ndefault:\n  name: '{{ config:app:name }}'\n```\n:::\n\n### URL\n\nEach site requires a URL to define the root domain Statamic will serve and generate all URLs relative to. The default `url` is `/`, which is portable and works fine in most typical sites. Statamic uses a little magic to work out what a full URL is based on the domain the site is running on.\n\n:::best-practice\nIt can be a good idea to change this to a **fully qualified, absolute URL**. This ensures that server/environment configurations or external quirks don't interfere with that \"magic\".\n\n```yaml\nen:\n  # ...\n  url: '{{ config:app:url }}'\nfr:\n  # ...\n  url: '{{ config:app:url }}/fr/'\n```\n\nBy default, this is linked to your `APP_URL` environment variable, which allows you to control the exact URL by environment:\n```env\n# production\nAPP_URL=https://mysite.com\n\n# development\nAPP_URL=http://mysite.test\n```\n:::\n\n### Locale\n\nEach site has a `locale` used to format region-specific data (like date strings, number formats, etc). This should correspond to the server's locale. By default, Statamic will fallback to your app's locale.\n\n### Language\n\nStatamic's control panel has been translated into more than a dozen languages. The language translations files live in `resources/lang`.\n\nYou may specify which language translation to be used for each site with the `lang` setting. If you leave it off, it'll use the short version of the `locale`. e.g. If the locale is `en_US`, the lang will be `en`.\n\n``` yaml\nde:\n  name: Deutsch\n  locale: de_DE\n  # Lang not needed, as `de` is implied\nde_CH:\n  name: 'Deutsch (Switzerland)'\n  locale: de_CH\n  lang: de_CH # We want the `de_CH` language, not `de`\n```\n\nNote that both Statamic and Laravel don't ship with frontend language translations out of the box. You have to provide your own string files for this. There is a great package called [Laravel Lang](https://github.com/Laravel-Lang/lang) containing over 75 languages that can help you out with this.\n\n### Additional attributes\n\nYou may also include additional arbitrary `attributes` in your site's config, which can later be accessed with the [site variable](/variables/site).\n\n``` yaml\nen:\n  # ...\n  attributes:\n    theme: standard\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<body class=\"theme-{{ site:attributes:theme }}\">\n```\n::tab blade\n```blade\n<body class=\"theme-{{ $site->attributes['theme'] }}\">\n```\n::\n\nIn Blade, you can also use the `attribute()` method, which supports dot notation and a fallback value for attributes that may not be set:\n\n```blade\n<body class=\"theme-{{ $site->attribute('theme', 'standard') }}\">\n```\n\n:::tip\nNothing fancy happens here, the values are passed along \"as is\" to your templates. If you need them to be editable, or store more complex data, you could use [Globals](/globals).\n:::\n\n## Text direction\n\nText direction is automatically inferred by Statamic, based on the [language](#language) of your configured site.\n\nFor example, most sites will be `ltr`, but Statamic will automatically use `rtl` for languages like Arabic or Hebrew.\n\nIf you need to reference text direction in your front end, you can make use the [site variable](/variables/site):\n\n::tabs\n\n::tab antlers\n```antlers\n<html dir=\"{{ site:direction }}\">\n```\n::tab blade\n```blade\n<html dir=\"{{ $site->direction }}\">\n```\n::\n\n## Renaming sites\n\nIf you rename a site's [handle](#handle), you'll need to update a few folders and config settings along with it. Replace `{old_handle}` with the new handle in these locations:\n\n**Content folders**\n\n- `content/collections/{old_handle}/`\n- `content/globals/{old_handle}/`\n- `content/trees/{old_handle}/`\n\n**Collection Config YAML Files**\n``` yaml\n# content/collections/{collection}.yaml\nsites:\n- {old_handle}\n- de\n- fr\n```\n\n## Permissions\n\nWithin the Control Panel, you will not be able to access items in a particular site if you do not have permission.\n\nYou may grant permission for any of your sites by adding an `access {site_handle} site` to the appropriate role.\n\nFor example:\n\n```yaml\npermissions:\n  - edit blog entries\n  - access english site # [tl!++]\n  - access french site  # [tl!++]\n```\n\n[Read more about permissions](/users#permissions)\n\n\n## Per-Site Views {#views}\n\n[Views](/views) can be organized into site directories.\n\nIf a requested view exists in a subdirectory with the same name as your site [handle](#handle), it will load it instead. This allows you to have site-specific views without any extra configuration.\n\n``` yaml\n# resources/sites.yaml\n\nsite_one:\n  # ...\nsite_two:\n  # ...\n```\n\n``` files theme:serendipity-light\nresources/views/\n    site_one/\n        home.antlers.html\n    home.antlers.html\n    page.antlers.html\n```\n\nFor example, given `template: home`, Statamic will load `site_one/home` because that view exists in the subdirectory. If you were to have `template: page`, it would load the one in the root directory because there's no site-specific variant.\n\n## Template snippets\n\nHere are a few common features you'll likely need to template while building a multi-site.\n\n### Building a site switcher {#site-switcher}\n\nThis will loop through your sites and indicate the current site as the active one. Check out all the [available variables inside the `sites` loop](/variables/sites).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ sites }}\n  <a class=\"{{ site:handle === handle ?= 'active' }}\" href=\"{{ url }}\">\n    {{ handle }}\n  </a>\n{{ /sites }}\n```\n::tab blade\n```blade\n@foreach ($sites as $switcherSite)\n  <a @class([\n      'active' => $switcherSite->handle == $site->handle\n    ])\n    href=\"{{ $switcherSite->url }}\"\n  >{{ $switcherSite->handle }}</a>\n@endforeach\n```\n::\n\n### Declaring the page language\n\nIndicate the current language of the site by setting the `lang` attribute on your `<html>` tag (most likely in your layout view), or the container element around translated content if the page mixes and matches languages.\n\n::tabs\n\n::tab antlers\n```antlers\n<html lang=\"{{ site:short_locale }}\">\n```\n::tab blade\n```blade\n<html lang=\"{{ $site->short_locale }}\">\n```\n::\n\n## Static caching\n\nIf your multi-site should use static caching, you will also need to add additional config parameters and different server rewrite rules. Please refer to the related section of the [static caching documentation](/static-caching#multisite) for the correct settings.\n\n## Enabling fields\n\nBy default, your existing fields won't be enabled for multi-site editing. To enable a field to be editable within another site, navigate to the field and click the globe icon (Localizable).\n"
  },
  {
    "path": "content/collections/pages/multi-user-collaboration.md",
    "content": "---\ntitle: 'Multi-User Collaboration'\nintro: 'Stop worrying if someone else is editing the same article at the same time and start enjoying a collaborative authoring process. Each field automatically locks as a user begins to edit, and unlocks when they leave or go idle.'\ntemplate: page\nblueprint: page\npro: true\nid: a3adf32a-37a5-4e96-beee-f107dc1b81a9\n---\n## Overview\n\n\n**Features include:**\n\n- Presence indicators when multiple users are editing the same entry\n- Field locking when another user begins editing\n- Content changes are updated in real-time for all users\n\n<figure>\n    <img src=\"/img/collaboration.png\" alt=\"Statamic's multi-user collaboration in action\">\n    <figcaption>A glimpse of multi-user collaboration in action.</figcaption>\n</figure>\n\n## Installation\n\nFollow the docs on the [Collaboration addon package](https://statamic.com/addons/statamic/collaboration) to get started!\n"
  },
  {
    "path": "content/collections/pages/navigation.md",
    "content": "---\nid: 2af9fc45-66d0-4ca5-9761-00017076144f\nblueprint: page\ntitle: Navigation\nintro: 'A nav (or navigation for long) is a hierarchy of links and text nodes that are used to build navs and menus on the frontend of your site. Trust me, you''ve seen them before. You''re looking at one right now, just move your eyeballs up a little bit. Yeah, there it is.'\nrelated_entries:\n  - ed746608-87f9-448f-bf57-051da132fef7\n  - 485f1703-fc6f-4d0f-94f2-e84ae625e1b7\n  - 3c34ef5c-781e-4a22-a09b-25f58bdb58a8\n  - 35c9cd07-f377-4fcb-b02c-72c1925e6fdf\n---\n## Overview\n\nEach Nav is a [structure](/structures) giving you the ability to rearrange items through the delightful experience of dragging and dropping boxes.\n\n<figure>\n    <img src=\"/img/collection-structure.webp\" alt=\"A Statamic structure page tree\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/structure-dark.webp\" alt=\"A Statamic structure page tree\" class=\"u-hide-in-light-mode\">\n</figure>\n\n\n- You can **reference** entries, enter hardcoded URLs (internal or external), or enter simple text blocks (which can be used as section headers for dropdown navs, for example).\n- You can **choose** which collections' entries will be available to choose from.\n- Any referenced entries will use the URLs **defined by the collection**, regardless of the position in the Structure.\n- You can place the same entry **multiple** times. Two times, three times, four times, even six times are all possible numbers of times you can place something.\n\n\n:::watch https://www.youtube.com/embed/POgIsLeWGGQ\nWatch how to build a simple nav with a Structured Collection\n:::\n\n## Storage\n\nNavs are stored in `content/navigation`. Each gets its own YAML file whose handle matches its filename.\n\nThe actual contents of the structure - the \"tree\" - is stored separately in `content/trees/navigation`.\n\n``` files theme:serendipity-light\ncontent/\n    navigation/\n        header.yaml\n        footer.yaml\n    trees/\n        header.yaml\n        footer.yaml\n```\n\n``` yaml\n# content/navigation/footer.yaml\ntitle: Footer\nmax_depth: 3\ncollections:\n  - pages\n  - posts\n  - documents\n```\n\n## Templating\n\nYou can work with the [nav](/tags/nav) to loop through and render your HTML with access to all the entries and nodes in the navigation.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ nav:footer }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n{{ /nav:footer }}\n</ul>\n```\n::tab blade\n```blade\n<statamic:nav:footer>\n  <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n</statamic:nav:footer>\n```\n::\n\nWithin the tag pair, you will have access to any fields defined on that particular nav item - the item itself or the entry. See [blueprints and data](#blueprints-and-data) below for more information.\n\n## Collections\n\nYour navigation tree _may_ contain references to entries. The control panel's entry selector will show you entries across all collections by default. You may narrow down which collections will appear in the selector in the config area.\n\n<figure>\n    <img src=\"/img/navigation-collection-picker.webp\" alt=\"Configuring navigation collections\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/navigation-collection-picker-dark.webp\" alt=\"Configuring navigation collections\" class=\"u-hide-in-light-mode\">\n    <figcaption>If you want to put pants in your navs, you can.</figcaption>\n</figure>\n\n## Blueprints and data\n\nOut of the box, nav items are fairly light. If you create a standard nav item, you can type in the URL and title. For entry reference nav items, you can override the title.\n\nIf you'd like to add more data, you can add fields to the nav's blueprint.\n\nAny fields you add will appear in the editor pane in the control panel.\n\n<figure>\n    <img src=\"/img/navigation-page-editor.png\" alt=\"Navigation Page Editor\" width=\"448\" height=\"282\">\n    <figcaption>The excerpt and icon fields have been added</figcaption>\n</figure>\n\nThe data will be saved in a `data` key on the tree branch.\n\n``` yaml\n-\n  title: My page\n  url: /my-page\n  data:\n    excerpt: This is my page\n    icon: page.svg\n```\n\nIn the case of entry reference nav items, any fields you add to the nav blueprint will override the fields for that entry. This is useful if you intentionally want to override an entry's value. If you want to do this, make sure that you use the same fieldtype as what's in the entry's blueprint. A good way to handle that is to make a [reusable field](/blueprints#reusable-fields).\n\n\n## Localization\n\nWhen running a [multi-site](/multi-site) installation, you can have a different tree for each nav. Learn more about [localizing navs](/tips/localizing-navigation).\n"
  },
  {
    "path": "content/collections/pages/netlify.md",
    "content": "---\nid: 7f809a5e-e555-4ccc-8488-d7310ff8c89c\nblueprint: page\ntitle: 'Deploying Statamic to Netlify'\nintro: |-\n  Netlify is a Jamstack service and cloud provider that lets you deploy your Statamic site statically with blazing fast performance using its Edge CDN.\nparent: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n:::warning\nPlease note that by hosting your site statically with a service like Netlify or Vercel, **you can't access the Control Panel in production** and are **not able to use dynamic features** like Statamic's built-in forms or random sorting in your templates.\n:::\n\nDeployments are triggered by committing changes to your Git repository. Alternatively, you can also upload the locally generated files from Netlify's dashboard via drag and drop.\n\n## Prerequisites\n\n:::tip\nWhile Netlify supports PHP versions from 7.4 through 8.3, it defaults to PHP 8.0. You can [specify the PHP version](https://docs.netlify.com/configure-builds/manage-dependencies/#php) using the `PHP_VERSION` environment variable.\n:::\n\n- A [Netlify](https://netlify.com) account\n- An account with one of Netlify's supported Git providers\n- Have the `statamic/ssg` package installed and set up in your project\n- Make sure you don't need or use any dynamic features, like forms or random sorting.\n- To make things easier, add a new script to your project's `composer.json` file with the following commands:\n```json\n\"scripts\": {\n    \"build\": [\n        \"npm run build\",\n        \"@php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\",\n        \"@php artisan key:generate\",\n        \"@php please ssg:generate\"\n    ],\n    // ...\n}\n```\n\nFeel free to customize this to fit your needs. Netlify automatically installs npm and composer dependencies. So there is no need to manually run `npm ci` or `composer install`.\n\n## Creating a New Site\n\nTo get started add a new site to your account. In this walk-through we'll assume you already have an existing project that you want to import since it's the most common use-case.\n\n<figure>\n    <img src=\"/img/deployment-netlify-import-project.jpg\" alt=\"Netlify dashboard to import a project\">\n</figure>\n\nNext, you will need to authorize Github (or another supported source control provider). This is a one-time process that allows you to quickly deploy new sites from this account.\n\n<figure>\n    <img src=\"/img/deployment-netlify-connect-git.jpg\" alt=\"Choose a Git provider for your project\">\n</figure>\n\nAfter you connected to your Git provider, pick the repo of the project you want to deploy to Netlify. So choose wisely 🧙‍♂️\n\n<figure>\n    <img src=\"/img/deployment-netlify-pick-repo.jpg\" alt=\"Pick a repository from your chosen Git provider\">\n</figure>\n\nThe next step is to configure your site's settings and deployment configuration.\n\n1. Choose the branch you want to deploy from. This could be the `main` branch or something like `production`, depending on your [Git workflow](/deploying/workflow).\n2. Leave the base directory input empty, unless your Statamic project is part of a monorepo or lives in a subfolder.\n3. Use `composer build` for the build command if you set up your `composer.json` like mentioned [above](#prerequisites).\n    - Otherwise just use `php please ssg:generate`\n4. Set the publish directory to `storage/app/static`\n5. Add `APP_KEY` env variable, by running `php artisan key:generate` locally, and copying from your `.env`\n    - ie. `APP_KEY` `your-app-key-value`\n6. Add `APP_URL` environment variable after your site has a configured domain\n    - ie. `APP_URL` `https://thats-numberwang-47392.netlify.com`\n\nInstead of doing all of this manually, you can also use a `netlify.toml` file at the base of your project. Netlify automatically detects the file and sets everything up for you. Feel free to use [this one](https://gist.github.com/joshuablum/845d6af82c710a9b9d8f1a57618f213d) as a reference or starting point.\n\n[More about file-based configuration in Netlify's docs](https://docs.netlify.com/configure-builds/file-based-configuration/).\n\n<figure>\n    <img src=\"/img/deployment-netlify-site-settings.jpg\" alt=\"Site and deploy settings for the site\">\n    <figcaption>Quite some options, don't get lost.</figcaption>\n</figure>\n\nThat's it! ✅\n\nDepending on how large and complex your project is, the **deployment might take a few seconds or minutes**.\n\nAfter this is done, you can visit your site via a URL provided by Netlify. It looks similar to this [https://thats-numberwang-47392.netlify.app](https://thats-numberwang-47392.netlify.app).\n\nBe sure to have a look at Netlify's docs on [custom domains](https://docs.netlify.com/domains-https/custom-domains/), [enabling HTTPS](https://docs.netlify.com/domains-https/https-ssl/), and how [Netlify forms](https://docs.netlify.com/forms/setup/) work.\n\n## SSG configuration for Netlify\n\nAfter you have installed the `statamic/ssg` package, you can publish its configuration with the following command:\n\n```php\nphp artisan vendor:publish --provider=\"Statamic\\StaticSite\\ServiceProvider\"\n```\n\nThis allows you to customize the behaviour of the package.\n\nLet's say you have additional folders and files that you need for your site. Just add them to the copy array:\n\n```php\n'copy' => [\n    public_path('css') => 'css',\n    public_path('js') => 'js',\n    public_path('assets') => 'assets',\n    public_path('favicon.ico') => 'favicon.ico',\n],\n```\n\n## Storing Assets in S3\n\nIf you are storing your assets in an S3 bucket, the `.env`s used will need to be different to the defaults that come with Laravel, as they are reserved by Netlify. For example, you can amend them to the following:\n\n```sh\n# .env\nAWS_S3_ACCESS_KEY_ID=\nAWS_S3_SECRET_ACCESS_KEY=\nAWS_S3_DEFAULT_REGION=\nAWS_S3_BUCKET=\nAWS_URL=\n```\n\nBe sure to also update these in your `s3` disk configuration:\n\n```php\n// config/filesystems.php\n's3' => [\n    'driver' => 's3',\n    'key' => env('AWS_S3_ACCESS_KEY_ID'),\n    'secret' => env('AWS_S3_SECRET_ACCESS_KEY'),\n    'region' => env('AWS_S3_DEFAULT_REGION'),\n    'bucket' => env('AWS_S3_BUCKET'),\n    'url' => env('AWS_URL'),\n],\n```\n\n## Using SEO Pro\n\nBy default, the SEO Pro addon generates the `sitemap.xml` and `humans.txt` files dynamically and on the fly.\n\nFor both files to be part of your generated site, explicitly add their URLs to the array of the `Additional URLs` section in the configuration file:\n\n```php\n# config/statamic/ssg.php\n'urls' => [\n    '/sitemap.xml',\n    '/humans.txt',\n],\n```\n"
  },
  {
    "path": "content/collections/pages/oauth.md",
    "content": "---\nid: 3dbb14fd-a762-4891-bce1-daf13b8c5981\nblueprint: page\ntitle: OAuth\ntemplate: page\npro: true\nrelated_entries:\n  - 6b691e04-8f28-4eb2-8288-b61433883fe4\n---\n## Overview\n\nStatamic supports OAuth authentication via [Laravel Socialite](https://github.com/laravel/socialite), which includes support for Facebook, Twitter, Google, LinkedIn, GitHub, and Bitbucket.\n\nThe [Socialite Providers][socialite-providers] Github organization contains over 100 additional pre-built providers that you can take advantage of as well.\n\nIf you require a provider not on the list (perhaps you need a custom one for your own application), you may [create your own provider](#custom-providers).\n\n## Installing Socialite\n\nInstall Socialite with the following Composer command:\n\n``` shell\ncomposer require laravel/socialite\n```\n\nEnable OAuth in `config/statamic/oauth.php` or in your environment file:\n\n``` env\nSTATAMIC_OAUTH_ENABLED=true\n```\n\nAdd the provider to the [oauth config](#configuration). This will allow Statamic to add buttons to the CP login form.\n\n``` php\n'providers' => [\n    'github',\n],\n```\n\nAdd your provider's credentials to `config/services.php` and [callback URL](#routes) as per the Socialite documentation:\n\n``` php\n'github' => [\n    'client_id' => env('GITHUB_CLIENT_ID'),\n    'client_secret' => env('GITHUB_CLIENT_SECRET'),\n    'redirect' => 'http://your-site.com/oauth/github/callback',\n],\n```\n\nIf you plan to use a third party provider, follow the steps [below](#third-party-providers).\n\n## Usage\n\nSend your users to the provider’s login URL to begin the OAuth workflow. Buttons for each configured provider will be available on the Control Panel's login page, but you may also do this on the front-end with the `oauth` tag:\n\n```\n<a href=\"{{ oauth:github }}\">Log in with Github</a>\n```\n\nOnce they've logged in at their provider's site, they will be redirected back to your site where a Statamic user account will either be retrieved or created.\nThey will then be automatically logged into your site with the Statamic account.\n\nHowever, you may [customize the user flow](#user-flow).\n\n\n## Configuration\n\nOAuth behavior may be configured in `config/statamic/oauth.php`.\n\n### Providers\n\nYou should add your intended OAuth providers to the config so Statamic can provide your users with buttons on the login page.\n\nYou can specify just the name of the provider, or use a name/label pair if you would like to customize how it's displayed.\n\n``` php\n'providers' => [\n    'facebook',\n    'github' => 'GitHub',\n    'twitter',\n],\n```\n\nIf a provider requires [\"stateless authentication\"](https://laravel.com/docs/socialite#stateless-authentication), you may pass an array and specify the `stateless` config option:\n\n``` php\n'providers' => [\n    'saml2' => ['stateless' => true, 'label' => 'Okta'],\n],\n```\n\n### Routes\n\nThere are 2 required routes in order for the OAuth workflow to function:\n  - A login redirect route, which sends users to the provider's login page.\n  - A callback route, which the provider will redirect to after a successful login.\n\nYou may customize these in `config/statamic/oauth.php`:\n\n``` php\n'routes' => [\n    'login' => 'oauth/{provider}',\n    'callback' => 'oauth/{provider}/callback'\n],\n```\n\nWhen you create your OAuth application, you will need to provide the callback URL.\n\n### User flow\n\nBy default, once a user has logged in at their provider's site, they will be redirected back to your site where a Statamic user account will either be retrieved or created.\nThey will then be automatically logged into your site with the Statamic account.\n\nAdditionally, any user data from the provider will be merged into that user's account.\n\nYou may choose to customize this flow.\n\n```php\n'create_user' => true,\n'merge_user_data' => true,\n'unauthorized_redirect' => null,\n```\n\nBy setting `'create_user' => false`, if a corresponding Statamic user account doesn't exist, one will not be created for them, and they will be redirected to the unauthorized error page.\n\n## Third party providers\n\nIf you would like to use a provider not natively supported by Socialite, you should use the [SocialiteProviders][socialite-providers] method.\n\n1. Require the appropriate provider using Composer:\n    ```\n    composer require socialiteproviders/dropbox\n    ```\n\n1. Next, add an event listener in your `AppServiceProvider`'s `boot` method:\n    ```php\n    // app/Providers/AppServiceProvider.php\n\n    Event::listen(function (\\SocialiteProviders\\Manager\\SocialiteWasCalled $event) {\n        $event->extendSocialite('dropbox', \\SocialiteProviders\\Dropbox\\Provider::class);\n    });\n    ```\n\n    Alternatively, if your application has an `EventServiceProvider.php` file, you can register the event listener in there:\n\n    ```php\n    protected $listen = [\n        \\SocialiteProviders\\Manager\\SocialiteWasCalled::class => [\n            'SocialiteProviders\\\\Dropbox\\\\DropboxExtendSocialite@handle',\n        ],\n    ];\n    ```\n\n3. Add the service credentials to `config/services.php` config:\n    ``` php\n    'dropbox' => [\n        'client_id' => env('DROPBOX_CLIENT_ID'),\n        'client_secret' => env('DROPBOX_CLIENT_SECRET'),\n        'redirect' => 'http://your-site.com/oauth/dropbox/callback',\n    ],\n    ```\n\n4. Add the provider to the `config/statamic/oauth.php` config:\n    ``` php\n    'providers' => [\n        'dropbox',\n    ],\n    ```\n\n## Custom providers\n\nIf your OAuth provider isn’t already available in Socialite or [SocialiteProviders][socialite-providers], you may create your own.\n\nTo create your own OAuth provider, you should make your own SocialiteProvider-ready provider. All that's needed is the event handler (eg. `DropboxExtendSocialite.php`) and the provider (eg. `Dropbox.php`).\n\nFollow the [third party installation steps](#third-party-providers), but skip the Composer bits. You can just keep the classes somewhere in your project.\n\n## Customizing user data\n\nAfter authenticating with the provider, Statamic will try to retrieve the corresponding user, or create one if it doesn't exist. You may customize how it's handled by adding a callback to your `AppServiceProvider`.\n\n### User data\n\nThe only data added to the user will be their `name`. If you would like to customize what gets added to the user, you can return an array from the provider's `withUserData` callback.\n\nThe closure will be given:\n- an instance of `Laravel\\Socialite\\Contracts\\User`\n- the existing `Statamic\\Contracts\\Auth\\User` if one already exists.\n\n``` php\nuse Statamic\\Facades\\OAuth;\n\nOAuth::provider('github')\n     ->withUserData(fn ($socialiteUser, $statamicUser) => [\n        'name' => $socialiteUser->getName(),\n        'created_at' => optional($statamicUser)->created_at\n                        ?? now()->format('Y-m-d'),\n    ]);\n```\n\n:::warning\nThis user data will get merged into the user every time they log in using OAuth. This includes if they had an existing non-OAuth user account.\n:::\n\n### Customize entire user creation\n\nIf you want more control over the actual user object being created, you can return a user from the provider's `withUser` callback. The closure will be given an instance of `Laravel\\Socialite\\Contracts\\User`.\n\n``` php\nuse Statamic\\Facades\\User;\nuse Statamic\\Facades\\OAuth;\n\nOAuth::provider('github')->withUser(function ($user) {\n    return User::make()\n        ->email($user->getEmail())\n        ->set('name', $user->getName());\n});\n```\n\n:::warning\nThis will only be used when the user is initially created. If you'd like to also update the data on every login, you should combine this with the `withUserData` option above.\n\n```php\npublic function boot()\n{\n    OAuth::provider('github')\n        ->withUserData(fn ($user) => $this->userData($user))\n        ->withUser(function ($user) {\n            return User::make()\n                ->email($user->getEmail())\n                ->data($this->userData($user));\n        });\n}\n\nprivate function userData($user)\n{\n    return [\n        'name' => $user->getName(),\n    ];\n}\n```\n:::\n\n[socialite-providers]: https://socialiteproviders.com/\n"
  },
  {
    "path": "content/collections/pages/overview.1.md",
    "content": "---\nid: e0e93aba-4abc-4433-9257-3321a4521d60\nblueprint: page\ntitle: 'Control Panel Overview'\nnav_title: Overview\nintro: >\n    The Statamic Control Panel is where you manage everything that makes your site… well, your site. It's the admin interface you use to create and edit content, manage users and permissions, tweak settings, access utilities, and interact with addons — all without needing to touch the filesystem directly\n---\n<figure>\n    <img src=\"/img/screenshot-cp-v6.webp\" alt=\"Statamic v6 Control Panel\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/screenshot-cp-v6-dark.webp\" alt=\"Statamic v6 Control Panel\" class=\"u-hide-in-light-mode\">\n    <figcaption>Behold — the Statamic Control Panel!</figcaption>\n</figure>\n\nThe control panel is built to be fast, modern, and flexible — and it gets a lot smarter in v6 with things like a command palette and updated UI patterns designed for real editors and developers alike.\n\nBelow is a quick explanation of the major areas you'll see once you're logged in.\n\n## Dashboard\n\nThe first screen you see after signing in is the [Dashboard](/dashboard) — a customizable area where you can add widgets for things you care about: recent entries, scheduled content, form submissions, updates, shortcuts, and more. Configure it once and it becomes your command center.\n\n## Content Management\n\nThis is where the magic happens. [Collections](/collections) organize your content types — blog posts, pages, products, whatever you need. Each collection can have multiple [blueprints](/blueprints) defining different field structures, and you can use [globals](/globals) for site-wide content that appears everywhere.\n\nThe [Asset Browser](/assets) handles all your files — images, documents, videos — with built-in image editing, organization, and optimization tools.\n\n## Users & Permissions\n\nThe Users section lets you create, manage, and invite people who can log into the control panel. [Roles and permissions](/users) let you control what each user or role can see and do, from read-only editors to full administrators.\n\n## Command Palette\n\nHit `cmd+k` (or `ctrl+k` on Windows/Linux) and the Command Palette pops up — a quick way to navigate around the control panel, jump right into editing specific entries, and run actions without clicking through menus. It's like Spotlight for your CMS.\n\n## Preferences\n\nEvery user can adjust their own Preferences — like theme (light/dark), start page, locale, and more, while admins can also set defaults or role-based preferences. Make the control panel work the way you work.\n\n## Forms & Submissions\n\n[Forms](/forms) let you collect data from your site visitors — contact forms, newsletter signups, surveys, whatever. All submissions are stored in the control panel where you can view, export, or process them however you need.\n\n## Content Tools\n\nThe control panel includes powerful tools that make content work easier:\n\n[Live Preview](/live-preview) — see your changes as you write, with real-time updates as you edit.\n\n[Git Automation](/git-automation) — manage how content updates sync with your Git workflow, keeping your content and code in sync.\n\n[Revisions](/revisions) — create versions of your content, track changes, and easily rollback to previous versions of your entries.\n\n[Utilities](/utilities) — standalone tools with their own screens and permissions, like the Cache Manager, PHP Info Viewer, and Email Config.\n\n[Multi-Site](/multi-site), [Translations](/cp-translations), [Conditional Fields](/control-panel/conditional-fields), [Elevated Sessions](/control-panel/elevated-sessions), [White Labeling](/control-panel/white-labeling), and more — all features that let you shape the CP experience to your needs.\n\n## Navigation & Extensibility\n\nStatamic's control panel nav is not set in stone. You can [customize what editors see](/control-panel/customizing-the-cp-nav), hide sections they don't need, reorder items, and if you're building addons you can extend or add your own control panel navigation items. The control panel adapts to your workflow, not the other way around.\n"
  },
  {
    "path": "content/collections/pages/overview.10.md",
    "content": "---\nid: 621873af-a669-42be-8bc3-5546a8c597e6\ntitle: 'Starter Kits Overview'\nnav_title: Overview\nintro: |-\n  Starter Kits are pre-built site packages that jump-start new Statamic sites with features, functionality, and even design.\n\n  Built by the core team or designers & developers in the community, Starter Kits can cover a wide range of uses, from fully-built, ready-to-go sites, to developer-focused boilerplates for common frontend frameworks. Starter Kits can be shared and even sold on the <a href=\"https://statamic.com/marketplace\">Statamic Marketplace</a>.\nblueprint: page\n---\n## Statamic starter kits vs WordPress themes\n\nWhile they may seem similar on the surface, Statamic starter kits and WordPress themes take a very different approach to the end-goal of speeding up the web design and development process. Allow us to explain the difference.\n\nMany WordPress themes are interchangeable because WordPress uses the same content model for all sites. This is both a strength and a weakness of the platform. The strength is the interchangeability, but the weakness is that not every business or website _should_ be the same. Sites often outgrow their themes with feature and content needs the theme doesn't support. When this happens, site owners need to either start hacking and slashing away at someone else's code, or installing plugins and hoping for the best.\n\nStatamic Starter Kits are designed to be a **starting place**. Good, clean code, ready to be changed, built-upon, and adapted to your needs. Each Starter Kit can have a unique content model, design, and set of features that fits its creator's purpose.\n\nWe envision Starter Kits as a great way to \"skip ahead\" in the usual development cycle of a website, and less of a \"no-code\" approach to web development.\n\n## Where to find starter kits\n\nThe best way to find starter kits is by exploring the [Statamic Marketplace](https://statamic.com/marketplace).\n\n<figure>\n    <img src=\"https://statamic.com/images/storage/products/HRU3TVeiAnVlzRecp8G6pUiYGagWTvWzoh7ZWC27.jpeg\" alt=\"Podcaster – a Statamic Starter Kit\">\n    <figcaption>This is <a class=\"font-bold text-blue-dark no-underline\" href=\"https://statamic.com/starter-kits/statamic/podcaster\">Podcaster</a> — a Starter Kit for podcasters.</figcaption>\n</figure>\n\n## Recommended reading\n\n- [How to install a starter kit](/starter-kits/installing-a-starter-kit)\n- [How to update a starter kit](/starter-kits/updating-a-starter-kit)\n- [How to create your own starter kit](/starter-kits/creating-a-starter-kit)\n\n---\n"
  },
  {
    "path": "content/collections/pages/overview.12.md",
    "content": "---\nid: b80820bb-c2e8-475f-98bd-8ea0ef9f5339\nblueprint: page\ntitle: 'Vue Components Overview'\noverview: \"Here's how you can add your own Vue 3 components to the Statamic Control\\_Panel.\"\nnav_title: Overview\n---\n## Registering Components\n\nIn order to use a custom Vue component, it needs to be registered. You should do this inside the `Statamic.booting()` callback.\n\nOnce registered, you (or Statamic) will be able to use the component.\n\n``` vue\n<script setup>\ndefineProps(['hello']);\n</script>\n\n<template>\n    <div>...</div>\n</template>\n```\n\n``` js\nimport MyComponent from './Components/MyComponent.vue';\n\nStatamic.booting(() => {\n    Statamic.$components.register('my-component', MyComponent);\n});\n```\n\n## Appending Components\n\nRegistered components may also be appended to the end of the page at any point.\n\n``` js\nconst component = Statamic.$components.append('publish-confirmation', {\n    props: { foo: 'bar' }\n});\n```\n\nThis will return an object _representing_ the component. On this object, you have access to a number of methods to interact with the Vue component.\n\n### Updating props\n\n``` js\ncomponent.prop('prop-name', newValue);\n```\n\n### Adding event listeners\n\nIt works just like Vue's `$on` method:\n\n``` js\n// inside component\nthis.$emit('event-name', { foo: 'bar' });\n```\n\n``` js\ncomponent.on('event-name', (payload) => {\n    console.log(payload); // { foo: 'bar' }\n});\n```\n\n### Destroying the component\n\n```js\ncomponent.destroy();\n```"
  },
  {
    "path": "content/collections/pages/overview.2.md",
    "content": "---\nid: 84100772-18e4-4a22-8759-219b242a320c\nblueprint: page\ntitle: 'Frontend Overview'\nintro: \"Frontend, backend, control panel, client-side, server-side, left-side, strong-side, front-side fakey 180...there's a lot of terminology flying around referring to the various aspects of a website. Let's clear 'em up, at least in the Statamic context.\"\nnav_title: Overview\ntemplate: page\n---\n## Clarification\n\nThe **frontend** of a website is the part users see and interact with in their browser. The .com bit. It's the text, images, videos, pages, layouts, RSS feeds, and other bits that your readers and visitors consume.\n\n:::tip\nIt's likely this isn't new information – most people who read these docs are developers with front-end experience. Please keep reading though! There's good info in here.\n:::\n\nWhen we refer to the frontend of a _Statamic_ site, we're talking about the templates and views, JavaScript/CSS files, media assets, and other resources used to render your final website.\n\nThe **backend** of a Statamic site is all of the PHP and Laravel code that you _can_ customize and extend to bring your own unique features and capabilities to life on your site.\n\nStatamic's **Control Panel** sits _outside_ both the frontend and backend as a tool used to publish and manage content, users, and assets.\n\n## The frontend is yours\n\nIn today's tech-driven ecosystem there are countless ways to build a website. Some might say _too_ many. You could...\n\n- Write a Single Page Application (SPA) with [Vue.js](https://vuejs.org) or [React](https://reactjs.org) to run your entire site without the need for page refreshes\n- Use HTML and Statamic's [Antlers](/antlers) template language to build a dynamic site with smart caching\n- Use [Vite][vite], [Webpack](https://webpack.js.org), [Laravel Mix][mix], or [Gulp](https://gulpjs.com) to compile your JavaScript and SCSS/LESS\n- Go for the [JAMStack](https://jamstack.org) approach and run a statically generated site without server-side processing\n- Build a standard Statamic site and deploy a static version to [Netlify](https://www.netlify.com)\n- Go skateboarding and stay away from computers and nerdy webmasters\n- <span class=\"font-display\">Kick it old-school and write your own HTML, plain CSS, and vanilla JavaScript</span>\n\nJust like the [honey badger](https://www.youtube.com/watch?v=4r7wHMg5Yjg), Statamic don't care. You can take any of these approaches or one of many others — including several that will be invented tomorrow and forgotten by autumn.\n\n**It's up to you.** Write or generate HTML somehow and let Statamic get it to the browser.\n\n## Path of least resistance\n\nIf you don't have a hard requirement, a strong preference, or just want our advice, we recommend writing your own HTML, use [Antlers](/antlers) or [Blade](/blade) in said HTML to pull content in, use [TailwindCSS](https://tailwindcss.com) as your CSS framework, and let [Vite][vite] compile any JavaScript, SCSS/LESS, or PostCSS as necessary.\n\nYou'll be able to take advantage of all of our powerful, tightly coupled [tags](/tags) that do most of the heavy lifting — like fetching and displaying content from collections and taxonomies, manipulating, assets, and rendering variables.\n\n## Other options\n\nYou don't have to go Antlers + Tailwind. At all. That's just our preference.\n\nYou could do so many different things, like:\n\n- Use our [GraphQL](/graphql) integration and build your frontend with [Gatsby.js](https://www.gatsbyjs.com/)\n- Use our [REST API](/rest-api) and build a single page application with [Vue.js](https://vuejs.org) or [React](https://reactjs.org/)\n- Use [Laravel Blade](https://laravel.com/docs/13.x/blade) and some controllers and write your own routes.\n\nIt's up to you.\n\n## Request lifecycle\n\nLet's take a quick look at what happens during a typical Statamic frontend request:\n\n1. User visits a URL.\n2. Statamic checks if there's any data matching the URL (e.g. an [entry](/collections) or [route](/routing#statamic-routes)).\n3. [Variables](/variables) for that item are fetched out the data store.\n4. Statamic loads the appropriate [view](/views) and injects the variables into it.\n5. The Contents of the rendered view is sent to the user's browser.\n\n[mix]: https://laravel.com/docs/mix\n[vite]: https://vitejs.dev"
  },
  {
    "path": "content/collections/pages/overview.3.md",
    "content": "---\nid: 9a1d8b88-c600-46f2-8727-1deb56f2e87a\nblueprint: page\ntitle: 'Fieldtypes Overview'\nintro: 'Fieldtypes are customizable form [fields](/fields) used to structure your content and provide an intuitive content management experience. Each fieldtype has its own UI, data format, and configuration options.'\ntemplate: page\noptions_content: 'Each fieldtype has a common set of options in addition to any unique ones specific to that type.'\noptions:\n  -\n    name: display\n    type: text\n    description: \"The field's label shown in the Control Panel.\"\n    required: false\n  -\n    name: handle\n    type: text\n    description: \"The field's template variable. Avoid using [reserved words](/tips/reserved-words#as-field-names) as handles.\"\n    required: true\n  -\n    name: instructions\n    type: text\n    description: 'Provide additional field instructions like this text. Markdown formatting is supported.'\n    required: false\n  -\n    name: instructions_position\n    type: text\n    description: 'Where the instructions should be positioned relative to the field. Options: `above` or `below`.'\n    required: false\n  -\n    name: variant\n    type: text\n    description: 'Show the field under its label or beside it. Options: `block` (Stacked), `inline` (Side by Side). Default: `block`.'\n    required: false\n  -\n    name: listable\n    type: mixed\n    description: 'Controls whether the field should be shown in Control Panel listings. Options: `hidden`, `true`, or `false`. Default: `hidden`.'\n    required: false\n  -\n    name: visibility\n    type: mixed\n    description: 'Controls whether the field should be shown in Control Panel publish forms. Options: `visible`, `read_only`, [`computed`](/computed-values) or `hidden`. Default: `visible`.'\n    required: false\n  -\n    name: sortable\n    type: toggle\n    description: 'Control if the field should be sortable in listing views.'\n    required: false\n  -\n    name: replicator_preview\n    type: toggle\n    description: 'Control preview visibility in Replicator/Bard sets.'\n    required: false\n  -\n    name: duplicate\n    type: toggle\n    description: 'Control if the field should be included when duplicating the item.'\n    required: false\n  -\n    name: actions\n    type: toggle\n    description: 'Show or hide field action controls, such as fullscreen mode.'\n    required: false\n  -\n    name: conditions\n    type: mixed\n    description: 'Configure rules that control whether the field should be shown or hidden. Learn more about [conditional fields](/conditional-fields).'\n    required: false\n  -\n    name: required\n    type: boolean\n    description: 'Control whether or not this field is required.'\n    required: false\n  -\n    name: validate\n    type: array\n    description: 'Configure rules that validate the value of this field before allowing the user to save. Learn more about [validation](/blueprints#validation).'\n    required: false\nrelated_entries:\n  - 9a1d8b88-c600-46f2-8727-1deb56f2e87a\n  - 54548616-fd6d-44a3-a379-bdf71c492c63\n  - 2940c834-7062-47a1-957c-88a69e790cbb\n  - dd52c1f6-661b-4408-83c6-691fa341aaa7\n  - dcf80ee6-209e-45aa-af42-46bbe01996e2\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1632748812\nnav_title: Overview\n---\n## Overview\n\nFieldtypes are essentially different types of form inputs you can choose from when building a [blueprint](/blueprints). They range from simple text fields and select boxes, to more complex WYSIWYG-style editors like Bard.\n\n:::watch https://www.youtube.com/embed/cs_jL6fCaA8\nWatch how to add fields to a blueprint.\n:::\n\n## List of Fieldtypes\n\nCheck out the full list of [all fieldtypes](/reference/fieldtypes) in our reference section.\n\n## Data Format\n\nFieldtypes are [augmented](/augmentation) to alter the output of your saved content according to how the field is expected to be used.\n\nFor example, a [markdown field](/fieldtypes/markdown) will automatically convert your plain text input into HTML according to your markdown options of choice. Given the very same input in a [textarea field](/fieldtypes/textarea), what you enter is what you return because that fieldtype doesn't alter the content. The documentation for each fieldtype will detail if any augmentation happens.\n\nThese same rules apply whether you're using [Antlers](/antlers), [Blade](/blade), [GraphQL](/graphql), or the [REST API](/rest-api).\n\n:::tip\nYou can retrieve the original, un-augmented data by using the [raw modifier](/modifiers/raw), like so:\n\n```\n{{ markdown_field | raw }}\n```\n:::"
  },
  {
    "path": "content/collections/pages/overview.4.md",
    "content": "---\nid: bc2ad29d-50c3-47fb-9213-8553bcb5a48a\nblueprint: page\ntitle: 'Addons Overview'\nintro: 'Developers can easily build new features that are compatible with everyone’s Statamic installations. Addons can then be easily shared or sold to others to let them extend their Statamic installation.'\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1632424769\nnav_title: Overview\n---\n## Finding addons\n\nYou can browse the [Statamic Marketplace](https://statamic.com/addons) to find addons.\n\n## Installing addons\n\nYou can use Composer to install any addon:\n\n``` shell\ncomposer require vendor/package\n```\n\nThe command can be found on the addon's page in the [Statamic Marketplace](https://statamic.com/addons).\n\n:::tip\nSome first party addons – such as the Static Site Generator or Eloquent Driver - have their own dedicated commands which will be noted on the same pages.\n\n```shell\nphp please install:ssg \n```\n:::\n\n## Creating addons\n\nTo learn how to create your own addon, as well as publishing it to the Statamic Marketplace, head over to the [Extending Statamic](/extending/addons) area.\n\n## Licensing\n\nAddons may require a license, which you can purchase at the [Marketplace](https://statamic.com/marketplace). Licenses may be attached to a site in your [account area](https://statamic.com/account/sites). Make sure that you have your site key entered into your Statamic project.\n\nYou can try out commercial addons locally for free. Be sure to purchase a license before deploying to production.\n\n## Editions\n\nAn addon may have multiple editions, which may cost different amounts and provide different sets of features.\n\nYou can choose which edition is installed by entering it into your `config/statamic/editions.php` file:\n\n``` php\n'addons' => [\n    'vendor/package' => 'pro', // e.g., 'jezzdk/statamic-google-maps' => 'pro'\n]\n```"
  },
  {
    "path": "content/collections/pages/overview.5.md",
    "content": "---\nid: 4e3ca511-fe21-497f-9166-3c7624607a91\nblueprint: page\ntitle: 'Tags Overview'\nnav_title: Overview\nintro: 'Tags are Antlers expressions that give you the ability to fetch, filter, and display content, enhance and simplify your markup, build forms, and add dynamic functionality to your templates.'\n---\n\nA their most basic level, Tags are PHP methods you call from your Antlers or Blade templates. They let you work with content, manipulate data, and build dynamic features without writing PHP directly in your templates.\n\nMany of them serve the same role as Controllers in a traditional MVC (Model-View-Controller) style application.\n\n## Basic Usage\n\nTags come in two flavors: single tags and tag pairs.\n\nSingle tags are self-contained and return a value:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:blog }}\n  <h1>{{ title }}</h1>\n{{ /collection:blog }}\n```\n::tab blade\n```blade\n<s:collection:blog>\n  <h1>{{ $title }}</h1>\n</s:collection:blog>\n```\n::\n\nTag pairs wrap content and can structure and  manipulate what's inside:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ entries:listing folder=\"blog\" }}\n  <article>\n    <h2>{{ title }}</h2>\n    <p>{{ excerpt }}</p>\n  </article>\n{{ /entries:listing }}\n```\n::tab blade\n```blade\n<s:entries:listing folder=\"blog\">\n  <article>\n    <h2>{{ $title }}</h2>\n    <p>{{ $excerpt }}</p>\n  </article>\n</s:entries:listing>\n```\n::\n\n## What Tags Can Do\n\nTags handle the heavy lifting in your templates:\n\n- **Fetch content** — Get entries from collections, taxonomies, globals, and more\n- **Filter and sort** — Narrow down results with powerful query parameters\n- **Manipulate data** — Transform strings, arrays, dates, and other values\n- **Build forms** — Create and handle form submissions\n- **Control logic and flow** — Conditionally show content, loop through data, and more\n- **Work with assets** — Resize images, generate responsive srcsets, and manage files\n\n## Common Tags\n\nSome tags you'll use frequently:\n\n- `{{ collection:* }}` — Fetch entries from collections\n- `{{ entries:listing }}` — List and filter entries\n- `{{ taxonomy:* }}` — Work with taxonomy terms\n- `{{ assets:* }}` — Handle images and files\n- `{{ form:* }}` — Build and process forms\n- `{{ if }}` / `{{ unless }}` — Conditional logic\n- `{{ partial }}` — Include reusable template snippets\n\nBrowse the [full tag reference](/tags/all-tags) to see everything available, or [build your own custom tags](/tags/building-a-tag) when you need something specific.\n"
  },
  {
    "path": "content/collections/pages/overview.6.md",
    "content": "---\nid: 9c1efbc5-c6a4-46f1-acce-d38b20122bd6\nblueprint: page\ntitle: 'Modifiers Overview'\nintro: 'Modifiers manipulate the data of your variables on the fly in Antlers templates. They can modify strings, filter arrays and lists, perform comparisons, handle basic math, simplify your markup, and even help you debug.'\nnav_title: Overview\n---\n## Overview\n\nModifiers are available exclusively in [Antlers][antlers] templates. Each modifier is a function that accepts the value of the variable it's attached to, can do just about anything with that data, and then returns it. Multiple modifiers chained onto a variable will be executed in sequence, each passing its modified value onto the next.\n\n## Example\n\nYou could take some text, render it as markdown, uppercase it, and ensure there are no widows (lines with only one word on them) like this:\n\n::tabs\n\n::tab antlers\n```antlers\n// This...\n{{ \"Ruth, Ruth, Ruth! Baby Ruth!\" | markdown | upper | widont }}\n\n// Becomes this!\n<p>RUTH, RUTH, RUTH! BABY&nbsp;RUTH!</p>\n```\n::tab blade\n```blade\n{!! Statamic::modify(\"Ruth, Ruth, Ruth! Baby Ruth!\")->markdown()->upper()->widont() !!}\n\n// Becomes this!\n<p>RUTH, RUTH, RUTH! BABY&nbsp;RUTH!</p>\n```\n::\n\n## Related reading\n\nEager for more knowledge? Check out [Antler's modifier syntax](/antlers#modifying-data) and discover how to [build your own](/extending/modifiers#creating-a-modifier).\n\n## Core modifiers\n\nYou can find a [full list of modifiers](/reference/modifiers) included with Statamic in the reference section.\n\n[antlers]: /antlers"
  },
  {
    "path": "content/collections/pages/overview.7.md",
    "content": "---\nid: 4b77c19b-129c-4271-a724-eea884eb3e2e\nblueprint: page\ntitle: 'Widgets Overview'\nnav_title: Overview\nintro: >\n    The Control Panel's dashboard may contain any number of widgets. A widget is simply a box that shows something. That something might be anything from a list of recently updated entries, to a randomized inspiration quote, and anything in between.\n---\n<figure>\n    <img src=\"/img/widgets/widgets-v6.webp\" alt=\"Widgets Overview\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/widgets/widgets-v6-dark.webp\" alt=\"Widgets Overview\" class=\"u-hide-in-light-mode\">\n    <figcaption>Look at me. I'm the Captain now.</figcaption>\n</figure>\n\nStatamic comes bundled with a handful of widgets, however you may also [create your own](/extending/widgets) or use ones [created by others](https://statamic.com/addons/tags/widget).\n\n## Configuration\nWidgets can be added to the dashboard by modifying the widgets array in the `config/statamic/cp.php` file.\n\nEach item in the array should specify the widget as the type, plus any widget-specific configuration values.\n\nYou may use the same widget multiple times, configured in different ways.\n\n## Available widgets\n\nCheck out the [list of available widgets](/reference/widgets) included in Statamic Core."
  },
  {
    "path": "content/collections/pages/overview.8.md",
    "content": "---\nid: 0b4733ea-4ca9-4bb9-9df1-f1ab95553dfe\nblueprint: page\ntitle: Variables Overview\nnav_title: Overview\nintro: Context-aware variables are always available in your [views](/views), giving you access to dynamic information about the current URL, user, loaded entry, site settings, and more.\ntemplate: variables.index\n---\n## Overview\n\nWhere appropriate, Statamic will inject data automatically for you to use in your views.\n\nFor example, when a view is loaded, you get automatic access to the variables applicable to it. You don't need to use a tag to \"get\" the data. If you're viewing an entry's URL, all of the [entry variables](#entry-variables) will just be there.\n\nIf you use a tag that does supply some data, it will typically make those variables available.\n\nThe [collection tag](/tags/collection) will loop over entries, again giving you access to entry variables within the loop. The [assets tag](/tags/assets) gives you [asset variables](#asset-variables), the [taxonomy tag](/tags/taxonomy) gives you [term variables](#term-variables), and so on.\n\nThe same is true for within tag pairs of [augmented values](/augmentation). Looping through a field configured to use an assets fieldtype? You'll be getting asset variables.\n\n\n## Reaching into the cascade\n\nLet's say you're on an entry's URL and you're looping through related entries. Within the loop, you'd have a `{{ title }}` which would be for the entry in that loop. But what if you want to get the `{{ title }}` from further up your view?\n\n### The current page scope\n\nVariables for the current page will be aliased into a `page` array. You can access this any time by prefixing a variable with `page:`.\n\n```\n{{ related_posts }}\n    {{ title }} // The title of the entry in the loop.\n    {{ page:title }} // The title of the entry used when loading the URL.\n{{ /related_posts }}\n```\n\n### Explicitly defined scopes\n\nYou aren't limited to the `page` scope. You can use the `{{ scope }}` tag to take a \"snapshot\" of the variable context at any point of the template and use it for reference elsewhere.\n\nFor example, we can create a scope named `stuff`.\n\n```\n{{ scope:stuff }}\n    {{ title }}\n\n    {{ collection:blog }}\n        {{ title }} // The title of the entry in the loop.\n        {{ stuff:title }} // The title variable at the time the scope tag was used.\n    {{ /collection:blog }}\n\n{{ /scope:stuff }}\n```\n\n\n## Globals\n\nYou can create your own [Global variables](/globals), which all get injected into the variable cascade, ready to be used in your views.\n\n## View front-matter\n\nInside Antlers views, you may define YAML front-matter. This may be a handy way to define variables without needing to add anything to content or blueprints.\n\nTo access this data, prefix the variables with `view:`.\n\n```\n---\nfoo: bar\n---\n{{ view:foo }}\n```\n```html\nbar\n```\n\n:::tip\nYou **must** define any front-matter variables at the top of the view file, even before things like Antlers comments.\n:::\n\n## Available variables\n\nThe following groups of variables are available in your views, depending on their context.\n"
  },
  {
    "path": "content/collections/pages/overview.md",
    "content": "---\nid: 20f0707b-619d-4a7a-b7dd-aea4122fa1db\nblueprint: page\ntitle: 'Content Modeling'\nintro: 'Before you build pages, templates, or implement features, there’s one foundational question to answer: **what shape should your content take?**'\nrelated_entries:\n  - 54548616-fd6d-44a3-a379-bdf71c492c63\n  - 2940c834-7062-47a1-957c-88a69e790cbb\n  - 1e91dd54-c452-4e3b-8972-dba83c048d3d\n  - 7202c698-942a-4dc0-b006-b982784efb03\nnav_title: Overview\n---\nContent modeling is just the process of deciding how your content is structured — what fields it has, how different pieces relate to each other, and where flexibility actually matters. In Statamic, that plays out with blueprints, fields, globals, collections, and relationships.\n\nA good content model makes everything easier:\n\n- Editors understand what goes where\n- Developers have more flexibility to pull the content you want into all the right places\n- Designers don't have to be told \"we can't do that\"\n- And future you doesn’t regret past you’s shortcuts\n\nThis guide walks through how to think about content first — separating structure from presentation, optimizing for changeability and flexibility, and building a site that doesn't revolve around _pages_, but well-structured content.\n\n## The Separation of Content and Presentation\n\nContent professionals have become accustomed to thinking about content and presentation together. They expect to see what the content will look like and often expect to change that appearance as well. \n\nTraditional WYSIWYG tools and visual builders reinforce this mindset. They blur the line between content and layout, and in doing so, let design decisions leak into the content itself.\n\nBut these are bandaids. \n\nAn author shouldn't be making decisions about layout — that's the job of the designer and/or UX professional. An author should focused on the **clarity of content**, not enforcing (or deciding) style. The job of the CMS (Statamic in this case) is to take that content, manage it well, and give designers and developers the freedom to present it however they need — today or years from now.\n\nWhen you separate content from presentation, you stop tying your data to a specific layout. You don’t have to rewrite everything when a design changes. You don’t have to migrate content just because a homepage gets redesigned. Your content is adaptable. You did the hard work once and now the rest of the work becomes easy.\n\nThis is where Statamic really shines, and it’s also why you won’t find an Elementor-style visual builder anywhere around here. Those tools are crutches — shortcuts at best, and long-term liabilities at worst. They tend to lock content into a moment in time and replace thoughtful design with convenience.\n\nAlright. Soap box over. Let’s get practical.\n\n## Start with Collections\n\nFirst start by determining all of your different **\"content types\"**. These usually map directly to [collections](/collections). \n\nFor example, if your site is going to have articles, news, case studies, and a handfull of on one-off pages, you'll probably end up with collections like:\n\n- Articles\n- News\n- Case Studies\n- Pages.\n\nEach collection represents a distinct type of content with its own purpose, structure, and lifecycle.\n\nAs you do this, watch for content that feels reusable. If something shows up in multiple places, it may deserve its own collection.\n\nFor example, on a marketing site for a software product, you might reference specific “features” across articles, case studies, and landing pages. Instead of rewriting that content everywhere, you can model features as their own collection and pull them into other entries using [relationship fields](/relationships).\n\n## Then Define Your Fields\n\nEvery collection uses one or more [blueprints](/blueprints). Blueprints define the fields that make up that content. If multiple blueprints need the same fields, group them into a [fieldset](/fieldset) and import it where needed.\n\nWhen defining fields, think beyond “what do we need right now?” and more in terms of “how might this content be reused or rearranged later?”\n\nIf a piece of content could reasonably stand on its own — be styled differently, moved elsewhere, or displayed independently — it probably deserves its own field.\n\nConsistency matters. Use clear, predictable naming conventions across your blueprints and fieldsets. Things like `body_content`, `hero_image`, `sidebar_callout`. Future you says thank you.\n\nSmall decisions like this add up. A clean, consistent content model is easier to understand, easier to extend, and far less likely to be “creatively worked around” by frustrated editors.\n\n## Global Variables for everything else\n\nFinally, there are [globals](/globals). \n\nIf something lives in one place but is used across the site — like header content, footer links, social profiles, or site-wide calls to action — globals are the right tool for the job.\n\nThey keep shared content centralized, editable, and out of places where it doesn’t belong.\n\n## What to Avoid\n\nMost content modeling problems don’t show up on day one. They show up months later, when the site grows, the design changes, or someone asks, “Can we reuse this somewhere else?”\n\nHere are some common traps to avoid.\n\n### Modeling Pages Instead of Content\n\nIf your content model mirrors your page layouts, you’re probably heading for trouble.\n\nFields like `left_column_text`, `homepage_feature_1`, or `about_page_body` are a code smell. They bake presentation decisions directly into the content and make reuse painful or impossible. As soon as `left_column_text` ends up needing to be used on the right side somewhere, a fairy dies — and no amount of clapping can revive her.\n\nModel what the content **is**, not where it happens to live today.\n\n### Over-Modeling Everything\n\nNot everything needs to be perfectly modeled.\n\nIf a piece of content is truly one-off, short-lived, or unlikely to be reused, don’t turn it into a dozen fields just because you can. Over-modeling can create friction and slow people down.\n\nModel for clarity and flexibility — not theoretical perfection.\n\n### Ignoring Future Change\n\nThe biggest mistake is assuming today’s structure is permanent.\n\nSites evolve. Designs change. Content gets reused in ways you didn’t anticipate. It gets pulled into mobile app or powers a customer-support ticketing app. A good content model leaves room for that without forcing a rewrite or migration.\n\nIf your model _only_ works for the site you’re building right now, it’s probably too brittle."
  },
  {
    "path": "content/collections/pages/permissions.md",
    "content": "---\ntitle: Permissions\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569347255\nid: ff397ebf-4b53-4dbd-b81b-0dec839e0e5f\n---\nPermissions are the abilities that can be assigned to [Roles](/users#permissions).\n\nOut of the box, Statamic has its own set of permissions that you can choose from to configure your roles. However, you are free to add your own that can be used throughout your project, or included with addons.\n\n## Basic permissions\n\nYou can register a basic permission in a service provider by specifying the string. Make sure to surround any permission registrations in a `Permission::extend` closure.\n\n``` php\nuse Statamic\\Facades\\Permission;\n\npublic function boot()\n{\n    Permission::extend(function () {\n        Permission::register('manage stuff')\n                  ->label('Manage Custom Stuff');\n    });\n}\n```\n\nThis will add an option to the permissions list when editing a role in the Control Panel.\n\nIf selected, this will add the permission string to the role:\n\n``` yaml\npermissions:\n  - manage stuff\n```\n\n## Nested permissions\n\nIt could be useful to only allow some permissions if others have already been granted. For example, you want a tree like this:\n\n``` files theme:serendipity-light\nview blog entries\n    edit blog entries\n        create blog entries\n        delete blog entries\n```\n\nInitially, only the `view` option will be selectable. When you check it, then the `edit` option becomes selectable.\nCheck that, and the `create` and `delete` options become selectable.\n\nThis can be achieved by passing an array of permissions to the `children` method on the parent permission:\n\n``` php\nPermission::register('view blog entries', function ($permission) {\n    $permission->children([\n        Permission::make('edit blog entries')->children([\n            Permission::make('create blog entries'),\n            Permission::make('delete blog entries')\n        ])\n    ]);\n});\n```\n\nThe second argument of the `register` method accepts a closure that allows you to modify the permission.\n\n\n## Policy based permissions\n\nWhen dealing with a permission that could apply to a variable number of items, it makes more sense to use a [Policy](https://laravel.com/docs/authorization#creating-policies).\n\nYou may combine your policy with a wildcard permission. A new permission will be created for each item you require.\n\nFor example, Statamic creates a `view {collection} entries` permission for each collection that exists.\n\nIt does this by using a `replacements` method to return a list of items to determines the replacements. It should return an array of arrays where `value` is the string to be inserted into the permission, and a `label` to be inserted into the label.\n\n``` php\nPermission::register('view {collection} entries', function ($permission) {\n    $permission\n        ->label('View :collection entries')\n        ->replacements('collection', function () {\n            return Collection::all()->map(function ($collection) {\n                return [\n                    'value' => $collection->handle(),\n                    'label' => $collection->title()\n                ];\n            });\n    });\n});\n```\n\nTo use your policy permissions, you should write the authorization checks from within a Policy class. For example:\n\n``` php\nclass EntryPolicy\n{\n    public function edit($user, $entry)\n    {\n        return $user->hasPermission(\"view {$entry->collectionName()} entries\");\n    }\n}\n```\n\nFinally, you may combine policy wildcard permissions with nested permissions.\n\n``` php\nPermission::register('view {collection} entries', function ($permission) {\n    $permission\n        ->label('View :collection entries')\n        ->replacements('collection', function () { /* ... */ });\n        ->children([\n            Permission::make('edit {collection} entries')->children([\n                Permission::make('create {collection} entries'),\n                Permission::make('delete {collection} entries')\n            ])\n        ])\n});\n```\n\n:::tip\nWhen using replacements, ensure your `label` string contains a placeholder prefixed with a colon.\n:::\n\n## Groups\n\nYou can put your permissions in your own group. Give it a name, a label, and then any permissions created inside\nthe callback will be added to that group.\n\n``` php\nPermission::group('myaddon', 'My Addon', function () {\n    Permission::make(...);\n});\n```\n\nIf you want to add permissions to an existing group (eg. the core ones like collections, taxonomies, etc.) you can\njust leave out the label argument:\n\n``` php\nPermission::group('collections', function () {\n    Permission::make(...);\n});\n```\n\n## Adding to the core permissions\n\nIt's possible to add to the built-in permission tree if you need to.\n\nFor example, maybe you want to add a permission to send tweets once an entry is published. You might want to jam\nthat in every collection's permission tree under its 'edit' permission.\n\nYou can use the `addChild` method on an existing permission to inject it at that position.\n\n``` php\nPermission::extend(function () {\n    Permission::get('edit {collection} entries')->addChild(\n        Permission::make('tweet {collection} entries')\n    );\n});\n```\n\n\n## Overriding Policies\n\nYou may override policies by registering a binding in your AppServiceProvider.\n\n```php\npublic function register()\n{\n    $this->app->bind(\n        \\Statamic\\Policies\\EntryPolicy::class, \n        \\App\\Policies\\CustomEntryPolicy::class\n    );\n}\n```\n\n```php\nclass CustomEntryPolicy extends \\Statamic\\Policies\\EntryPolicy\n{\n    public function edit($user, $entry)\n    {\n        // ...\n    }\n}\n```\n\nKeep in mind that most of Statamic policies will grant access earlier if the user is a super user. If you need to disable or override the super user logic, you will need to also adjust the `before` method. For example:\n\n```php\nclass CustomEntryPolicy extends \\Statamic\\Policies\\EntryPolicy\n{\n    public function before($user, $ability) // [tl! **:start]\n    {\n        if ($ability === 'edit') {\n            // Returning null here will allow the method to be called.\n            return null;\n        }\n        \n        return parent::before($user, $ability);\n    } // [tl! **:end]\n    \n    \n    public function edit($user, $entry)\n    {\n        // ...\n    }\n}\n```\n"
  },
  {
    "path": "content/collections/pages/ploi.md",
    "content": "---\nid: cf38dba4-5cce-4b81-a2f5-e82665e4e11f\nblueprint: page\ntitle: 'Deploying Statamic with Ploi'\nintro: |-\n  Ploi provisions and deploys PHP applications on DigitalOcean,\n  Linode, Vultr, Amazon, Hetzner and other hosting platforms. It's a piece of 🍰 to deploy a Statamic site with it.\nparent: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n\n:::tip\nUse the coupon `RAD-DEPLOIMENT` to get 25% off your Ploi subscription. Can be used once per account and only works with the manual renewal and a duration of up to five months.\n:::\n\nAssuming you have a [Ploi](https://ploi.io) account, the first thing to do is authorize your hosting provider of choice. In this walk-through we'll use [Hetzner](https://www.hetzner.com) as the example. This is a one-time step and will allow you to easily spin up and provision new server stacks anytime.\n\nGo to your [Hetzner Cloud Console](https://console.hetzner.cloud) (or other cloud provider of choice) and create an API token. Check out the [Hetzner docs on generating API tokens](https://docs.hetzner.com/cloud/api/getting-started/generating-api-token/) if you need it. Make sure the token has **read and write** access.\n\n<figure>\n    <img src=\"/img/deployment-ploi-hosting-setup.jpg\" alt=\"Deployment hosting setup example\">\n</figure>\n\n## Spinning Up a New Server\n\nOnce you have connected to your hosting provider, the next step is to spin up a new server. Ploi automatically tailors the server stack for Statamic and Laravel, so you only need to choose the server size most suitable for your project and you'll be billed accordingly by Hetzner.\n\n<figure>\n    <img src=\"/img/deployment-ploi-create-server.jpg\" alt=\"Create server example\">\n</figure>\n\n## Creating a New Site\n\nThe next step is to create a new site. This will scaffold out the directory structure and nginx config on the server, and further allow you to configure your site's environment variables, deployments, and so on.\n\n<figure>\n    <img src=\"/img/deployment-ploi-create-site.jpg\" alt=\"Create site example\">\n</figure>\n\n## Configuring Deployment\n\n<figure>\n    <img src=\"/img/deployment-ploi-site-setup-example.jpg\" alt=\"Install repo example\">\n</figure>\n\nFinally, setup your deployment by pointing your site to your source control repository. Ploi will create a sensible deployment script for you for one-click deployments.\n\n\nAlternatively, you can use Ploi's [1-click Statamic install](https://ploi.io/statamic) feature to quickly create a new site and an optional repository. You can even use this feature with one of [Statamic's starter kits](https://statamic.com/starter-kits).\n\n<figure>\n    <img src=\"/img/deployment-ploi-statamic-preset-example-pixelated.jpg\" alt=\"Install Statamic with Ploi's 1-click feature example\">\n    <figcaption>Whip up a fresh install right from the Ploi dashboard 🚀</figcaption>\n</figure>\n\nAfter doing this, you'll be able to customize the deployment script if needed. You can also enable \"**quick deploy**\", which will automatically trigger deployments when you push changes to your chosen branch. You can also [use GitHub actions to trigger a deployment](https://ploi.io/documentation/deployment/how-to-trigger-deployments-via-github-actions) with Ploi.\n\n<figure>\n    <img src=\"/img/deployment-ploi-script-example.jpg\" alt=\"Deployment script example\">\n</figure>\n\nThe \"Deploy script\" area is where you'd add commands to install Composer and NPM dependencies, compile CSS and JavaScript if you need to, and clear Statamic's cache. Most deploy scripts look something like this:\n\n``` shell\ncd /home/ploi/{example}.{tld}\ngit pull origin main\ncomposer install --no-interaction --prefer-dist --optimize-autoloader\necho ... sudo-S service php8.1-fpm reload\nphp please cache:clear\nnpm ci && npm run production\n```\n\nIf you're planning on using the Git integration, you may want to prevent content changes from the Control Panel from triggering \"full\" deployments in Ploi. Learn more about this on the [Git Automation](/git-automation#customizing-commits) page.\n\n## Statamic specific features\n\nPloi lets you interact with your Statamic installation without you having to connect to your server via SSH. This includes clearing the (static) cache, warming the stache, generating meta data for assets etc.\n\n<figure>\n    <img src=\"/img/deployment-ploi-statamic-features.jpg\" alt=\"Ploi's Statamic specific features\">\n    <figcaption>Access the Statamic and Laravel CLI right from the web UI.</figcaption>\n</figure>\n\n## Advanced Control\n\nPloi is [optimized for Laravel](https://ploi.io/laravel-optimized) and offers advanced control of queue workers, cron jobs, SSL certificates, database access, and more.\n\n<figure>\n    <img src=\"/img/deployment-ploi-advanced.jpg\" alt=\"Advanced Ploi features\">\n    <figcaption>Ploi has a lot of handy features.</figcaption>\n</figure>\n"
  },
  {
    "path": "content/collections/pages/preferences.md",
    "content": "---\nid: 452c268b-b885-4deb-8e46-1cc3ebc66e4f\nblueprint: page\ntitle: Preferences\nintro: Preferences are easy to manage settings available from and generally affecting only the inside of the control panel. They can be set differently per-user, role, and globally.\n---\nWhere application configuration lives in PHP config files, preferences can be accessed from the control panel where they can be edited by clients or users. The actual preferences themselves are stored in YAML files, whether on the user, role, or [default preferences file](#storage).\n\n## Accessing preferences\n\nUsers can access preferences through the cog icon in the upper right hand corner of the CP.\n\n<figure>\n    <img src=\"/img/preferences-nav-item.webp\" alt=\"CP Preferences\" class=\"u-hide-in-dark-mode\" width=\"500\">\n    <img src=\"/img/preferences-nav-item-dark.webp\" alt=\"CP Preferences\" class=\"u-hide-in-light-mode\" width=\"500\">\n    <figcaption>Manage your own preferences!</figcaption>\n</figure>\n\n## Customizing preferences for other users\n\nIn order to customize preferences for other users, you must first enable [Statamic Pro](/tips/how-to-enable-statamic-pro), and you must either be a super user or have permissions to manage preferences.\n\n<figure>\n    <img src=\"/img/manage-preferences-permission.webp\" alt=\"Manage Preferences Permission\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/manage-preferences-permission-dark.webp\" alt=\"Manage Preferences Permission\" class=\"u-hide-in-light-mode\">\n    <figcaption>Are you rad enough to manage global preferences?</figcaption>\n</figure>\n\nThis will allow you to customize the default preferences for all users, or on a role-by-role basis, though end-users will still have the ability to further customize their own CP nav as they see fit.\n\n<figure>\n    <img src=\"/img/preferences-other-users.webp\" alt=\"Preferences for Other Users\" class=\"u-hide-in-dark-mode\" width=\"550\">\n    <img src=\"/img/preferences-other-users-dark.webp\" alt=\"Preferences for Other Users\" class=\"u-hide-in-light-mode\" width=\"550\">\n    <figcaption>Manage the preferences for other users!</figcaption>\n</figure>\n\n\n## Precedence\n\nYou may specify different sets of preferences, which will override each other appropriately.\n\n- Default preferences, which apply to everyone.\n- Role preferences, which apply to users assigned to that role.\n- User preferences, which only apply to that user.\n\n:::tip Heads up\nSince a user may have multiple roles, they will inherit the preferences of their primary (or first) role.\n:::\n\n## Storage\n\nDefault preferences are stored in `resources/preferences.yaml` as a simple array.\n\n```yaml\nlocale: en\nstart_page: collections/articles\n```\n\nRole and user preferences are stored in their existing respective locations as the same array in a `preferences` key.\n\n## Themes\n\nThemes let you control the look and feel of the Control Panel.\n\nChoose from community-made themes, or create your own by assigning colours from the Tailwind palette to each UI role. Selecting a theme previews it instantly, updating the Control Panel in real time.\n\nCustom themes can be shared and published for others to install.\n\n<figure>\n    <img src=\"/img/preferences-theme.webp\" alt=\"Theme Preferences\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/preferences-theme-dark.webp\" alt=\"Theme Preferences\" class=\"u-hide-in-light-mode\">\n</figure>\n\n## Adding fields\n\nYou may add additional preference fields from within a service provider.\n\nThe closure should return an array that has sections (tabs) at the top level, and each section should have a `fields` array that contains all the field definitions.\n\n```php\npublic function boot()\n{\n    Preference::extend(fn ($preference) => [\n        'extras' => [\n            'display' => __('Extras'),\n            'fields' => [\n                'color' => [\n                    'type' => 'text',\n                    'display' => __('Color'),\n                ],\n                'size' => [\n                    'type' => 'select',\n                    'display' => __('Size'),\n                    'options' => [\n                        's' => __('Small'),\n                        'm' => __('Medium'),\n                        'l' => __('Large')\n                    ],\n                ],\n            ]\n        ]\n    ]);\n}\n```\n\n:::tip\nIf you don't want to put a field in an additional section, you can place it in the `general` section. The tab label will only be visible when there are more than one.\n:::\n\n## Getting and setting values\n\n### Using PHP\n\nYou can get a preference, with an optional fallback value to be returned if the preference isn't set. This will respect the default/role/user cascade.\n\n```php\nPreference::get($key, $fallback);\n```\n\nTo set a value, you should set it in the respective area, then save it.\n\n```php\nPreference::default()->set($key, $value)->save();\n$role->setPreference($key, $value)->save();\n$user->setPreference($key, $value)->save();\n```\n\n### Using JavaScript\n\nYou can get and set values using JavaScript too.\n\n```js\nthis.$preferences.get(key, fallback);\n```\n\nSetting values will perform an AJAX request, so you will need to wait until it's completed.\n\n```js\nthis.$preferences.set(key, value).then(response => {\n    // do something once the ajax request completes\n});\n```\n\n:::tip\nSetting values from JS can only apply it to the user's preferences.\n:::\n"
  },
  {
    "path": "content/collections/pages/progress.md",
    "content": "---\ntitle: Progress\nintro: |\n  Control the magic progress bar at the top of the page.\nid: 28068f9a-f269-4646-87e4-881e5477558d\n---\nYou can control the progress bar at the top of the page through the `$progress` instance method.\n\nThis progress bar will get a little further in small intervals automatically but will never reach 100% until it's told to.\n\nThe component can track the progress from multiple places, and will only be considered complete once all of them are complete.\n\n``` js\nimport { progress } from '@statamic/cms/api';\n\nprogress.start($name); // Starts the progress bar\nprogress.complete($name); // Instantly progress to 100% and disappear\n\nprogress.loading($name, true); // Alias of .start() - Useful for passing a boolean\nprogress.loading($name, false); // Alias of complete()\n\nprogress.names(); // The names of the items that are being tracked.\nprogress.count(); // How many are being tracked.\nprogress.isComplete(); // Whether all the items that were being tracked have completed.\n```\n\n:::tip\nIf you have a component that may appear multiple times on one page (like a Fieldtype), make sure the name is unique. You could use the browser's crypto API for this:\n\n``` js\nconst uniqueId = crypto.randomUUID();\n\nprogress.start(`things-${uniqueId}`);\n```\n:::\n"
  },
  {
    "path": "content/collections/pages/protecting-content.md",
    "content": "---\ntitle: 'Protecting Content'\nintro: It's common to want to put a site online before it's ready to be viewed by the public. Statamic has built-in ways of making this very easy for you.\ntemplate: page\nblueprint: page\nid: 75be125b-7d92-496c-ac5d-7098560d3d44\n---\n## Overview\n\nYou may deny front-end access to your content on a **per-page**, **per-collection**, or **site-wide** basis.\n\nThere are a number of drivers for protecting content available out of the box:\n\n- [auth](#authentication) for only allowing authenticated users.\n- [ip_address](#ip-address) for allowing specific IP addresses.\n- [password](#password) will force users to enter a specified password.\n\nYou can also [create your own drivers](#custom-drivers).\n\nWhichever approach you choose, know that it's designed to help you out. We’ve tried to keep the syntax as simple as possible while allowing for flexibility. Because of this, if Statamic sees you’ve set `protect`, but the scheme has been configured incorrectly, _all users will be denied_.\n\n## Caveats\n\n* Protection only applies to the frontend of your site routed through Statamic (like entry URLs). Custom routes defined in your `routes/web.php` file and the Control Panel will be unaffected.\n* Protected pages are automatically excluded from the [static cache](/static-caching#important-preface), unless the driver explicitly opts in by [marking itself cacheable](#cacheable-drivers).\n\n\n## Protecting an entry\n\nTo protect an entry, add a `protect` variable with a corresponding scheme name.\n\nFor example, Statamic comes pre-configured with a `logged_in` protection scheme that only shows the content to authenticated users. You might have an entry like this:\n\n``` yaml\n---\ntitle: Members Only\nprotect: logged_in\n---\nWhen visiting this entry's URL, logged in users will see it,\nbut logged out users will be redirected to a login page.\n```\n\n## Protecting a collection\n\nTo protect an entire collection, inject a `protect` variable into your collection. To do this, add the following to your collection's YAML config file. This cannot be done in the control panel.\n\n``` yaml\n---\ninject:\n  protect: logged_in\n```\n\n\n## Configuring schemes\n\nThe configuration file is located at `config/statamic/protect.php`. In this file you may specify a number of different schemes which you can reference throughout your content files.\n\nYou are free to use the same driver in multiple schemes, configured in different ways. Check below for details on how to configure each driver.\n\n\n## IP address\n\nAdd the IP address(es) you wish to allow to the aptly named `allowed` array.\n\n``` php\n'ip_address' => [\n    'driver' => 'ip_address',\n    'allowed' => ['127.0.0.1', '192.168.0.10'],\n]\n```\n\n\n## Authentication\n\nAdding this scheme to a page will redirect to a login page unless the user is already logged in as a Statamic user.\n\n``` php\n'logged_in' => [\n    'driver' => 'auth',\n    'login_url' => '/login',\n    'append_redirect' => true,\n]\n```\n\nIf the `login_url` has not been defined the user will see an “Access Denied” page instead of a login screen. In this case, the user could log in through the Control Panel and then come back.\n\nThe `append_redirect` setting will add `?redirect=/the-protected-url` to your login_url. This pairs with the [user:login_form tag’s allow_request_redirect parameter](https://docs.statamic.com/tags/user-login_form#parameters) which will redirect the user to the intended page once successfully logged in.\n\nThis protection method _does not_ take into account any user roles. They are simply either logged in or they're not.\n\n\n## Password\n\nThis is perfect for times when you want to password-protect one or more files but don’t want to set up user accounts for this one purpose. This scheme does not relate to member accounts in any way.\n\n``` php\n'password' => [\n    'driver' => 'password',\n    'allowed' => ['secret', 'confidential'],\n    'form_url' => null,\n]\n```\n\nYou can also define the password for a protected entry on the entries themselves. This might be helpful if each has a different password.\n\n```yaml\n# content/collections/pages/secret-page.md\n\nprotect: password\npassword: local-password\n```\n\n``` php\n// config/statamic/protect.php\n\n'password' => [\n    'driver' => 'password',\n    'allowed' => ['secret', 'confidential'],\n    'form_url' => null,\n    'field' => 'password', // [tl! add]\n]\n```\n\n### Password form\n\n<figure>\n    <img src=\"/img/password-protected.webp\" alt=\"A Statamic password protected page\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/password-protected-dark.webp\" alt=\"A Statamic password protected page\" class=\"u-hide-in-light-mode\">\n    <figcaption>The default password protected login screen.</figcaption>\n</figure>\n\nYou’ll need to provide a way for people to enter passwords for URLs. Statamic has a built-in generic password entry form. If you want to customize it, you have two options:\n\n#### Override the view\n\n::tabs\n\n::tab antlers\n\nOverride the view by creating `vendor/statamic/auth/protect/password.antlers.html` in your `views` directory, and use the  [protect:password_form](/tags/protect-password_form) tag to build a form. No config change required. For example:\n\n```antlers\n{{ protect:password_form }}\n    {{ if no_token }}\n        No token has been provided.\n    {{ else }}\n\n        {{ if error }}\n            <div class=\"error\">{{ error }}</div>\n        {{ /if }}\n\n        <input type=\"password\" name=\"password\" />\n\n        {{ errors:password }}\n            <div class=\"inline-error\">{{ value }}</div>\n        {{ /errors:password }}\n\n        <button>Submit</button>\n\n    {{ /if }}\n{{ /protect:password_form }}\n```\n::tab blade\n\nOverride the view by creating `vendor/statamic/auth/protect/password.blade.php` in your `views` directory, and use the [protect:password_form](/tags/protect-password_form) tag to build a form. No config change required. For example:\n\n```blade\n<s:protect:password_form>\n  @if ($no_token)\n    No token has been provided.\n  @else\n    @if ($error)\n      <div class=\"error\">{{ $error }}</div>\n    @endif\n\n    <input type=\"password\" name=\"password\" />\n\n    @if (isset($errors['password']))\n      @foreach ($errors['password'] as $error)\n        <div class=\"inline-error\">{{ $error }}</div>\n      @endforeach\n    @endif\n\n    <button>Submit</button>\n  @endif\n</s:protect:password_form>\n```\n::\n\nThe `protect:password_form` tag is going to wrap everything between the tags in an HTML form tag pointing to the appropriate place.\n\nThe HTML of the form itself is up to you. The only requirement is to name the password input `password`. You can do anything else you want.\n\n#### Custom form URL\n\nIf you would like more control over the location of the password form, you may change `form_url` in the scheme's config:\n\n``` php\n'form_url' => '/password-entry'\n```\n\nYou can create a page or a route for this. In the corresponding view, you can build a form as described above.\n\n### Validation errors\nWhen a validation error is encountered, `error` and `errors` variables will be available to you.\n\nThe `error` variable will be a string with the first error, useful if you want to display an error at the top of your form.\n\nThe `errors` variable will be an array keyed by field names, each containing an array of messages. This is useful for _inline_ errors.\n\n### Token\nWhen visiting a password protected page, Statamic will generate a token and append it to the form’s URL. Without a token, the form cannot function correctly. In the example above, you can see the `no_token` boolean will be populated for you. This may happen if you visit the form URL directly.\n\n### Invalid passwords\nIf someone submits a password and it isn’t valid, Statamic will redirect back with the appropriate validation error. Valid passwords can vary from piece of content to piece of content. This one form is smart enough to handle all password management between password-protected URLs.\n\n### Valid passwords\nA valid password is anything matching one of the passwords in the allowed list as configured on the scheme. This means that you can send three people three different passwords to access the same URL, each having their own way in. Additionally, you could also set just one password and send that to 100 people and they can all use the same password.\n\nAs always with online security, be careful with who you share passwords with or you'll find yourself changing them often.\n\n:::warning\nThis protection method is meant for short-term access control. For example, showing a client your progress without the public or to prevent Google from indexing a staging site. It's about as secure as curtain over an open window: just good enough for passer-bys.\n:::\n\n### Password expiration\nEach user’s passwords will expire along with their session. To manually invalidate a password, remove it from the list of allowed passwords on the page. The next time a user with that password visits this page they’ll be redirected to the password form just like everyone else.\n\n\n## Endgame protection\n\nIf you want to protect a page from anyone - regardless of authentication status, IP address, time of day, weather, or beverage preference - you can simply add `protect: true` to the entry's front-matter.\n\nOne may find this useful to quickly disable something.\n\n\n## Site-wide protection\n\nTo protect your whole site at once, add a scheme name to `default` in your `protect.php` configuration file.\n\nFor example, to make sure your whole site is only accessible to a single IP address, you could add:\n\n``` php\n'default' => 'test',\n\n'schemes' => [\n    'test' => [\n        'driver' => 'ip_address',\n        'allowed' => ['127.0.0.1']\n    ]\n]\n```\n\n\n## Custom drivers\n\n### Writing the driver\n\nTo create your custom protection driver, you should extend the `Statamic\\Auth\\Protect\\Protector` class and add a `protect` method.\n\nThe protect method should typically:\n\n  - Call `abort(403)` to deny access.\n  - Call `abort(redirect($url))` to redirect somewhere (eg. how the auth driver redirects to a login page)\n  - Do nothing, which would allow access.\n\nHere's a silly example that will randomly allow or deny access:\n\n``` php\n<?php\n\nuse Statamic\\Auth\\Protect\\Protectors\\Protector;\n\nclass CoinFlip extends Protector\n{\n    public function protect()\n    {\n        $heads = (bool) random_int(0, 1);\n\n        abort_if($heads, 403);\n    }\n}\n```\n\nWithin this class, you have a number of properties available to you.\n\n``` php\n$this->scheme; // The name of the scheme.\n$this->config; // The configuration array of the scheme.\n$this->url;    // The URL the protection was triggered on.\n$this->data;   // The data object (eg. the Entry) being protected.\n```\n\n### Cacheable drivers\n\nBy default, any page using a protection scheme is excluded from the [static cache](/static-caching) — Statamic adds an `X-Statamic-Protected` header to the response which prevents caching. This is a safe default because the first visitor's view of a protected page would otherwise get served to everyone.\n\nIf your custom driver's protection logic doesn't depend on the visitor (for example: a scheme that only allows access during a specific date range, or only on certain environments), you can opt it into static caching by overriding the `cacheable` method and returning `true`.\n\n``` php\n<?php\n\nuse Statamic\\Auth\\Protect\\Protectors\\Protector;\n\nclass ComingSoon extends Protector\n{\n    public function protect()\n    {\n        abort_if(now()->lt('2026-01-01'), 404);\n    }\n\n    public function cacheable()\n    {\n        return true;\n    }\n}\n```\n\nWhen `cacheable()` returns `true`, the `X-Statamic-Protected` header will not be added and the page is eligible for the static cache.\n\n:::warning\nOnly mark a driver as cacheable when its `protect()` logic produces the same outcome for every visitor. User-specific, IP-specific, or password-based protection must not be cached.\n:::\n\n### Registering the driver\n\nInside a service provider's `boot` method, you can use the `extend` method on the protector manager class.\n\n``` php\nuse Statamic\\Auth\\Protect\\ProtectorManager;\n\napp(ProtectorManager::class)->extend('coin_flip', function ($app) {\n    return new CoinFlip;\n});\n```\n\nThe first argument passed to the `extend` method is the name of the driver. This will correspond to your `driver` option in the `protect.php` configuration file. The second argument is a Closure that should return an `Protector` instance. The Closure will be passed an $app instance, which is an instance of the service container.\n\nOnce your extension is registered, update your `protect.php` configuration file's `driver` option to the name of your extension.\n"
  },
  {
    "path": "content/collections/pages/publish-forms.md",
    "content": "---\ntitle: 'Publish Forms'\nintro: |\n  Build custom forms by harnessing the power of Blueprints and Fieldtypes.\nid: b4b46ceb-9feb-4587-8f0d-2080511bf9e3\n---\n\n## Overview\n\nWhen creating or editing content (entries, pages, etc), you are presented with a form view. This is what we call the \"Publish\" form. You're free to use these in your own addons or custom features.\n\nThe publish form flow looks like this:\n\n- Get a blueprint\n- Get some data\n- Blueprint performs some pre-processing on the data\n- Pass them both along to a Vue component\n- User hits save\n- Blueprint does some validation\n- Blueprint does some post-processing on the data\n- Do something with the data\n\nThe required components depends on the complexity of what you're building.\n\n- Very simple forms may not need any Vue or JavaScript at all, and could simply use the `PublishForm` class directly from your controller.\n- If you need JavaScript or Vue, the `PublishContainer` component can be paired with blueprint data to render an entire form.\n- The `PublishContainer` component can have its contents overridden if you need more control over the layout or behavior of the form.\n\n## Simple Forms\n\nYou can create a basic Publish Form without having to think about Vue or Blade. \n\nYou'll need a route and a controller. The controller needs to get the blueprint and its values, as well as store the updated values.\n\nFor example, if you wanted to create a Publish Form for an Eloquent model, the code might look like this:\n\n```php\nuse Statamic\\Facades\\Blueprint;\n\nclass Product extends Model\n{\n    public function values(): array\n    {\n        return [\n            'name' => $this->name,\n            'description' => $this->description,\n        ];       \n    }\n\n    public function blueprint()\n    {\n        return Blueprint::make(...);\n    }\n}\n```\n\n```php\nRoute::get('products/{product}', [ProductController::class, 'edit'])->name('product.edit');\nRoute::patch('products/{product}', [ProductController::class, 'update'])->name('product.update');\n```\n\n```php\nuse App\\Models\\Product;\nuse Illuminate\\Support\\Request;\nuse Statamic\\CP\\PublishForm;\n\nclass ProductController\n{\n    public function edit(Product $product)\n    {\n        return PublishForm::make($product->blueprint())\n            ->values($product->values())\n            ->submittingTo(cp_route('product.update', $product));\n    }\n\n    public function update(Request $request, Product $product)\n    {\n        $values = PublishForm::make($product->blueprint())->submit($request->all());\n        \n        $product->update($values);\n    }\n}\n```\n\nThe `PublishForm` class accepts various other methods:\n\n| Method                        | Description                                                       |\n|-------------------------------|-------------------------------------------------------------------|\n| `title($title)`               | Title of the publish form page.                                   |\n| `icon($icon)`                 | Icon to be shown in the header, next to the page title.           |\n| `values($values)`             | The publish form values.                                          |\n| `parent($parent)`             | Provides a \"parent\" object to the fieldtypes                      |\n| `readOnly()`                  | Marks the publish form as read-only.                              |\n| `asConfig()`                  | Marks it as a \"config\" form, which renders slightly differently.  |\n| `submittingTo($url, $method)` | Specify the submission URL and HTTP method (defaults to `PATCH`). |\n\n## Complex Forms\n\nFor more complex forms, you can use the underlying components to build out the functionality you need.\n\nYou'll need a route and controller on the backend, and a Vue component on the frontend responsible for holding the form's values and submitting them somewhere.\n\n### Preparing for the front-end\n\nFor example's sake, we'll be using the publish form to update Eloquent models (a `Product` model), much like a typical Laravel application.\n\n```php\nRoute::get('products/{product}', [ProductController::class, 'edit'])->name('product.edit');\nRoute::patch('products/{product}', [ProductController::class, 'update'])->name('product.update');\n```\n\n``` php\nuse App\\Models\\Product;\nuse Inertia\\Inertia;\n\npublic function edit(Product $product)\n{\n    // Get an array of values from the item that you want to be populated\n    // in the form. eg. ['title' => 'My Product', 'slug' => 'my-product']\n    $values = $product->toArray();\n\n    // Get a blueprint. This might come from an actual blueprint yaml file\n    // or even defined in this class. Read more about blueprints below.\n    $blueprint = $this->getBlueprint();\n\n    // Get a Fields object, a representation of the fields in a blueprint\n    // that factors in imported fieldsets, config overrides, etc.\n    $fields = $blueprint->fields();\n\n    // Add the values to the object. This will let you do things like\n    // validation, and processing, which is about to happen.\n    $fields = $fields->addValues($values);\n\n    // Pre-process the values. This will convert the raw values into values\n    // that the corresponding fieldtype vue components will be expecting.\n    $fields = $fields->preProcess();\n\n    // You'll probably prefer chaining all of that.\n    // $fields = $blueprint->fields()->addValues($values)->preProcess();\n    \n    // We're returning a Vue component here with Inertia. We're passing \n    // the blueprint, the values and the meta.\n    return Inertia::render('app::Products/Edit', [\n        'blueprint' => $blueprint->toPublishArray(),\n        'initialValues' => $fields->values(),\n        'initialMeta' => $fields->meta(),\n    ]);\n}\n```\n\n:::tip\nIf you haven't already, now is a good time to [set up JavaScript & Vite](https://v6.statamic.dev/control-panel/css-javascript) for the Control Panel.\n:::\n\n### The front-end\n\nStatamic provides a `PublishContainer` component, which is the workhorse of any publish form. Most of the time, you can use it self-closed with some props, and it will render exactly what you need.\n\n```vue\n<script setup>\nimport { Header, PublishContainer } from '@statamic/cms/ui';\n\nconst props = defineProps({\n    blueprint: Object,\n    initialValues: Object,\n    initialMeta: Object,\n});\n\nconst values = ref(props.initialValues);\nconst meta = ref(props.initialMeta);\n</script>\n\n<template>\n    <Header title=\"Edit Product\" />\n    \n    <PublishContainer\n        v-model=\"values\"\n        :blueprint=\"blueprint\"\n        :meta=\"meta\"\n    />\n</template>\n```\n\nThe Publish Container will render any tabs, sections and fields appropriately based on the provided `blueprint`.\n\nYou may customize the layout of the form by providing slot content.\n\n```html\n<PublishContainer>\n    <Tabs />\n    <!-- etc -->\n</PublishContainer>\n```\n\nPlease see our [UI Component docs](https://statamic.dev/?path=/docs/components-publishcontainer--docs&args=icon:hr) for full information on the available props and events.\n\n\n### Handling the form submission\n\nThe `SavePipeline` pairs with a `PublishContainer` to save your data, render any validation errors, fire hooks, etc. \n\nThe data from your Publish Container will be sent `through` the steps. The only required step is the `Request`.\n\nYou provide the pipeline class with a reference to the Publish Container, the saving state, and errors, and it will update them for you appropriately. \n\nYou may provide additional steps, such as the `AfterSaveHooks` here.\n\nOnce everything is done, the `then` callback will be run, like a promise. \n\nAny errors can be caught in the `catch` callback. If the pipeline is intentionally stopped, `e` will be an instance of `PipelineStopped`.\n\n```vue\n<script setup>\nimport { Header, PublishContainer } from '@statamic/cms/ui';\nimport { Pipeline, BeforeSaveHooks, Request, AfterSaveHooks } from '@statamic/cms/save-pipeline'; // [tl! add]\nimport { ref, useTemplateRef } from 'vue'; // [tl! add]\n\ndefineProps({\n\tblueprint: Object,\n\tinitialValues: Object,\n\tinitialMeta: Object,\n});\n\nconst values = ref(props.initialValues);\nconst meta = ref(props.initialMeta);\nconst saving = ref(false); // [tl! add:2]\nconst errors = ref({});\nconst container = useTemplateRef('container');\n\nfunction save() { // [tl! add:14]\n    new Pipeline()\n        .provide({ container, errors, saving })\n        .through([\n\t        new BeforeSaveHooks('product'),\n            new Request('/cp/products/{product}', 'PATCH'),\n            new AfterSaveHooks('product'),\n        ])\n        .then((response) => {\n            //\n        })\n        .catch((e) => {\n            //\n        });\n}\n</script>\n\n<template>\n\t<Header title=\"Edit Product\">\n\t    <Button text=\"Save\" variant=\"primary\" :disabled=\"saving\" @click=\"save\" /> <!-- [tl! add] -->\n\t</Header>\n\t<!-- [tl! add:2,1] [tl! add:6,1] -->\n\t<PublishContainer\n\t\tref=\"container\"\n\t\tv-model=\"values\"\n\t\t:blueprint=\"blueprint\"\n\t\t:meta=\"meta\"\n        :errors=\"errors\"\n\t/>\n</template>\n```\n\nIn your controller, you'll need to get the blueprint, validate the values and process them before updating your model.\n\n```php\nuse App\\Models\\Product;\nuse Illuminate\\Http\\Request;\n\npublic function update(Request $request, Product $product)\n{\n    $blueprint = $this->getBlueprint();\n\n    // Get a Fields object, and populate it with the submitted values.\n    $fields = $blueprint->fields()->addValues($request->all());\n\n    // Perform validation. Like Laravel's standard validation, if it fails,\n    // a 422 response will be sent back with all the validation errors.\n    $fields->validate();\n\n    // Perform post-processing. This will convert values the Vue components\n    // were using into values suitable for putting into storage.\n    $values = $fields->process()->values();\n\n    // Do something with the values. Here we'll update the product model.\n    $product->update($values);\n\n    // Return something if you want. But it's not even necessary.\n}\n```\n\nYou've just rendered an item in a Publish Form and handled updating it! Give yourself a pat on the back. 👏\n\n:::tip\nSince the values are being processed through the blueprint's fieldtypes, their values will be saved in such a way that you may need augmentation to use them.\n\nFor instance, the assets fieldtype will save an array of paths relative to the configured asset container, and when augmented will return an array of Asset objects. So, you may want to make sure that when you retrieve your data later, that it's [augmented](/extending/augmentation).\n:::\n\n## Blueprints\n\nIn the examples above, we just said \"get a blueprint\" but didn't tell you _how_ to get a blueprint. There's a couple ways to do it:\n\n### Get an actual user defined blueprint\n\nGet one from where all the blueprints are typically stored, by its handle. If it doesn't exist, it'll return `null`.\n\n``` php\nuse Statamic\\Facades\\Blueprint;\n\nBlueprint::find('example'); // resources/blueprints/example.yaml\n```\n\n### Create one on the fly\n\nIf you're wanting a blueprint just for sake of rendering this one specific form, you can create it in PHP. No YAML file necessary.\n\nUsing the `makeFromFields` method, you can pass in an array of fields using the fieldset syntax:\n\n``` php\nBlueprint::makeFromFields([\n    'title' => [\n        'type' => 'text',\n        'validate' => 'required',\n        'width' => 50,\n    ],\n    'handle' => [\n        'type' => 'text',\n        'validate' => 'required|alpha_dash',\n        'width' => 50,\n    ],\n]);\n```\n\nThis will give you a blueprint with a single section (no tabs or sidebar).\n\nIf you want to get fancy, you can `make` a Blueprint manually. The `setContents` method will expect an array in Blueprint syntax.\n\n``` php\nBlueprint::make()->setContents([\n    'sections' => [\n        'main' => ['fields' => [\n            ['handle' => 'title', 'field' => ['type' => 'text']],\n            ['handle' => 'content', 'field' => ['type' => 'markdown']],\n        ]],\n        'sidebar' => ['fields' => [\n            ['handle' => 'slug', 'field' => ['type' => 'slug']],\n        ]]\n    ]\n]);\n```\n"
  },
  {
    "path": "content/collections/pages/query-scopes-and-filters.md",
    "content": "---\ntitle: 'Query Scopes & Filters'\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569347415\nintro: Query scopes and filters allow you to narrow down query results using custom conditions.\nid: 290e9a74-7c6b-4fd0-a90a-23f7ac38d0c5\n---\nYou may create scopes that can be used in various places, such as inside the collection tag or inside control panel listings.\n\n## Scopes\n\nAny scope classes located within `app/Scopes` will be automatically registered.\n\nYou may create a scope class by running `php please make:scope YourScope`, which will give you a class with a few methods for you to implement, for example:\n\n``` php\n<?php\n\nnamespace App\\Scopes;\n\nuse Statamic\\Query\\Scopes\\Scope;\n\nclass Featured extends Scope\n{\n    public function apply($query, $values)\n    {\n        $query->where('featured', true);\n    }\n}\n```\n\nThe `apply` method will give you a query builder instance, allowing you to modify it how you see fit.\n\nIt will also give you `$values`, which will be an array of contextual values. For example, when [using the scope on a collection tag](/tags/collection#custom-query-scopes), you will get all the parameter values. When used as a [filter](#filters) inside the control panel, you will get all of your filter's field values.\n\nExample: Suppose a collection named \"portfolio\" and a dynamically obtained \"slug\" variable. The scope is called PorfolioScope. In your Antlers template:\n``` antlers\n{{ collection:portfolio query_scope=\"portfolio_scope\" slug=\"{portfolio}\" }}\n    {{ content }}\n{{ /collection:portfolio }}\n```\n\nIn `PortfolioScope`:\n``` php\npublic function apply($query, $values)\n{\n    $slug = $values['slug'];\n    $query->where('slug', $slug);\n}\n```\n\nThis will gives you the content of that page.\n\n\n### Using scopes programmatically\n\nIn order to use a scope as a query builder method, like \"local scopes\" in Eloquent, you have to register it on the respective query builder:\n\n```php\nuse Statamic\\Facades\\Entry;\n\npublic function boot()\n{\n    Entry::allowQueryScope(Featured::class);\n}\n```\n```php\nEntry::query()->where('this', 'that')->featured()->get();\n```\nHowever, unlike Eloquent's local scopes, Statamic's scopes accept an array of context. Make sure to pass an associative array rather than individual arguments:\n```php\n$query->featured([\n    'field' => 'value',\n    'foo' => 'bar',\n]);\n```\n\n\n\n## Filters\n\nFilters are UI based [scopes](#scopes) that will be displayed in listings inside the Control Panel.\n\nYou're able to configure any number of fields to a filter to allow your users to refine their listings.\n\nYou may create a filter class by running `php please make:filter YourScope`, which will give you a class with a few methods for you to implement, for example:\n\n``` php\n<?php\n\nnamespace App\\Scopes;\n\nuse Statamic\\Query\\Scopes\\Filter;\n\nclass Featured extends Filter\n{\n    public function fieldItems()\n    {\n        return [\n            'featured' => [\n                'type' => 'radio',\n                'options' => [\n                    'featured' => __('Featured'),\n                    'not_featured' => __('Not Featured'),\n                ]\n            ]\n        ];\n    }\n    \n    public function autoApply()\n    {\n        return [\n            'featured' => 'not_featured',\n        ];\n    }\n\n    public function apply($query, $values)\n    {\n        $query->where('featured', $values['featured'] === 'featured');\n    }\n\n    public function badge($values)\n    {\n        return $values['featured'] === 'featured'\n            ? __('is featured')\n            : __('not featured');\n    }\n\n    public function visibleTo($key)\n    {\n        return $key === 'entries' && $this->context['collection'] == 'blog';\n    }\n}\n```\n\nThe `fieldItems` method lets you define which filter fields will be displayed, just like a field inside a Blueprint.\n\nThe `apply` method works exactly as it would in a standard [scope](#scopes).\n\nThe `badge` method lets you define the badge text to be used when the filter is active on a listing.\n\nThe `visibleTo` method allows you to control in which listings this filter will be displayed. You will be given a key that represents the type of listing. For example, an author filter might be appropriate for the `entries` listing but not `users`. You may also be given an array of contextual data which will vary depending on the listing. For instance, for `entries`, the current collection name can be accessed with `$this->context['collection']`.\n\nThe `autoApply` method lets you define a default value to apply.\n\nYou may also pin your filters to the filters bar by setting the `$pinned` class property:\n\n```php\npublic $pinned = true;\n```\n"
  },
  {
    "path": "content/collections/pages/quick-start-guide.md",
    "content": "---\ntitle: 'Quick start guide'\nintro: \"A step-by-step guide to installing and building your first Statamic site.\"\nvideo: https://www.youtube.com/playlist?list=PLVZTm2PNrzMwYLGotkQvTvjsXAkANJIkc\nid: 1d1920fb-604c-4ac1-8c99-f0de44abc06b\n---\n## Overview\n\nMuch of the documentation is intended to be used as a reference sheet for various features, explaining how they work and what options and settings they provide. But not this guide. This is for gluing it all together, assuming you know very little about how Statamic works. We'll only make a couple of assumptions here before we get started.\n\n1. You are comfortable working with HTML.\n2. You have a local dev environment with [composer](https://getcomposer.org/) installed.\n3. You can copy and paste a few commands into the command line.\n4. You have more than 5 minutes to spare. Let's enjoy ourselves here.\n\n### What we're building\n\nWe're going to build a simple personal website for a fictitious young aspiring programmer named Kurt Logan. Kurt always has and always will live in the 1980s and is very excited at the prospect of having his very own place in <span class=\"c-spaced\">Cyberspace</span>.\n\n**This is not a \"5 minute quick install guide\" – we're going to be building a simple yet full site from scratch so you can see how everything comes together. It will likely take around 20-30 minutes.**\n\n## High level approach\n\nA high level approach to building a site in Statamic often looks like this.\n\n1. Start with a static HTML site or series of different layouts\n2. Break static files up into the appropriate [views](/antlers) (layouts, templates, and partials)\n3. Create applicable [collections](/collections) to hold content and set up [routes](/routing) to determine your URL patterns\n4. Stub out top level pages and map them to the proper templates\n5. Configure [blueprints](/blueprints) to hold fields that match your HTML (like title, author, date, content) and move static content out of your markup and into entries using the beautiful UI\n6. Keep going until your site is done\n\nOnce familiar with Statamic, many developers begin building their static site right in Statamic, often blending all the steps into a smooth flowing river of productivity.\n\n<!-- ## Prerequisites\n\nWe want this quick start guide to be just that — quick. Rather than stop after each of the first steps to explain development environment stuff, we recommend you follow the [Getting Your Development Environment Up & Running](#) guide first to make sure you're ready to run Statamic on your machine. -->\n\n## Install Statamic\n\nLet's start right at the very beginning. Installing Statamic.\n\nThere are a [few ways to do it](/installing), but we recommend using our CLI installer. So let's get that installed to your local machine.\n\n``` shell\ncomposer global require statamic/cli\n```\n\nNow you can run the `statamic new` command wherever you prefer to keep your site projects (we use `~/Sites` but you do you) to get a fresh site up and running.\n\n``` shell\ncd ~/Sites && statamic new cyberspace-place\n```\n\nYou'll be asked a couple of questions, like whether you want to install a blank site or a [Starter Kit](/starter-kits) (to keep it really simple, start with a blank site), create your first user, or initialize a Git repository.\n\nOnce the installer is done and if everything worked as expected, you should be able to visit [http://cyberspace-place.test](http://cyberspace-place.test) and see the Statamic welcome screen.\n\nIf you encounter any errors, Google them frantically and try anything and everything suggested until it magically begins working.\n\n**Just kidding**, that's a terrible idea. Please don't do that. You should check our [troubleshooting](/troubleshooting) guide and [GitHub discussions](https://github.com/statamic/cms/discussions) to look for a validated solution before resorting to such measures. We try our best to have answers to all the most common things you might encounter. Modern web development is amazing when everything is up to date, and can be pretty frustrating when it isn't. We feel this pain too.\n\n<figure>\n    <img src=\"/img/quick-start/installed-6.webp\" alt=\"Statamic Welcome Screen\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/installed-6-dark.webp\" balt=\"Statamic Welcome Screen\" class=\"u-hide-in-light-mode\">\n    <figcaption>If you see this you are right on track.</figcaption>\n</figure>\n\nNext, in your command line navigate into the new site (`cd cyberspace-place`) and open the project directory in your code editor. We like [VS Code](https://code.visualstudio.com/) but there are a ton of great editors and IDEs out there.\n\n## Signing Into the Control Panel\n\nAs part of the install process, you should have created a super user account, but if you said no by accident, we've got your back.\n\nAt any time you can run `php please make:user` from the command line and follow along with the prompts (name, email, etc). For the purpose of this walkthrough, be sure to say `yes` when asked if the user should be a **super user** otherwise you'll just have to do it again. And again. And again until you finally say `yes`. Never be afraid of committing to success.\n\n<figure>\n    <img src=\"/img/quick-start/make-user.webp\" alt=\"Statamic Make:User Command\">\n    <figcaption>You can customize user fields later.</figcaption>\n</figure>\n\nNow you can sign in. Head to [http://cyberspace-place.test/cp](http://cyberspace-place.test/cp) and use your email address and password to sign into the control panel.\n\n<figure>\n    <img src=\"/img/quick-start/login.webp\" alt=\"Statamic Login Screen\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/login-dark.webp\" alt=\"Statamic Login Screen\" class=\"u-hide-in-light-mode\">\n    <figcaption>If you see this screen at <code>/cp</code> you've just earned 200 XP!</figcaption>\n</figure>\n\n## Make a home page\n\nNext, let's get some content of _our_ choosing to show on the homepage. Head to `Collections → Pages` in the control panel, and you'll see an empty home page entry waiting for you. Click on the entry's title to edit it. Type anything you want in the `content` field and then click **Save & Publish**.\n\n<figure>\n    <img src=\"/img/quick-start/editing-home.webp\" alt=\"Editing the home page\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/editing-home-dark.webp\" alt=\"Editing the home page\" class=\"u-hide-in-light-mode\">\n    <figcaption>Don't overthink it. Just type some aedgaeduhadfubugra</figcaption>\n</figure>\n\nNote that the entry is using the `home` template (you can see it there in the `template` field). Let's edit it and reveal your new and incredible content to the browser.\n\nIn your code editor, open the file `resources/views/home.antlers.html`. This is the home template. The \"name\" of a template is the filename _up until the file extension_. Any view ending in `.antlers.html` will be parsed with Statamic's [Antlers](/antlers) template parser.\n\n:::tip\nIf a view file ends with `.blade.php` it will use Laravel's [Blade templates](/blade). This same pattern applies for any other template engine you may wish to install in the future, like Twig or something that hasn't been invented yet.\n:::\n\nDelete all the placeholder HTML from the template and replace it with the following:\n\n```\n{{ content }}\n```\n\nRefresh the site in your browser and you should see your content in all of its glory. Each of those double curly tags is a **variable**. When on a URL that matches an entry's route rule, all of that entry's field data is available automatically in the defined template. We'll get into adding new fields in just a bit.\n\n<figure>\n    <img src=\"/img/quick-start/new-home.webp\" alt=\"Your new home page\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/new-home-dark.webp\" alt=\"Your new home page\" class=\"u-hide-in-light-mode\">\n    <figcaption>What did you write? Was it a dad joke?</figcaption>\n</figure>\n\n## Customize the Layout\n\nYou probably noticed that there is some _very_ basic styling going on. That's coming from the **layout**. Time to customize that too. Open `resources/views/layout.antlers.html` and replace it with this:\n\n```\n<!doctype html>\n<html>\n<head>\n    <title>{{ title }}</title>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <script src=\"https://cdn.tailwindcss.com\"></script>\n</head>\n<body class=\"bg-gray-900 text-white text-lg font-mono\">\n    <div class=\"container max-w-lg mx-auto py-8\">\n        {{ template_content }}\n    </div>\n</body>\n</html>\n\n```\n\nYour layout file contains any markup you want present no matter what page you’re on. It's usually the best place to put your `<head>` meta markup, persistent site navigation, site footer, and other global things.\n\nThink of layouts like a **picture frame**, and everything that changes from section to section, page to page _inside_ the frame — goes into templates. In practice, templates are injected inline wherever you put a `{{ template_content }}` variable in your layout to create a complete HTML document.\n\n<figure>\n    <img src=\"/img/quick-start/new-layout.webp\" alt=\"Your new layout\">\n    <figcaption>If copy & pasted properly you should see this 👆</figcaption>\n</figure>\n\n## Now let's build a blog\n\nYou might have known it was coming next – it's the staple of every CMS walkthrough. How easy is it to build a blog? You're about to find out.\n\nBut first, let's talk about what a blog is. A \"blog\" is a collection of posts that shares common traits or attributes. A typical blog post might contain a title, featured image, an author, a few tags, and the article content.\n\nThere is always a list (sometimes called an \"archive\") of blog posts linking to each post's unique URL, and sometimes the homepage has a short list of the most recent posts as well. Let's detail exactly what we're going to build, and then build it.\n\nHere's our todo list:\n\n- Create a blog \"Collection\" with the following fields: `title` , `featured_image` , `author` , and `content`\n- Create a blog index page (`/blog`)\n- Create a blog detail page (`/blog/why-i-love-mustard`)\n- Add a list of the most recent 5 blog entries to the homepage\n\n### Create a new collection\n\nHead back to the Control Panel and click on the Collections link in the sidebar. Click the blue **Create Collection** button and then call your new collection \"Blog\".\n\n<figure>\n    <img src=\"/img/quick-start/create-collection-v6.webp\" alt=\"Creating a blog collection\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/create-collection-v6-dark.webp\" alt=\"Creating a blog collection\" class=\"u-hide-in-light-mode\">\n    <figcaption>Name it whatever you want, as long as you name it Blog.</figcaption>\n</figure>\n\n## Scaffold your views\n\nLet's save you a minute or two and generate the index and show template. Click on **Scaffold Views**\n\n<figure>\n    <img src=\"/img/quick-start/scaffold-views-link-v6.webp\" alt=\"Link to Scaffold Views\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/scaffold-views-link-v6-dark.webp\" alt=\"Link to Scaffold Views\" class=\"u-hide-in-light-mode\">\n    <figcaption>Click it.</figcaption>\n</figure>\n\nAnd then click the Create Views button. The defaults are perfect.\n\n<figure>\n    <img src=\"/img/quick-start/scaffold-views-v6.webp\" alt=\"Scaffold collection views\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/scaffold-views-v6-dark.webp\" alt=\"Scaffold collection views\" class=\"u-hide-in-light-mode\">\n    <figcaption>Click the button.</figcaption>\n</figure>\n\nTwo new files will be created. We'll be editing them soon:\n\n- `resources/views/blog/index.antlers.html`\n- `resources/views/blog/show.antlers.html`\n\n## Configure the collection\n\nNext, let's configure the collection to behave the way a typical blog should. Click **Configure Collection**.\n\n<figure>\n    <img src=\"/img/quick-start/configure-collection-link-v6.webp\" alt=\"Link to configure your collection\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/configure-collection-link-v6-dark.webp\" alt=\"Link to configure your collection\" class=\"u-hide-in-light-mode\">\n    <figcaption>And now click this.</figcaption>\n</figure>\n\n\n:::tip\nStatamic does its best to take a \"start simple and add things as needed\" approach to features and settings, in contrast to other platforms that take a \"everything is included and rip out what you don't want\" approach.\n\nThis means that Statamic doesn't do everything right out the box, but is much simpler to customize how you want everything to work.\n:::\n\nWe'll review some of the important settings, but we only need to touch two of them to make a blog:\n\n- Enable Publish Dates (the subs-setting defaults are perfect)\n- Set your route rule\n\n<figure>\n    <img src=\"/img/quick-start/blog-settings-v6.webp\" alt=\"Settings to make a blog\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/blog-settings-v6-dark.webp\" alt=\"Settings to make a blog\" class=\"u-hide-in-light-mode\">\n    <figcaption>These are the only two you need to set.</figcaption>\n</figure>\n\nBy enabling **Publish Dates**, Statamic will add a date field to your list of available entry fields (called a Blueprint), and will use the specified date to determine whether a given entry should be visible or not. Typical blog posts with a date in the future would be a _scheduled_ post and not yet published, and one in the past is published, and therefore visible. This is how we'll configure our Blog Collection, and is the default behavior when you enable this feature.\n\nAs you scroll you'll notice a **Content Model** section. That template you scaffolded in the previous step is automatically selected as the default template for new Blog entries.\n\nAnd finally in the **Routing & URLs** section you'll find the **Route** setting. Here you can create the URL pattern that all of your entries will follow. You can change this anytime and use any of the Collection's fields as variables in the pattern by surrounding them in single braces, `{like_this}`.\n\nHere are some common patterns you could choose from:\n\n| Example URL | Route Pattern Rule |\n|-----------------------------------|-----------------------------------|\n|`/blog/2021-12-24/merry-christmas` | `/blog/{year}-{month}-{day}/{slug}` |\n|`/blog/2020/still-bored` | `/blog/{year}/{slug}` |\n|`/blog/happy-new-year` | `/blog/{slug}` |\n| `/evergreen-syle` | `/{slug}` |\n\n:::tip\nCheck out the full list of [available variables](/collections#meta-variables). Try saying \"available variables\" 3x fast. It's not the _best_ tongue-twister, but it does qualify.\n:::\n\nWhen in doubt, keep it simple. And then save your changes.\n\n## Creating your first entry\n\nWe like to make things work and then make them better. With that in mind, let's make our first blog post and get it to show on the frontend before we configure all the custom fields and whatnot.\n\nHead back to your blog Collection screen and click **Create Entry**.\n\n<figure>\n    <img src=\"/img/quick-start/create-entry-link-v6.webp\" alt=\"Link to create your first blog entry\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/create-entry-link-v6-dark.webp\" alt=\"Link to create your first blog entry\" class=\"u-hide-in-light-mode\">\n    <figcaption>And finally, click this.</figcaption>\n</figure>\n\nNow you can see all the default fields for your new Collection. They're the same as the Home entry you edited a few moments ago. Go ahead and make a new blog post. Make two if you'd like! It's up to you.\n\n| Field | Notes |\n|-----------------------------------|-----------------------------------|\n| **Title** | The required title of the entry |\n| **Content** | A simple [Markdown](/fieldtypes/markdown) field |\n| **Author** | Defaults to whoever is logged in |\n| **Template** | When not _explicitly set_ will use the Collection's default |\n| **Slug** | Automatically generated off the title until you edit it manually |\n| **Date** | Defaults to today |\n\n## Time for more frontend\n\nIt's code editor time! Let's get that list of the 5 most recent entries onto the homepage since it already exists and is one of our todos. Open `resources/views/home.antlers.html` and replace that lonely `{{ content }}` with this markup (don't worry, we'll explain what's going on in a moment):\n\n```\n// resources/views/home.antlers.html\n\n<h1 class=\"text-2xl font-bold my-6\">Welcome to my CyberSpace Place!</h1>\n{{ content }}\n\n<section class=\"border border-green-400 mt-12\">\n    <h2 class=\"p-5\">Recent Blog Posts</h2>\n    {{ collection:blog limit=\"5\" }}\n        <a href=\"{{ url }}\" class=\"flex items-center justify-between p-5 border-t border-green-400 text-green-400 hover:text-green-900 hover:bg-green-400\">\n            <span>{{ title }}</span>\n            <span class=\"text-green-900 text-sm\">{{ date }}</span>\n        </a>\n    {{ /collection:blog }}\n</section>\n```\n\nIf you refresh your homepage (and managed to name your placeholder entry or two the same as us), you should see this:\n\n<figure>\n    <img src=\"/img/quick-start/new-homepage-v6.webp\" alt=\"Link to create your first blog entry\">\n    <figcaption>We said it would look ugly, but we lied.</figcaption>\n</figure>\n\nLet's take a closer look at how this works. Stripping out all the styling in the example, here's the most basic [Antlers](/antlers) template snippet that fetches your entries.\n\n```\n{{ collection:blog limit=\"5\" }}\n    <a href=\"{{ url }}\">{{ title }}</a>\n{{ /collection:blog }}\n```\n\nHere you can see we're telling the [Collection Tag](/tags/collection) tag to use the `blog` collection and limit the number of returned entries to 5. Inside the tag pair is a loop that iterates over each entry with access to all the data available as `{{ variables }}`.\n\nThe `url` will follow the pattern you set in the route rule (`/blog/hello-from-cyberspace` perhaps?) and if you were to click it, you'd see a new page using the `resources/views/blog/show.antlers.html` template, which is empty so there's not much to look at. Let's edit that next.\n\n## The blog \"show\" template\n\nNow that we're on an entry's very own unique URL, you no longer need that `{{ collection:blog }}` tag pair to fetch data. All of the entry's data is available automatically. Here's a really simple snippet you can drop in so you can see the data pull through.\n\n```\n// resources/views/blog/show.antlers.html\n\n<h1 class=\"text-3xl bg-green-400 text-center text-green-900 font-bold mt-6 p-6\">{{ title }}</h1>\n<div class=\"border text-center text-green-600 border-green-400 mt-8 p-3 text-xs uppercase\">\n    Published on {{ date }} by {{ author:name }}\n</div>\n\n<article class=\"space-y-4 mt-8 text-sm text-green-400 leading-loose\">\n    {{ content }}\n</article>\n```\n\nA few cool things to note here in this code example:\n\n- The author's `name` is being accessed by reaching into the `{{ author }}` object. You can retrieve any data (but not password) on a user this way. Pretty cool.\n- The `content` field is being automatically converted from Markdown to HTML because we're using a [Markdown](/fieldtypes/markdown) field. If you were to use a generic [Textarea](/fieldtypes/textarea) field, you'd need to transform the Markdown yourself by using a [modifier](/modifiers). It would look like this: `{{ textarea | markdown }}`.\n\n<figure>\n    <img src=\"/img/quick-start/blog-show-v6.webp\" alt=\"A blog post\">\n    <figcaption>How close does yours look?</figcaption>\n</figure>\n\n## Blog index\n\nNext, let's make that blog index page. Head back to the control panel and go to the **Pages** collection. Create a new entry and call it \"Blog\", \"My Blog\", or even \"My CyberBlog\" — just make sure the slug is `blog`. Set the template to `blog/index`.\n\nBack to your code editor — open up the `resources/views/blog/index.antlers.html` template and drop in this snippet. It's essentially what we built on the home page, but without the limit.\n\n```\n// resources/views/blog/index.antlers.html\n\n<h1 class=\"text-2xl font-bold my-6\">{{ title }}</h1>\n{{ content }}\n\n<section class=\"border border-green-400 mt-12\">\n{{ collection:blog }}\n    <a href=\"{{ url }}\" class=\"flex items-center justify-between p-5 border-t border-green-400 text-green-400 hover:text-green-900 hover:bg-green-400\">\n        <span>{{ title }}</span>\n        <span class=\"text-green-900 text-sm\">{{ date }}</span>\n    </a>\n{{ /collection:blog }}\n</section>\n```\n\nAnd stop right there. We've now duplicated a whole chunk of code for one tiny little bit — `limit=\"5'`. Let's DRY this up (reduce code duplication).\n\n:::tip\nIt's totally fine to duplicate code sometimes, especially if you have to make some code significantly more complex to reuse it. Just keep that in mind. We'll keep this simple.\n:::\n\n## Your first partial\n\nPartials are reusable template chunks. Create a new file named `_listing.antlers.html` in the `resources/views/blog/` directory. Prefixing a template with an underscore is a common convention to indicate that it's a reusable partial and not a full layout. You could also create a subdirectory named `partials` — it's up to you. Just be consistent.\n\nInside that new template file, copy and paste the entire `<section>` chunk that includes the Collection tag pair from either the homepage, the blog index, or this guide. We can create a variable on the fly here so you can pass a desired limit into your partial. Replace that second line with this:\n\n```\n{{ collection:blog :limit=\"limit\" }}\n```\n\nPrefixing the `limit` parameter with a colon tells Statamic to look for a variable named \"limit\" as the argument. If there isn't one it will be `null`, which will not set a limit which is how we want it on the blog index template.\n\nYour blog index template can now look like as simple as this:\n\n```\n// resources/views/blog/index.antlers.html\n\n<h1 class=\"text-2xl font-bold my-6\">{{ title }}</h1>\n{{ content }}\n{{ partial:blog/listing }}\n```\n\nNow let's dry up the home template. We know we need to pass that limit in, but if you recall (or visit the homepage), we had that extra `<h2>` above the `collection:blog` tag. This is a perfect opportunity to add a \"slot\".\n\nSwitch to your new `blog/listing` partial and add `{{ slot }}` to the line right above the collection tag, like so:\n\n```\n// resources/views/blog/_listing.antlers.html\n\n<section class=\"border border-green-400 mt-12\">\n    {{ slot }}\n    {{ collection:blog :limit=\"limit\" }}\n...\n```\n\nBack in your `home` template, you can now replace that chunk of markup with a call to the partial, setting the limit, and using it as a tag pair to send the contents in as the `slot`. A super helpful little pattern.\n\nHere's your entire home template:\n\n```\n// resources/views/home.antlers.html\n\n<h1 class=\"text-2xl font-bold my-6\">Welcome to my CyberSpace Place!</h1>\n{{ content }}\n{{ partial:blog/listing limit=\"5\" }}\n    <h2 class=\"p-5\">Recent Blog Posts</h2>\n{{ /partial:blog/listing }}\n```\n\n## The nav\n\nWe're almost done, but before we head back to the control panel to add a few more fields to your blog blueprint, let's add a nav.\n\nYour `home` and `blog` entries are both in an \"ordered\" Pages collection. If you look at this default collection's config you'll see that it has the **Orderable** setting on and that the root page is considered the home page. This lets you have a page with a slug of `/`.\n\nWe can use the [Nav tag](/tags/nav) to fetch the entries in the Pages collection in the order you have them arranged.\n\nOpen up your layout file and drop in this nav snippet, right after the open body tag.\n\n```\n// resources/views/layout.antlers.html\n// ...\n\n<nav class=\"bg-black text-xs uppercase text-green-500 text-center flex items-center justify-center space-x-4\">\n    {{ nav:collection:pages include_home=\"true\" }}\n        <a href=\"{{ url }}\" class=\"p-2 block hover:text-yellow-200\">{{ title }}</a>\n    {{ /nav:collection:pages  }}\n</nav>\n```\n\nThe nav tag works very much like the `collections` tag. It loops through the entries and gives you access to all the data inside each.\n\n## Customizing your blueprint\n\nWe've got a pretty functional site going here, but so far we've only worked with default fields. Few sites can be so simple, so let's spice it up a bit.\n\nHead to the **Blueprints** area in the sidebar and click **Blog**. Now you're looking at all the fields you've been working with, organized into Tab Sections.\n\nTab Sections let you group fields into Tabs which can help you stay organized, keep similar fields together, or help push optional, unusual fields out of mind for most authors. It's up to you how you'd like to organize these.\n\n<figure>\n    <img src=\"/img/quick-start/blueprint-sections-v6.webp\" alt=\"A Blueprint and its default fields\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/blueprint-sections-v6-dark.webp\" alt=\"A Blueprint and its default fields\" class=\"u-hide-in-light-mode\">\n    <figcaption>This is content modeling right here.</figcaption>\n</figure>\n\nYou can drag, drop, and rearrange fields inside and across your sections. This order will be how you see the fields in the publish screen.\n\n:::tip\n**The Sidebar** is a special section. It controls the fields shown in the publish sidebar when your browser is wide enough, and collapses those fields to a tab when it isn't. If you delete the Sidebar section, you won't have one — and if you create a new one called \"Sidebar\", it'll work just as before.\n:::\n\nLet's create a new field called `featured_image`.\n\nClick **Create Field** in the **Main** section and behold! A big list of fieldtypes! You can learn more about [each Fieldtype](/fieldtypes) elsewhere in the docs, but here are a few quick tips on narrowing down what you're looking for.\n\nWhen this screen is opened, you're automatically focused in the search box, so you can start typing the fieldtype name if you know it (Hint: you could type `assets` now). Or, you can narrow the fields down by type – All, Text, Media, and Relationship. You'd find the Assets fieldtype inside Media.\n\n<figure>\n    <img src=\"/img/quick-start/fieldtypes-v6.webp\" alt=\"A list of Statamic's fieldtypes\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/fieldtypes-v6-dark.webp\" alt=\"A list of Statamic's fieldtypes\" class=\"u-hide-in-light-mode\">\n    <figcaption>Over 40 different types to pick from!</figcaption>\n</figure>\n\nFind the **Assets** fieldtype and click it. Assets fields let you pick from and upload new files.\n\nNext, give the field the `Display` name \"Featured Image\" and you'll see the `Handle` get slugified automatically to `featured_image`. This will be the variable name you will use in your templates to get the asset's data. The only additional setting you should tweak for now is to set `Max Files` to `1`. When you're done, click **Finish**.\n\n<figure>\n    <img src=\"/img/quick-start/fieldtype-config-v6.webp\" alt=\"Configuring an Assets fieldtype\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/fieldtype-config-v6-dark.webp\" alt=\"Configuring an Assets fieldtype\" class=\"u-hide-in-light-mode\">\n    <figcaption>Every fieldtype has shared & unique options.</figcaption>\n</figure>\n\nHead back to your Blog collection and edit an entry (or create a new one if you'd like). You'll see your new field right there. Upload any image you have on your computer. If you need a dummy image, we recommend Google Image Searching for \"rad 90s aesthetic\". That's a gold mine right there.\n\nHover over the thumbnail for your new image and click the Edit button (it looks like a pencil). There you can make a few adjustments to the image – like setting an Alt tag.\n\n<figure>\n    <img src=\"/img/quick-start/asset-editor-v6.webp\" alt=\"Adding an Alt tag to an image\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/asset-editor-v6-dark.webp\" alt=\"Adding an Alt tag to an image\" class=\"u-hide-in-light-mode\">\n    <figcaption>Seeeegaaaahh!</figcaption>\n</figure>\n\n:::tip\nAssets can have Blueprints too!\n:::\n\nWhen you're done, **Save & Publish** your changes.\n\n## Wiring up the new field\n\nHead back to `resources/views/blog/show.antlers.html` in your code editor. Add the following snippet anywhere you'd like in the template. Either before or after the `{{ content }}` variable is probably a good place.\n\n```\n// resources/views/blog/show.antlers.html\n// ...\n\n<img src=\"{{ featured_image }}\" class=\"border-2 border-green-400 p-1\" alt=\"{{ featured_image:alt }}\" />\n```\n\nRefresh the page and there you have it — a basic but fully functional website. Hopefully you'll have a better idea how the basics fit together, as well as the relationship between the control panel and the frontend. There are so many more things you can do – like add [Taxonomies](/taxonomies), [Forms](/forms), [dynamic image manipulations](/tags/glide), fetch data with JavaScript with our [Content API](/rest-api) and on and on.\n\nAnd make sure to not miss the list of [Tags](/tags) and [Modifiers](/modifiers) that do all sorts of powerful things in your templates.\n\n## Going deeper\n\nWe have a screencast series that covers getting started but goes much further and deeper. Feel free to [check that out here](https://www.youtube.com/playlist?list=PLVZTm2PNrzMwYLGotkQvTvjsXAkANJIkc). Good luck!\n"
  },
  {
    "path": "content/collections/pages/relationship-fieldtypes.md",
    "content": "---\ntitle: 'Relationship Fieldtypes'\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569347303\nintro: The Relationship fieldtype is one of the more powerful fields in Statamic's core. So powerful, in fact, that it earns its very own page in the docs. This is that page.\nid: 06813e5d-158e-4318-aa4a-b29fd87d107f\n---\nBy default, the relationship fieldtype lets you select entries from various collections as well as create and edit items on the fly from _within_ the field.\n\nYou can create your own relationship fields that provide the ability to select all different sorts of items from anywhere.\n\n## Example\n\nTo illustrate that you can get items from anywhere — even remote APIs — we'll build a field where you can select GitHub repositories for a given user.\n\nIn your blueprints, you'll be able to use `type: repos` (whatever you name your fieldtype) and all the options that the relationship field would normally give you, like `max_items`:\n\n``` yaml\nfields:\n  handle: repos\n  field:\n    type: repos\n    max_items: 3\n```\n\n## Creating the Fieldtype\n\nYou will need to create the fieldtype – no Vue component necessary – so you can skip it with the <nobr>`--php`</nobr> flag:\n\n``` shell\nphp please make:fieldtype Repos --php\n```\n\nThen instead of extending `Fieldtype`, you'll extend the existing `Relationship` fieldtype:\n\n``` php\nuse Statamic\\Fieldtypes\\Relationship;\n\nclass Repos extends Relationship\n{\n    //\n}\n```\n\nThere are a handful of methods and properties inside the `Relationship` class, and you can override them to control how it functions.\n\nThere are three main areas you will want to customize. The index items, the selected item data, and the listing data.\n\n## Index Items\n\nThe index items are what you'll see in the item selector stack.\n\nYou can either override the `getIndexQuery` method if you're dealing with items being retrieved through the Statamic API. You'll need to return a QueryBuilder.\n\n``` php\npublic function getIndexQuery($request)\n{\n    return Entry::query()->whereIn('collection', $request->collections);\n}\n```\n\nOr, you can override `getIndexItems` for full control. We'll use this for our GitHub example.\n\n``` php\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Http;\n\npublic function getIndexItems($request)\n{\n    $repos = Http::github()\n        ->get(\"/users/{$this->config('username')}/repos\")\n        ->json();\n\n    return $this->formatRepos($repos);\n}\n\nprotected function formatRepos($repos)\n{\n    return collect($repos)->map(function ($repo) {\n        $updated = Carbon::parse($repo['updated_at']);\n\n        return [\n            'id'               => $repo['id'],\n            'name'             => $repo['name'],\n            'description'      => $repo['description'],\n            'stars'            => $repo['stargazers_count'],\n            'updated'          => $updated->timestamp,\n            'updated_relative' => $updated->diffForHumans(),\n            'owner'            => $repo['owner']['login'],\n        ];\n    });\n}\n```\n\nYou can customize which columns will be used in the selector by overriding the `getColumns` method:\n\n``` php\nuse Statamic\\CP\\Column;\n\nprotected function getColumns()\n{\n    return [\n        Column::make('name'),\n        Column::make('description'),\n        Column::make('stars'),\n        Column::make('updated')->value('updated_relative'),\n    ];\n}\n```\n\n## Selected Item Data\n\nOnce you select items, their `id` values will be used as the value for your field. If you were to hit save, you would see\nsomething like this in your content files:\n\n``` yaml\nrepos:\n  - 54376134\n  - 89473529\n```\n\nIn order to convert those values into something useful, you'll either need to override the `getItemData` method or the `toItemArray` method. For our example, we'll use the former:\n\n``` php\npublic function getItemData($values)\n{\n    $repos = collect($values)->map(fn ($id) => Http::github()->get(\"/repositories/{$id}\")->json());\n\n    return $this->formatRepos($repos);\n}\n```\n\n### Listing Data\n\nWhen field data is to be displayed in a listing view (eg. in the entries listing table or the entry fieldtype), you may customize the display by overwriting the `preProcessIndex` method.\n\nIn our GitHub field, let's show only the repo names:\n\n```php\npublic function preProcessIndex($data)\n{\n    return collect($data)\n        ->map(fn ($id) => Http::github()->get(\"/repositories/{$id}\")->json('name'))\n        ->join(', ');\n}\n```\n\n## Hints\n\nHints are short bits of secondary text shown next to selected items and dropdown options. They're useful when multiple items could share the same title and you need a way to disambiguate them — like an entry titled \"About\" that could exist in several collections.\n\nOverride the `getItemHint` method to return a string (or `null` for no hint):\n\n``` php\npublic function getItemHint($item): ?string\n{\n    return $item['owner'];\n}\n```\n\nThe built-in `Entries` and `Terms` fieldtypes use this to show the collection or taxonomy title when more than one is configured.\n\n## Creating Items\n\nTo disable creation of items, you can add the canCreate property.\n\n``` php\nprotected $canCreate = false;\n```\n\n\n## Searching\n\nBy default, the search bar will be visible in the selector stack. When a user types into it, its value will be submitted in the `search` query parameter. You can tweak your logic to account for searching in your `getIndexItems` method. For example:\n\n``` php\npublic function getIndexItems($request)\n{\n    return $request->search\n        ? $this->searchRepos($request->search)\n        : $this->userRepos();\n}\n```\n\nTo disable searching, you can add the canSearch property.\n\n``` php\nprotected $canSearch = false;\n```\n\n\n## Customizing the view\n\nBy default, the fieldtype will show the standard draggable block, with the `title` as the text. You may provide your\nown Vue component to the `itemComponent` property to replace it.\n\n``` php\nprotected $itemComponent = 'GithubRepoRelationshipItem';\n```\n\n``` js\nimport GithubRepoRelationshipItem from './GithubRepoRelationshipItem.vue';\n\nStatamic.$components.register('GithubRepoRelationshipItem', GithubRepoRelationshipItem);\n```\n\n``` vue\n<script setup>\ndefineProps({\n    item: Object,\n});\n</script>\n\n<template>\n    <div class=\"mb-1 item\">\n        <div class=\"item-move\">&nbsp;</div>\n        <div class=\"item-inner\">\n            <div class=\"p-3\">\n                <p class=\"mb-2 text-lg\">{{ item.name }}</p>\n                <p class=\"text-grey\">{{ item.owner }} – ★ {{ item.stars }} –  {{ item.updated_relative }}</p>\n            </div>\n        </div>\n        <dropdown-list class=\"pr-1\">\n            <ul class=\"dropdown-menu\">\n                <li class=\"warning\"><a @click.prevent=\"$emit('removed')\" v-text=\"__('Unlink')\"></a></li>\n            </ul>\n        </dropdown-list>\n    </div>\n</template>\n```\n\nAn `item` prop will be passed to your component which will contain one the objects provided by the `getItemData` method.\n\nIn order to allow your users to remove their selection, you should emit a `removed` event, as shown above.\n"
  },
  {
    "path": "content/collections/pages/relationships.md",
    "content": "---\nid: 8ed04215-9f46-4000-bd67-c71b21b67d85\nblueprint: page\ntitle: Relationships\ntemplate: page\nintro: 'Content is often related to other content and bits of data. A blog post may have an author and 3 other recommended posts. A product may have a brand and a category. A hot dog may have a bun and some mustard. This page covers ways to create and take advantage of these types of relationships.'\nrelated_entries:\n  - d0c65546-74f1-4a15-89d5-1562a95ee2c6\n  - acee879a-c832-449d-a714-c57ea5862717\n  - 31adcc00-4fbb-4fe9-9b48-401061273096\n  - 0f8102b9-c948-4264-8cb8-cbfbd0415a04\n---\n## Overview\n\nStatamic relationships are defined by storing an `id` or `handle` of one piece of content (an entry, term, or user for example) in a variable on another piece of content. Once linked in this simple-but-specific manner, you can fetch and display the related content by using the variable in your templates.\n\n## Fieldtypes\n\nThere are 13 fieldtypes that manage relationships in one fashion or another. When you use these fieldtypes in your [blueprint](/blueprints), the relationships are automatically resolved on the front-end of your site and you can work directly with the data it references.\n\n- [Assets](/fieldtypes/assets)\n- [Collections](/fieldtypes/collections)\n- [Entries](/fieldtypes/entries)\n- [Form](/fieldtypes/form)\n- [Link](/fieldtypes/link)\n- [Navs](/fieldtypes/navs)\n- [Sites](/fieldtypes/sites)\n- [Structures](/fieldtypes/structures)\n- [Taxonomies](/fieldtypes/taxonomies)\n- [Taxonomy Terms](/fieldtypes/terms)\n- [User Groups](/fieldtypes/user-groups)\n- [User Roles](/fieldtypes/user-roles)\n- [Users](/fieldtypes/users)\n\n\n## Example\n\nLet's use this example product entry to walk through displaying data from the three relationships: photo, author, and related products.\n\n``` yaml\n# /content/products/wayne-gretzky-pog-collection.md\ntitle: Wayne Gretzky Pog Collection\nprice: 2495.00\ntemplate: products.show\nid: 123-1234-12-4321\nphoto: products/gretzky-pogs-FINAL-(2).jpg\nauthor: abc-abcd-ab-dcba\nrelated_products:\n  - 789-7890-78-0987\n  - abc-1234-bc-4eba\n```\n\n### Field breakdown\n- `id` is the unique identifier given to this particular entry\n- `photo` is a reference to an asset image of the product (why didn't they clean up the filename?)\n- `author` is the id of the user who created this entry\n- `related_products` is an array of other product entry ids\n\n### Templating\n\nIn this following template example you can see how easy it is to use the data from related entries, assets, and users. You don't need to write queries, request data filter results, or anything complicated. As long as you've used the appropriate [fieldtypes](#fieldtypes) in your [blueprint](/blueprints), the data will be ready and waiting for you to use in your view template.\n\n::tabs\n\n::tab antlers\n```antlers\n<!-- resources/views/products/show.antlers.html -->\n\n<div class=\"product\">\n  <div class=\"flex justify-between\">\n    <h1>{{ title }}</h1>\n    <h2 class=\"text-green\">${{ price }}</h2>\n  </div>\n  <img src=\"{{ photo:url }}\" alt=\"{{ alt }}\">\n  <p>Listed by: {{ author:name }}</p>\n</div>\n\n<div class=\"mt-8\">\n  <h3>Related Products</h3>\n  <div class=\"flex flex-wrap -mx-2\">\n    {{ related_products }}\n    <div class=\"w-1/2 p-4 border m-2\">\n      <div class=\"font-bold\">{{ title }}</div>\n      <div class=\"text-green\">{{ price }}</div>\n      <img src=\"{{ photo }}\" alt=\"{{ alt }}\">\n    </div>\n    {{ /related_products }}\n  </div>\n</div>\n```\n::tab blade\n```blade\n<!-- resources/views/products/show.blade.php -->\n\n<div class=\"product\">\n  <div class=\"flex justify-between\">\n    <h1>{{ $title }}</h1>\n    <h2 class=\"text-green\">${{ $price }}</h2>\n  </div>\n  <img src=\"{{ $photo->url }}\" alt=\"{{ $photo->alt }}\">\n  <p>Listed by: {{ $author->name }}</p>\n</div>\n\n<div class=\"mt-8\">\n  <h3>Related Products</h3>\n  <div class=\"flex flex-wrap -mx-2\">\n    @foreach ($related_products as $product)\n    <div class=\"w-1/2 p-4 border m-2\">\n      <div class=\"font-bold\">{{ $product->title }}</div>\n      <div class=\"text-green\">{{ $product->price }}</div>\n      <img src=\"{{ $product->photo->url }}\" alt=\"{{ $product->photo->alt }}\">\n    </div>\n    @endforeach\n  </div>\n</div>\n```\n::\n\n## Manual fetching\n\nIf you _aren't_ using a relationship fieldtype but _do_ have an `id` or `handle` to fetch data from you can use the [get_content tag](/tags/get_content).\n\n::tabs\n\n::tab antlers\n```antlers\n<!-- You can hardcode the ID -->\n{{ get_content from=\"123-1234-12-4321\" }}\n  <a href=\"{{ url }}\">{{ title }}</a>\n{{ /get_content }}\n\n<!-- Or pass the variable holding it -->\n{{ get_content :from=\"related_id\" }}\n  <a href=\"{{ url }}\">{{ title }}</a>\n{{ /get_content }}\n```\n::tab blade\n```blade\n<!-- You can hardcode the ID -->\n<s:get_content from=\"123-1234-12-4321\">\n  <a href=\"{{ $url }}\">{{ $title }}</a>\n</s:get_content>\n\n<!-- Or pass the variable holding it -->\n<s:get_content :from=\"related_id\">\n  <a href=\"{{ $url }}\">{{ $title }}</a>\n</s:get_content>\n```\n::\n"
  },
  {
    "path": "content/collections/pages/release-schedule-support-policy.md",
    "content": "---\nid: c9ee871c-002b-48d7-b845-1abac58de337\nblueprint: page\ntitle: 'Release Schedule & Support Policy'\nnav_title: 'Release Schedule'\nintro: 'For all Statamic releases, bug fixes are provided for 1 year and security fixes are provided for 18 months.  For all first party addons, only the latest major release receives bug fixes. In addition, please review the [Laravel Support Policy](https://laravel.com/docs/master/releases#support-policy).'\n---\n\n## Versioning scheme\n\nStatamic and its other first-party packages follow [Semantic Versioning](https://semver.org/). Major releases are released every year (~Q1). Minor and patch releases may be released as often as every few days. Minor and patch releases should never contain breaking changes.\n\n## Support policy\n\n<table>\n   <thead>\n      <tr>\n         <th>Statamic</th>\n         <th>Laravel</th>\n         <th>PHP</th>\n         <th>Release</th>\n         <th>Bug Fixes Until</th>\n         <th>Security Fixes Until</th>\n      </tr>\n   </thead>\n   <tbody>\n      <tr>\n         <td>3.4*</td>\n         <td>8-9</td>\n         <td>7.4-8.1</td>\n         <td>Jan 2023</td>\n         <td>Jan 2023</td>\n         <td>Jul 2024</td>\n      </tr>\n      <tr>\n         <td>4</td>\n         <td>9-10</td>\n         <td>8.0-8.3</td>\n         <td>Mar 2023</td>\n         <td>May 2024</td>\n         <td>Sep 2024</td>\n      </tr>\n      <tr>\n         <td>5</td>\n         <td>10-12</td>\n         <td>8.2-8.4</td>\n         <td>May 2024</td>\n         <td>Mar 2026</td>\n         <td>Dec 2026</td>\n      </tr>\n      <tr>\n         <td>6</td>\n         <td>12-13</td>\n         <td>8.3-8.5</td>\n         <td>Jan 2026</td>\n         <td>Mar 2027</td>\n         <td>Dec 2027</td>\n      </tr>\n      <tr>\n         <td>7</td>\n         <td>13-14</td>\n         <td>8.4-8.6</td>\n         <td>Q1 2027</td>\n         <td>TBC</td>\n         <td>TBC</td>\n      </tr>\n   </tbody>\n</table>\n\n_*Prior to Semantic Versioning_\n\nThese dates are subject to changes based on factors outside of our control, such as the release schedule and required versions of Laravel and major Laravel packages we depend upon.\n"
  },
  {
    "path": "content/collections/pages/repositories.md",
    "content": "---\ntitle: Repositories\ntemplate: page\nupdated_by: 42bb2659-2277-44da-a5ea-2f1eed146402\nupdated_at: 1569347424\nintro: Statamic uses a repository pattern to retrieve data from various places.\nid: c3da9537-5d5f-4b84-a4be-882b89217151\n---\n\nFor example, when you call `Entry::whereCollection('blog')`, it asks \"the entry repository\" to get the blog entries\ninstead of immediately assuming the entries will be located in a blog directory on the filesystem.\n\nOut of the box, Statamic will typically use an implementation that gets data from the \"Stache\", which is our file-backed database.\n\n## Custom Repositories\n\nLet's say you want to store your entries in a database. You would need to swap the default entry repository (which gets entries from the Stache)\nwith your own that would get entries from a database.\n\nIn a service provider's `register` you can re-bind the contract using the `Statamic::repository()` method:\n\n``` php\n<?php\n\nnamespace App\\Providers;\n\nuse App\\DatabaseEntryRepository;\nuse Illuminate\\Support\\ServiceProvider;\nuse Statamic\\Contracts\\Entries\\EntryRepository;\nuse Statamic\\Statamic;\n\nclass AppServiceProvider extends ServiceProvider\n{\n    public function register()\n    {\n        Statamic::repository(\n            EntryRepository::class,\n            DatabaseEntryRepository::class\n        );\n    }\n}\n```\n\nIf you only need to customize small parts, feel free to have your repository class extend Statamic's default ones. For example:\n\n``` php\nuse Statamic\\Stache\\Repositories\\EntryRepository;\n\nclass MyEntryRepository extends EntryRepository\n{\n    //\n}\n```\n\n## Custom Data Classes\n\nEach repository is also responsible for making instances of their items. eg. an `EntryRepository` makes `Entry` classes. The `make` method will resolve the appropriate class out of the container based on the contract.\n\nThe repository has a `bindings` method that defines these. Your custom repository may override these to return different classes.\n\n``` php\npublic static function bindings(): array\n{\n    return [\n        Statamic\\Contracts\\Entries\\Entry::class => DatabaseEntry::class,\n        Statamic\\Contracts\\Entries\\QueryBuilder::class => DatabaseEntryQueryBuilder::class,\n    ];\n}\n```\n\nAlternatively, if you want to use a custom item class without customizing the entire repository, you're free to just re-bind that class in your service provider's `boot` method (so it's re-bound after everything else). This could be more useful to you if you just need to customize a method or two.\n\n``` php\nclass AppServiceProvider extends ServiceProvider\n{\n    public function boot()\n    {\n        $this->app->bind(\n            Statamic\\Contracts\\Entries\\Entry::class,\n            CustomEntry::class\n        );\n    }\n}\n```\n\n:::tip\nMake sure to clear your cache after changing a binding like this.\n:::\n"
  },
  {
    "path": "content/collections/pages/requirements.md",
    "content": "---\ntitle: Requirements\nintro: Statamic is a modern PHP application built as a [Laravel](https://laravel.com) package, which carries with it the same [server requirements](https://laravel.com/docs/13.x/deployment#server-requirements) as Laravel itself. To manipulate images (resize, crop, etc), you will also need the GD Library or ImageMagick installed on your server.\ntemplate: page\nid: 792644d2-8bd2-421d-a080-e0be7fca125c\nblueprint: page\n---\n## Server requirements\n\nTo run Statamic you'll need a server meeting the following requirements. These are standard defaults (at minimum) for most modern hosting platforms.\n\n- PHP 8.3 or above\n- BCMath PHP Extension\n- Ctype PHP Extension\n- Exif PHP Extension\n- JSON PHP Extension\n- Mbstring PHP Extension\n- OpenSSL PHP Extension\n- PDO PHP Extension\n- Tokenizer PHP Extension\n- XML PHP Extension\n- GD Library or ImageMagick\n- Composer\n\n## Development environments\n\nDepending on your operating system, we recommend the following development environments:\n\n### macOS and Windows: Laravel Herd\n\n[Laravel Herd](https://herd.laravel.com) is a blazing fast, native development environment for macOS and Windows. Herd includes `php`, `composer` and `npm` - *almost* everything you need to setup Statamic locally.\n\nWe've written [a guide](/installing/laravel-herd) on installing Herd and setting up your Statamic site.\n\n### Linux\n\nTo develop locally with Statamic on Linux, you'll need to install `php`, `composer` and `npm`.\n\nIf you're using Ubuntu (or another variant of Debian), you may find our [Ubuntu guide](/installing/ubuntu) helpful.\n\n## Recommended hosts\n\nWe recommend using [Digital Ocean](https://m.do.co/c/6469827e2269) to host most small to medium Statamic sites. Their servers are fast, inexpensive, and we use them ourselves. _**Full disclosure:** that's an affiliate link but we wouldn't recommend them if it wasn't an excellent option._\n\nSome developers choose to pair Digital Ocean with a tool like [Laravel Forge](/deploying/laravel-forge) or [Ploi](/deploying/ploi), which help you provision servers and handle deployments. However, if you're comfortable doing that yourself, then feel free!\n\nWe also maintain a user-contributed [Github repo](https://github.com/statamic/hosts) full of other host recommendations.\n"
  },
  {
    "path": "content/collections/pages/resource-apis.md",
    "content": "---\nid: 50dfaf8c-8ca4-4b58-ac84-e4c50099e42e\nblueprint: page\ntitle: 'Resource APIs'\ntemplate: repositories/index\n---\n"
  },
  {
    "path": "content/collections/pages/rest-api.md",
    "content": "---\nid: 2e0d2f8f-319d-4cce-bd90-16d6ad32ad37\nblueprint: page\ntitle: 'REST API'\nintro: 'The Content REST API is a **read-only** API for delivering content from Statamic to your frontend, external apps, SPAs, and numerous other possible sources. Content is delivered as JSON data.'\npro: true\n---\n(If you're interested in [GraphQL](/graphql), we have that too.)\n\n## Enable the API\n\nTo enable the REST API, add the following to your `.env` file:\n\n```env\nSTATAMIC_API_ENABLED=true\n```\n\nOr you can enable it for all environments in `config/statamic/api.php`:\n\n```php\n'enabled' => true,\n```\n\nYou will also need to [enable the resources](#enable-resources) you want to be available. For security, they're all disabled by default.\n\n### Enable resources\n\nYou can enable resources (ie. Collections, Taxonomies, etc.) in your `config/statamic/api.php` config:\n\n```php\n'resources' => [\n  'collections' => true,\n  'taxonomies' => true,\n  // etc\n]\n```\n\n### Enable specific sub-resources\n\nIf you want more granular control over which sub-resources are enabled within a resource type (ie. enabling specific Collection queries only), you can use array syntax:\n\n```php\n'resources' => [\n    'collections' => [\n        'articles' => true,\n        'pages' => true,\n        // 'events' => false, // Sub-resources are disabled by default\n    ],\n    'taxonomies' => true,\n    // etc.\n]\n```\n\n\n## Endpoints\n\n`\nhttps://yourdomain.tld/api/{endpoint}\n`\nYou may send requests to the following endpoints:\n\n- [Entries](#entries) / [Entry](#entry)\n- [Collection Tree](#collection-tree) / [Navigation Tree](#navigation-tree)\n- [Taxonomy Terms](#taxonomy-terms) / [Taxonomy Term](#taxonomy-term)\n- [Assets](#assets) / [Asset](#asset)\n- [Globals](#globals) / [Global](#global)\n- [Forms](#forms) / [Form](#form)\n- [Users](#users) / [User](#user)\n\n### Customizing the API URL\nYou may customize the route in your API config file or with an environment variable.\n\n```php\n// config/statamic/api.php\n 'route' => 'not_api',\n```\n\n ```env\n STATAMIC_API_ROUTE=not_api\n ```\n\n\n## Filtering\n\n### Enabling filters\n\nFor security, [filtering](#filtering) is disabled by default. To enable, you'll need to opt in by defining a list of `allowed_filters` for each sub-resource in your `config/statamic/api.php` config:\n\n```php\n'resources' => [\n    'collections' => [\n        'articles' => [\n            'allowed_filters' => ['title', 'status'],\n        ],\n        'pages' => [\n            'allowed_filters' => ['title'],\n        ],\n        'events' => true, // Enable this collection without filters\n        'products' => true, // Enable this collection without filters\n    ],\n    'taxonomies' => [\n        'topics' => [\n            'allowed_filters' => ['slug'],\n        ],\n        'tags' => true, // Enable this taxonomy without filters\n    ],\n    // etc.\n],\n```\n\nFor endpoints that don't have sub-resources (ie. users), you can define `allowed_filters` at the top level of that resource config:\n\n```php\n'resources' => [\n    'users' => [\n        'allowed_filters' => ['name', 'email'],\n    ],\n],\n```\n\n### Using filters\n\nYou may filter results by using the `filter` query parameter.\n\n``` url\n/endpoint?filter[{field}:{condition}]={value}\n```\n\nYou may use the [conditions](/conditions) available to the collection tag. eg. `contains`, `is`, `isnt` (or `not`), etc. For example:\n\n``` url\n/endpoint?filter[title:contains]=awesome&filter[featured]=true\n```\n\nThis would filter down the results to where the `title` value contains the string `\"awesome\"`, and the `featured`\nvalue is `true`. When you omit the condition, it defaults to `is`.\n\n### Advanced filtering config\n\nYou can also allow filters on all enabled sub-resources using a `*` wildcard config. For example, here we'll enable only the `articles`, `pages`, and `products` collections, with `title` filtering enabled on each, in addition to `status` filtering on the `articles` collection specifically: \n\n```php\n'resources' => [\n    'collections' => [\n        '*' => [\n            'allowed_filters' => ['title'], // Enabled for all collections\n        ],\n        'articles' => [\n            'allowed_filters' => ['status'], // Also enable on articles\n        ],\n        'pages' => true,\n        'products' => true,\n    ],\n],\n```\n\nIf you've enabled filters using the `*` wildcard config, you can disable filters on a specific sub-resource by setting `allowed_filters` to `false`:\n\n```php\n'resources' => [\n    'collections' => [\n        '*' => [\n            'allowed_filters' => ['title'], // Enabled for all collections\n        ],\n        'articles' => [\n            'allowed_filters' => false, // Disable filters on articles\n        ],\n        'pages' => true,\n        'products' => true,\n    ],\n],\n```\n\nOr you can enable endpoints and filters on all sub-resources at once by setting both `enabled` and `allowed_filters` within your `*` wildcard config:\n\n```php\n'resources' => [\n    'collections' => [\n        '*' => [\n            'enabled' => true, // All collection endpoints enabled\n            'allowed_filters' => ['title'], // With filters enabled for all\n        ],\n    ],\n],\n```\n\n\n## Sorting\n\nYou may sort results by using the `sort` query parameter:\n\n``` url\n/endpoint?sort=field\n```\n\nYou can sort in reverse by prefixing the field with a `-`:\n\n``` url\n/endpoint?sort=-field\n```\n\nYou may sort by multiple fields by comma separating them. The reverse flag can be combined with any field:\n\n``` url\n/endpoint?sort=one,-two,three\n```\n\nYou can sort nested fields using the `->` operator, like this: \n```url\n/endpoint?sort=nested->field\n```\n\n## Selecting fields\n\nYou may specify which top level fields should be included in the response.\n\n``` url\n/endpoint?fields=id,title,content\n```\n\n## Pagination\n\nResults will be paginated into 25 items per page by default. You may specify the items per page and which page you are viewing with the `limit` and `page` parameters:\n\n``` url\n/endpoint?limit=10&page=1\n```\n\nThe response will contain your `data`, `links` to easily get next/previous URLs, and `meta` information for more easily creating a paginator.\n\n``` json\n{\n    \"data\": [\n        {...},\n        {...},\n    ],\n    \"links\": {\n        \"first\": \"/endpoint?limit=10&page=1\",\n        \"last\": \"/endpoint?limit=10&page=3\",\n        \"prev\": null,\n        \"next\": \"/endpoint?limit=10&page=2\",\n    },\n    \"meta\": {\n        \"current_page\": 1,\n        \"from\": 1,\n        \"to\": 10,\n        \"total\": 29,\n        \"per_page\": 10,\n        \"path\": \"/endpoint\",\n    }\n}\n```\n\n---\n\n## Entries\n\n`GET` `/api/collections/{collection}/entries`\n\nGets entries within a collection.\n\n``` json\n{\n  \"data\": [\n    {\n      \"title\": \"My First Day\"\n    }\n  ],\n  \"links\": {...},\n  \"meta\": {...}\n}\n```\n\n:::tip\nIf you are using [Multi-Site](/multi-site), the entries endpoint will serve from all sites at once. If needed, you can limit the fetched data to a specific site with a `site` [filter](#filtering) (ie. `&filter[site]=fr`).\n:::\n\n\n## Entry\n\n`GET` `/api/collections/{collection}/entries/{id}`\n\nGets a single entry.\n\n``` json\n{\n  \"data\": {\n    \"title\": \"My First Day\"\n  }\n}\n```\n\n\n## Collection Tree\n\n`GET` `/api/collections/{collection}/tree`\n\nGets entry tree for a structured collection.\n\n``` json\n{\n  \"data\": [\n    {\n      \"page\": {\n        \"title\": \"About\",\n        \"url\": \"/about\"\n      },\n      \"depth\": 1,\n      \"children\": [\n        {\n          \"page\": {\n            \"title\": \"Articles\",\n            \"url\": \"/about/articles\"\n          },\n          \"depth\": 2,\n          \"children\": []\n        }\n      ]\n    }\n  ]\n}\n```\n\n### Params\n\nOn this endpoint, the [fields](#selecting-fields) param will allow you to select fields within each `page` object. You may also set a `max_depth` to limit nesting depth, or `site` to choose the site.\n\n```url\n/api/collections/{collection}/tree?fields=title,url&max_depth=2&site=fr\n```\n\n\n## Navigation Tree\n\n`GET` `/api/navs/{nav}/tree`\n\nGets tree for a navigation structure.\n\n``` json\n{\n  \"data\": [\n    {\n      \"page\": {\n        \"title\": \"Recommended Products\",\n        \"url\": \"https://rainforest.store/?cid=statamic\",\n      },\n      \"depth\": 1,\n      \"children\": [\n        {\n          \"page\": {\n            \"title\": \"Books\",\n            \"url\": \"https://rainforest.store/?cid=statamic&type=books\",\n          },\n          \"depth\": 2,\n          \"children\": []\n        }\n      ]\n    }\n  ]\n}\n```\n\n### Params\n\nOn this endpoint, the [fields](#selecting-fields) param will allow you to select fields within each `page` object. You may also set a `max_depth` to limit nesting depth, or `site` to choose the site.\n\n```url\n/api/navs/{nav}/tree?fields=title,url&max_depth=2&site=fr\n```\n\n\n## Taxonomy Terms\n\n`GET` `/api/taxonomies/{taxonomy}/terms`\n\nGets terms in a taxonomy.\n\n``` json\n{\n  \"data\": [\n    {\n      \"title\": \"Music\",\n    }\n  ],\n  \"links\": {...},\n  \"meta\": {...}\n}\n```\n\n:::tip\nIf you are using [Multi-Site](/multi-site), you can select the site using a `site` [filter](#filtering) (ie. `&filter[site]=fr`).\n:::\n\n## Taxonomy term\n\n`GET` `/api/taxonomies/{taxonomy}/terms/{slug}`\n\nGets a single taxonomy term.\n\n``` json\n{\n  \"data\": {\n    \"title\": \"My First Day\"\n  }\n}\n```\n\n## Globals\n\n`GET` `/api/globals`\n\nGets all globals.\n\n``` json\n{\n  \"data\": [\n    {\n      \"handle\": \"global\",\n      \"api_url\": \"http://example.com/api/globals/global\",\n      \"foo\": \"bar\",\n    },\n    {\n      \"handle\": \"another\",\n      \"api_url\": \"http://example.com/api/globals/another\",\n      \"baz\": \"qux\",\n    }\n  ],\n}\n```\n\n:::tip\nIf you are using [Multi-Site](/multi-site), you can select the site using the `site` parameter (ie. `&site=fr`).\n:::\n\n## Global\n\n`GET` `/api/globals/{handle}`\n\nGets a single global set's variables.\n\n``` json\n{\n  \"data\": {\n    \"handle\": \"global\",\n    \"api_url\": \"http://example.com/api/globals/global\",\n    \"foo\": \"bar\",\n  }\n}\n```\n\n## Forms\n\n`GET` `/api/forms`\n\nGets all forms.\n\n``` json\n{\n  \"data\": [\n    {\n      \"handle\": \"contact\",\n      \"title\": \"Contact\",\n      \"fields\": {\n        \"name\": {...},\n        \"email\": {...},\n        \"inquiry\": {...}\n      },\n      \"api_url\": \"http://example.com/api/forms/contact\",\n    },\n    {\n      \"handle\": \"newsletter\",\n      \"title\": \"Subscribe to Newsletter\",\n      \"fields\": {\n        \"email\": {...}\n      },\n      \"api_url\": \"http://example.com/api/forms/newsletter\",\n    }\n  ],\n}\n```\n\n## Form\n\n`GET` `/api/forms/{handle}`\n\nGets a single form.\n\n``` json\n{\n  \"data\": {\n    \"handle\": \"contact\",\n    \"title\": \"Contact\",\n    \"fields\": {\n      \"name\": {...},\n      \"email\": {...},\n      \"inquiry\": {...}\n    },\n    \"api_url\": \"http://example.com/api/forms/contact\",\n  }\n}\n```\n\n## Users\n\n`GET` `/api/users`\n\nGet users.\n\n``` json\n{\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"email\": \"john@smith.com\",\n      \"api_url\": \"http://example.com/api/users/1\"\n    }\n  ],\n  \"links\": {...},\n  \"meta\": {...}\n}\n```\n\n## User\n\n`GET` `/api/users/{id}`\n\nGet a single user.\n\n``` json\n{\n  \"data\": {\n    \"id\": \"1\",\n    \"email\": \"john@smith.com\",\n    \"api_url\": \"http://example.com/api/users/1\"\n  }\n}\n```\n\n## Assets\n\n`GET` `/api/assets/{container}`\n\nGet a container's asset data.\n\n``` json\n{\n  \"data\": [\n    {\n      \"id\": \"main::foo.jpg\",\n      \"url\": \"/assets/foo.jpg\",\n      \"api_url\": \"http://example.com/api/assets/main/foo.jpg\",\n      \"alt\": \"A picture of nothing.\"\n    }\n  ],\n  \"links\": {...},\n  \"meta\": {...}\n}\n```\n\n## Asset\n\n`GET` `/api/assets/{container}/{path}`\n\nGet a single asset's data.\n\nThe `path` in the URL should be the relative path from the container's root.\n\n``` json\n{\n  \"data\": {\n    \"id\": \"main::foo.jpg\",\n    \"url\": \"/assets/foo.jpg\",\n    \"api_url\": \"http://example.com/api/assets/main/foo.jpg\",\n    \"alt\": \"A picture of nothing.\"\n  }\n}\n```\n\n## Customizing resources\n\nBy default, the resources generally use the item's [Augmented](/augmentation) data.\n\nYou are free to override the resource classes with your own, in turn letting you customize the responses.\n\nIn a service provider, use the `map` method to define the overriding resources:\n\n``` php\nuse App\\Http\\Resources\\CustomEntryResource;\nuse Statamic\\Http\\Resources\\API\\Resource;\nuse Statamic\\Http\\Resources\\API\\EntryResource;\n\nclass AppServiceProvider extends Provider\n{\n    public function boot()\n    {\n        Resource::map([\n            EntryResource::class => CustomEntryResource::class,\n        ]);\n    }\n}\n```\n\n``` php\n<?php\n\nnamespace App\\Http\\Resources;\n\nuse Statamic\\Http\\Resources\\API\\EntryResource;\n\nclass CustomEntryResource extends EntryResource\n{\n    public function toArray($request)\n    {\n        return [\n            'id' => $this->resource->id(),\n            'title' => $this->resource->value('title'),\n        ];\n    }\n}\n```\n\n## Caching\n\nAPI responses are cached by default. You may customize the cache expiry in `config/statamic/api.php`.\n\n```php\n'cache' => [\n    'expiry' => 60,\n],\n```\n\n### Cache invalidation\n\nCached responses are automatically invalidated when content is changed. Depending on your API usage and blueprint schema, you may also wish to ignore specific events when invalidating.\n\n```php\n'cache' => [\n    'expiry' => 60,\n    'ignored_events' => [\n        \\Statamic\\Events\\UserSaved::class,\n        \\Statamic\\Events\\UserDeleted::class,\n    ],\n],\n```\n\n### Disabling caching\n\nIf you wish to disable caching altogether, set `cache` to `false`.\n\n```php\n'cache' => false,\n```\n\n### Custom cache driver\n\nIf you need a more intricate caching solution, you may reference a custom cache driver class and pass extra config along if necessary.\n\n```php\n'cache' => [\n    'class' => CustomCacher::class,\n    'expiry' => 60,\n    'foo' => 'bar',\n],\n```\n\nBe sure to extend `Statamic\\API\\AbstractCacher` and implement the required methods. You can access custom config via the `config()` method, ie. `$this->config('foo')`.\n\n```php\nuse Statamic\\API\\AbstractCacher;\n\nclass CustomCacher extends AbstractCacher\n{\n    public function get(Request $request)\n    {\n        //\n    }\n\n    public function put(Request $request, JsonResponse $response)\n    {\n        //\n    }\n\n    public function handleInvalidationEvent(Event $event)\n    {\n        //\n    }\n}\n```\n\n## Rate limiting\n\nThe REST API is rate limited to **60 requests per minute** by default.\n\nYou can change this configuration in your `RouteServiceProvider`. Learn more about [rate limits in Laravel](https://laravel.com/docs/master/rate-limiting).\n\n```php\n// app/Providers/RouteServiceProvider.php\nprotected function configureRateLimiting()\n{\n    RateLimiter::for('api', function (Request $request) {\n        return Limit::perMinute(60);\n    });\n}\n```\n\n## Authentication\n\nOut of the box, the REST API is publicly accessible. \n\nYou can restrict access to the API by adding the `STATAMIC_API_AUTH_TOKEN` key to your `.env` file. It should be set to a long, random string.\n\n```php\nSTATAMIC_API_AUTH_TOKEN=a-long-random-string\n```\n\nThen, when you make requests to the REST API, you'll need to include the token in the `Authorization` header, like this:\n\n```curl\ncurl -X GET \"https://example.com/api/collections/pages/entries\" \\\n  -H \"Authorization: Bearer a-long-random-string\" \\\n  -H \"Accept: application/json\"\n```\n\n### Authenticating users\n\nIf you want to authenticate based on users, we recommend using [Laravel Sanctum](https://laravel.com/docs/master/sanctum) instead. \n\nTo use Sanctum, you'll need to [store users in the database](/tips/storing-users-in-a-database) and add the `auth:sanctum` middleware to the REST API's middleware group:\n\n```php\n$this->app[\\Illuminate\\Contracts\\Http\\Kernel::class]->prependMiddlewareToGroup(config(‘statamic.api.middleware’), ‘auth:sanctum’);\n```\n"
  },
  {
    "path": "content/collections/pages/revisions.md",
    "content": "---\ntitle: Revisions\nintro: Revisions adds an entire publishing workflow to your authoring process. You can create revisions, review and rollback to previous revisions of your content, and more.\ntemplate: page\nblueprint: page\nid: 6177b316-0eed-4dec-83d1-e5a48a8e00b6\npro: true\n---\n\n## Overview\n\nRevisions is Statamic's publishing workflow feature which provides different _states_ and corresponding behaviors for your entries — published, unpublished, working copy, and revision.\n\n<figure>\n    <img src=\"/img/publish-revision.webp\" alt=\"Revisions\" width=\"450\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/publish-revision-dark.webp\" alt=\"Revisions\" width=\"450\" class=\"u-hide-in-light-mode\">\n    <figcaption>Leave notes describing your updates. Kinda like Git!</figcaption>\n</figure>\n\n## Enabling\n\nRevisions is a **Pro feature**, make sure you've [enabled Pro](/licensing).\n\nEnable revisions globally by setting `STATAMIC_REVISIONS_ENABLED=true` in your `.env` file. Now you can set `revisions: true` on any or all collections you'd like to use revisions.\n\n:::best-practice\nWe recommend leaving Revisions **off** while your site is in development. It'll add extra steps to each update to your content, slow you down, and you'll probably end up annoyed by what's actually a really awesome feature.\n:::\n\n## Storage\n\nRevisions are tucked away in the `storage/statamic/revisions` directory by default. Out of the box, this directory is ignored by Git. If you want to version control your revisions, you could tweak the `.gitignore` file, however a better solution would be to move the directory to somewhere more visible:\n\n``` php\n// config/statamic/revisions.php\n\n'path' => base_path('content/revisions'),\n```\n\n## Revision states\n\n### Unpublished\n\nA new entry begins in the unpublished state. As long as your entry _remains_ unpublished, you're simply working directly on the entry located in your content/collections/{collection} directory. It will not be visible from the front-end of your site until it's published, and you can save a revision at any point.\n\n### Revision\nRevisions are [stored as YAML files](#storage) and include all the data for your entries at the time of revisions, including additional meta data about the author, timestamp, and so on.\n\nRevisions can be previewed and restored as the [working copy](#working-copy) so you can edit and/or publish them if you wish.\n\n### Working Copy\n\nThe working copy, if you have one, is stored along with your revisions. At no point do you ever directly edit and save changes to the published (aka \"live\") entry.\n\n### Published\n\nPublishing an entry will create a revision, at which point any additional changes to your entry will be stored on the _working copy_ until you choose to publish them. This will let you collaborate and improve existing content without pushing changes live or dealing with feature branches in git (something beyond most content writers and editors).\n\n### Unpublishing\n\nUnpublishing an entry will create a revision and remove it from the front-end, at which point you begin working directly on the entry again.\n\n## History\n\nThe history view will show you all revisions, publish, unpublish, and restore states, and let you preview and restore from any previous point of the entry.\n\n<figure>\n    <img src=\"/img/revisions.webp\" alt=\"Revisions\" class=\"u-hide-in-dark-mode\" width=\"398\">\n    <img src=\"/img/revisions-dark.webp\" alt=\"Revisions\" class=\"u-hide-in-light-mode\" width=\"398\">\n    <figcaption>This is your revision history. You can tell because it says so.</figcaption>\n</figure>\n\n## Workflow\n\nFor those interested in the super-granular details, here is the result of each possible state change:\n\n### Saving an *unpublished* entry\n- No revision is created.\n- The actual entry is saved.\n- The actual entry is considered the working copy.\n\n### Saving a *published* entry\n- The working copy is saved.\n- The actual entry is _not_ saved.\n\n### Publishing an entry\n- The entry gets updated with the contents of the working copy, marked as published, and saved.\n- A revision is created.\n- The working copy is deleted.\n\n### Unpublishing an entry\n- The entry is marked as unpublished, and saved.\n- A revision is created.\n- The working copy is deleted.\n\n### Manually creating a revision\n- A revision is created.\n\n### Restoring a revision while the entry is *published*\n- The working copy is updated to the contents of the revision.\n- The actual entry is left untouched.\n\n### Restoring a revision while the entry is *unpublished*\n- The actual entry is updated to the contents of the revision.\n- The entry is left unpublished, even if the selected revision was published.\n"
  },
  {
    "path": "content/collections/pages/routing.1.md",
    "content": "---\nid: 421a9f22-bc1c-45e9-81ca-2ba8ad2a5744\nblueprint: page\ntitle: Routing\nintro: 'You can register Control Panel routes to build custom pages.'\n---\n:::tip\nThis guide is intended for apps adding routes to the Control Panel. If you're building an addon, please see our [Building an Addon](/addons/building-an-addon#routing) guide instead.\n:::\n\nTo register a custom route:\n\n1. Create a routes file. Name it whatever you want, for example: `routes/cp.php`\n2. Then push the routes by adding this to your `app/Providers/AppServiceProvider.php`:\n\n    ```php\n    use Illuminate\\Support\\Facades\\Route;\n    use Statamic\\Statamic;\n\n    public function boot()\n    {\n        Statamic::pushCpRoutes(function () {\n            Route::namespace('\\App\\Http\\Controllers')->group(function () {\n                require base_path('routes/cp.php');\n            });\n        });\n    }\n    ```\n\n3. Any routes in the file will have the appropriate name prefix and middleware applied."
  },
  {
    "path": "content/collections/pages/routing.md",
    "content": "---\nid: 8d9cfb16-36bf-45d0-babb-e501a35ddae6\nblueprint: page\ntitle: Routing\ntemplate: page\nintro: 'Statamic has several ways it routes requests and defines URLs and patterns, all of which are listed and described in this section.'\n---\n## Overview\n\nAll site requests are handled by Statamic unless you [create your own Laravel routes](#laravel-routes). Here are the ways Statamic defines URLs.\n\n## Content routes\n[Collection entries](/collections#routing) and [taxonomy terms](/taxonomies#routing) can have their own URLs as defined by their own flexible route patterns in their respective configuration areas.\n\n## Statamic routes\n\nStatamic provides a `Route::statamic()` method to do all the CMS \"magic\" for you, like injecting data (globals and system variables, for example), applying middleware, fetching the view, layout, and so on.\n\n``` php\nRoute::statamic('uri', 'view', ['foo' => 'bar']);\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ myglobal }} // globals are available\n{{ foo }} // bar\n```\n::tab blade\n```blade\n{{ $myglobal }} // globals are available\n{{ $foo }} // bar\n```\n::\n\nThe first argument is the URI, the second is the name of the [template](/views#templates), and the third is an optional array of additional data.\n\nWhen the template is the same as the URI, you can provide the one argument and Statamic will fall back to use the URI as the template:\n\n```php\nRoute::statamic('my-page'); // Implies 'my-page'\nRoute::statamic('/my-page'); // Implies 'my-page'\nRoute::statamic('/foo/bar'); // Implies 'foo.bar'\n```\n\n### Parameters\n\nYou may use wildcard parameters in your routes. This allows you to match multiple URLs with the same route.\n\n``` php\nRoute::statamic('things/{thing}', 'things.show');\n```\n\nThe parameter values will be available in your templates. For example, if you visited `/things/foo`:\n\n```\n{{ thing }}\n```\n\n```html\nfoo\n```\n\n### Layout\n\nWhen using `Route::statamic()`, Statamic will automatically inject the selected view into the default layout. You can customize which layout is used by adding a `layout` to the route data.\n\n``` php\nRoute::statamic('uri', 'view', ['layout' => 'custom']);\n```\n\n### Content type headers\n\nYou can control the content type headers by setting `'content_type' => '{content_type}'`. To make your life easier we also support a few shorthand syntaxes for the most common content types. Nobody wants to memorize this stuff, ourselves included.\n\n| Shorthand | Resolves to |\n|-----------|-------------|\n| `json` | `application/json` |\n| `xml` | `text/xml` |\n| `atom` | `application/atom+xml` (ensures `utf8` charset) |\n\n### Dynamic closure based routes\n\nIf needed, you can define more dynamic view or data logic by passing a closure.\n\nFor example, you might want to dynamically return a view based on dynamic segments in your URI. You can do this by passing a closure into the second argument:\n\n```php\nRoute::statamic('/{component}/{mode}', function ($component, $mode) {\n    return view($component, ['mode' => $mode]);\n});\n```\n\nBy returning `view()` from a closure, Statamic will still apply [all the \"magic\"](#statamic-routes) like middleware, layout, globals, system variables, etc.\n\n_Note: If you don't return `view()`, middleware will still get applied, but layout, globals, system variables, etc. will not be. For example, returning an array would output JSON, just like it would with `Route::get()` in Laravel, but with Statamic's middlware stack applied._\n\n#### Dynamic route data\n\nOr, maybe you just want to dynamically compose data that's passed into a static view. You can do this by passing a closure into the third data argument:\n\n```php\nRoute::statamic('stats/{category}', 'statistics.show', function ($category) {\n    return ['stats' => Stats::gatherDataExpensively($category)];\n});\n```\n\n_Note: Passing closures into both the second and the third parameter are not supported. If you need to dynamically handle both your view and your data, pass a closure into the second argmuent [as detailed above](#dynamic-closure-based-routes)._\n\n#### Dependency injection\n\nYou may also type-hint dependencies in your closure based routes, just as you can [with Laravel](https://laravel.com/docs/routing#dependency-injection):\n\n```php\nuse Illuminate\\Http\\Request;\n\nRoute::statamic('stats', 'statistics.show', function (Request $request) {\n    return ['stats' => Stats::gatherDataExpensively($request->category)];\n});\n```\n\n### Disabling {#disabling-statamic-routes}\n\nIf you want to defer **everything** to explicit Laravel routes (perhaps you're using Statamic as a headless CMS or API), you can disable this behavior by setting it in `config/statamic/routes.php`.\n\n``` php\n// config/statamic/routes.php\n\n'enabled' => false,\n```\n\n\n## Laravel routes\n\nYou can also configure regular Laravel routes much like you would in a regular Laravel application in `routes/web.php`. You can use closures, point to a [controller](/controllers), and so on. This is [standard Laravel stuff](https://laravel.com/docs/routing) and the standard Laravel docs apply.\n\n:::tip\nIf you're using [Static Caching](/static-caching), make sure to add Statamic's `Cache` middleware to any Laravel routes so they get static-ly cached.\n\n```php\nRoute::get('/thingy', function () {\n\t// ...\n})->middleware(\\Statamic\\StaticCaching\\Middleware\\Cache::class);\n```\n:::\n\n## Redirects\n\nCreating redirects can be done in your `routes/web.php` using native Laravel Route methods:\n\n``` php\nRoute::redirect('/here', '/there');\nRoute::redirect('/here', '/there', 301);\nRoute::permanentRedirect('/here', '/there');\n```\n\n[More details on the Laravel docs](https://laravel.com/docs/routing#redirect-routes).\n\n## Absolute domain redirects\n\nDomain names ending in a dot (e.g. `https://example.com./`) are technically valid \"absolute\" domains per [RFC 1034](https://www.rfc-editor.org/rfc/rfc1034) and [RFC 1035](https://www.rfc-editor.org/rfc/rfc1035), but they can cause real problems:\n\n- Browsers treat them as a different origin for CORS, so scripts, fonts, and other cross-origin assets may fail to load.\n- If a user first hits your site via the dot variant while [static caching](/static-caching) is active, the cached HTML will contain dot-suffixed internal links and poison the cache for every subsequent visitor.\n\nStatamic ships with a `RedirectAbsoluteDomains` middleware that redirects `https://example.com./foo` → `https://example.com/foo`. It's opt-in — register it application-wide in `bootstrap/app.php`:\n\n```php\n->withMiddleware(function (Middleware $middleware) {\n    $middleware->append(\\Statamic\\Http\\Middleware\\RedirectAbsoluteDomains::class); // [tl! add]\n})\n```\n\n:::tip\nAppend it at the app level (not just the `web` group) so it also covers the control panel and any other routes.\n:::\n\n\n## Error pages\n\nWhenever an error is encountered, a view will be rendered based on the status code. It will look for the view in `resources/views/errors/{status_code}.antlers.html`.\n\nYou can use a custom layout for errors by creating a `resources/views/errors/layout.antlers.html` view.\n\nStatamic will automatically render `404` pages for any unhandled routes.\n\n:::tip\nFor 5xx errors (e.g. 500, 503, etc) only the template will be rendered. It will not be injected into a layout.\n:::\n"
  },
  {
    "path": "content/collections/pages/scheduling.md",
    "content": "---\nid: 0df63f01-4b97-4c15-89a9-015c02ea3748\nblueprint: page\ntitle: Task Scheduling\nintro: \"Manage scheduled tasks using Laravel's task scheduler.\"\ntemplate: page\nrelated_entries:\n  - 7202c698-942a-4dc0-b006-b982784efb03\n  - ffa24da8-3fee-4fc9-a81b-fcae8917bd74\n---\nStatamic leverages task scheduling via Laravel's Task Scheduler.\n\nIn a nutshell, you can create a single cron job which will allow things to happen on a schedule, without any visitors needing to be on the site.\n\n[Learn more about scheduling tasks in the Laravel docs](https://laravel.com/docs/13.x/scheduling)\n\n## Running the scheduler\n\n### In production\n\nIn production, you will need to set up a single once-per-minute cron entry that runs the `schedule:run` Artisan command.\n\nUsing a service like Laravel Forge makes this simple.\n\n```sh\n* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1\n```\n\n### In development\n\nTypically, you would not add a scheduler cron entry to your local development machine. Instead, you may use the `schedule:work` Artisan command. This command will run in the foreground and invoke the scheduler every minute until you terminate the command:\n\n```sh\nphp artisan schedule:work\n```   \n\n[Learn more about running the scheduler](https://laravel.com/docs/13.x/scheduling#running-the-scheduler)\n\n## Included tasks\n\nThe following tasks will be executed whenever the task scheduler is running, without you needing to enable anything.\n\n### EntryScheduleReached\n\nStatamic will dispatch a `Statamic\\Events\\EntryScheduleReached` event whenever a scheduled entry reaches its target date. This event is used in multiple places such as updating search indexes and invalidating caches.\n\nThe event will be dispatched on the minute _after_ the scheduled time.\n\n\n## Defining schedules\n\nOne way to add your own scheduled tasks is by adding items to your `routes/console.php` file.\n\n```php\nSchedule::command('my-command')->daily();\n\nSchedule::job(new Heartbeat)->everyFiveMinutes();\n```\n\n[Learn more about defining schedules](https://laravel.com/docs/13.x/scheduling#defining-schedules)\n"
  },
  {
    "path": "content/collections/pages/search.md",
    "content": "---\nid: 420f083d-99be-4d54-9f81-3c09cb1f97b7\nblueprint: page\ntitle: Search\nintro: \"Help your visitors find what they're looking for with search. Use  configurable indexes to configure which fields are important, which aren't, and fine-tune your way to relevant results.\"\ntemplate: page\nrelated_entries:\n  - 5fcf5a56-c120-4988-a4c7-0c5e942327b7\n  - 2022056a-d901-423a-aaa7-ee04fff40739\n  - fe8ec156-447d-4f03-974f-0251a8c53244\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1633035293\n---\n## Overview\n\nThere are four components (coincidentally, the same number of Ninja Turtles) whose powers combine to provide you fully comprehensive powers of search.\n\n1. Forms\n2. Results\n3. Indexes\n4. Drivers\n{.c-list-turtles}\n\n## Forms\n\nThe search form is the entry point to your site search. Search forms are basic, vanilla HTML forms with a `text` or `search` input named `q` submitting to any URL with a `search:results` tag in its view template.\n\nYou can create that page however you wish: it could be an entry, a custom route, or something even fancier we didn't think of.\nThis [Laracasts video](https://laracasts.com/series/learn-statamic-with-jack/episodes/11) shows how to setup search quickly.\n\n```\n<form action=\"/search/results\">\n    <input type=\"search\" name=\"q\" placeholder=\"Search\">\n    <button type=\"submit\">Go find it!</button>\n</form>\n```\n\n## Results\n\nResults are powered by the [Search Results](/tags/search) tag. The tag will look for that `q` input sent by the [form](#forms) as a query string in the URL (e.g. `/search/results?q=where's%20the%20beef`).\n\nInside the tag you have access to all the content and variables from each result.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ search:results index=\"default\" }}\n    {{ if no_results }}\n        <h2>No results found for {{ get:q }}.</h2>\n    {{ else }}\n        <a href=\"{{ url }}\">\n            <h2>{{ title }}</h2>\n            <p>{{ description | truncate:180 }}</p>\n        </a>\n    {{ /if }}\n{{ /search:results }}\n```\n::tab blade\n```blade\n<statamic:search:results\n  index=\"default\"\n  as=\"results\"\n>\n  @forelse ($results as $result)\n    <a href=\"{{ $result->url }}\">\n        <h2>{{ $result->title }}</h2>\n        <p>{{ Statamic::modify($result->description)->truncate(180) }}</p>\n    </a>\n  @empty\n    <h2>No results found for {{ get('q') }}.</h2>\n  @endforelse\n</statamic:search:results>\n```\n::\n\nThe tag has a lot more fine-tuned control available, like renaming the query parameter, filtering on fields and collections, and so on. You can read more about it in the [search results tag](/tags/search) docs.\n\n## Indexes\n\nA search index is an ephemeral copy of your content, optimized for speed and performance while executing search queries. Without indexes, each search would require a scan of every entry in your site. Not an efficient way to go about it, but still technically possible.\n\nIndexes are configured in `config/statamic/search.php` and you can create as many as you want. Each index can hold different pieces of content — and any one piece of content may be stored in any number of indexes.\n\n:::tip\nAn **index** is a collection of **records**, each representing a single search item. A record might be an entry, a taxonomy term, or even a user.\n:::\n\nYour site's default index includes _only_ the title from _all_ collections. The default config looks like this:\n\n``` php\n'default' => [\n    'driver' => 'local',\n    'searchables' => 'content',\n    'fields' => ['title'],\n],\n```\n\n### Search a specific index\n\nThe index you wish you to search can be specified as a parameter on your [search results](#results) tag.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ search:results index=\"docs\" }} ... {{ /search:results }}\n```\n::tab blade\n```blade\n<statamic:search:results\n  index=\"docs\"\n>\n  ...\n</statamic:search:results>\n```\n::\n\n### Searchables\n\nThe searchables value determines what items are contained in a given index. By passing an array of searchable values you can customize your index however you'd like. For example, to index all blog posts and news articles together, you can do this:\n\n``` php\n'searchables' => ['collection:blog', 'collection:news']\n```\n\n#### Possible options include:\n\n- `content`\n- `collection:*`\n- `collection:{collection handle}`\n- `taxonomy:{taxonomy handle}`\n- `assets:{container handle}`\n- `users`\n- Custom ones may be added by addons. [Read about creating custom searchables](/extending/search)\n\n### Filtering searchables\n\nYou may choose to allow only a subset of searchable items.\n\nFor example, you may want to have a toggle field that controls whether an entry gets indexed or not. You can specify a closure with that logic.\n\n```php\n'searchables' => ['collection:blog'],\n'filter' => function ($item) {\n    return $item->status() === 'published' && ! $item->exclude_from_search;\n}\n```\n\nNow, only published entries that don't have the `exclude_from_search` toggle field enabled will be indexed.\n\n\n:::tip Heads up\nYour filter will override any native filters. For example, entries will filter out drafts by default. If your filter doesn't also remove drafts, they could be indexed.\n:::\n\nAlternatively you can specify a class to handle the filtering. This is useful when you want to cache your config using `php artisan config:cache`.\n\n``` php\n'filter' => \\App\\SearchFilters\\BlogFilter::class,\n```\n\n``` php\nnamespace App\\SearchFilters;\n\nclass BlogFilter\n{\n    public function handle($item)\n    {\n        return $item->status() === 'published' && ! $item->exclude_from_search;\n    }\n}\n```\n\n### Records & fields\n\nThe best practice for creating search indexes is to simplify your record structure as much as possible. Each record should contain only enough information to be discoverable on its own, and no more. You can customize this record by deciding which _fields_ are included in the index.\n\n### Transforming fields\n\nBy default, the data in the entry/term/etc that corresponds to the `fields` you've selected will be stored in the index. However, you're able to tailor the values exactly how you want using `transformers`.\n\nEach transformer is a closure that would correspond to a field in your index's `fields` array.\n\n``` php\n'fields' => ['title', 'address'],\n'transformers' => [\n\n    // Return a value to store in the index.\n    'title' => function ($title) {\n        return ucfirst($title);\n    },\n\n    // Return an array of values to be stored.\n    // These will all be separate searchable fields in the index.\n    // $value is the current value\n    // $searchable is the object that $value has been plucked from\n    'address' => function ($value, $searchable) {\n        return [\n            '_geoloc' => $value['geolocation'],\n            'location' => $value['location'],\n            'region' => $value['region'],\n        ];\n    }\n]\n```\n\nAlternatively you can specify a class to handle the transformation. This is useful when you want to cache your config using `php artisan config:cache`.\n\n``` php\n'fields' => ['title', 'address'],\n'transformers' => [\n    'title' => \\App\\SearchTransformers\\MyTransfomer::class,\n]\n```\n\n``` php\nnamespace App\\SearchTransformers;\n\nclass MyTransformer\n{\n    public function handle($value, $field, $searchable)\n    {\n        // $value is the current value\n        // $field is the index from the transformers array\n        // $searchable is the object that $value has been plucked from\n\n        return ucfirst($value);\n    }\n}\n```\n\n### Updating indexes\n\nWhenever you save an item in the Control Panel it will automatically update any appropriate indexes. If you edit content by hand, you can tell Statamic to scan for new and updated records via the command line.\n\n``` shell\n# Select which indexes to update\nphp please search:update\n\n# Update a specific index\nphp please search:update name\n\n# Update all indexes\nphp please search:update --all\n```\n\n### Connecting indexes\n\nWhen a search is performed in the control panel (in collections, taxonomies, or asset containers, for example), Statamic will search the configured index for that content type.\n\nIf an index _hasn't_ been defined or specified, Statamic will perform a less efficient, generic search. It'll be slower and less relevant, but better than nothing.\n\nYou can define which search index will be used by adding it to the respective YAML config file:\n\n``` yaml\n# content/collections/blog.yaml\ntitle: Blog\nsearch_index: blog\n```\n\n:::tip About entries\nAfter specifying that an index contains entries from a collection (in [searchables](#searchables)), you **must also** specify the index in the collection config itself because collections and entries can be in multiple indexes.\n\nAlso, since draft entries are not included in search indexes by default, you'll want to include them for your collection-linked index. You can add a filter that allows everything.\n\n```php\n'articles' => [\n    'driver' => 'local',\n    'searchables' => ['collection:articles'],\n    'filter' => fn () => true, // [tl! ++]\n]\n```\n:::\n\n### Localization\n\nYou may choose to use separate indexes to store localized content. For example, English entries go in one index, French entries go in another, and so on.\n\nTake these site and search configs for example:\n\n```yaml\n# resources/sites.yaml\nen:\n  url: /\nfr:\n  url: /fr/\nde:\n  url: /de/\n```\n\n```php\n// config/statamic/search.php\n'indexes' => [\n    'default' => [\n        'driver' => 'local',\n        'searchables' => 'content',\n    ]\n]\n```\n\nBy default, all entries will go into the `default` index, regardless of what site they're in. You can enable localization by setting the `sites` you want.\n\n```php\n'indexes' => [\n    'default' => [\n        'driver' => 'local',\n        'searchables' => 'content',\n        'sites' => ['en', 'fr'], // You can also use \"all\" [tl! ++ **]\n    ]\n]\n```\n\nThis will create dynamic indexes named after the specified sites:\n\n- `default_en`\n- `default_fr`\n\nIf you have a localized index and include searchables that do not support localization (like assets or users), they will appear in each localized index.\n\n### Customizing index names\n\nYou can override how index names are generated by registering a callback in a service provider's `boot` method. This is handy when you want to prefix names by environment to avoid collisions between staging and production when using a shared cloud provider like Algolia.\n\n```php\nuse Statamic\\Search\\Algolia\\Index as AlgoliaIndex;\n\npublic function boot()\n{\n    AlgoliaIndex::resolveNameUsing(fn ($name, $locale) => app()->environment().'_'.($locale ? $name.'_'.$locale : $name));\n}\n```\n\nThe callback receives the configured `$name` and the `$locale` (or `null` for non-localized indexes). Statamic's default naming logic is bypassed entirely, so if you want the locale suffix, append it yourself.\n\n\n## Drivers\n\nStatamic takes a \"driver\" based approach to search engines. Drivers are interchangeable so you can gain new features or integrate with 3rd party services without ever having to change your data or frontend.\n\nThe native [local driver](#local-driver) is simple and requires no additional configuration, while the included [algolia driver](#algolia-driver) makes it super simple to integrate with [Algolia](https://algolia.com), one of the leading search as a service providers.\n\nYou can build your own custom search drivers or look at the [Addon Marketplace](https://statamic.com/addons/tags/search) to see what the community has already created.\n\n### Local {#local-driver}\n\nThe `local` driver (aka \"Comb\") uses JSON to store key/value pairs, mapping fields to the content IDs they belong to. It lacks advanced features you would see in a service like Algolia, but hey, It Just Works™. It's a great way to get a search started quickly.\n\n#### Settings\n\nYou may provide local driver specific settings in a `settings` array.\n\n```php\n'driver' => 'local',\n'searchables' => 'content',\n// [tl! **:start]\n'min_characters' => 3,\n'use_stemming' => true,\n// [tl! **:end]\n```\n\n- `match_weights`: An array of weights for each field to use when calculating relevance scores. Defaults to:\n    ```php\n    [\n        'partial_word' => 1,\n        'partial_first_word' => 2,\n        'partial_word_start' => 1,\n        'partial_first_word_start' => 2,\n        'whole_word' => 5,\n        'whole_first_word' => 5,\n        'partial_whole' => 2,\n        'partial_whole_start' => 2,\n        'whole' => 10,\n    ]\n    ```\n- `min_characters`: The minimum number of characters required in a search query. Defaults to `1`.\n- `min_word_characters`: The minimum number of characters required in a word in a search query. Defaults to `2`.\n- `score_threshold`: The minimum score required for a result to be included in the search results. Defaults to `1`.\n- `property_weights`: An array of weights for each property to use when calculating relevance scores. Defaults to `[]`.\n- `query_mode`: The query mode to use when searching (e.g. \"whole\", \"words\", \"boolean\"). Defaults to `boolean`.\n- `use_stemming`: Whether to match singular and plural forms of each word (e.g. \"cats\" matches \"cat\"). Powered by Laravel's `Str::singular()`, so it handles English inflection only — not true verb stemming like \"jumping\" → \"jump\". Only applies when `query_mode` is `words` or `boolean`. Defaults to `false`.\n- `use_alternates`: Whether to match a small set of typographic alternates in the query. Specifically: `and` ↔ `&`, and swapping between straight (`'`) and curly (`‘` / `’`) apostrophes. It does _not_ handle US/UK spelling differences (e.g. \"color\" / \"colour\") or synonyms. Only applies when `query_mode` is `words` or `boolean`. Defaults to `false`.\n- `include_full_query`: Whether to include the full search query in the search results. Defaults to `true`.\n- `stop_words`: An array of stop words to exclude from the search query. Defaults to `[]`.\n- `limit`: Whether to limit the number of results returned. Defaults to `null`.\n- `enable_too_many_results`: Whether to enable a warning when too many results are returned. Defaults to `false`.\n- `sort_by_score`: Whether to sort the search results by relevance score. Defaults to `true`.\n- `group_by_category`: Whether to group the search results by category. Defaults to `false`.\n- `exclude_properties`: An array of properties to exclude from the search results. Defaults to `[]`.\n- `include_properties`: An array of properties to include in the search results. Defaults to `[]`.\n\n### Algolia {#algolia-driver}\n\nAlgolia is a full-featured search and navigation cloud service. They offer fast and relevant search with results in under 100 ms (99% under 20 ms). Results are prioritized and displayed using a customizable ranking formula.\n\n``` php\n'default' => [\n    'driver' => 'algolia',\n    'searchables' => 'content',\n],\n```\n\nTo set up the Algolia driver, create an account on [their site](https://www.algolia.com/), drop your API credentials into your `.env`, and install the composer dependency.\n\n``` env\nALGOLIA_APP_ID=your-algolia-app-id\nALGOLIA_SECRET=your-algolia-admin-key\n```\n\n``` shell\ncomposer require algolia/algoliasearch-client-php:^3.4\n```\n\nStatamic will automatically create and sync your indexes as you create and modify entries once you kick off the initial index creation by running the command `php please search:update`.\n\n#### Settings\nYou may provide Algolia-specific [settings](https://www.algolia.com/doc/api-reference/settings-api-parameters/) in a `settings` array.\n\n```php\n'driver' => 'algolia',\n'searchables' => 'content',\n'settings' => [ // [tl! **:start]\n    'attributesForFaceting' => [\n        'filterOnly(post_tags)',\n        'filterOnly(post_categories)',\n    ],\n    'customRanking' => [\n        'asc(attribute1)',\n        'desc(attribute2)',\n        'typo',\n        'words'\n    ]\n] // [tl! **:end]\n```\n\n#### Templating with Algolia\n\nWe recommend using the [Javascript implementation](https://www.algolia.com/doc/api-client/getting-started/install/javascript/?language=javascript) to communicate directly with them for the frontend of your site. This bypasses Statamic entirely in the request lifecycle and is incredibly fast.\n\n\n## Extras\n\n### Config cascade\n\nYou can add values into the defaults array, which will cascade down to all the indexes, regardless of which driver they use.\n\nYou can also add values to the drivers array, which will cascade down to any indexes using that respective driver. A good use case for this is to share API credentials across indexes.\n\nAny values you add to an individual index will only be applied there.\n\n\n## Control Panel\n\nStatamic configures a `cp` search index behind the scenes, used by the Command Palette.\n\nBy default, it uses the `local` driver and includes content (entries/terms/assets), users, and any addon-provided searchables.\n\nYou can override the configuration in your `search.php` config file:\n\n```php\n// config/statamic/search.php\n\n'indexes' => [  \n\n\t// ...\n  \n    'cp' => [  \n        'driver' => 'local',  \n        'searchables' => ['content', 'users', 'addons'],  \n        'fields' => ['title'],\n    ],\n    \n],\n```\n\n\n## Digging deeper\n\nSearch is split into a handful of different parts behind the scenes.\n\n- An `Index` class will exist for each configured index. It will know how to send data to and from the underlying driver.\n- `Searchable` classes are the things that can be indexed and searched. (e.g. an `Entry`)\n- `ProvidesSearchables` classes (or just \"Providers\") are classes that define all the applicable searchable items. (e.g. an `Entries` provider gives `Entry` instances.)\n- `Result` classes are wrappers that allow Statamic to use the searchable objects in a consistent way. (e.g. each result should have an identifier, type, Control Panel URL, etc)\n\n\n### Custom Searchables\n\nIn order to allow searching of custom items, you must create a provider and make your object implement Searchable.\n\nIn the following examples, we'll assume you are wanting to store products as Eloquent models.\n\n#### Implement Searchable\n\nThe object you want to make searchable should implement both the `Statamic\\Contracts\\Data\\Augmentable` and `Statamic\\Contracts\\Search\\Searchable` interfaces. To make things easier, you can import the `HasAugmentedData` and `Searchable` traits which will handle most of the heavy lifting for you.\n\n```php\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Statamic\\Contracts\\Data\\Augmentable;\nuse Statamic\\Contracts\\Search\\Result as ResultContract;\nuse Statamic\\Contracts\\Search\\Searchable as SearchableContract;\nuse Statamic\\Data\\HasAugmentedData;\nuse Statamic\\Search\\Result;\nuse Statamic\\Search\\Searchable;\n\nclass Product extends Model implements Augmentable, ContainsQueryableValues, SearchableContract\n{\n    use HasAugmentedData, Searchable;\n\n    /**\n     * The identifier that will be used in search indexes.\n     */\n    public function getSearchReference(): string\n    {\n        return 'product::'.$this->id;\n    }\n\n    /**\n     * The indexed value for a given field.\n     */\n    public function getSearchValue(string $field)\n    {\n        return $this->$field;\n    }\n\n    /**\n     * Convert to a search result class.\n     * You can use the Result class, or feel free to create your own.\n     */\n    public function toSearchResult(): ResultContract\n    {\n        return new Result($this, 'product');\n    }\n\n/**\n     * Returns an array of the model's attributes to be used in augmentation.\n     */\n    public function augmentedArrayData()\n    {\n        return $this->attributesToArray();\n    }\n}\n```\n\nWhen you're making a searchable from an Eloquent model, you'll need to override some `offset`/`__call` methods to prevent conflicts between Eloquent and Statamic's implementations of those methods:\n\n```php\n/**\n * These methods exist on both Eloquent's Model class and in Statamic's HasAugmentedInstance trait.\n * To prevent conflicts, we need to override these methods and force them to call Eloquent's method.\n */\n\npublic function offsetGet($offset): mixed\n{\n    return parent::offsetGet($offset);\n}\n\npublic function offsetSet($offset, $value): void\n{\n    parent::offsetSet($offset, $value);\n}\n\npublic function offsetUnset($offset): void\n{\n    parent::offsetUnset($offset);\n}\n\npublic function offsetExists($offset): bool\n{\n    return parent::offsetExists($offset);\n}\n\npublic function __call($method, $parameters)\n{\n    return parent::__call($method, $parameters);\n}\n```\n\n#### Create Provider\n\nYou should have a class that implements `ProvidesSearchables`, however it's even easier for you to extend `Provider`.\n\n```php\nuse Statamic\\Search\\Searchables\\Provider;\n\nclass ProductProvider extends Provider\n{\n    /**\n     * The handle used within search configs.\n     *\n     * e.g. For 'searchables' => ['collection:blog', 'products:hats', 'users']\n     *      they'd be 'collection', 'products', and 'users'.\n     */\n    protected static $handle = 'products';\n\n    /**\n     * The prefix used in each Searchable's reference.\n     *\n     * e.g. For 'entry::123', it would be 'entry'.\n     */\n    protected static $referencePrefix = 'product';\n\n    /**\n     * Convert an array of keys to the actual objects.\n     * The keys will be searchable references with the prefix removed.\n     */\n    public function find(array $keys): Collection\n    {\n        return Product::find($keys);\n    }\n\n    /**\n     * Get all searchables and return a collection of \n     * their references.\n     * \n     * e.g. 'entry::123'\n     */\n    public function provide(): Collection\n    {\n        return Product::query()\n            ->pluck('id')\n            ->map(fn ($id) => \"product::{$id}\");\n\n        // If you wanted to allow subsets of products, you could specify them in your\n        // config then retrieve them appropriately here using keys.\n        // e.g. 'searchables' => ['products:hats', 'products:shoes'],\n        // $this->keys would be ['keys', 'hats'].\n        return Product::whereIn('type', $this->keys)\n            ->pluck('id')\n            ->map(fn ($id) => \"product::{$id}\");\n    }\n\n    /**\n     * Check if a given object belongs to this provider.\n     */\n    public function contains($searchable): bool\n    {\n        return $searchable instanceof Product;\n    }\n}\n```\n\n#### Register the Provider\n\nIn order to use the provider, first register it within a service provider, and then it will be available to your search config by its handle.\n\n```php\nuse Statamic\\Facades\\Search;\n\npublic function boot()\n{\n    ProductProvider::register();\n}\n```\n\n```php\n// config/statamic/search.php\n\n'indexes' => [\n    'products_and_blog_posts' => [\n        'driver' => 'local',\n        'searchables' => [\n            'products', // [tl! ++]\n            'collections:blog'\n        ],\n        'fields' => ['title']\n    ]\n]\n```\n\nYou can also include your searchable in the `content` or `addons` wildcard searchables, which are used by default for front-end and Control Panel searches.\n\n```php\n// Pass the handle...\nSearch::addContentSearchable('product');\nSearch::addCpSearchable('order');\n\n// Or the provider class...\nSearch::addContentSearchable(ProductsProvider::class);\nSearch::addCpSearchable(OrdersProvider::class);\n```\n\n#### Event Listeners\n\nYou will want to update the indexes when you create, edit, or delete your searchable items.\n\nContinuing with the Eloquent example, we can hook into model events in your service provider.\n\n```php\nuse Illuminate\\Support\\Facades\\Event;\nuse Statamic\\Facades\\Search;\n\nEvent::listen('eloquent.saved: App\\Models\\Product', function ($model) {\n    Search::updateWithinIndexes($model);\n});\n\nEvent::listen('eloquent.deleted: App\\Models\\Product', function ($model) {\n    Search::deleteFromIndexes($model);\n});\n```\n\nThe `updateWithinIndexes` method will update the record in all appropriate indexes. If a filter determines that the record should be removed (e.g. if it changed into a draft), it'll remove it.\n\nThe `deleteFromIndexes` method will remove it from all appropriate indexes.\n\n### Custom Index Drivers\n\nStatamic comes with two native search index drivers: Comb and Algolia. Comb is our \"local\" driver, where indexes are stored as json files. Algolia integrates with the service using their API.\n\nFor this example, we'll integrate with a fictional service called FastSearch.\n\n#### Create Index\n\nYou should have a class that extends `Index`.\n\n```php\nuse Statamic\\Search\\Index;\n\nclass FastSearchIndex extends Index\n{\n    private $client;\n\n    public function __construct(FastSearchClient $client, $name, array $config, string $locale = null)\n    {\n        // In this example, we'll accept a fictional client class that will perform API requests.\n        // If you have a constructor, don't forget to construct the parent class too.\n        $this->client = $client;\n        parent::__construct($name, $config, $locale);\n    }\n\n    /**\n     * Return a query builder that will perform the search.\n     */\n    public function search($query)\n    {\n        return (new FastSearchQuery($this))->query($query);\n    }\n\n    /**\n     * Check whether the index actually exists.\n     * i.e. Does it exist in the service, or as a json file, etc.\n     */\n    public function exists()\n    {\n        $this->client->indexExists($this->name);\n    }\n\n    /**\n     * Insert items into the index.\n     */\n    public function insertDocuments(Documents $documents)\n    {\n        $this->client->insertObjects($documents->all());\n    }\n\n    /**\n     * Delete an item from the index.\n     */\n    public function delete($document)\n    {\n        $this->client->deleteObject($document);\n    }\n\n    /**\n     * Delete the entire index.\n     */\n    protected function deleteIndex()\n    {\n        $this->client->deleteIndex($this->name);\n    }\n}\n```\n\n#### Register Index\n\n```php\npublic function boot()\n{\n    Search::extend('fast', function ($app, $config, $name) {\n        $client = new FastSearchApiClient('api-key');\n        return new FastSearchIndex($client, $name, $config);\n    });\n}\n```\n\n#### Create Query Builder\n\nIn the index class, the `search` method wanted a query builder. You can create a class that extends our own, which only requires you to define a single method.\n\n```php\n<?php\n\nnamespace App\\Search;\n\nuse Statamic\\Search\\QueryBuilder;\nuse Statamic\\Support\\Str;\n\nclass CustomSearchQuery extends QueryBuilder\n{\n    /**\n     * Get search results as an array.\n     * e.g. [\n     *  ['title' => 'One', 'search_score' => 500],\n     *  ['title' => 'Two', 'search_score' => 400],\n     * ]\n     */\n    public function getSearchResults()\n    {\n        $results = $this->index->searchUsingApi($query);\n\n        // Statamic will expects a search_score to be in each result.\n        // Some services like Algolia don't have scores and will just return them in order.\n        // This is a trick to set the scores in sequential order, highest first.\n        return $results->map(function ($result, $i) use ($results) {\n            $result['search_score'] = $results->count() - $i;\n\n            return $result;\n        });\n    }\n}\n```\n\nThis `getSearchResults` method is used in the parent class in order to allow basic filtering and other query methods. Of course, you are free to build as much of your own query builder as you like.\n"
  },
  {
    "path": "content/collections/pages/sites-api.md",
    "content": "---\nid: 124cb9e8-b5e0-4af6-8271-98ca6b0131b4\nblueprint: page\ntitle: 'Sites API'\nintro: 'We have an API that can be used to manage your Statamic Sites in your [statamic.com](https://statamic.com) account. This is most useful with our Platform Plan, which you can [contact us](https://statamic.com/support) directly about for more information.'\n---\n\n## Authentication\n\nTo access the sites API, you'll need to create a token in your statamic.com account. All of the following endpoints require that your request contains an `Authorization` header with your token preceded by `Bearer`.\n\n### Example Headers\n\n| Header Name | Header Value |\n| --- | --- |\n| `Authorization` | `Bearer 44\\|seLDYsDrqyxS2cT8PYremnysuqrovpYHSJ1lzjb3` |\n| `Accept` | `application/json` |\n\n### Using Laravel's HTTP Facade\n\nIf you are using Laravel's `Http` facade to make your requests, you can use the `acceptJson()` and `withToken()` helper methods to make simple JSON requests with your bearer token:\n\n```php\nHttp::acceptJson()\n  ->withToken($token)\n  ->post('https://statamic.com/api/v1/sites', $payload);\n```\n\n_*For more info, read more about [headers](https://laravel.com/docs/13.x/http-client#headers) and [bearer tokens](https://laravel.com/docs/13.x/http-client#bearer-tokens) in Laravel.*_\n\n## Endpoints\n\n- [Sites Index](#sites-index)\n- [Create Site](#create-site)\n- [Delete Site](#delete-site)\n\n### Sites Index\n\n#### `GET https://statamic.com/api/v1/sites`\n\n#### Example Output\n\n```json\n{\n  \"data\": [\n    {\n      \"name\": \"Wayne's World\",\n      \"key\": \"pg4x2qrly2my8dl1\",\n      \"domains\": [\n        \"waynesworld.ca\"\n      ],\n      \"created_at\": \"2021-11-19 09:32:52\"\n    },\n    {\n      \"name\": \"Bobby's World\",\n      \"key\": \"1o0xe7rzdd9wq58j\",\n      \"domains\": [\n        \"bobbysworld.ca\"\n      ],\n      \"created_at\": \"2021-11-19 09:33:10\"\n    }\n  ]\n}\n```\n\n### Create Site\n\n#### `POST https://statamic.com/api/v1/sites`\n\n#### Params\n\n| Param | Required | Example |\n| --- | --- | --- |\n| `name` | yes | `Jurassic World` |\n| `domain` | no | `jurassicworld.ca` |\n\n#### Example Output\n\n```json\n{\n  \"data\": {\n    \"name\": \"Jurassic World\",\n    \"key\": \"pwkknrxl6y7z1n9v\",\n    \"domains\": [\n      \"jurassicworld.ca\"\n    ],\n    \"created_at\": \"2021-11-18 19:45:41\"\n  }\n}\n```\n\n### Delete Site\n\n#### `DELETE https://statamic.com/api/v1/sites/[your-site-key-here]`\n\n#### Example Output\n\n```json\n{\n  \"message\": \"Site [pwkknrxl6y7z1n9v] deleted.\"\n}\n```\n"
  },
  {
    "path": "content/collections/pages/slugs.md",
    "content": "---\ntitle: Slugs\nid: d84613d5-2b2b-465d-8f02-c71b6d2aadf1\n---\n## Slugify Vue Component\n\nYou can use the `<slugify>` component to generate a slug based off another property:\n\n``` html\n<slugify :from=\"title\" v-model=\"slug\">\n    <input slot-scope=\"{}\" :value=\"slug\" @input=\"slug = $event.target.value\" />\n</slugify>\n```\n\nWhen the value of the `from` prop changes (ie. the `title` property), it will update the `slug` property.\n\nIf you update the slug manually (ie. by typing in the field), the component will realize, and prevent any further automatic slug generation.\n\nIf you want underscores instead of dashes, you can pass in `separator=\"_\"`.\n\n\n## JavaScript API\n\n### Basic Slugs\nYou may also create slugs programmatically.\n\n```js\nimport { slug } from '@statamic/cms/api';\n\nslug.create('Hello World'); // hello-world\n```\n\nYou may also define the separating character:\n\n```js\nimport { slug } from '@statamic/cms/api';\n\nslug.separatedBy('_').create('Hello World'); // hello_world\n```\n\nYou may use the `str_slug` and `snake_case` global methods respectively as aliases for both of these:\n\n```js\nstr_slug('Hello World'); // hello-world\nsnake_case('Hello World'); // hello_world\n```\n\n### More Oomph\n\nWhen you need more accurate slugs, you can leverage PHP's more powerful slug logic. By calling `async`, the `create` method will become Promise-based as it requests slugs from the server:\n\n```js\nimport { slug } from '@statamic/cms/api';\n\nslug.async().create('Hello World').then(slug => {\n    console.log(slug); // 'hello-world'\n})\n```\n\nThis is particularly useful when you need to provide the language:\n\n```js\nimport { slug } from '@statamic/cms/api';\n\nslug.in('zh').async().separatedBy('_')\n        .create('你好世界')\n        .then(slug => console.log(slug)); // ni_hao_shi_jie\n```\n\n:::tip\nIf you don't provide a language, the language of the selected site in the control panel will be used.\n:::\n\n### Debouncing\n\nIf you will be calling this repeatedly, such as via user's keystrokes, debouncing is useful to prevent excess calls to the server.\n\nDebouncing will be automatically handled as long as you call `create` on the same instance:\n\n```js\nimport { slug } from '@statamic/cms/api';\n\nconst slugger = slug.async().separatedBy('_');\n\nslugger.create('one').then(slug => console.log(slug));\nslugger.create('two').then(slug => console.log(slug));\nslugger.create('three').then(slug => console.log(slug));\n\n// console: 'three'\n```\n"
  },
  {
    "path": "content/collections/pages/stache.md",
    "content": "---\ntitle: Stache\ntemplate: page\nintro: >\n    Instead of using a relational database like MySQL as a storage system, Statamic aggregates the data in your content files into an efficient, index-based system and stores it in Laravel's application cache. We call this the \"stache\", and we like to make mustache jokes about it.\nblueprint: page\nid: 499d808b-18be-42e9-acd0-91bcdff73193\n---\n## Overview\n\n**The stache is ephemeral** and can be blown away and rebuilt from scratch at any time without losing data. This is most often done when content or settings change, or when updates are deployed to a production server.\n\n<figure class='bg-mint'>\n    <img src=\"/img/tom-selleck-lg.jpg\" alt=\"Tom Selleck as Magnum P.I.\">\n    <figcaption>Behold, the stache of all staches!</figcaption>\n</figure>\n\n## The Stache is watching your files {#watcher}\n\nEach page request from the frontend or Control Panel triggers a scan of the `last_modified` timestamps on all content and configuration files in your Statamic application. When Statamic sees a change, the Stache performs selective updates to any corresponding indexes.\n\n:::best-practice\nThis is great for local development, but on a production environment **you should make sure the watcher is disabled.** If you're editing content through the control panel, or only ever pushing content through deployments, you are adding extra overhead to every request for no reason.\n\nBy setting it to `auto`, it will be enabled when running on the `local` environment (`APP_ENV=local`) and disabled everywhere else. This is the default behavior for new sites.\n\n``` env\nSTATAMIC_STACHE_WATCHER=auto\n```\n\n``` php\nreturn [\n   'watcher' => env('STATAMIC_STACHE_WATCHER', 'auto'), // [tl! highlight]\n   ...\n];\n```\n\nOf course, you may set it to `false` to explicitly disable it everywhere.\n:::\n\n## Clearing the Stache {#clearing}\n\nThe [CLI](/cli) has commands to clear, warm, and refresh (clear and then immediately warm) the stache.\n\n``` shell\nphp please stache:clear\nphp please stache:warm\nphp please stache:refresh\n```\n\n:::best-practice\nIt's a good idea to perform a `php please stache:refresh` when deploying changes to your production server so they're immediately available for the next request.\n:::\n\n## Parallel warming {#parallel-warming}\n\nFor large sites, you can warm stores in parallel rather than sequentially. On content-heavy projects this can cut warm times by **6x or more** with proportional drops in peak memory usage.\n\nParallel warming is **disabled by default**. Enable it via env vars:\n\n``` env\nSTATAMIC_STACHE_PARALLEL_WARMING=true\nSTATAMIC_STACHE_CONCURRENCY_DRIVER=fork\nSTATAMIC_STACHE_MAX_PROCESSES=0\nSTATAMIC_STACHE_MIN_STORES_PARALLEL=3\n```\n\nOr in `config/statamic/stache.php`:\n\n```php\n// config/statamic/stache.php\n'warming' => [\n    'parallel_processing' => env('STATAMIC_STACHE_PARALLEL_WARMING', false),\n    'max_processes' => env('STATAMIC_STACHE_MAX_PROCESSES', 0),\n    'min_stores_for_parallel' => env('STATAMIC_STACHE_MIN_STORES_PARALLEL', 3),\n    'concurrency_driver' => env('STATAMIC_STACHE_CONCURRENCY_DRIVER', 'process'),\n],\n```\n\n### Concurrency drivers\n\nPowered by [Laravel's Concurrency](https://laravel.com/docs/concurrency) facade:\n\n| Driver | Description |\n|--------|-------------|\n| `process` | Spawns separate PHP processes. Works everywhere, but has the most overhead per task. The default. |\n| `fork`    | Uses the `pcntl` extension to fork the current process. Significantly faster but **CLI-only**. Requires `spatie/fork`. |\n| `sync`    | Runs everything sequentially in the current process. Useful for debugging. |\n\nFor deploys, `fork` is almost always the right choice. Install it with:\n\n``` shell\ncomposer require spatie/fork\n```\n\nThe `fork` driver requires the `pcntl` PHP extension. It's compiled in by default on Linux and macOS (and works on Forge, Vapor, Laravel Cloud, and Herd) but is **unavailable on Windows** and is sometimes disabled on shared hosting. Verify with:\n\n``` shell\nphp -m | grep pcntl\n```\n\nIf `pcntl` isn't available, use the `process` driver instead.\n\n### Tuning\n\n- `max_processes` — `0` auto-detects CPU cores. Bump it up if your CI/deploy box has plenty of headroom.\n- `min_stores_for_parallel` — small sites with only a couple stores won't benefit from parallelism (the orchestration overhead exceeds the win), so this skips it below the threshold.\n\n:::tip\nParallel warming only applies to CLI operations like `php please stache:warm` and `stache:refresh`. Web requests can't fork, so on-demand warming during a request always runs sequentially.\n:::\n\n## Stores\n\nThe Stache is comprised of different stores responsible for fetching their own data sets.\n\nFor instance, if you wanted to get a `Collection` object, the `CollectionStore` would be in charge. It knows that any YAML file inside `content/collections` translates into one.\n\nThe following stores exist in the Stache:\n\n- `taxonomies`\n- `terms` (grouped by taxonomy)\n- `collections`\n- `entries` (grouped by collection)\n- `collection-trees`\n- `navigation`\n- `nav-trees`\n- `globals`\n- `asset-containers`\n- `assets`* (grouped by container)\n- `users`\n\nYou're able to customize all the stores inside the Stache by referencing the keys above. You can change the directories for each of them. You can also change the class if you need to customize any of its logic.\n\n```php\n// config/statamic/stache.php\n'stores' => [\n    'entries' => [\n        'class' => EntriesStore::class,\n        'directory' => base_path('content/collections')\n    ]\n]\n```\n\n:::tip\nIf you only want to change the `directory`, you don't need to include the `class`.\n:::\n\n\\* The `assets` store cannot have its directory customized here. You configure its location through the [container](/assets#containers).\n\n### Excluding stores {#excluding-stores}\n\nYou can exclude a registered store from the `stache:warm` and `stache:clear` operations. This is useful when you've [swapped out a repository](/extending/repositories) with your own implementation (e.g. a custom database-backed driver) and don't want the Stache to waste time warming or clearing stores that aren't backed by files.\n\n```php\nuse Statamic\\Facades\\Stache;\n\nStache::exclude('entries');\nStache::exclude('terms');\n```\n\nCall this from a service provider's `boot` method. The store stays registered (queries still work through it), but it's skipped during warming and clearing.\n\n:::tip\nThe [Eloquent Driver](https://github.com/statamic/eloquent-driver) handles this automatically for whichever repositories you've configured it to manage.\n:::\n\n## Indexes\n\nEach store will organize data from its items into indexes. It'll then use those to narrow down items when performing queries.\n\nFor instance, you will find an index of all entry titles, which might look like this:\n\n``` txt\nentry-id-1: Entry One\nentry-id-2: Entry Two\n```\n\n### Default indexes\n\nAll stores will have a number of predefined indexes, like id and path. Some stores will have their own predefined indexes. eg. Entry stores will also have title, slug, uri, etc.\n\n### When does indexing happen?\n\nIndexes will only be created when needed, when a query is performed.\n\nWhen saving an item, its corresponding values will be updated in each of its store's indexes.\neg. An entry's title will be inserted into the title index, its slug into the slug index, and so on.\n\nWhen deleting, it will be removed from each index.\n\nIndexes may be created in advance by running the following command:\n\n``` shell\nphp please stache:warm\n```\n\n### Configuring additional indexes\n\nTake this tag, for example:\n\n```\n{{ collection:blog awesome:is=\"true\" }}\n```\n\nUnder the hood, it would be doing `->where('awesome', true)`, which would look for the `awesome` index. If it didn't exist, it would create it right there.\n\nCreating an index could take some time, depending on how much content you have.\n\nIf you know you will be needing these indexes in advance, you can add them to a store's configuration in `config/statamic/stache.php`:\n\n``` php\nreturn [\n    'stores' => [\n        // ...\n        'entries' => [\n            'class' => Stores\\EntriesStore::class,\n            'directory' => base_path('content/collections'),\n            'indexes' => [\n                'awesome',\n            ]\n        ],\n        // ...\n    ],\n]\n```\n\nOr, add it to all stores:\n\n``` php\nreturn [\n    'indexes' => [\n        'awesome',\n    ]\n];\n```\n\nAny additional indexes you have will be updated [when appropriate](#when-does-indexing-happen).\n\n## Cache driver\n\nBy default the Stache places its data in the default [Laravel cache store](https://laravel.com/docs/cache#configuration), there's no special configuration necessary to change it.\n\nWhatever your default caching driver is for the rest of your app is where your Stache will be.\n\nBy default it's in the filesystem, but of course you can feel free to use Redis, Memcached, etc.\n\n``` env\nCACHE_STORE=redis\n```\n\nIf you want to change which cache store is used by the Stache, you can change the `statamic.stache.cache_store` configuration key:\n\n```php\n// config/statamic/stache.php\nreturn [\n    'cache_store' => 'stache-cache',\n]\n```\n\n## Locks\n\nStatamic will create indexes and build the cache on demand where appropriate. Depending on the amount of content you have, this\ncould be a resource-heavy operation. To prevent excess CPU and memory usage, subsequent requests will be locked while the cache is being updated.\n\nWhen a page is requested while the cache is being updated, it will wait until it's ready. If it's not ready after the configured timeout\nlength (default of 30 seconds), a 503 response will be served with a `<meta>` tag that'll immediately re-request the page.\n\n``` php\nreturn [\n    'lock' => [\n        'enabled' => true,\n        'timeout' => 30,\n    ]\n]\n```\n\n## Diving even deeper\n\nYou can dive even deeper and learn how to build your own Stache Indexes and fine-tune performance with Michael Aerni's 2021 Statameet talk.\n\n:::watch https://www.youtube.com/embed/KDO2mIRjr18\nDive deeper into the Stache!\n:::\n"
  },
  {
    "path": "content/collections/pages/starter-kits.md",
    "content": "---\nid: 3a2e714c-57de-4b16-a916-7e7aba22de03\ntitle: 'Starter Kits'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/static-caching.md",
    "content": "---\ntitle: 'Static Caching'\ntemplate: page\nblueprint: page\nintro: |\n    Nothing loads faster than static pages. Instead of rendering pages dynamically on demand, Statamic can cache static pages and pass routing to Apache or Nginx with reverse proxying.\nid: ffa24da8-3fee-4fc9-a81b-fcae8917bd74\n---\n## Important preface\n\nCertain features — such as forms with server-side validation, page protection, or content randomization — may not work with static page caching. (You may want to check out the [nocache tag](/tags/nocache) though.) As long as you understand that, you can leverage static caching for maximum performance.\n\nWhatever is on the page the first time it's visited is what will be cached for all users. For example, if you're using page protection and a user who has access visits the page, it'll be accessible to everyone.\n\nQuery parameters are ignored by default, so `/blog` and `/blog?utm_source=twitter` will serve the same cached page. You can [change this behavior](#query-parameters) if needed.\n\nProtected pages are excluded from the static cache by default. If you've written a [custom protection driver](/protecting-content#custom-drivers) whose logic doesn't vary between visitors, you can opt it back into caching by marking it [cacheable](/protecting-content#cacheable-drivers).\n\n:::tip\nYou can **alternatively** use the [static site generator](https://github.com/statamic/ssg) to pre-generate and deploy **fully static HTML sites**.\n:::\n\n## Caching strategies\n\nEach caching strategy can be configured independently. Inside `config/statamic/static_caching.php` you will find two pre-configured strategies - one for each supported driver.\n\n``` php\nreturn [\n    'strategy' => 'half',\n\n    'strategies' => [\n        'half' => [\n            'driver' => 'application',\n        ],\n        'full' => [\n            'driver' => 'file',\n        ]\n    ]\n];\n```\n\nSet `strategy` to the name of the strategy you wish to use, or `null` to disable static caching completely.\n\n## Application driver\n\nThe application driver will store your cached page content within Laravel's cache. We refer to this as **half measure**.\n\nThis will still run every request through a full instance of Statamic but will serve all request data from a pre-rendered cache, speeding up load times often by half or more. This is an easy, one-and-done setting.\n\n``` php\nreturn [\n    'strategy' => 'half',\n\n    'strategies' => [\n        'half' => [\n            'driver' => 'application',\n        ]\n    ]\n];\n```\n\n:::tip\nYou may use the [nocache tag](/tags/nocache) to keep parts of your pages dynamic.\n:::\n\n### Caching 404s\n\nWhen using the application driver, 404 responses are statically cached automatically. This stops a heavy \"page not found\" view from being re-rendered on every request when bots or broken links repeatedly hit non-existent URLs.\n\nYou can go a step further and have Statamic share a single cached 404 across every 404-ing URL. The first 404 is rendered and cached, and every subsequent 404 — regardless of URL — is served that same cached response. Each URL still gets its own cache entry, but the rendering work is skipped.\n\n```php\n// config/statamic/static_caching.php\n\n'share_errors' => true,\n```\n\nBoth behaviors are only available when using half measure. With full measure, the 404 never reaches PHP if the rewrite rules send the request to `index.php`, so there's nothing to cache.\n\n## File driver\n\nThe file driver will generate completely static `.html` pages ready for your web server to serve directly. This means that the HTML files will be loaded before it even reaches PHP.\n\nWe refer to this as <mark>full measure</mark>. This is probably the lightning you seek. ⚡️\n\n``` php\nreturn [\n    'strategy' => 'full',\n\n    'strategies' => [\n        'full' => [\n            'driver' => 'file',\n            'path' => public_path('static'),\n        ]\n    ]\n];\n```\n\n:::tip Heads up!\nWhen using full-measure caching, the [nocache tag](/tags/nocache) will rely on JavaScript.\n:::\n\n\n### Permissions\n\nUsing the file driver, you can configure the permissions for the directories and files that are getting created using the `static_caching.strategies.full` config option.\n\n```php\n'strategies' => [\n    'full' => [\n        'driver' => 'file',\n        'path' => public_path('static'),\n        'permissions' => [ // [tl! focus]\n            'directory' => 0755, // [tl! focus]\n            'file' => 0644, // [tl! focus]\n        ], // [tl! focus]\n    ],\n]\n```\n\n## Server rewrite rules\n\nYou will need to configure its rewrite rules when using full measure caching. Here are the rules for each type of server.\n\n:::tip\nIf you're using Laravel Herd or Laravel Valet, you don't need to worry about configuring rewrite rules locally. They will automatically handle the rewrite rules for you.\n:::\n\n### Apache\n\nOn Apache servers, you can define rewrite rules inside an `.htaccess` file:\n\n``` htaccess\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{QUERY_STRING} !live-preview\nRewriteRule ^ index.php [L]\n\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{QUERY_STRING} live-preview\nRewriteRule ^ index.php [L]\n\nRewriteCond %{DOCUMENT_ROOT}/static/%{REQUEST_URI}_%{QUERY_STRING}\\.html -s\nRewriteCond %{REQUEST_METHOD} GET\nRewriteRule .* static/%{REQUEST_URI}_%{QUERY_STRING}\\.html [L,T=text/html]\n```\n\n:::tip\nWhen you have the `ignore_query_strings` option enabled, replace the last chunk of the `.htaccess` snippet with this:\n\n``` htaccess\nRewriteCond %{DOCUMENT_ROOT}/static%{REQUEST_URI}\\.html -f\nRewriteRule ^ static%{REQUEST_URI}\\.html [L]\n\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteRule ^ index.php [L]\n```\n:::\n\n### Nginx\n\n:::tip\nIf you're using [Laravel Forge](https://forge.laravel.com) and selected the \"Statamic\" type when creating your site, this will already be configured for you.\n:::\n\nOn Nginx servers, you will need to edit your `.conf` files. They are not located within your project, and may be in a slightly different place depending on your server setup.\n\nIf you're using a service like [Laravel Forge](https://forge.laravel.com) or [Ploi](https://ploi.io/statamic), you can edit your `nginx.conf` from within the UI.\n\n``` nginx\nset $try_location @static;\n\nif ($request_method != GET) {\n    set $try_location @not_static;\n}\n\nif ($args ~* \"live-preview=(.*)\") {\n    set $try_location @not_static;\n}\n\nlocation / {\n    try_files $uri $try_location;\n}\n\nlocation @static {\n    try_files /static${uri}_$args.html $uri $uri/ /index.php?$args;\n}\n\nlocation @not_static {\n    try_files $uri /index.php?$args;\n}\n```\n\n:::tip\nWhen you have the `ignore_query_strings` option enabled, you should update the `try_files` line inside the `@static` block:\n\n``` nginx\nlocation @static {\n    try_files /static${uri}_$args.html $uri $uri/ /index.php?$args; # [tl! remove]\n    try_files /static${uri}_.html $uri $uri/ /index.php?$args; # [tl! add]\n}\n```\n:::\n\n\n:::tip\nIf your site needs to support URLs with a trailing slash, make sure to update the NGINX config:\n\n``` nginx\nlocation / {\n    try_files $uri $try_location; # [tl! remove]\n    try_files $uri $uri/ $try_location; # [tl! add]\n}\n\nlocation @static {\n    try_files /static${uri}_$args.html $uri $uri/ /index.php?$args; # [tl! remove]\n    rewrite ^/(.*)/$ /$1 last; # [tl! add]\n    try_files /static${uri}_$args.html /static${uri}/_$args.html $uri $uri/ /index.php?$args; # [tl! add]\n}\n\nlocation @not_static {\n    try_files $uri /index.php?$args; # [tl! remove]\n    try_files $uri $uri/ /index.php?$args; # [tl! add]\n}\n```\n:::\n\n\n### IIS\n\nOn Windows IIS servers, your rewrite rules can be placed in a `web.config` file.\n\n``` xml\n<rule name=\"Static Caching\" stopProcessing=\"true\">\n  <match url=\"^(.*)\"  />\n  <action type=\"Rewrite\" url=\"/static/{R:1}_{QUERY_STRING}.html\"  />\n</rule>\n```\n\n## Warming the static cache\n\nBefore users visit your website, you may wish to warm the static cache to make first time loads much faster. To do this, run:\n\n```\nphp please static:warm\n```\n\nThe `static:warm` command supports various arguments:\n\n* **`--queue`**\n    Indicates that URIs should be warmed on the queue (in the background).\n* **`--insecure`**\n    Allows the command to skip SSL verification. This can come in handy when running the site behind a reverse proxy or when using self-signed certificates, for example.\n* **`--user` and `--password`**\n    Allows you to specify credentials to be used when your site is secured with [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme). Otherwise, you might end up with a `401 Unauthorized` error running the command.\n* **`--uncached`**\n    Ensure that only *uncached* pages are warmed. Perfect for when you just want to 'fill in the gaps' in your cache after some URLs were invalidated, without visiting every single URL in your website. This avoids unnecessary server load.\n* **`--include` and `--exclude`**\n    Accepts a comma-separated list of URLs you'd like to be included/excluded in the warming process.\n    Example: `--include='/about,/contact,/blog/*'`\n* **`--max-depth`**\n    Allows you to specify the max depth of pages that should be warmed.\n    For example with `--max-depth=1` it will visit pages like `/about` and `/products` but not `/products/cool-new-shoes-1` or `/any/other/path/that/is/too/deep`.\n* **`--max-requests`**\n    Limits the number of requests made by the command. Likely makes the most sense to be used alongside the `--uncached` option.\n* **`--header`**\n    Allows you to specify custom HTTP headers to be sent with each request. Can be used multiple times to set multiple headers. Useful for APIs, protected routes, or any scenario where custom headers are required. \n\n    For example: `--header=\"Authorization: Bearer your_token\" --header=\"X-Ignore-Cache: true\"`\n\n    You can find [practical examples](#custom-headers) of this parameter below.\n\nDepending on your site's setup, it might be a good idea to add this command to your deployment script.\n\n### Concurrency\n\nYou may configure the amount of concurrent requests when warming the static cache in your strategy.\n\nBy default the pool will use `25`, but feel free to adjust it up or down based on your server's resources.\n\n\n```php\n    'strategies' => [\n        'full' => [\n            'driver' => 'file',\n            'path' => public_path('static'),\n            'lock_hold_length' => 0,\n            'warm_concurrency' => 10, // [tl! highlight]\n        ],\n    ],\n```\n\n:::tip\nLower the `warm_concurrency` to reduce the overhead and slow the process down, raise it to warm faster by using more CPU.\n:::\n\n### Queuing\n\nWhen you're using a queue driver other than `sync`, Statamic will push the warming out to the queue.\n\nIf needed, you can explicitly tell Statamic which queue and queue connection should be used when warming the static cache:\n\n```php\n// config/statamic/static_caching.php\n\n'warm_queue' => env('STATAMIC_STATIC_WARM_QUEUE'),\n\n'warm_queue_connection' => env('STATAMIC_STATIC_WARM_QUEUE_CONNECTION'),\n```\n\n```\nSTATAMIC_STATIC_WARM_QUEUE=warming\nSTATAMIC_STATIC_WARM_QUEUE_CONNECTION=database\n```\n\n### Warming additional URLs\n\nStatamic will automatically warm pages for entries, taxonomy terms and any basic `Route::statamic()` routes. If you wish to warm additional URLs as part of the `static:warm` command, you may add a hook into your `AppServiceProvider`'s `boot` method:\n\n```php\nuse Statamic\\Console\\Commands\\StaticWarm;\n\nclass AppServiceProvider\n{\n    public function boot()\n    {\n        StaticWarm::hook('additional', function ($urls, $next) {\n            return $next($urls->merge([\n                '/custom-1',\n                '/custom-2',\n                'https://different-domain.com/custom-3',\n            ]));\n        });\n    }\n}\n```\n\nWhen you're adding a lot of additional URLs, you may want to use a dedicated class instead:\n\n```php\nuse App\\StaticWarmExtras;\nuse Statamic\\Console\\Commands\\StaticWarm;\n\nclass AppServiceProvider\n{\n    public function boot()\n    {\n        StaticWarm::hook('additional', function ($urls, $next) {\n            return $next($urls->merge(StaticWarmExtras::handle()));\n        });\n    }\n}\n```\n\n### Custom headers\n\nThe `--headers` option can be used in advanced scenarios to control how the static cache is warmed. Here are some practical examples:\n\n#### Bypassing cache for refreshes with Nginx\n\nIf you have custom Nginx rules, you can check for a specific header (e.g., `X-Cache-Refresh: 1`) and bypass the `try_files` static cache, forcing a fresh request to the backend. For example:\n\n```nginx\nlocation / {\n    if ($http_x_cache_refresh = \"1\") {\n        proxy_pass http://127.0.0.1:8000; # your statamic server\n        break;\n    }\n    try_files $uri $try_location;\n}\n```\n\nThen, you can run:\n\n```\nphp please static:warm --header=\"X-Cache-Refresh: 1\"\n```\n\n#### Warming behind authentication\n\nIf your site is protected by HTTP authentication or expects a specific header, you can use `--header` to provide the necessary credentials or tokens so the warm requests are not blocked. For example:\n\n```\nphp please static:warm --header=\"Authorization: Bearer your_token\"\n```\n\nThis ensures the cache warming requests are accepted by your backend even when authentication is required.\n\n### Warming behind Cloudflare\n\nCloudflare's bot protection (particularly \"Verified Bots\" and \"Bot Fight Mode\") can block or challenge the outgoing requests that `static:warm` makes back to your own site, since those requests look like automated traffic. When this happens you'll typically see `403` responses, challenge pages, or silently failing warms.\n\nThe fix is to allow your server's own public IP through Cloudflare's WAF before it hits any bot rules. Create a WAF custom rule:\n\n- **Field:** `IP Source Address`\n- **Operator:** `equals`\n- **Value:** your server's public IPv4 (and IPv6 if applicable)\n- **Action:** `Skip` → skip *All remaining custom rules*, *Managed Rules*, *Rate limiting rules*, and *Bot Fight Mode / Super Bot Fight Mode*\n\nIf you're on a load-balanced or multi-node setup, add each origin IP. Once the rule is in place, `static:warm` requests will bypass bot challenges and complete normally.\n\n:::tip\nIf you can't whitelist an IP (shared hosting, dynamic IPs), an alternative is to send a secret header with `--header=\"X-Warm-Secret: your-token\"` and add a Cloudflare WAF rule that skips bot checks when that header is present. Keep the token out of source control.\n:::\n\n## Excluding Pages\n\nYou may wish to exclude certain URLs from being cached.\n\n```php\nreturn [\n    'exclude' => [\n        'class' => null,\n        'urls' => [\n            '/contact', // [tl! add]\n            '/blog/*',  // Excludes /blog/post-name, but not /blog [tl! add]\n            '/news*',   // Exclude /news, /news/article, and /newspaper [tl! add]\n        ],\n    ],\n];\n```\n\nQuery strings will be omitted from exclusion rules automatically, regardless of whether wildcards are used. For example, choosing to ignore `/blog` will also ignore `/blog?page=2`, etc.\n\n:::tip\nRather than excluding entire pages, you may consider using the [nocache tag](/tags/nocache) to keep parts of your page dynamic, like forms, listings, or randomized areas.\n:::\n\n:::tip Another tip\nCSRF tokens will automatically be excluded from the cache. You don't even need to use a `nocache` tag for that. ([With some exceptions](#csrf-tokens))\n:::\n\nIf you'd like to dynamically exclude URLs from being cached (for example: if you want to add a \"Exclude from Cache\" toggle to entries), you can create your own excluder class:\n\n```php\n// config/statamic/static_caching.php\n\nreturn [\n    'exclude' => [\n        'class' => App\\StaticCaching\\CustomExcluder::class, // [tl! add]\n        'urls' => [],\n    ],\n];\n```\n\n```php\n// app/StaticCaching/CustomExcluder.php\n\n<?php\n\nnamespace App\\StaticCaching;\n\nuse Statamic\\Support\\Str;\nuse Statamic\\StaticCaching\\UrlExcluder;\n\nclass CustomExcluder implements UrlExcluder\n{\n    public function __construct(protected string $baseUrl, protected array $exclusions)\n    {\n    }\n\n    public function getBaseUrl(): string\n    {\n        return $this->baseUrl;\n    }\n\n    public function getExclusions(): array\n    {\n        return $this->exclusions;\n    }\n\n    public function isExcluded(string $url): bool\n    {\n        // Your custom logic here.\n        // Return `true` for any URLs you wish to be excluded.\n        return false;\n    }\n}\n```\n\nAlternatively, you may also prevent URLs from being cached by adding the `X-Statamic-Uncacheable: true` header to requests. \n\n## Invalidation\n\nA statically cached page will be served until it is invalidated. You have several options for how to invalidate your cache.\n\n### Time Limit\n\nWhen using the application driver, you may specify the `expiry` time in minutes in the `static_caching.php` config file. After this length of time, the next request will be served fresh. By leaving the expiry setting `null`, it will never expire, except when you manually run `php artisan cache:clear`.\n\n**The expiry option is not available when using the file driver.** The generated HTML files will be served before PHP ever gets hit, and there's just nothing we can do about that.\n\n### When Saving\n\nWhen saving content, the corresponding item’s URL will be flushed from the static cache automatically.\n\nYou may also set specific rules for invalidating other pages when content is saved. For example:\n\n``` php\nreturn [\n    'invalidation' => [\n        'class' => null,\n        'rules' => [\n            'collections' => [\n                'blog' => [\n                    'urls' => [\n                        '/blog',\n                        '/blog/category/*',\n                        '/',\n                    ],\n                ],\n            ],\n            'taxonomies' => [\n                'tags' => [\n                    'urls' => [\n                        '/blog',\n                        '/blog/category/*',\n                        '/',\n                    ],\n                ],\n            ],\n            'globals' => [\n                'settings' => [\n                    'urls' => [\n                        '/*'\n                    ],\n                ],\n            ],\n            'navigation' => [\n                'links' => [\n                    'urls' => [\n                        '/*'\n                    ],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n#### Explanation\n\n- “when an entry in the blog collection is saved, we should invalidate the `/blog` page, any pages beginning with `/blog/category/`, and the home page.”\n- “when a term in the tags taxonomy is saved, we should invalidate those same pages”\n- “when the settings global set is saved, we invalidate all urls”\n- “when the links navigation is saved, we invalidate all urls”\n\nYou may add as many rules as you need.\n\n#### Invalidating the entire static cache\n\nYou may also choose to invalidate the entire static cache by specifying `all`.\n\n``` php\nreturn [\n    'invalidation' => [\n        'class' => null,\n        'rules' => 'all', // [tl! highlight]\n    ],\n];\n```\n\n#### Using fields in invalidation rules\n\nYou may even use fields from your entry or term's data in invalidation rules, with support for basic if statements!\n\n```php\n'collections' => [\n    'pages' => [\n        'urls' => [\n            '/{parent_uri}',\n            '/offices/{office_slug}/*',\n            '{{ if office_is_headquarters }}/corporate{{ /if }}',\n        ],\n    ],\n],\n```\n\nAs a bonus, you can also use `{parent_uri}` to invalidate the parent entry's URI.\n\n### On a schedule\n\nIf you have the scheduler running, Statamic will use the same set of rules mentioned above, but when scheduled entries are due to become active.\n\nFor example, if you schedule an entry for Friday at 8am, and you have the scheduler running, appropriate pages will be invalidated just as if you had clicked saved on that entry at Friday at 8am.\n\n[Learn how to use the scheduler](/scheduling)\n\n### Custom invalidator class\n\nYou can also specify a custom invalidator class to **programmatically determine which URLs should be invalidated**. To achieve that, override or extend [the default invalidator class](https://github.com/statamic/cms/blob/01f8dfd1cbe304be1848d2e4be167a0c49727170/src/StaticCaching/DefaultInvalidator.php).\n\n```php\nreturn [\n    'invalidation' => [\n        'class' => App\\StaticCaching\\CustomInvalidator::class,  // [tl! highlight]\n        'rules' => [],\n    ],\n];\n```\n\nIt's worth noting that the container binding for the Default Invalidator won't be used now, so you'll need to bind it yourself in your `AppServiceProvider`:\n\n```php\nuse App\\StaticCaching\\CustomInvalidator;\nuse Statamic\\StaticCaching\\Cacher;\n\nclass AppServiceProvider\n{\n    public function boot()\n    {\n        $this->app->bind(CustomInvalidator::class, function ($app) {\n            return new CustomInvalidator(\n                $app[Cacher::class],\n                $app['config']['statamic.static_caching.invalidation.rules']\n            );\n        });\n    }\n}\n```\n\nIn your class you can then define the logic that decides how URLs should get invalidated.\n\n```php\n// app/StaticCaching/CustomInvalidator.php\n\n<?php\n\nnamespace App\\StaticCaching;\n\nuse Statamic\\Entries\\Entry;\nuse Statamic\\StaticCaching\\DefaultInvalidator;\n\nclass CustomInvalidator extends DefaultInvalidator\n{\n    public function invalidate($item)\n    {\n        // Flushes everything by setting the invalidation rules to `all`.\n        if ($this->rules === 'all') {\n            return $this->cacher->flush();\n        }\n\n        $urls = [];\n\n        // Invalidates entries from the `events` collection.\n        if ($item instanceof Entry && $item->collectionHandle() === 'events') {\n            $urls[] = $item->uri();\n        }\n\n        // Flush the URLs we've added to the $urls array.\n        if (count($urls) >= 1) {\n            $this->cacher->invalidateUrls($urls);\n\n            return;\n        }\n\n        // Otherwise, when the $urls array is empty, fallback to the default invalidation logic.\n        parent::invalidate($item);\n    }\n}\n```\n\n### By force\n\nTo clear the static file cache you can run `php please static:clear` (and/or delete the appropriate static file locations).\n\n## Background Re-caching\n\nBy default, when a page is invalidated, the cached item is deleted. This means the next page visitor will get a fresh version, which might be slow.\n\nTo refresh the item rather than delete it, you may opt in to background re-caching.\n\n```php\n'background_recache' => true,\n```\n\nIf you are using full-measure static caching, you will need to adjust your server rewrite rules.\n\n```nginx\nif ($args ~* \"live-preview=(.*)\") {\n    set $try_location @not_static;\n}\n\nif ($arg___recache = \"YOUR-TOKEN\") { # [tl! ++]\n    set $try_location @not_static; # [tl! ++]\n} # [tl! ++]\n\nlocation / {\n    try_files $uri $try_location;\n}\n```\n\nYou can get the token by running the `static:recache-token` command:\n\n```bash\n$ php please static:recache-token\n[INFO] Your token is: YOUR-TOKEN\n```\n\nOr, if you'd rather set it explicitly, you may do that:\n\n```php\n'recache_token' => 'no-one-will-guess-this',\n```\n\n## File locations\n\nWhen using the file driver, the static HTML files are stored in the `static` directory of your webroot, but you can change it.\n\n``` php\nreturn [\n    'strategies' => [\n        'full' => [\n            'driver' => 'file',\n            'path' => public_path('static'),\n        ]\n    ]\n];\n```\n\nYou will need to update your appropriate server rewrite rules.\n\n\n## Query parameters\n\nBy default, Statamic will ignore query parameters and cache each URL once. This is the recommended setting for most sites.\n\nHowever, if you wish to enable this behaviour so pages with different query parameters are cached separately (useful for pagination or displaying pages differently based on user input), you can do so:\n\n```php\nreturn [\n    'ignore_query_strings' => false,\n];\n```\n\n### Allowed and disallowed query parameters\n\nIf you're using half measure caching, you may specify which query parameters Statamic should include in it's \"normalized\" static caching URL. This is useful if you only want certain query parameters to be persisted in your cache:\n\n```php\n'allowed_query_strings' => [\n    'page',\n],\n```\n\n**For example:** if you allow the `page` query parameter, and visit `/blog?page=2&utm_medium=social`, Statamic will serve/write the cached page for `/blog?page=2`.\n\nYou can also do the opposite, by specifying which query parameters should be excluded from the \"normalized\" static caching URL:\n\n```php\n'disallowed_query_strings' => [\n    'fbclid', 'gclid', 'msclkid', 'utm_campaign', 'utm_content', 'utm_medium', 'utm_source', 'utm_term',\n],\n```\n\n**For example:** if you disallow the UTM query parameters, and visit `/blog?page=2&utm_medium=social`, Statamic will serve/write the cached page for `/blog?page=2`.\n\nThe `ignore_query_strings` option should be set to `false` in order for the `allowed_query_strings` & `disallowed_query_strings` to work.\n\n## Multi-site\n\nWhen using static caching alongside [multi-site](/multi-site), some additional configuration is needed.\n\n### Paths\n\nThe `path` config option accepts an array, allowing you to define a different path for each site:\n\n``` php\nreturn [\n    'strategies' => [\n        'full' => [\n            'driver' => 'file',\n            'path' => [\n               'english' => public_path('static') . '/domain.com/',\n               'french' => public_path('static') . '/domain.fr/',\n               'german' => public_path('static') . '/domain.de/',\n            ],\n        ],\n    ],\n];\n```\n\nFor sites with subdirectory URLs rather than separate domains, you should ensure that all sites with the same domain have the same path.\n\nFor example: the `english` and `french` sites below are on the same domain, whereas `german` is on its own domain.\n\n``` php\nreturn [\n    'strategies' => [\n        'full' => [\n            'driver' => 'file',\n            'path' => [\n               'english' => public_path('static') . '/domain.com/',\n               'french' => public_path('static') . '/domain.com/',\n\n               'german' => public_path('static') . '/domain.de/',\n            ],\n        ],\n    ],\n];\n```\n\n_**Note:** You only need to configure paths when you're using full-measure static caching._\n\n### Rewrite rules\n\nWhen you have sites across multiple domains, you will need to modify the rewrite rules on your server to include the domain name.\n\n_**Note:** You only need to configure rewrite rules when you're using full-measure static caching._\n\n#### Apache\n\nYou should update the rewrites in your `.htaccess` file to include `%{HTTP_HOST}`:\n\n``` htaccess\nRewriteCond %{DOCUMENT_ROOT}/static/%{HTTP_HOST}/%{REQUEST_URI}_%{QUERY_STRING}\\.html -s\nRewriteCond %{REQUEST_METHOD} GET\nRewriteRule .* static/%{HTTP_HOST}/%{REQUEST_URI}_%{QUERY_STRING}\\.html [L,T=text/html]\n```\n\n#### Nginx\n\nYou should update the `try_files` line inside the `@static` block:\n\n``` nginx\nlocation @static {\n    try_files /static${uri}_$args.html $uri $uri/ /index.php?$args; # [tl! remove]\n    try_files /static/${host}${uri}_$args.html $uri $uri/ /index.php?$args; # [tl! add]\n}\n```\n\nThe `${host}` argument should correspond to the domains set up in the path. This will be dependant on the server. If you're running different environments and need to use caching for them, you should define the paths using an ENV variable that corresponds to each server domain. The path can be configured in the `static_caching` config:\n\nFor example:\n\n``` php\n'strategies' => [\n    'full' => [\n        'driver' => 'file',\n        'path' => public_path('static') . '/' .env('APP_DOMAIN'), // [tl! focus]\n        'lock_hold_length' => 0,\n        'warm_concurrency' => 10\n    ],\n],\n```\n\nand then on your server\n\n```\n# Production\nAPP_DOMAIN=domain1.com\n\n# Dev\nAPP_DOMAIN=domain1.devserver.com\n```\n\n#### IIS\n\n``` xml\n<rule name=\"Static Caching\" stopProcessing=\"true\">\n  <match url=\"^(.*)\"  />\n  <action type=\"Rewrite\" url=\"/static/{SERVER_NAME}/{R:1}_{QUERY_STRING}.html\"  />\n</rule>\n```\n\n:::tip\n`{SERVER_NAME}` is used here instead of `{HTTP_HOST}` because `{HTTP_HOST}` may include the port.\n:::\n\n### Invalidation rules\n\nIn the [invalidation rules array](#when-saving) explained above, the URLs are relative.\n\nIf you are using sites with multiple domains, you should define URLs in additional domains using absolute URLs. Relative URLs will assume the first site's domain.\n\n```php\nreturn [\n    'invalidation' => [\n        'rules' => [\n            'collections' => [\n                'blog' => [\n                    'urls' => [\n                        '/blog', // [tl! **]\n                        'https://domaintwo.com/articles',  // [tl! **]\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n:::tip\nRather than hardcoding the domains, you could use a config key or a variable.\n\n```php\n<?php\n$two = config('statamic.sites.sites.two.url'); // [tl! **]\n\nreturn [\n    // ...\n    'urls' => [\n        '/blog',\n        $two.'articles', // [tl! **]\n    ]\n```\n:::\n\n## Replacers\n\nWhen a page is being statically cached on the first request, or loaded on subsequent requests, they are sent through \"replacers\".\n\nStatamic includes two replacers out of the box. One will replace [CSRF tokens](#csrf-tokens), the other will handle [nocache](/tags/nocache) tag usages.\n\nA replacer is a class that implements a `Statamic\\StaticCaching\\Replacer` interface. You will be passed responses to the appropriate methods where you can adjust them as necessary.\n\nYou can then enable your class by adding it to `config/statamic/static_caching.php`:\n\n```php\n'replacers' => [\n    CsrfTokenReplacer::class,\n    NoCacheReplacer::class,\n    MyReplacer::class, // [tl!++]\n]\n```\n\n### CSRF Tokens\n\nWhen using half measure, CSRF tokens will be replaced without any caveats.\n\nWhen using full measure, tokens will automatically be replaced in `<input>` and `<meta>` tags where their value/content is the token.\n\n```\n<meta name=\"csrf-token\" content=\"{{ csrf_token }}\" />\n<input type=\"hidden\" name=\"_token\" value=\"{{ csrf_token }}\" />\n```\n\nIf you need to output a CSRF token in another place while using full measure, you'll need to use nocache tags.\n\n```\n<span>\n{{ nocache }} {{# [tl!++] #}}\n    {{ csrf_token }}\n{{ /nocache }} {{# [tl!++] #}}\n</span>\n```\n\n## Locks\n\nTo prevent race conditions, the static cache middleware uses an atomic lock around the bits that write to the cache. If multiple requests for the same uncached URL come in at once, only the first one will render and write the page — the rest wait, and once the lock releases they get served the freshly cached response instead of redundantly writing it themselves.\n\nCached responses are served _without_ acquiring the lock, so a hit is never blocked by a concurrent miss for a different URL.\n\n### Lock store\n\nLocks use [Laravel's atomic cache locks](https://laravel.com/docs/cache#atomic-locks) on the [`static_cache` store](#custom-cache-store) if you've defined one, otherwise the default cache store. For horizontally-scaled setups (multiple app servers) you should point this at a shared driver like Redis or Memcached so the lock is visible across nodes. The default `file` driver only locks within a single server.\n\n### Lock timeout\n\nRequests will wait up to 30 seconds for an in-progress request to finish caching the page. If that timeout is exceeded, you'll get a blank `503 Service Unavailable` response with a meta refresh that retries automatically.\n\n### File write locks\n\nWhen using the `file` driver, there's a separate `lock_hold_length` option that controls how long (in seconds) a worker should retry while another worker holds the file write lock. Defaults to `0` — no retry, the second writer just bails.\n\n```php\n'strategies' => [\n    'full' => [\n        'driver' => 'file',\n        'path' => public_path('static'),\n        'lock_hold_length' => 0, // [tl! highlight]\n    ],\n],\n```\n\nYou generally don't need to touch this — the middleware-level lock above already serializes writes for the same URL. Bump it up only if you're seeing contention from outside the middleware (e.g. multiple `static:warm` runs or external tooling writing to the same directory).\n\n## Custom cache store\n\nStatic caching leverages [Laravel's application cache](https://laravel.com/docs/cache) to store mappings of the URLs to the filenames, as well as the [locks](#locks) used by the middleware. To ensure proper invalidation of changes to your content, Statamic uses a cache store _outside_ of the default one. Otherwise, running the `php artisan cache:clear` command can lead invalidation to fail.\n\nThe cache store can be customized in `config/cache.php`.\n\n```php\n'static_cache' => [\n    'driver' => 'file',\n    'path' => storage_path('statamic/static-urls-cache'),\n],\n```\n\nBy default, running `php artisan cache:clear` won't clear Statamic's cache store. To do this, run `php please static:clear`.\n"
  },
  {
    "path": "content/collections/pages/structures.md",
    "content": "---\nid: 3c34ef5c-781e-4a22-a09b-25f58bdb58a8\nblueprint: page\ntitle: Structures\nintro: 'A structure is a hierarchy of items used to build navigation on the front-end of your site and optionally dictate the URL structure for entire collections.'\ntemplate: page\nrelated_entries:\n  - 2af9fc45-66d0-4ca5-9761-00017076144f\n  - ed746608-87f9-448f-bf57-051da132fef7\n---\n## Overview\n\nStructures are a flexible way to create hierarchies of different items. Statamic 2's \"Pages\" feature has been replaced by a \"Structured Collection\" (more on that in a bit).\n\nEach structure is a hierarchy of links and/or titles. These links may be to entries in one or more [collections](/collections), external URLs, or even anchor links in your content.\n\n<figure>\n    <img src=\"/img/structure.webp\" alt=\"A Statamic structure page tree\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/structure-dark.webp\" alt=\"A Statamic structure page tree\" class=\"u-hide-in-light-mode\">\n    <figcaption>Part of the structure of this very site.</figcaption>\n</figure>\n\n## Two Flavors\n\nStructures come in two flavors, unlike Pringles. They can control the discrete URL pattern and order for a collection (like v2's Pages), or manage ad hoc navigation trees. And _just like_ Pringles &mdash; once you pop the top, you can't stop.\n\n\n## Structured collections\n\nThe first type of structure is for defining the URL structure for a collection. This would be the equivalent of \"Pages\" in Statamic v2.\n\n- An \"orderable\" collection will be using a Structure under the hood.\n- You will only be able to add entries from that collection.\n- The order and arrangement of the entries will dictate their URLs.\n- This will make `parent_uri` and `depth` variables available to the Collection's route.\n- You can only place an entry once.\n- You can create internal and external redirects.\n- The structure is stored on the collection itself, and its tree is stored in `content/trees/collections`.\n\n[Read more about using Structures to manage your Collections](/collections#ordering)\n\n## Navigation (or \"Navs\") {#navigation}\n\nFreestyle navigation structures exist to manage a nav out of existing entries, as well as freeform links and text (non-link) elements.\n\n- You can reference entries, enter hardcoded URLs (internal or external), or enter simple text blocks (which can be used as section headers for dropdown navs, for example).\n- You can select which collections' entries will be available to choose from.\n- Any referenced entries will use the URLs defined by the collection, regardless of the position in the Structure.\n- You can place the same entry multiple times.\n- The structure is stored as a YAML file inside `content/navigation`, and its tree is stored in `content/trees/navigation`.\n\n[Read more about Navigation](/navigation)\n\n\n## Templating\n\nYou can work with the [nav tag](/tags/nav) to loop through and render your HTML with its links.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ nav:top_nav }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n{{ /nav:top_nav }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n  <s:nav:top_nav>\n    <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n  </s:nav:top_nav>\n</ul>\n```\n::\n\n## Tree\n\nThe tree is what defines the structure. It contains an array of items, each of which is considered a \"page\".\n\n``` yaml\ntree:\n  -\n    entry: id-of-about\n    children:\n      -\n        entry: id-of-hobbies\n  -\n    entry: id-of-blog\n  -\n    title: Support\n    children:\n      -\n        entry: id-of-contact\n      -\n        title: 'GitHub Repo'\n        url: 'https://github.com/example/repo'\n```\n\n:::tip\nWhile you **can** edit a structure through the files, it's **much easier** to manage in Control Panel with a simple drag and drop interface.\n:::\n\nEach page may have an optional `children` array which is itself another tree. You can repeat this pattern and go as deep as necessary. The `max_depth` setting on the Structure will prevent you from placing pages any deeper when using the Control Panel.\n\n- An entry reference should contain an `entry` key with a value of the entry's ID. The about, hobbies, blog, and contact pages in the snippet above are examples.\n- A hardcoded link* should contain a `url` key with either an internal or external URL. The `title` is optional. The GitHub page in the snippet above is an example.\n- Text* can just contain a `title`. The Support page above is an example.\n\n_\\* Text and link branches are only available in Navs._\n"
  },
  {
    "path": "content/collections/pages/tags.md",
    "content": "---\nid: 75ce3bdc-03d0-4d6a-a2a5-d867d868d763\ntitle: Tags\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/taxonomies.md",
    "content": "---\nid: 6a18eac8-6139-419c-9d64-a2c960ccc3cd\nblueprint: page\ntitle: Taxonomies\nintro: 'A taxonomy is a system of classifying data around a set of unique characteristics. Scientists have been using this system for years, grouping all living creatures into Kingdoms, Class, Species and so on. Taxonomies are the primary means for grouping content together by topic or a shared attribute.'\nrelated_entries:\n  - 7202c698-942a-4dc0-b006-b982784efb03\n  - ba832b71-a567-491c-b1a3-3b3fae214703\n  - 3f5506d6-03e0-4fcf-b4e8-334c48d51f81\n  - 420f083d-99be-4d54-9f81-3c09cb1f97b7\n---\n## Overview\n\nTaxonomies give you the ability to tag your entries and then fetch and sort all the entries that share any given tag. `Categories` and `tags` are probably the most common taxonomies, but you're not limited to those two. There are many useful taxonomies that can help group and sort your content. For example, `topic`, `color`, `genre`, and `size`.\n\nPractically speaking, taxonomies are very similar to [collections](/collections). They can have their own fields as defined by [blueprints](/blueprints) and also have their own URLs.\n\nEach entry in a taxonomy is often called a **term**.\n\n:::watch https://youtube.com/embed/vssXeEC118M\nWatch how to set up your first Taxonomy\n:::\n\n## Collections\n\nEach collection defines which taxonomies are part of its content model in their blueprint. Thus, taxonomies and their terms are connected to entries _through_ the collection in a strict relationship. Once you attach a taxonomy to a collection, the fields, variables, and routes are added automatically.\n\nTaxonomies can be attached to any number of collections but their terms are _global_, which means that any data stored on each term will be the same no matter the collection it's being related through. This is usually what you want, but if it isn't you can create additional taxonomies for specific collections. For example: `product_tags` in addition to `tags`.\n\n## Blueprints\n\nEach taxonomy uses blueprints to define the available fields when creating and editing its terms.\n\nIf you don't explicitly create a blueprint, your terms will have a basic set of fields: title, markdown content, slug, etc. Of course, you're able to create your own.\n\nIf you create _more than_ one blueprint you'll be given the option to choose which one you want when creating a new term.\n\n## Routing\n\nTaxonomy routes are automatically created for you **if the corresponding view exists**.\n\n:::tip\nURLs use slugs with dashes, and views use handles with underscores.\n:::\n\n- **Global Taxonomy Details**\n  - Display the details of the taxonomy, so you can list the terms.\n  - Accessible at `/{taxonomy-slug}` (eg. `/tags`)\n  - The `{taxonomy_handle}/index` view will be used (eg. `tags/index.antlers.html`)\n- **Global Term details**\n  - Display the details of the term, so you can list the entries.\n  - Accessible at `/{taxonomy-slug}/{term-slug}` (eg. `/tags/t-shirts`)\n  - The `{taxonomy_handle}/show` view will be used. (eg. `tags/show.antlers.html`)\n\nFor each taxonomy [assigned to a collection](#collections) you will also get these routes:\n\n- **Collection Taxonomy Details**\n  - Display the details of the taxonomy, so you can list the terms.\n  - Only terms that have been used in entries in the collection will be displayed.\n  - Accessible at `/{collection-url}/{taxonomy-slug}` (eg. `/products/tags`)\n  - The `{collection_handle}/{taxonomy_handle}/index` view will be used (eg. `products/tags/index.antlers.html`)\n- **Collection Term details**\n  - Display the details of the term, so you can list the entries.\n  - Only entries that exist in the collection will be displayed.\n  - Accessible at `/{collection-url}/{taxonomy-slug}/{term-slug}` (eg. `/products/tags/t-shirts`)\n  - The `{collection_handle}/{taxonomy_handle}/show` view will be used. (eg. `products/tags/show.antlers.html`)\n\n## Term values and slugs\n\nA term **value** is how you might identify a term in your content. For example, “Star Wars”.\n\nA term **slug** is the URL-safe version, and is what Statamic uses internally to track terms, e.g. `star-wars`. The slug is created automatically based on a few rules. Let’s cover them now.\n\nHow we slugify your terms:\n\n``` yaml\ntags:\n  - Star Wars\n  - Tatooine\n  - Droids We're Not Looking For\n```\n\n- The value `Star Wars` will be converted to lowercase, and all spaces and special characters will be replaced with hyphens: `star-wars`.\n- If a term with the slug `star-wars` already exists, the relation is made.\n- If no such term yet exists one will be created, and the entered value (`Star Wars`) will become the title.\n\nTitles are saved on a first-come, first-serve basis, which means consistency is important. If you enter `Star Wars` in one entry, and `star wars` in another, whichever term Statamic encounters first will be used as the title.\n\nTo further clarify, `Star wars`, `star wars`, `StAr WaRS`, and `star-wars` are all treated as the same term. If case-sensitivity is important, you can add a `title` field to the taxonomy blueprint.\n\n## Templating\n\n### Views\n\nTaxonomies use the following view template naming convention:\n\n| Purpose | View |\n|---|---|\n| Taxonomy Index  | `{taxonomy_name}/index` |\n| Single Term | `{taxonomy_name}/show` |\n| Taxonomy Index (for collection)  | `{collection}/{taxonomy_name}/index` |\n| Single Term (for collection) | `{collection}/{taxonomy_name}/show` |\n\nFor example, you would set up your \"topics\" index page in `resources/views/topics/index.antlers.html` and then a specific topic with a list of all entries inside it at `resources/views/topics/show.antlers.html`.\n\nThe collection equivalents would automatically filter terms that have been associated to entries in that collection.\n\n### Outputting terms\n\nTerm values will be [augmented](/augmentation) into term objects and will have access to all data\n\n``` yaml\ntags:\n  - awesome\n  - sauce\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ tags }}\n  {{ title }}, {{ url }}, {{ slug }}, etc\n{{ /tags }}\n```\n::tab blade\n```blade\n@foreach ($tags as $tag)\n  {{ $tag->title }}, {{ $tag->url }}, {{ $tag->slug }}, etc\n@endforeach\n```\n::\n\n```\nAwesome, /tags/awesome, awesome, etc\nSauce, /tags/sauce, sauce, etc\n```\n\nWhen the collection can be inferred, the `url` and `permalink` values will include the collection's URL. (eg. `/blog/tags/awesome` instead of just `/tags/awesome`)\n- ✅ Looping through tags on an entry's page.\n- ✅ Looping through tags while inside a collection tag pair.\n- ✅ Looping through terms in a taxonomy tag pair, using the collection parameter.\n- ❌ Looping through terms in a taxonomy tag pair, without specifying a collection.\n\n### Listings and indexes\n\nWhen on a [taxonomy route](#routing), you can list the terms by using a `terms` tag pair. For example:\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n  {{ terms }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n  {{ /terms }}\n</ul>\n```\n\n:::tip\nYou can replace the `terms` tag with the name of the taxonomy. eg. `{{ tags }}` or `{{ categories }}`\n:::\n\n:::tip\nIf your taxonomy name conflicts with a [tag](/tags), you will need to [disambiguate](/antlers#disambiguating-variables) it by using a dollar symbol (`$`).\n\nFor example, if your taxonomy is named `section`, there is also a [tag named section](/tags/section).\n\n```\n{{ $section }}...{{ /$section }}\n```\n:::\n\n::tab blade\n```blade\n<ul>\n  @foreach ($terms as $term)\n    <li><a href=\"{{ $term->url }}\">{{ $term->title }}</a></li>\n  @endforeach\n</ul>\n```\n\n:::tip\nYou can replace the `terms` tag with the name of the taxonomy. eg. `$tags` or `$categories`\n:::\n::\n\n\n### Listing term entries\n\nWhen on a [term route](#routing), you can list the entries by using an `entries` tag pair. For example:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ entries paginate=\"5\" }}\n  <ul>\n  {{ results }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n  {{ /results }}\n  </ul>\n{{ /entries }}\n```\n::tab blade\n```blade\n@php($results = $entries->paginate(5))\n\n<ul>\n\t@foreach ($results->items() as $result)\n\t\t<li><a href=\"{{ $result->url }}\">{{ $result->title }}</a></li>\n\t@endforeach\n</ul>\n```\n::\n\n## Search indexes\n\nYou can configure search indexes for your collections to improve the efficiency and relevancy of your users searches. Learn [how to connect indexes](/search#connecting-indexes).\n"
  },
  {
    "path": "content/collections/pages/testing.md",
    "content": "---\nid: 15db07e8-6e83-4b6e-89bb-d050b5d2c823\nblueprint: page\ntitle: Testing\ntemplate: page\nnav_title: Testing\nintro: \"There's only one thing better than manual testing... automated testing. Addons are scaffolded with PHPUnit test suites out-of-the-box. Learn how to write & run tests.\"\n---\nWhen you create an addon with the `php please make:addon` command, Statamic will automatically scaffold the necessary files for a PHPUnit test suite:\n\n``` files theme:serendipity-light\ntests/\n    ExampleTest.php\n    TestCase.php\nphpunit.xml\n```\n\n## The `TestCase`\n\nThe `TestCase` class extends Statamic's built-in `AddonTestCase` which is responsible for booting your addon's service provider, amongst other things. \n\nUnder the hood, Statamic's `AddonTestCase` extends [Orchestra Testbench](https://github.com/orchestral/testbench)'s `TestCase` class. Testbench allows you to test against a *real* Laravel application.\n\nIf you need to change any config settings for your test suite, like enabling Statamic Pro or configuring the REST API, add a `resolveApplicationConfiguration` method to your `TestCase`:\n\n```php\nprotected function resolveApplicationConfiguration($app)\n{\n    parent::resolveApplicationConfiguration($app);\n\n    $app['config']->set('statamic.editions.pro', true);\n\n    $app['config']->set('statamic.api.resources', [\n        'collections' => true,\n        'navs' => true,\n        'taxonomies' => true,\n        'assets' => true,\n        'globals' => true,\n        'forms' => true,\n        'users' => true,\n    ]);\n}\n```\n\n\n## Writing Tests\n\nAll of your tests should extend your addon's `TestCase` class, like so:\n\n```php\n<?php\n\nnamespace Acme\\Example\\Tests;\n\nuse Acme\\Example\\Tests\\TestCase;\n\nclass ExampleTest extends TestCase\n{\n    /**\n     * A basic test example.\n     */\n    public function test_that_true_is_true(): void\n    {\n        $this->assertTrue(true);\n    }\n}\n```\n\nFor more information on writing tests, please review the [Laravel Testing Documentation](https://laravel.com/docs/13.x/testing).\n\n### The Stache\n\nDuring tests, any Stache items (like entries, terms, global sets) will be saved inside your `tests/__fixtures__` directory.\n\nHowever, most of the time, you'll probably want to blow away old content between test runs. To do this, you may add the `PreventsSavingStacheItemsToDisk` trait to your tests:\n\n```php\nuse Statamic\\Testing\\Concerns\\PreventsSavingStacheItemsToDisk; // [tl! focus]\n\nclass ExampleTest extends TestCase\n{\n\tuse PreventsSavingStacheItemsToDisk; // [tl! focus]\n\n    // ...\n}\n```\n\n### Update Scripts\n\nTo test an [update script](/addons/building-an-addon#update-scripts), import the `RunsUpdateScripts` trait and call `$this->runUpdateScript()` with your script class.\n\n```php\nuse Statamic\\Testing\\Concerns\\RunsUpdateScripts; // [tl! focus]\n\nclass ExampleTest extends TestCase\n{\n\tuse RunsUpdateScripts; // [tl! focus]\n\n    #[Test]\n    public function it_does_what_it_needs_to_do()\n    {\n        $this->runUpdateScript(YourUpdateScript::class); // [tl! focus]\n    }\n    \n    // ...\n}\n```\n\n:::warning\nThe `RunsUpdateScripts` trait is only available in Statamic v6.3.0 and above. You may need to bump your minimum supported version to use it.\n:::\n\n### Inertia.js\n\nThe Control Panel is powered by [Inertia.js](https://inertiajs.com), allowing Statamic to render pages as Vue components instead of traditional Blade views.\n\nTo assert an Inertia response, use the `->assertInertia()` macro:\n\n```php\nuse Inertia\\Testing\\AssertableInertia as Assert;\n\n$this\n    ->get(cp_route('my-addon.index'))\n    ->assertInertia(fn (Assert $page) => $page\n        ->component('my-addon::Foo')\n        ->has('message');\n    );\n```\n\nFor more details, see the [Inertia.js testing documentation](https://inertiajs.com/docs/v2/advanced/testing).\n\n## Running Tests\n\nOnce you've written some tests, you can run them using `phpunit`:\n\n```bash\n./vendor/bin/phpunit\n```\n\nYou may run a specific test by passing the `--filter` argument:\n\n```bash\n# Runs all tests in the CheckoutTest\n./vendor/bin/phpunit --filter=CheckoutTest\n\n# Runs the specific user_cant_checkout_without_payment test\n./vendor/bin/phpunit --filter=user_cant_checkout_without_payment\n\n# Runs all tests with checkout in their name.\n./vendor/bin/phpunit --filter=checkout\n```\n\n### GitHub Actions\n\nWhen you're using GitHub to store your addon's source code, you can take advantage of GitHub Actions so your addon's tests are run whenever you push to your repository or whenever a pull request is submitted.\n\nRunning tests on GitHub Actions (or any CI platform for that matter) saves you running the tests locally after every change and also means you can run your addon's tests against multiple PHP & Laravel versions.\n\nFor ease of use, here's an example GitHub Actions workflow:\n\n```yaml\nname: Test Suite\n\non:\n  push:\n  pull_request:\n\njobs:\n  php_tests:\n    strategy:\n      matrix:\n        php: [8.3, 8.4, 8.5]\n        laravel: [12.*, 13.*]\n        os: [ubuntu-latest]\n\n    name: ${{ matrix.php }} - ${{ matrix.laravel }}\n    \n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v1\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php }}\n          extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick\n\n      - name: Install dependencies\n        run: |\n          composer require \"laravel/framework:${{ matrix.laravel }}\" --no-interaction --no-update\n          composer install --no-interaction\n\n      - name: Run PHPUnit\n        run: vendor/bin/phpunit\n```"
  },
  {
    "path": "content/collections/pages/tips.md",
    "content": "---\nid: b6c40452-8da0-4f03-a53c-714cc3338b9d\nblueprint: page\ntitle: 'Tips & Tricks'\ntemplate: tips/index\n---\nHere's a collection of little tips & tricks to help you on your way.\n"
  },
  {
    "path": "content/collections/pages/toast-notifications.md",
    "content": "---\ntitle: Toast Notifications\nintro: Simple notification messages that \"pop\" into the screen like toast popping out of a toaster.\nid: 52af4663-ebbd-467c-8659-9c7bb94cb7cc\n---\nYou may display simple toast notifications through the `$toast` instance method.\n\n``` js\nthis.$toast.info('message');    // Basic message\nthis.$toast.success('message'); // Green success\nthis.$toast.error('message');   // Red error\nthis.$toast.success('message', { duration: 3000 }); // Auto-disappear after this many milliseconds\n```\n\nYou may also trigger these from the server using the `Toast` facade.\n\n```php\nuse Statamic\\Facades\\CP\\Toast;\n\nToast::info('message');\nToast::success('message');\nToast::error('message');\n\nToast::info('message')->duration(3000);\n```\n\nYou don't have to return them in a response, simply calling them is enough. They will automatically be routed through the response into JavaScript.\n"
  },
  {
    "path": "content/collections/pages/troubleshooting.md",
    "content": "---\nid: cd0428cf-2c4a-43b9-8e61-dd8123799ed8\nblueprint: page\ntitle: Troubleshooting\nintro: 'Here are some common pitfalls.'\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1632426159\ntemplate: troubleshooting/index\n---\n"
  },
  {
    "path": "content/collections/pages/ubuntu.md",
    "content": "---\nid: c0009fa6-0f8f-4b45-8d65-0cb784d07031\nblueprint: page\ntitle: 'How to Install Statamic on Ubuntu'\nbreadcrumb_title: Ubuntu\nintro: 'A full walk-through for installing, configuring and running Statamic on an Ubuntu server, perfect for production use.'\nparent: ab08f409-8bbe-4ede-b421-d05777d292f7\n---\n## Prerequisites\n\nTo install Statamic on an Ubuntu instance you will need the following:\n\n- An Ubuntu 24.04 VPS with root access enabled or a user with sudo privileges (you can follow our [Digital Ocean](/installing/digital-ocean) or [Linode](/installing/linode) guides to get yours set up)\n- A server with at least 1GB memory\n- A valid domain name pointed to your server and SSL certificate in place\n- PHP 8.3+\n\n## Update Packages\n\nIf this is the first time using `apt` in this session, you should sure package lists and installed packages are up to date.\n\n``` shell\nsudo apt-get update\nsudo apt-get upgrade\n```\n\n## Install PHP & Required Modules\n\n``` shell\nsudo apt install php-common php-fpm php-json php-mbstring zip unzip php-zip php-cli php-xml php-tokenizer php-curl php-gd -y\n```\n\n## Install Composer\n\nInstall Composer with the following command:\n\n``` curl\ncurl -sS https://getcomposer.org/installer | php\n```\n\nNext, you need to move the `composer.phar` file to a globally accessible directory and update its permissions.\n\n``` shell\nsudo mv composer.phar /usr/local/bin/composer\nsudo chmod +x /usr/local/bin/composer\n```\n\n:::tip\nDo not run `composer` as root. If you followed the steps for creating a Digital Ocean droplet, you will need to create a new user to run `composer` as.\n:::\n\nNow you can check to make sure Composer is installed and configured correctly by running this command:\n\n``` shell\ncomposer\n```\n\n## Install Statamic CLI\n\nNext up, let's install the Statamic CLI. To do this, run the following command in your terminal:\n\n``` shell\ncomposer global require statamic/cli\n```\n\nUpon installation, you can now use the `statamic new` command to spin up fresh Statamic sites with a CLI setup wizard 🧙‍♂️ to guide you through a variety of settings and options.\n\nOur CLI is essentially a super fancy wrapper around the `composer create-project` command. You can choose to not install it, but only at your own annoyance.\n\n## Install & Configure Nginx\n\nNext, install [Nginx](https://nginx.com) with the following command:\n\n``` shell\nsudo apt install nginx -y\n```\n\nAfter the install completes, Nginx will start automatically. You can verify the service by running the following the command:\n\n``` shell\nsudo systemctl status nginx\n```\n\n## Create a new Statamic Application\n\nNow we'll create a new Statamic application using the `statamic new` command.\n\nFirst, navigate to the default Nginx root directory:\n\n``` shell\ncd /var/www/html\n```\n\nNext, run the following install command (you may need to update your [$PATH](/knowledge-base/troubleshooting/command-not-found-statamic)):\n\n``` shell\nstatamic new example.com\n```\n\n:::tip\nIf you run into a TTY error like `TTY mode requires /dev/tty to be read/writable`, you can simulate TTY by calling `script` and using the `--no-interaction` flag.\n\n```\nscript -q -c \"statamic new --no-interaction example.com\"\n```\n:::\n\n## Set Permissions\n\nOnce Statamic is installed, you'll need to grant appropriate permissions to the non-root user so Statamic and Laravel can write to the necessary system directories.\n\n``` shell\nsudo chmod -R 755 /var/www/example.com\nsudo chown -R www-data:www-data /var/www/html/example.com\n```\n\nNext, let's set up the minimum recommended config file to serve your site. Create a new file with `sudo vim` (or your command line editor of choice) at `/etc/nginx/sites-available/example.com`, making sure to replace `example.com` everywhere with your desired domain.\n\n```php\nserver {\n    listen 80;\n    server_name example.com; // [tl! highlight:1]\n    root /var/www/html/example.com/public;\n\n    add_header X-Frame-Options \"SAMEORIGIN\";\n    add_header X-XSS-Protection \"1; mode=block\";\n    add_header X-Content-Type-Options \"nosniff\";\n\n    index index.html index.htm index.php;\n\n    charset utf-8;\n\n    set $try_location @static;\n\n    if ($request_method != GET) {\n        set $try_location @not_static;\n    }\n\n    if ($args ~* \"live-preview=(.*)\") {\n        set $try_location @not_static;\n    }\n\n    location / {\n        try_files $uri $try_location;\n    }\n\n    location @static {\n        try_files /static${uri}_$args.html $uri $uri/ /index.php?$args;\n    }\n\n    location @not_static {\n        try_files $uri /index.php?$args;\n    }\n\n    location = /favicon.ico { access_log off; log_not_found off; }\n    location = /robots.txt  { access_log off; log_not_found off; }\n\n    error_page 404 /index.php;\n\n    location ~ \\.php$ {\n        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;\n        fastcgi_index index.php;\n        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;\n        include fastcgi_params;\n    }\n\n    location ~ /\\.(?!well-known).* {\n        deny all;\n    }\n}\n```\n\n:::tip\nYou should ensure that the PHP path matches the version of PHP-FPM you have installed. \n\nFor example: the example config above specifies `fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;`, but if you update all packages, you may end up with a later version.\n:::\n\nYou can confirm that the configuration doesn’t contain any syntax errors with the following command:\n\n``` shell\nsudo nginx -t\n```\n\nYou should see output similar to this:\n\n``` shell\n# Output\nnginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful\n```\n\nSymlink the newly created config file and reload Nginx to apply these changes:\n\n``` shell\nsudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/\nsudo systemctl reload nginx\n```\n\nNow visit your IP Address or `https://example.com` (but like the actual domain) if you've pointed your domain's A Record and you should see the Statamic landing page.\n\n<figure>\n    <img src=\"/img/quick-start/installed-6.webp\" alt=\"Statamic Welcome Screen\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/quick-start/installed-6-dark.webp\" balt=\"Statamic Welcome Screen\" class=\"u-hide-in-light-mode\">\n    <figcaption>If you see this, you have just won.</figcaption>\n</figure>\n\n## Conclusion\n\nThat's pretty much it. Time for you to take it from here.\n\nIf this is your production server, you'll probably want to add cache expiry headers and so on, but those rules and what you cache to the browser are all up to you.\n\nThere are many other variables at play when building your own server. It's unrealistic to think we've covered them all here, or that this article won't get out of date at times as things change.\n\nIf you run into issues not covered here, you may want to read through [this discussion](https://github.com/statamic/cms/discussions/10487) where several folks work through many edge cases happening all at once."
  },
  {
    "path": "content/collections/pages/ui-components.md",
    "content": "---\nid: f38a3e52-10ba-4bfa-9298-0c95b324c662\ntitle: 'UI Components'\nnav_title: 'UI Components'\nblueprint: link\nredirect:\n  url: 'https://ui.statamic.dev'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/updating-a-starter-kit.md",
    "content": "---\nid: 512cbb65-0091-47e2-a3b6-c42aae64349e\nblueprint: page\ntitle: 'Updating a Starter Kit'\nintro: |\n  Starter Kits aren't all designed to be updatable, but when they are you'll need to know how.\ntemplate: page\nnav_title: Updating\n---\n\n## Not all starter kits are updatable\n\nAs their name implies, starter kits were originally intended to be a way to \"start\" a site. Once installed, you're on your own and can customize as you see fit.\n\nHowever, some kits _are_ built in a way that they can be updated. Updatable kits will be noted on the Statamic Marketplace.\n\nFor example, the [Podcaster](https://statamic.com/starter-kits/statamic/podcaster) Kit is designed in an opinionated way, with settings for you to tweak brand colors. If a CSS or JavaScript bug is encountered one day, the developer of the Kit can fix it and you can simply pull down the update into your site.\n\n## Performing updates\n\nWhen initially installing an updatable Starter Kit, it will be left as a dependency in your `composer.json` file.\n\nTo update, you will need to update via Composer:\n\n```shell\ncomposer update\n```\n\n(Or `composer update vendor/starter-kit-name` to avoid updating everything else.)\n\nAdditionally, you may need to run additional commands to re-compile or publish assets. These should be explained in the Starter Kit's documentation, but would typically be something like this:\n\n```shell\nphp artisan vendor:publish --tag=starter-kit-name\nnpm run dev\n```\n"
  },
  {
    "path": "content/collections/pages/updating.md",
    "content": "---\ntitle: Updating\nid: e6f05019-6bdd-488e-ba45-39ae7ea5cee7\nblueprint: page\nintro: Updates are handled by [Composer](https://getcomposer.org/), PHP's dependency manager. We recommend running all updates locally (not on production) via the command line and deploying those changes to production after verifying everything still works as it should.\n---\n\n:::best-practice\nWe recommend running **all updates** locally to eliminate downtime and any possibility of an unexpected change or timeout affecting your site.\n:::\n## Composer\n\nIf you installed with Composer, you can update your installation with the following command:\n```\ncomposer update statamic/cms --with-dependencies\n```\n Note: You may prefer to run `composer update` to update _all_ of your dependencies.\n\n ## Statamic CLI\n\nIf you installed with [Statamic CLI](/cli), you can update your installation with the following command:\n```\nstatamic update\n```\n\n## Control Panel\n\nThe Control Panel will inform you when updates are available.\n\nFrom within the **Tools &rarr; Updates** section, Statamic will provide you with the appropriate Composer commands to run.\n\nIf you choose to install a non-latest version, your `statamic/cms`  Composer version dependency will be fixed to whichever explicit version you choose. To go back to a constraint-style version, you'll need to update your `composer.json` file.\n\nFor example, if you chose `v6.0.1` in the control panel, this will be your Composer constraint.\n\n```json\n{\n    \"require\": {\n        \"statamic/cms\": \"6.0.1\",\n    }\n}\n```\n\nTo go back to a more traditional version range constraint, you may want to replace it with this:\n\n```json\n{\n    \"require\": {\n        \"statamic/cms\": \"^6.0\",\n    }\n}\n```\n\n## Major upgrades\n\nUpgrading between major Statamic versions sometimes involves extra manual steps. Check out [these guides](/upgrade-guide) for further details.\n"
  },
  {
    "path": "content/collections/pages/upgrade-guide.md",
    "content": "---\ntitle: 'Upgrade guide'\nintro: How to upgrade between various versions of Statamic.\ntemplate: page\nid: f12f8ba3-19ff-48cb-a07b-653b05082d7e\nblueprint: page\n---\n- [5.0 to 6.0](/getting-started/upgrade-guide/5-to-6)\n- [4.0 to 5.0](/getting-started/upgrade-guide/4-to-5)\n- [3.4 to 4.0](/getting-started/upgrade-guide/3-4-to-4-0)\n- [3.3 to 3.4](/getting-started/upgrade-guide/3-3-to-3-4)\n- [3.2 to 3.3](/getting-started/upgrade-guide/3-2-to-3-3)\n- [3.1 to 3.2](/getting-started/upgrade-guide/3-1-to-3-2)\n- [3.0 to 3.1](/getting-started/upgrade-guide/3-0-to-3-1)\n- [2.x to 3.x](/getting-started/upgrade-guide/v2-to-v3)\n- [Bard 1 to 2](/getting-started/upgrade-guide/bard-v1-to-v2)\n- [Vue 2 to 3](/getting-started/upgrade-guide/vue-2-to-3)\n\nIf you intend to **upgrade Laravel itself**, please refer to its [upgrade guide](https://laravel.com/docs/upgrade).\nYou can also (semi-)automate the Laravel upgrade using [Laravel Shift](https://laravelshift.com) which is by far the most _rad_ way to upgrade.\n"
  },
  {
    "path": "content/collections/pages/users.md",
    "content": "---\nid: 6b691e04-8f28-4eb2-8288-b61433883fe4\nblueprint: page\ntitle: Users\nintro: 'Users are the member accounts to your site or application. What a user can do with their account is up to you. They could have limited or full access to the Control Panel, a login-only area of the front-end, or even something more custom by tapping into Laravel.'\ntemplate: page\npro: true\nrelated_entries:\n  - 878f0dd7-2d31-479c-b58d-bc60685fa7d2\n  - 748f88ce-85f6-491b-8e9c-fa2b1895be31\n  - 4c3f5caa-a861-4ffd-a856-1692cafeb870\n  - 1ee69ba0-2fa4-4155-9b8d-82536ce95f99\n  - 55993382-c928-48d0-8559-c88b226d4657\n---\n## Overview\n\nThe most common and obvious reason users exist is to have the means to access the Control Panel and manage the content of your site. But there is so much more a user can do, if you so desire.\n\n<figure>\n    <img src=\"/img/users-index.webp\" alt=\"List of Statamic Control Panel users\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/users-index-dark.webp\" alt=\"List of Statamic Control Panel users\" class=\"u-hide-in-light-mode\">\n    <figcaption>Why hasn't the Hoff logged in? And why is he inpersonating Jason?</figcaption>\n</figure>\n\n## Creating users\n\nThe easiest way to create your **first user** is by running `php please make:user` terminal command. After entering basic information, setting a password, and saying `yes` to [super user](#super-users), you can log into the control panel at `example.com/cp`.\n\n:::watch https://youtube.com/embed/KuiPocGq3L8\nWatch a new user being born. 🐣\n:::\n\nYou can also [create users by hand](/tips/creating-users-by-hand) in a YAML file if you'd prefer, or don't have access to the command line. And don't worry, the password field will automatically get encrypted as soon as Statamic spots it.\n\n### New user invitations\n\nWhen creating users in the Control Panel you can send email invitations to help guide those users into activating their accounts and signing in for the first time. You can even customize a lovely little welcome message for them.\n\n<figure>\n    <img src=\"/img/user-invitation.webp\" alt=\"A user invitation screen\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/user-invitation-dark.webp\" alt=\"A user invitation screen\" class=\"u-hide-in-light-mode\">\n    <figcaption>An opportunity for a knock knock joke, perhaps?</figcaption>\n</figure>\n\n:::tip\nBe sure to [configure the email driver](/email) so those emails actually go out.\n:::\n\n## User fields\n\nYou're more than welcome — encouraged even — to customize what fields and information you'd like to store on your users. For example, you could store author bios and social media links to be used in articles on your front-end.\n\nTo customize these fields, edit the included `user` [blueprint](/blueprints) and configure it however you'd like.\n\n## Permissions\n\n<div class=\"c-pro-badge\">\n    <a href=\"/licensing\">\n        <div class=\"c-pro-badge__text\">\n            <span>⭐️</span> Pro Feature <span>⭐️</span>\n        </div>\n    </a>\n</div>\n\nA User by itself has no permission to access or change any aspect of Statamic. It takes explicit permissions for a user to access the control panel, create, edit, or publish content, create users, and so on.\n\nPermissions are grouped into **roles**, and are very simple to manage in the Control Panel and are stored in `resources/users/roles.yaml`.\n\nIn turn, **roles** are attached directly to individual users or [user groups](#user-groups).\n\n### Statamic's native permissions {#native-permissions}\n\n| Permission                                                | Handle                                       |\n| --------------------------------------------------------- | -------------------------------------------- |\n| Access the Control Panel                                  | `access cp`                                  |\n| Configure Sites                                           | `configure sites`                            |\n| Configure Fields                                          | `configure fields`                           |\n| Configure Form Fields                                     | `configure form fields`                      |\n| Manage Preferences                                        | `manage preferences`                         |\n| Access site                                               | `access {site} site`                         |\n| Create, edit, and delete collections                      | `configure collections`                      |\n| View entries                                              | `view {collection} entries`                  |\n| ↳  Edit entries                                           | `edit {collection} entries`                  |\n| &nbsp;&nbsp;↳  Create entries                             | `create {collection} entries`                |\n| &nbsp;&nbsp;↳  Delete entries                             | `delete {collection} entries`                |\n| &nbsp;&nbsp;↳  Publish entries                            | `publish {collection} entries`               |\n| &nbsp;&nbsp;↳  Reorder entries                            | `reorder {collection} entries`               |\n| &nbsp;&nbsp;↳  Edit other author's entries                | `edit other authors {collection} entries`    |\n| &nbsp;&nbsp;&nbsp;&nbsp;↳  Publish other author's entries | `publish other authors {collection} entries` |\n| &nbsp;&nbsp;&nbsp;&nbsp;↳  Delete other author's entries  | `delete other authors {collection} entries`  |\n| Create, edit, and delete navs                             | `configure navs`                             |\n| ↳  View nav                                               | `view {nav} nav`                             |\n| &nbsp;&nbsp;↳  Edit nav                                   | `edit {nav} nav`                             |\n| Create, edit and delete global sets                       | `configure globals`                          |\n| Edit global variables                                     | `edit {global} globals`                      |\n| Create, edit and delete taxonomies                        | `configure taxonomies`                       |\n| View terms                                                | `view {taxonomy} terms`                      |\n| ↳ Edit terms                                              | `edit {taxonomy} terms`                      |\n| &nbsp;&nbsp;↳  Create terms                               | `create {taxonomy} terms`                    |\n| &nbsp;&nbsp;↳  Delete terms                               | `delete {taxonomy} terms`                    |\n| Configure asset containers                                | `configure asset containers`                 |\n| View asset container                                      | `view {container} assets`                    |\n| ↳  Upload assets                                          | `upload {container} assets`                  |\n| ↳  Edit folders                                           | `edit {container} folders`                   |\n| ↳  Edit assets                                            | `edit {container} assets`                    |\n| &nbsp;&nbsp;↳  Move assets                                | `move {container} assets`                    |\n| &nbsp;&nbsp;↳  Rename assets                              | `rename {container} assets`                  |\n| &nbsp;&nbsp;↳  Delete assets                              | `delete {container} assets`                  |\n| View users                                                | `view users`                                 |\n| ↳ Edit users                                              | `edit users`                                 |\n| &nbsp;&nbsp;↳ Create users                                | `create users`                               |\n| &nbsp;&nbsp;↳ Delete users                                | `delete users`                               |\n| &nbsp;&nbsp;↳ Change passwords                            | `change passwords`                           |\n| &nbsp;&nbsp;↳ Assign user groups                          | `assign user groups`                         |\n| &nbsp;&nbsp;↳ Assign roles                                | `assign roles`                               |\n| Edit user groups                                          | `edit user groups`                           |\n| Edit roles                                                | `edit roles`                                 |\n| Impersonate users                                         | `impersonate users`                          |\n| View updates                                              | `view updates`                               |\n| Configure forms                                           | `configure forms`                            |\n| View form submissions                                     | `view {form} form submissions`               |\n| &nbsp;&nbsp;↳ Delete form submissions                     | `delete {form} form submissions`             |\n| Configure addons                                          | `configure addons`                           |\n| Edit addon settings                                       | `edit {addon} settings`                      |\n| Access utility                                            | `access {utility} utility`                   |\n| Resolve Duplicate IDs                                     | `resolve duplicate ids`                      |\n| View GraphQL                                              | `view graphql`                               |\n\n### Author permissions\n\nAuthor permissions are a little bit special. They determine the control users can have over their own entries or those created by other authors.\n\n:::warning Important!\nThis feature only has any effect if your entry blueprint has an `author` field. If you don't already have an `author` field, this functionality is not available.\n:::\n\n### Site permissions\n\nWhen using the [multi-site](/multi-site) feature, Statamic will check for appropriate site permissions in addition to whatever it's checking.\n\nFor example, when you try to edit a `blog` entry in the `french` site, Statamic will check if you have both the `edit blog entries` and `access french site` permissions.\n\n\n### Super users\n\nSuper Admin accounts are special accounts with **access and permission to everything**. This includes things reserved only for super users like the ability to _create more super users_. It's important to prevent the robot apocalypse and this is an important firewall. We're just doing our part to save the world.\n\n## User groups\n\n<div class=\"c-pro-badge\">\n    <a href=\"/licensing\">\n        <div class=\"c-pro-badge__text\">\n            <span>⭐️</span> Pro Feature <span>⭐️</span>\n        </div>\n    </a>\n</div>\n\nUser groups allow you to attach roles, include users, thereby assign all the corresponding permissions automatically. This approach is much simpler than assigning roles individually if you have a lot of users.\n\nUser groups are stored in `resources/users/groups.yaml`.\n\n## Password resets\n\nLet's face it. People forget their passwords. A lot, and often. Statamic supports password resets. For users with Control Panel access, the login screen (found by default at `example.com/cp`) already handles this for you automatically.\n\nYou can also create your own password reset pages for front-end users by using the [user:forgot_password_form](/tags/user-forgot_password_form) tag.\n\nThe user will receive an email with a temporary, single-use token allowing them to set a new password and log in again.\n\n## Password validation\n\nBy default, passwords need to be 8 characters long. If you'd like to customize the default rules, you can use the `Password` rule object. (Requires at least Laravel 8.43).\n\nThese rules will be used when creating passwords throughout Statamic. In the `make:user` command, in the `user:register_form` tag, or during the password activation/reset flows. If you create the password by hand in user yaml files, the rules will be bypassed.\n\nYou can drop this into your `AppServiceProvider`'s `boot` method.\n\n```php\nuse Illuminate\\Validation\\Rules\\Password;\n\npublic function boot()\n{\n    Password::defaults(function () {\n        return Password::min(16);\n    });\n}\n```\n\nConsult the [Laravel documentation](https://laravel.com/docs/13.x/validation#validating-passwords) to see all the available methods for customizing the password rule.\n\n## Storing user records {#storage}\n\nWhile users are stored in files by default — like everything else in Statamic — they can also be located in a database or really anywhere else. Here are links to articles for the different scenarios you may find yourself in.\n\n- [Storing Laravel Users in Files](/tips/storing-laravel-users-in-files)\n- [Storing Users in a Database](/tips/storing-users-in-a-database)\n- [Custom User Storage](/tips/storing-users-somewhere-custom)\n- [Using an Independent Auth Guard](/tips/using-an-independent-authentication-guard)\n\n## Avatars\n\nEach user account has an avatar field named `avatar`. By default it's an [Assets Field](/fieldtypes/assets) that falls back to the user's initials.\n\nThis avatar is used throughout the Control Panel to represent the user when the context is important. For example, on your user dropdown menu, as an entry's \"Author\", or while using [Real Time Collaboration](https://github.com/statamic/collaboration).\n\n<figure>\n    <img src=\"/img/user-avatar.webp\" alt=\"A user's avatar in the control panel global header\" width=\"500\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/user-avatar-dark.webp\" alt=\"A user's avatar in the control panel global header\" width=\"500\" class=\"u-hide-in-light-mode\">\n    <figcaption>Behold — an avatar! Little SNL joke there, <a href=\"https://www.youtube.com/watch?v=Q8PdffUfoF0\" target=\"_blank\">for anyone in the mood</a></figcaption>\n</figure>\n\n## Ordering\n\nBy default, users are ordered alphabetically by their email. However, if you wish, you can change the field and direction used to order users in the Control Panel and when returned with the [`{{ users }}`](/tags/users) tag.\n\n```php\n// config/statamic/users.php\n\n'sort_field' => 'email',\n'sort_direction' => 'asc',\n```\n\n## Language preference\n\nEach user can have their own preferred language in the Control Panel. Head to your preferences area by clicking on the ⚙️ gear/cog icon in the global header and then go to **Preferences**.\n\nYou can set the language for _everyone_ by going to **Default**, or you can set by Role or just the current user (yourself) with **Override For User**.\n\n<figure>\n    <img src=\"/img/user-language-preference.webp\" alt=\"User Language Preferences\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/user-language-preference-dark.webp\" alt=\"User Language Preferences\" class=\"u-hide-in-light-mode\">\n    <figcaption>Last we checked, Statamic has been translated into a lot of languages.</figcaption>\n</figure>\n\n## Impersonate users\n\nStatamic gives you the ability to impersonate users via the Control Panel. This lets you see the Control Panel and front end of your site through the eyes of the user you chose. This is pretty neat if certain content or capabilities are limited through roles and permissions and you want to test those things. It saves quite some time since there's no need to manually sign out and in again with a different user anymore.\n\n<figure>\n    <img src=\"/img/user-impersonation.webp\" alt=\"List view of Statamic Control Panel users with a dropdown showing various options, one of them being 'Start Impersonation'\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/user-impersonation-dark.webp\" alt=\"List view of Statamic Control Panel users with a dropdown showing various options, one of them being 'Start Impersonation'\" class=\"u-hide-in-light-mode\">\n    <figcaption>Masquerade as someone else 🎭</figcaption>\n</figure>\n\nYou can configure impersonation in `config/statamic/users.php`, like setting the redirect destination after starting impersonation or disabling it. Additionally, there is a dedicated `impersonate users` permission that you can assign to roles and users to allow or disallow them using this feature.\n\n## OAuth\n\nIn addition to conventional user authentication, Statamic also provides a simple, convenient way to authenticate with OAuth providers through [Laravel Socialite](https://github.com/laravel/socialite). Socialite currently supports authentication with Facebook, Twitter, LinkedIn, Google, GitHub, GitLab and Bitbucket, while dozens of additional providers are available though [third-party Socialite Providers](https://socialiteproviders.netlify.com/).\n\nLearn how to [configure OAuth](/oauth) on your site.\n\n## Two-Factor Authentication\n\nStatamic includes first-party support for two-factor authentication (2FA), providing an extra layer of account security. \n\nOnce enabled, users must enter a time-based one-time password (TOTP) from an authenticator app — like Google Authenticator or 1Password — alongside their password when logging in.\n\nTo enable 2FA, head to your **Profile** in the Control Panel. Scan the QR code with your authenticator app, enter the generated code, and you’re set. You’ll also receive a set of recovery codes — store these somewhere safe in case you lose access to your authenticator app.\n\n2FA is optional by default, but you can enforce it for specific roles via configuration:\n\n```php\n// config/statamic/users.php\n\n'two_factor_enforced_roles' => [\n    // Enforce for everyone\n    '*',\n\n    // Enforce for super users\n    'super_users',\n\n    // Enforce for a specific role\n    'marketing_managers',\n    'user_admin',\n],\n```\n\nYou may also disable 2FA altogether by setting `STATAMIC_TWO_FACTOR_ENABLED=false` in your `.env` file.\n\n:::warning\nStatamic uses your `APP_KEY` to encrypt the two-factor authentication secret and recovery codes.\n\nYou may run into issues with two-factor authentication if you have different `APP_KEY` values between environments *and* they share the same users (eg. you're tracking users in Git). You may want to disable 2FA locally in this case.\n:::\n\n### Frontend Two-Factor Authentication\n\nUsers who authenticate through your site's frontend (via [`{{ user:login_form }}`](/tags/user-login_form)) can also set up and challenge 2FA without ever touching the Control Panel. Statamic ships a set of tags for building those pages yourself:\n\n- [`{{ user:two_factor_challenge_form }}`](/tags/user-two_factor_challenge_form) — the code verification form shown during login\n- [`{{ user:two_factor_enable_form }}`](/tags/user-two_factor_enable_form) — step 1 of setup, generates the secret\n- [`{{ user:two_factor_setup_form }}`](/tags/user-two_factor_setup_form) — step 2 of setup, displays the QR code and confirms the code\n- [`{{ user:disable_two_factor_form }}`](/tags/user-disable_two_factor_form) — lets users turn 2FA off\n- [`{{ user:two_factor_recovery_codes }}`](/tags/user-two_factor_recovery_codes) and [`{{ user:reset_two_factor_recovery_codes_form }}`](/tags/user-reset_two_factor_recovery_codes_form) — show and regenerate recovery codes\n- [`{{ user:two_factor_enabled }}`](/tags/user-two_factor_enabled) — a boolean for conditionally rendering the above\n\nWhen a user with 2FA enabled signs in on the frontend, Statamic redirects them to a challenge page. When 2FA is enforced for the user's role and they haven't set it up, Statamic redirects them to a setup page. Point these redirects at your own pages with the following config keys:\n\n```php\n// config/statamic/users.php\n\n'two_factor_challenge_url' => '/account/2fa/challenge',\n'two_factor_setup_url' => '/account/2fa/setup',\n```\n\nLeave either value `null` to use Statamic's built-in page for that step. Control Panel flows are unaffected — they always use their own pages.\n\n## Passkeys\n\nStatamic supports **passkeys** as a secure alternative to email-and-password logins. Passkeys are a passwordless authentication method built on WebAuthn and are supported by most modern operating systems and password managers. On macOS, iOS, and iPadOS, for example, you can sign in using Touch ID or Face ID.\n\nTo add a passkey for the Control Panel, log in and visit your **profile**, where passkeys are managed from the Actions dropdown.\n\n<figure>\n    <img src=\"/img/passkeys-setup.webp\" alt=\"Actions dropdown on the user profile page\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/passkeys-setup-dark.webp\" alt=\"Actions dropdown on the user profile page\" class=\"u-hide-in-light-mode\">\n</figure>\n\nClick **Create Passkey** and follow the prompts to complete setup. Once a passkey has been added, you can use it to sign in without entering your email address and password.\n\n<figure>\n    <img src=\"/img/login-with-passkey-option.webp\" alt=\"Passkey button on sign in page\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/login-with-passkey-option-dark.webp\" alt=\"Passkey button on sign in page\" class=\"u-hide-in-light-mode\">\n</figure>\n\nPasskey behaviour, including whether password logins are still allowed for users with passkeys and whether “remember me” applies when logging in with a passkey, can be configured in `config/statamic/webauthn.php`.\n\n## Rate limiting\n\nStatamic's authentication and passkey endpoints are rate limited by IP address to help protect against brute force attacks. The defaults apply to both the front-end and Control Panel:\n\n| Limiter | Default | Routes |\n| --- | --- | --- |\n| `statamic.auth` | 4 per minute | Front-end login, register, password email, password reset |\n| `statamic.cp.auth` | Inherits `statamic.auth` | Control Panel login, password email, password reset |\n| `statamic.passkeys` | 30 per minute | Front-end passkey authentication |\n| `statamic.cp.passkeys` | Inherits `statamic.passkeys` | Control Panel passkey authentication |\n\nYou can customize any of these limits by redefining the named rate limiter in your `AppServiceProvider`'s `boot` method:\n\n```php\nuse Illuminate\\Cache\\RateLimiting\\Limit;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\RateLimiter;\n\npublic function boot()\n{\n    RateLimiter::for('statamic.auth', function (Request $request) {\n        return Limit::perMinute(10)->by($request->ip());\n    });\n}\n```\n\nOverriding `statamic.auth` will affect both the front-end and Control Panel buckets unless you also define a separate `statamic.cp.auth` limiter. The same inheritance applies to `statamic.passkeys` and `statamic.cp.passkeys`.\n\nConsult the [Laravel documentation](https://laravel.com/docs/13.x/routing#rate-limiting) to learn more about defining rate limiters."
  },
  {
    "path": "content/collections/pages/utilities.md",
    "content": "---\ntitle: Utilities\nid: aa6e0a79-9d3f-493b-92c9-df4d2257bc64\nintro: Utilities are simple tools with their own views, routes, navigation items, and permissions.\n---\n## What's a utility?\n\nA utility is really just a route or two with a view, injected into the _Utilities_ area of the control panel,\nand wrapped up with a permission. You _could_ make the same thing by wiring up the individual parts, but creating\na utility is a shortcut.\n\nTo get an idea for what a utility is, take a look at the utilities Statamic ships with:\n- A page for viewing cache information and a button to clear it.\n- A page to list PHP settings from `phpinfo()`.\n- A page letting you clear search indexes.\n- A page to view email configuration and send a test.\n\n## Creating a utility\n\nRegistering a utility will give you a route, nav item, and a permission for free.\n\nIn a service provider's `boot` method, you can register a utility with the `Utility` facade.\n\nStart with `Utility::register()` with the handle of the utility, then chain as many methods as you want.\n\nMake sure to surround any utility registrations in a `Utility::extend` closure.\n\n``` php\nuse Statamic\\Facades\\Utility;\n\npublic function boot()\n{\n    Utility::extend(function () {\n        Utility::register('data_wangjangler')\n            ->inertia('my-addon::DataWangjangler', fn ($request) => [\n                'items' => Item::all(),\n            ]);\n    });\n}\n```\n\nThe first argument is the name of [an Inertia page component](/control-panel/css-javascript#inertia), and the second is an optional closure that returns props for the component.\n\nYou'll need to register the Inertia page using `Statamic.$inertia.register()`:\n\n``` js\nimport DataWangjangler from './components/DataWangjangler.vue';\n\nStatamic.booting(() => {\n    Statamic.$inertia.register('my-addon::DataWangjangler', DataWangjangler);\n});\n```\n\nThen create your Vue component:\n\n``` vue\n<script setup>\nimport { Head, router } from '@statamic/cms/inertia';\n\nconst props = defineProps({\n    items: Array,\n});\n\nfunction wangjangle() {\n    router.post(cp_url('utilities/data-wangjangler/process'));\n}\n</script>\n\n<template>\n    <Head title=\"Data Wangjangler\" />\n\n    <ui-header title=\"Data Wangjangler\">\n        <template #actions>\n            <ui-button variant=\"primary\" @click=\"wangjangle\">\n                Wangjangle that data\n            </ui-button>\n        </template>\n    </ui-header>\n\n    <ui-panel>\n        <ul>\n            <li v-for=\"item in items\" :key=\"item.id\">{{ item.name }}</li>\n        </ul>\n    </ui-panel>\n</template>\n```\n\n:::tip\nFor help setting up Vite and Vue in your project, please see the [Vite Tooling](/addons/vite-tooling) (for addons) or [CSS & JavaScript](/control-panel/css-javascript#inertia) guides.\n:::\n\n## Customizing the navigation and card\n\nYou can customize the nav item, description, icon, and other details on the index listing by chaining the corresponding methods.\n\nThe `icon()` method accepts the name of an [icon included in Statamic](https://ui.statamic.dev/?path=/docs/components-icon--docs#available-icons), or an SVG string containing a custom icon (be sure to use `fill=\"currentColor\"`):\n\n``` php\nuse Statamic\\Facades\\Utility;\n\npublic function boot()\n{\n    Utility::extend(function () {\n        Utility::register('data_wangjangler')\n            ->inertia('my-addon::DataWangjangler')\n            ->title('Data Wangjangler')\n            ->navTitle('Wangjangler')\n            // Icon included in Statamic\n            ->icon('share-mega-phone')\n            // Custom icon\n            ->icon('<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path d=\"M246.0422 221.6193c-14.2079 -17.2455 -21.3609 -38.4104 -21.3609 -59.5753 0 -78.4865 94.0663 -156.875 188.1325 -156.875 51.9324 0 94.0662 42.1338 94.0662 94.0662 0 94.0662 -78.3885 188.1325 -156.973 188.1325 -21.1649 0 -42.3298 -7.153 -59.5753 -21.3609L58.6936 497.6449c-12.2482 12.2482 -32.1392 12.2482 -44.3875 0s-12.2482 -32.1393 0 -44.3875l231.7361 -231.6381Z\" fill=\"currentColor\" stroke-width=\"1\"></path></svg>')\n            ->description('Wanjangles your data at the click of a button.')\n            ->docsUrl('https://yoursite.com/docs/wangjangler');\n    });\n}\n```\n\n## Using a custom controller\n\nInstead of passing data to the utility in your service provider, you may define a custom controller instead.\n\n``` php\nUtility::register('data_wangjangler')\n    ->action(WangjanglerController::class) // call the __invoke method\n    ->action([WangjanglerController::class, 'index']); // call the index method\n```\n\nIn your controller, you can do whatever you need to do, then return an Inertia.js Vue component:\n\n``` php\nuse Inertia\\Inertia;\n\nclass WangjanglerController\n{\n    public function __invoke()\n    {\n        $items = Item::all();\n\n        return Inertia::render('my-addon::DataWangjangler', [\n            'items' => $items,\n        ]);\n    }\n}\n```\n\nData will be passed to the component as props:\n\n```vue\n<script setup>\ndefineProps({\n    items: Array,\n});\n</script>\n```\n\n## Routing\n\nA route will be created for you automatically, using the slugified version of the handle you initially provided. eg. `/cp/utilities/data-wangjangler`\n\nIf your utility needs to _do_ something (like how you click a button in the cache manager utility to actually clear the cache), you may register additional routes.\n\n``` php\nUtility::register('data_wangjangler')->routes(function ($router) {\n    $router->post('/', [WangjanglerController::class, 'process'])->name('process');\n});\n```\n\n``` php\n// WangjanglerController.php\n\npublic function process(Request $request)\n{\n    // Do the processing...\n\n    return redirect()->back()->with('success', 'Data has been wangjangled!');\n}\n```\n\nYou can use the `cp_route` helper in PHP to generate URLs to your utility routes:\n\n``` php\ncp_route('utilities.data-wangjangler') // /cp/utilities/data-wangjangler\ncp_route('utilities.data-wangjangler.process') // /cp/utilities/data-wangjangler/process\n```\n\n## Permissions\n\nA single permission will be registered automatically using the handle.\neg. `access data_wangjangler utility`\n\nUsers without this permission will not see the utility in the navigation or utility listing.\n\n## Using Blade\n\nFor simpler utilities, or if you prefer not to use Vue, you may build utilities using Blade, but there's a few limitations to be aware of:\n\n- Blade-rendered pages trigger a full page reload rather than the SPA-style transitions used elsewhere in the Control Panel.\n- Under the hood, Blade views are rendered inside a Vue component, which means `<script>` tags are not supported within your views.\n\nTo use a Blade view instead of an Inertia component, call the `->view()` method when creating your utility:\n\n``` php\nuse Statamic\\Facades\\Utility;\n\npublic function boot()\n{\n    Utility::extend(function () {\n        Utility::register('data_wangjangler')\n            ->inertia('my-addon::DataWangjangler', fn ($request) => [ // [tl! --]\n            ->view('wangjangler', fn ($request) => [ // [tl! ++]\n                'items' => Item::all(),\n            ]);\n    });\n}\n```\n\nAlternatively, when using a custom controller, you may use the `view()` helper instead of `Inertia::render()`.\n\nYour Blade view doesn't need to extend a layout, Statamic will handle wrapping it automatically:\n\n``` blade\n<ui-header title=\"Data Wangjangler\" />\n\n<ui-panel>\n    <ul>\n        @foreach ($items as $item)\n            <li>{{ $item->name }}</li>\n        @endforeach\n    </ul>\n</ui-panel>\n```\n"
  },
  {
    "path": "content/collections/pages/v2-to-v3.md",
    "content": "---\nid: 031d1f39-1026-47c7-8d85-18642b33f017\nblueprint: page\ntitle: 'Upgrading from v2 to v3'\nintro: 'A guide for upgrading your existing Statamic v2 projects to v3.'\ntemplate: page\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1630938195\n---\n## Overview\nStatamic 3 takes everything you love about v2, rewrites all the old stuff (Laravel 5.1 and Vue.js 1) with the latest hotness, adds roughly 80 million fantastic new features, and speeds up performance by 5x-200x. What's not to love about that?\n\n:::tip\nTry out our [migrator package](https://github.com/statamic/migrator) to help automate most of your v2 upgrade!\n:::\n\n## Philosophies\n\nWe've taken the lessons learned from years of work on Statamic v2 and updated our philosophy-level approach to flat file content management. These principles guide many of the changes &mdash; breaking and otherwise &mdash; you'll find in Statamic v3.\n\nWe don't break things for the fun of it. We hope understanding these philosophies will help defuse any frustration you may encounter when faced with needing to relearn something fundamental.\n\n### Git changes should be as small and discrete as possible\n\nWherever reasonable and possible, we opt for patterns resulting in smaller git changes, especially those avoiding filename changes. Filename changes cause a messy `delete` and `add` diff in your git history. Examples of this principle in action:\n\n- **Publish status** is now controlled by a YAML variable and no longer results in a file move/delete/add.\n- **Pages** and their parent/child folder hierarchies are now standard entries combined with a single YAML file storing the tree (this feature is called [Structures](/structures)). Rearranging your nav results in a single file change instead of a huge file/folder wangjanglification.\n\n### Building in the open benefits everyone\n\n- Statamic v3 is now installed via [Composer][composer].\n- Dependencies can be updated to take advantage of features and fixes without needing a Statamic core update or patch release.\n- The vendor directory is not included, making the package much, much smaller.\n- Your own Statamic site repos can be `public`.\n\n### Reinventing Laravel's wheels is counterproductive\n\nStatamic v3 is built as a _Laravel package_ instead of a \"siloed\" application like v2. This makes it drop-in friendly for existing Laravel applications and  your own Statamic sites become much easier to extend and customize &mdash; often without needing to make addons.\n\nTo accomplish this we've had to follow more Laravel conventions, a positive thing in many ways, which result in numerous small changes. For example...\n\n- Application-level configuration settings are now in Laravel config files — PHP files in `config/statamic` — managed **only** on the file level.\n- Many behaviors can now be overridden in your own [service provider](https://laravel.com/docs/providers).\n- Addons are now [Composer][composer] packages.\n- You can swap in your own favorite template language by customizing Laravel's view handler. We've heard some people like [Twig](https://twig.symfony.com).\n\n\n---\n\n\n## Breaking Changes: Core\n\nFirst, let's look at the breaking changes on the core application side. These are changes affecting the control panel, front-end, cli, and generally anything that doesn't require custom PHP.\n\n### General\n\n- `site/content` directory is now `content`\n- `site/users` directory is now `users`\n- `site/settings/*.yaml` have moved to php config files in `config/statamic`. See [settings](#settings).\n\n### Theming and Views\n\n- The concept of \"themes\" is gone. Your site just has just one frontend, and it's in `resources`.\n- Templates (views) are now located in the `resources/views` instead of separate `templates`, `layouts`, and `partials` directories.\n- Removed `theme:partial` tag in favor of `partial`.\n- The `content` field is no longer automatically parsed for Antlers. You can flag it for parsing in the [blueprint](/blueprints). (As well as any other fields)\n- Inside loops `index` now starts at `0`, `count` starts at `1`, and `zero_index` is no more.\n- Statamic no longer automatically registers routes for taxonomies. If you need them, you can register them yourself.\n\n### Collections\n\n- The `folder.yaml` is now up one directory so it's not mixed in amongst all the entries.\n- It's also renamed to the handle of the collection. eg `blog.yaml`\n- Instead of _all_ values in the YAML file cascading to entries, only values nested inside `data` will.\n- Date based collections should have `date: true`, instead of `order: date`\n- Ordered collections should have `orderable: true`, instead of `order: numeric`\n- Mounting to a page is reversed. Instead of adding `mount: collection` to an page, you now add `mount: page-id` to the collection.\n- Instead of `fieldset: foo`, you should use `blueprints: [foo]` (it's plural, and an array).\n\n### Globals\n\n- Data gets nested inside a `data` key. This separates the data from the meta data, which you don't really want to be used as global variables.\n- When using [multiple sites](/globals#localization), the data gets stored in completely separate files.\n\n\n### Users\n\n- The locale preference for the CP has become an actual [preference](/cp-translations#peruser-override). Move `locale: en` from the top level of the user file into the `preferences` array.\n\n### Fieldtypes\n\n- Array fieldtype terminology was changed from `value => text` to `key => value`, and thus config options were also changed to `add_button`, `key_header` and `value_header` to match the new terminology.\n- Relate and Suggest fieldtypes have been removed in favor of [Relationship](/relationships) fieldtypes.\n- Redactor fieldtype has been removed in favor of the [Bard](/fieldtypes/bard) fieldtype.\n- Pages and Collection fieldtypes have been removed in favor of the [Entries](/fieldtypes/entries) fieldtype.\n\n### Fieldsets\n\nFieldsets technically still exist, although they are now a smaller, companion feature to Blueprints. Blueprints get attached to content. Fieldsets are an optional feature and can be used inside blueprints.\n\n- In content etc, you should reference `blueprint: foo`  instead of `fieldset: foo`.\n- There is no more `fieldsets` fieldtype. You should use the `blueprints` fieldtype.\n- Field conditions use a slightly different syntax for multiple `OR` conditions and null/empty checks.\n- Consider using the `statamic:migrate:fieldset` command to convert your v2 fieldsets to blueprints.\n- The `partial` fieldtype has been removed in favor of the [import](/blueprints#importing-fieldsets) feature in Blueprints.\n\n### Tags\n\n- `get_content`\n    - Now only accepts IDs, not URLs.\n- `get_values`\n    - Removed. Use `get_content`.\n- `glide`\n    - When used as a tag pair simply passes data inside. It no longer parses the contents as the `src` parameter.\n- `glide:generate`\n    - Removed. This is now the default behavior of the main `glide` tag when used as a pair. You can use `glide:batch` for this use case.\n- `entries`, `entries:listing`, `pages`\n    - Removed. Use `collection`.\n- `form`\n    - Replaced `name` element in `fields` array with `handle`.\n    - Replaced `field` element in `fields` array with a [renderable view](/tags/form).\n- `form:create`\n    - `attr` is no longer a parameter, add them directly to the [tag](/tags/form-create)\n- `form:submissions`\n    - No longer inherits parameters from the `collection` tag.\n- `nav:exists`\n    - Removed. It's much cleaner to use the main nav tag, alias the results to a variable, and check if it's empty.\n- `relate`\n    - No longer inherits parameters from the `collection` tag.\n    - You can probably remove this tag entirely, as the values should be [augmented](/augmentation).\n\n#### Tag Conditions\n\nContent tag conditions still exist, but now use our new content query builders under the hood.  It's worth noting some of these conditions may differ in behavior slightly, as they are now implemented using more agnostic query builder compatible comparisons or regular expressions instead of PHP logic.\n\n**The most notable breaking changes are as follows:**\n\n- All comparisons and regex patterns are now case-insensitive by default.\n- `is`, `equals`, `not`, `isnt`, and `aint` no longer work with `_strict` modifier.\n- `contains` and `doesnt_contain` no longer work on array/object data.\n- `matches`, `match`, `regex` and `doesnt_match` now ignore pattern delimiters and modifiers.\n- `is_uppercase`, `is_lowercase`, `is_today`, `is_yesterday`, `is_between`, `is_leap_year`, `is_weekday`, `is_weekend`, `in_array` and `is_json` conditions were removed.\n\n\n---\n\n\n## Breaking Changes: API\n\n### Global Functions\n\nMost of the global functions have been removed in an effort to prevent pollution of the global namespace.\n\n- Removed `array_get()`, use `Statamic\\Support\\Arr::get()`\n- Removed `array_reindex()`\n- Removed `array_filter_key()`\n- Removed `translate()`, use `trans()`\n- Removed `translate_choice()`, use `trans_choice()`\n- Removed `bool_str()`, use `Statamic\\Support\\Str:bool()`\n- Removed `str_bool()`, use `Statamic\\Support\\Str::toBool()`\n- Removed `site_locale()`, use `Statamic\\Facades\\Site::current()->handle()`\n- Removed `default_locale()`, use `Statamic\\Facades\\Config::getDefaultLocale()`\n- `cp_resource_url()` is now `Statamic\\Statamic::assetUrl()` (being consistent with [what Laravel considers an asset](https://laravel.com/docs/mix))\n- `resource_url()` is now `Statamic\\Statamic::url()`\n- Removed `path()`, use `Statamic\\Facades\\Path::tidy($from . '/' . $to)`\n- Removed `root_path()`, use `base_path()`\n- Removed `webroot_path()`, use `public_path()`\n- Removed `site_path()`\n- Removed `local_path()`\n- Removed `bundles_path()`\n- Removed `addons_path()`\n- Removed `settings_path()`\n- Removed `site_storage_path()`\n- Removed `cache_path()`\n- Removed `logs_path()`\n- Removed `temp_path()`\n- Removed `carbon()`, use `Illuminate\\Support\\Carbon`\n- Removed `datastore()`\n- Removed `site_root()`\n- Removed `resources_root()`\n- Removed `collect_content()`\n- Removed `collect_entries()`, use `EntryCollection::make()`\n- Removed `collect_globals()`, use `GlobalCollection::make()`\n- Removed `collect_assets()`, use `AssetCollection::make()`\n- Removed `collect_terms()`, use `TermCollection::make()`\n- Removed `collect_users()`, use `UserCollection::make()`\n- Removed `collect_files()`, use `FileCollection::make()`\n- Removed `collect_pages()`\n- Removed `me()`, use `Statamic\\Facades\\User::current()`\n- Removed `addon()`\n- Removed `nav()`\n- Removed `col_class()`\n- Removed `svg()`\n- Removed `inline_svg()`\n- Removed `is_id()`\n- Removed `active_for()`\n- Removed `nav_is()`\n- Removed `format_url()`, use `Statamic\\Facades\\URL::format()`\n- Removed `markdown()`, use `Statamic\\Support\\Html::markdown()`\n- Removed `textile()`, use `Statamic\\Support\\Html::textile()`\n- Removed `t()`, use `__()`\n- Removed `slugify()`, use `Statamic\\Support\\Str::slug()`\n- Removed `smartypants()`, use `Statamic\\Support\\Html::smartypants()`\n- Removed `bool()`, use `Statamic\\Support\\Str::toBool()`\n- Removed `int()`, use `intval()`\n- Removed `d()`, use `dump()`\n- Removed `gravatar()`, use `Statamic\\Support\\URL::gravatar()`\n- Removed `format_input_options()` from both PHP and JS (use `HasInputOptions` mixin)\n- Removed `format_update()`\n- Removed `modify()`\n- Removed `refreshing_addons()`\n- Removed `cp_middleware()`\n- Removed `sanitize()`, use `Statamic\\Support\\Str::sanitize()`\n- Removed `sanitize_array()`, use `Statamic\\Support\\Arr::sanitize()`\n- Removed `array_filter_recursive`\n- Removed `array_filter_use_both`, use `array_filter($arr, $cb, ARRAY_FILTER_USE_BOTH)`\n- Removed `mb_str_word_count`\n- Removed `to_moment_js_date_format`\n\n\n### Suggest Modes\n\nSuggest modes have been replaced by [customized relationship fieldtypes](/extending/relationship-fieldtypes).\n\n### Statamic\\API\n\n`Statamic\\API` classes are now `Statamic\\Facades`\n\n#### Statamic\\API\\Addon\n\n- Removed `manager()`\n- Renamed `create` to `make`\n\n#### Statamic\\API\\Arr\n\n- Moved to `Statamic\\Support\\Arr`\n\n#### Statamic\\API\\Asset\n\n- Renamed `whereId` to `findById`\n- Renamed `whereUrl` to `findByUrl`\n\n#### Statamic\\API\\AssetContainer\n\n- Removed `wherePath`\n- Renamed `create` to `make`\n\n#### Statamic\\API\\Assets\n\n- Removed, use `Asset`.\n\n#### Statamic\\API\\Auth\n\n- Removed. Use `Illuminate\\Support\\Facades\\Auth`\n\n#### Statamic\\API\\AssetContainer\n\n- Removed `create`, use `make`.\n- Removed `wherePath`\n\n#### Statamic\\API\\Cache\n\n- Removed. Use `Illuminate\\Support\\Facades\\Cache`\n- `put()` would accept a third argument for minutes, `null` for forever. The Laravel class requires a time. Use `Cache::forever()` to put forever.\n\n#### Statamic\\API\\Collection\n\n- Renamed `create` to `make`.\n- Removed `handleExists`\n- Renamed `whereHandle` to `findByHandle`\n\n#### Statamic\\API\\Content\n\n- Removed. Use `Data`.\n\n#### Statamic\\API\\Cookie\n\n- Removed. Use `Illuminate\\Support\\Facades\\Cookie`\n\n#### Statamic\\API\\Crypt\n\n- Removed. Use `Illuminate\\Support\\Facades\\Crypt`\n\n#### Statamic\\API\\Email\n\n- Removed. Use [Laravel Mailables](https://laravel.com/docs/mail).\n\n#### Statamic\\API\\Entries\n\n- Removed. Use `Entry`\n\n#### Statamic\\API\\Entry\n\n- `whereCollection()` now only supports a single collection (use `whereInCollection()` if you need to pass multiple)\n- Renamed `whereUri` to `findByUri`\n- Removed `exists`\n- Removed `slugExists`\n- Removed `countWhereCollection()`\n- Renamed `create` to `make`\n\n#### Statamic\\API\\Event\n\n- Removed. Use `Illuminate\\Support\\Facades\\Event`\n\n#### Statamic\\API\\Fieldset\n\n- Tip: You probably want `Blueprint` now.\n- Removed `create`\n- Renamed `get` to `find`, and removed the `$type` argument.\n\n#### Statamic\\API\\Form\n\n- Renamed `get` to `find`\n- Removed `getAllFormsets`\n- Renamed `create` to `make`\n- Removed `fields`\n\n#### Statamic\\API\\Globals\n\n- Removed. Use `GlobalSet`\n\n#### Statamic\\API\\GlobalSet\n\n- Renamed `create` to `make`\n- Renamed `whereHandle` to `findByHandle`\n\n#### Statamic\\API\\Hash\n\n- Removed. Use `Illuminate\\Support\\Facades\\Hash`\n\n#### Statamic\\API\\Helper\n\n- Removed\n\n#### Statamic\\API\\Please\n\n- Removed. Use `Illuminate\\Support\\Facades\\Artisan`.\n\n#### Statamic\\API\\Str\n\n- Moved to `Statamic\\Support\\Str`\n\n#### Statamic\\API\\URL\n\n- Removed `removeSiteRoot()`\n\n#### Statamic\\API\\Zip\n\n- Removed because core no longer uses it.\n\n[composer]: https://getcomposer.org\n"
  },
  {
    "path": "content/collections/pages/validation.md",
    "content": "---\nid: 268d444c-88e3-4b52-bfc6-165749ef3ec1\nblueprint: page\ntitle: Validation\nintro: 'Statamic allows you to validate your data using Laravel''s validation system.'\ntemplate: page\n---\n\n## Overview\n\nWhile configuring a [blueprint or fieldset field](/blueprints), switch to the **Validation** tab where you can choose from [any built in Laravel rule][laravel-validation].\n\n<figure>\n    <img src=\"/img/field-validation.webp\" alt=\"Field validation\" class=\"u-hide-in-dark-mode\"/>\n    <img src=\"/img/field-validation-dark.webp\" alt=\"Field validation\" class=\"u-hide-in-light-mode\"/>\n    <figcaption>Add validation rules (with a shortcut for requiring)</figcaption>\n</figure>\n\nIn this screenshot, you can see that the field has an `alpha_dash` and `min:4` rule which means you can only type letters and dashes, like a slug, and that it\nmust be at least 4 characters. You have plenty of options to be creative and confident that your data will be entered the way you need it to be.\n\nHere's a peek at how that YAML is structured.\n\n```yaml\n-\n  handle: your_field\n  field:\n    type: text\n    validate:\n      - alpha_dash\n      - 'min:4'\n```\n\n:::tip\nIf you're interested in customizing user password validation, you can read about that [here](/users#password-validation).\n:::\n\n\n## Required fields\n\nBeing the most common type of validation rule, we give you a shortcut for that. Simply toggle it on, or add `required: true` to the YAML.\n\n\n## Validating nestable fields\n\nStatamic's [Grid](/fieldtypes/grid), [Replicator](/fieldtypes/replicator), and [Bard](/fieldtypes/bard) fields can all contain sub-fields.\n\nYou can add validation to those sub-fields as you would with any top level fields.\n\nHowever, if you want to use validation rules that target other fields, and the target field is in the same context (e.g. in the same Grid row, or same Replicator set), you may use the `{this}` placeholder. The path to the nested field will be expanded by Statamic for you.\n\n```yaml\n-\n  handle: variations\n  field:\n    type: grid\n    fields:\n      -\n        handle: purchasable\n        type: toggle\n      -\n        handle: price\n        type: integer\n        validate: 'required_with:{this}.purchasable'\n```\n\nThis would make sure that the `price` field is only required if the `purchasable` toggle is true within that same set. Without `{this}`, it would be checking for a `purchasable` field at the top level of your form.\n\n:::best-practice\nRather than reaching for `{this}`, you can consider using conditional fields along with the `sometimes|required` rules.\n\n```yaml\n    fields:\n      -\n        handle: purchasable\n        type: toggle\n      -\n        handle: price\n        type: integer\n        if:\n          purchasable: true\n        validate:\n          - sometimes\n          - required\n```\n:::\n\n\n## Available rules\n\n### All Laravel rules\n\nYou may use any validation rule provided by Laravel. You can view the complete list [on their documentation][laravel-validation]. You may also use [custom rules](#custom-rules).\n\n### Unique entry value\n\n```yaml\n-\n  handle: highlander\n  field:\n    type: text\n    validate: 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n```\n\nThis works like Laravel's `unique` validation rule, but for Statamic entries. The rule should be used verbatim as shown above. Statamic will replace the `collection`, `id`, and `site` behind the scenes.\n\nYou can then customize the error message right in your `resources/lang/{lang}/validation.php` file, like so\n\n```php\n'custom' => [\n    'highlander' => [\n        'unique_entry_value' => 'There can be only one!',\n    ]\n],\n```\n\n### Unique term value\n\n```yaml\n-\n  handle: foo\n  field:\n    type: text\n    validate: 'new \\Statamic\\Rules\\UniqueTermValue({taxonomy}, {id}, {site})'\n```\n\nThis works like the `UniqueEntryValue` rule, but for taxonomy terms.\n\n### Unique user value\n\n```yaml\n-\n  handle: login\n  field:\n    type: text\n    validate: 'new \\Statamic\\Rules\\UniqueUserValue({id})'\n```\n\nThis works like the `UniqueEntryValue` rule, but for users.\n\n:::tip\nIf you want to override the field that is being validated (e.g. in Livewire Form Objects), you can do so by passing a second parameter to the validation rule, such as `new \\Statamic\\Rules\\UniqueUserValue({id}, \"username\")`.\n:::\n\n## Custom Rules\n\nYou may use custom validation rules via Laravel's `Rule` objects.  \n[Documentation on those is here](https://laravel.com/docs/13.x/validation#using-rule-objects).\n\nTo references those from your field, you can add them to your `validation` array as if you were writing PHP:\n\n<figure>\n    <img src=\"/img/field-validation-custom-rule.webp\" alt=\"Custom Field validation rules\" class=\"u-hide-in-dark-mode\"/>\n    <img src=\"/img/field-validation-custom-rule-dark.webp\" alt=\"Custom Field validation rules\" class=\"u-hide-in-light-mode\"/>\n    <figcaption>Custom Field validation rules</figcaption>\n</figure>\n\nIf you're writing directly into the YAML, make sure to escape any quotes:\n\n```yaml\nvalidate:\n  - required\n  - string\n  - new App\\Rules\\Uppercase\n  - 'new App\\Rules\\AnotherRule(''with argument'')'\n```\n\n## Validating programmatically\n\nYou may validate programmatically via the blueprint, if you need to do so within custom code.\n\n```php\n$values = $request->all();\n\n$valid = $blueprint->fields()->addValues($values)->validate();\n\n$entry->data($valid)->save();\n```\n\nThe `validate` method would throw a `ValidationException` if invalid and return the appropriate response.  \n\n[laravel-validation]: https://laravel.com/docs/13.x/validation#available-validation-rules\n"
  },
  {
    "path": "content/collections/pages/variables.md",
    "content": "---\ntitle: 'Variables'\nblueprint: link\nid: 662a5918-ac0f-42a1-bf40-5a7a320e87e1\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/vercel.md",
    "content": "---\nid: 01ab4b2b-bee2-4697-b3c6-cb129d783589\nblueprint: page\ntitle: 'Deploying Statamic with Vercel'\nparent: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n---\n\n:::warning\nPlease note that by hosting your site statically with a service like Vercel or Netlify, **you can't access the Control Panel in production** and are **not able to use dynamic features** like Statamic's built-in forms or random sorting in your templates.\n:::\n\nDeployments are triggered by committing to Git and pushing to GitHub.\n\n- Create a new file `build.sh` in your project and paste from the [example code snippet](#example-build-script) below\n- Run `chmod +x build.sh` on your terminal to make sure the file can be executed when deploying\n- Import a new site in your [Vercel](https://vercel.com) account\n- Link the site to your desired GitHub repository\n- Set build command to `./build.sh`\n- Set output directory to `storage/app/static`\n- Add `APP_KEY` env variable, by running `php artisan key:generate` locally, and copying from your `.env`\n    - ie. `APP_KEY` `your-app-key-value`\n- Add `APP_URL` environment variable after your site has a configured domain\n    - ie. `APP_URL` `https://thats-numberwang-47392.vercel.app`\n\n#### Example Build Script\n\nAdd the following snippet to `build.sh` file to install PHP, Composer, and run the `ssg:generate` command:\n\n```\n#!/bin/sh\n\n# Install PHP & WGET\ndnf clean metadata\ndnf install -y php8.2 php8.2-{common,mbstring,gd,bcmath,xml,fpm,intl,zip}\ndnf install -y wget\n\n# INSTALL COMPOSER\nEXPECTED_CHECKSUM=\"$(wget -q -O - https://composer.github.io/installer.sig)\"\nphp -r \"copy('https://getcomposer.org/installer', 'composer-setup.php');\"\nACTUAL_CHECKSUM=\"$(php -r \"echo hash_file('sha384', 'composer-setup.php');\")\"\n\nif [ \"$EXPECTED_CHECKSUM\" != \"$ACTUAL_CHECKSUM\" ]\nthen\n    >&2 echo 'ERROR: Invalid installer checksum'\n    rm composer-setup.php\n    exit 1\nfi\n\nphp composer-setup.php --quiet\nrm composer-setup.php\n\n# INSTALL COMPOSER DEPENDENCIES\nphp composer.phar install\n\n# GENERATE APP KEY\nphp artisan key:generate\n\n# BUILD STATIC SITE\nphp please stache:warm -n -q\nphp please ssg:generate\n```\n"
  },
  {
    "path": "content/collections/pages/view-models.md",
    "content": "---\ntitle: 'View Models'\nintro: View Models give you a chance to manipulate or set data in PHP _right before_ everything is passed into your view, parsed, and then rendered.\ntemplate: page\nid: fbf59081-ba24-4e82-b011-b687be228c89\nblueprint: page\n---\n## Overview\nHave you ever had some complex data or conditions you found challenging to work with in your [Antlers][antlers] templates? Sure you have. Have you ever peeled a banana and had the stem hang onto the peel stubbornly only to have the fruit poke its face out of a surprise gap like a hoodie? You can probably relate to that too.\n\nWhile Antlers is powerful and flexible, what if you could just jump into PHP-land, work out some tricky logic, and put the data back in place before it was rendered?\n\nEnough rhetorical questions – in Statamic you can now solve one of those two problems with a View Model. 🍌\n\n:::tip\nViewModels manipulate the _view's_ data at the **last possible moment before render**, not the entry data itself. This approach isn't appropriate for globally altering or manipulating content.\n\nYou may consider creating a [computed value](/computed-values) for an entry instead, which would work anywhere, for example in other parts of PHP, or within collection loops in your templates.\n:::\n\n## What's a View Model?\n\nBy defining a `view_model` in your entry data or anywhere in the [cascade][cascade], Statamic will run the `data()` method of that named class and merge any array data you return before injecting it into your view/template.\n\nWhile _inside_ the view model, you have access to the full cascade and can set new variables or modify existing ones.\n\n## Example\nLet's assume we have a [Replicator][replicator] field with a bunch of content blocks (an associative array) and we'd like to calculate some stats on how much content there is and how long it might take to read it.\n\n**First**, let's define the view model location. Rather than putting it in an entry, we'll put it in the collection so that it's applied to every entry.\n\n```yaml\n# content/collections/articles.yaml\ntitle: Articles\ninject:\n  view_model: App\\ViewModels\\ArticleStats\n```\n\n```yaml\n# content/collections/articles/a-long-article.md\ntitle: \"A Long Article Plz Read it Mmmkay?\"\ncontent:\n  -\n    type: text\n    text: # Piles of content live here\n```\n\n**Next,** we'll loop through the content, assemble a giant string of all the content, perform some math, and return the stats.\n\n```php\n<?php\n\nnamespace App\\ViewModels;\n\nuse Statamic\\View\\ViewModel;\n\nclass ArticleStats extends ViewModel\n{\n    public function data(): array\n    {\n        // Combine content blocks\n        $html = collect($this->cascade->get('content'))\n                ->implode('text', \" \");\n\n        // Remove HTML tags\n        $content = strip_tags($html);\n\n        // Calculate stats\n        $character_count = strlen($content);\n        $word_count      = mb_str_word_count($content);\n        $read_time       = ceil($word_count / 200);\n\n        return [\n            'character_count' => $character_count,\n            'word_count'      => $word_count,\n            'read_time'       => $read_time\n        ];\n    }\n}\n```\n\n**Finally,** we'll show these stats in our view.\n\n```\n<h1>{{ title }}</h1>\n<p class=\"meta\">\n    {{ word_count }} words / read time approx {{ read_time }}m.\n</p>\n```\n\nView models help keep your views nice and clean. Use them often and you'll find that they're quickly becoming your new best friend. Unless you find a better friend named [Computed Values](/computed-values). He's kind of the new kid in town and his jean jacket is straight fire.\n\n\n[antlers]: /antlers\n[cascade]: /cascade\n[replicator]: /fieldtypes/replicator\n"
  },
  {
    "path": "content/collections/pages/views.md",
    "content": "---\nid: 74c47654-8c47-49b1-a616-ed940ce19977\nblueprint: page\ntitle: Views\nintro: 'Views contain HTML, have access to your data, and are used to render the front-end of your site. Layouts, templates, and partials are all different types of views.'\ntemplate: page\nrelated_entries:\n  - dcf80ee6-209e-45aa-af42-46bbe01996e2\n  - fbf59081-ba24-4e82-b011-b687be228c89\n  - c7816387-ebc4-4204-b5f2-8e7073a4db8b\n  - 5e848460-9bbc-449e-8edd-182d918163ff\n  - 3d5efc5c-17b1-480b-bb77-53faf3d9552c\n---\n## Overview\nViews contain the HTML served by the frontend of your site and are stored in the `resources/views` directory. A simple view might look something like this (but should it?):\n\n```\n// resources/views/greeting.antlers.html\n\n<html>\n  <body>\n    <p>The invention of the shovel was ground breaking.</p>\n  </body>\n</html>\n```\n\nEach file inside your `resources/views` directory is a **view**. Each view can be used in different ways — given different roles and responsibilities.\n\n:::watch https://www.youtube.com/embed/leI3qRhzHLQ\nSee how layouts and templates work together.\n:::\n\n## Layouts\n\n**Layouts** are the foundation of your frontend's HTML. Any markup you want to present no matter what page you're on, no matter where you go, how far you travel, or loud you sing, should go into a layout.\n\nBy default, Statamic will look for and use `/resources/views/layout.antlers.html`, but you're welcome to create other layouts and configure specific entries, whole sections, or the whole site to use those instead.\n\nLayouts usually contain `<head></head>` markup, global header, navigation, footer, JavaScript includes, and so on. In between all that HTML is your _template_ area — the magical place where unique, non-global things happen. Use the `{{ template_content }}` variable to set where you'd like that to live.\n\n```\n// resources/views/layout.antlers.html\n<html>\n  <head>\n    <title>{{ title }} | {{ site_name }}</title>\n    <link rel=\"stylesheet\" href=\"/css/tailwind.css\">\n  </head>\n  <body>\n    {{ partial:nav }}\n    {{ template_content }}\n    <footer>\n      &copy; {{ now format=\"Y\" }} {{ site_name }}\n    </footer>\n    <script src=\"/js/site.js\"></script>\n  </body>\n</html>\n```\n\nAn entry can control the layout it's rendered with by setting the `layout` system variable.\n\n``` yaml\n# Use /resources/views/rss.antlers.html\nlayout: rss\n```\n\nAs mentioned above, Statamic will use the `resources/views/layout.antlers.html` view as the default layout. You can change the default layout in your `config/statamic/system.php` config file:\n\n```php\n'layout' => env('STATAMIC_LAYOUT', 'layout'),\n```\n\n## Templates\n\nTemplates are views that can be used by any entry or section on your site. The template's contents will be inserted into the `{{ template_content }}` variable in your layout much like the way a painting goes into an ornate picture frame.\n\nAn entry can control the template it's rendered with by setting the `template` system variable.\n\n``` yaml\n# This fake entry will use /resources/views/gallery.antlers.html\ntemplate: gallery\ntitle: Photo Gallery\n```\n\n:::tip\nYou can use the [template](/fieldtypes/template) fieldtype to make choosing your template in any entry easy. Any [fieldtype](/fieldtypes) that returns a string like in the example above works too, so you have a lot of flexibility.\n:::\n\n### Map templates to entry blueprints\n\n\nTo automatically map the template from an entry's blueprint, set the collection's default template to `@blueprint`.\n\n``` yaml\n# collections/{collection}.yaml\ntemplate: '@blueprint'\n```\n\nBy doing this, Statamic will look for the corresponding template in `/resources/views/{collection}/{blueprint}.antlers.html`.\n\nFor example, if you have an `articles` collection entry that uses a blueprint with the handle of `long`, the `/resources/views/articles/long.antlers.html` template will be used.\n\n:::tip\nYou can still set a template on the entry level and override the default.\n:::\n\n## Partials\n\nPartials are reusable views that may find themselves in any number of other layouts, templates, and other partials. You can use any view as a partial by using the [partial](/tags/partial) tag.\n\n```\n// This will import /resources/views/blog/_card.antlers.html\n{{ partial:blog/card }}\n```\n\n:::best-practice\nWe recommend prefixing any views intended to be _only_ used as partials with an underscore, `_like-this.antlers.html` and reference them `{{ partial:like-this }}`. The underscore is not necessary in the partial tag definition.\n:::\n\n:::watch https://www.youtube.com/embed/Ddz6mD-jT7E\nWe have a video about Partials too!\n:::\n\n## Using Blade\n\nIf your view ends with `.blade.php` instead of `.antlers.html`, it will be rendered with Laravel's [Blade](https://laravel.com/docs/blade) engine. All of the same data will be injected into the view, but you won't have access to Statamic's [tags](/tags).\n\nThis is useful if...\n\n- You want to reuse existing Laravel views and keep your markup DRY.\n- You have some gnarly loops to work with and can benefit from temporary variables and the `foreach` loop approach.\n- You're really used to using Blade and don't want to learn anything else even if it's really simple, similar, and powerful. You do you.\n\n\n## Recommended conventions\n\nWe recommend the following conventions for consistency. These are just suggestions, not requirements.\n\n### Naming\n\n- Use lowercase filenames\n- Use hyphens to separate words\n- Prefix partials with _underscores\n- Be consistent with plurality (e.g. blog, <strike>articles</strike>, faq)\n\n### Organizing\n\nThere are a few recommended ways to organize your layouts, templates, and partials. But you don't have to take _our_ word for it. 🌈\n\n### Go super flat\n\nPartials are indicated by a prefixed underscore (`_header`), layout by the word `layout` and everything else is a template. **Best for small sites.**\n\n``` files theme:serendipity-light\nresources/views/\n  _header.antlers.html\n  about.antlers.html\n  article.antlers.html\n  layout.antlers.html\n  listing.antlers.html\n  page.antlers.html\n```\n\n### Organize by type\n\nThis is a bit more of a Statamic v2 style where views are grouped by type - partials, layouts, and templates. **Best for medium sized sites.**\n\n``` files theme:serendipity-light\nresources/views/\n  partials/\n    _card.antlers.html\n    _footer.antlers.html\n    _nav.antlers.html\n  layouts/\n    amp.antlers.html\n    api.antlers.html\n    main.antlers.html\n  templates/\n    about.antlers.html\n    article-list.antlers.html\n    article-show.antlers.html\n    faq-list.antlers.html\n    faq-show.antlers.html\n    form.antlers.html\n```\n\n### Organize by section\n\nA more Laravel/application approach where views are grouped by section (or collection), along with their own partials and alternate layout files. **Best for large sites.**\n\n``` files theme:serendipity-light\nresources/views/\n  blog/\n    _card.antlers.html\n    index.antlers.html\n    layout.antlers.html\n    rss.antlers.html\n    show.antlers.html\n  contact/\n    index.antlers.html\n    success.antlers.html\n  faq/\n    layout.antlers.html\n    index.antlers.html\n    show.antlers.html\n  layout.antlers.html\n```\n"
  },
  {
    "path": "content/collections/pages/vite-tooling.md",
    "content": "---\nid: 5f26a634-19ae-4413-8b9e-1ed9c2c76bb0\nblueprint: page\ntitle: 'Vite Tooling'\ntemplate: page\nintro: 'How to use Vite in your addon.'\n---\n## Files\nWe recommend using Vite to manage your addon's asset build process. To use Vite, you'll need the following files inside your addon.\n\n``` files theme:serendipity-light\nyour-addon/\n    resources/\n        dist/\n        js/\n            addon.js\n        css/\n            addon.css\n    src/\n        ServiceProvider.php\n    vite.config.js\n    package.json\n```\n\n### package.json\nHere's `package.json`, which contains the commands you'll need to run, and the dependencies needed to run Vite.\n\n- The `laravel-vite-plugin` package allows a simpler wrapper around common Vite options, and provides hot reloading.\n- The `@statamic/cms` package allows you to import Vue components from Statamic. As it's not a \"real\" npm package, the code is being pulled from your addon's `vendor` directory.\n\n```json\n{\n    \"private\": true,\n    \"scripts\": {\n        \"dev\": \"vite\",\n        \"build\": \"vite build\"\n    },\n    \"dependencies\": {\n        \"@statamic/cms\": \"file:./vendor/statamic/cms/resources/dist-package\"\n    },\n    \"devDependencies\": {\n        \"laravel-vite-plugin\": \"^1.2.0\",\n        \"vite\": \"^6.3.4\"\n    }\n}\n```\n\n:::tip Note\nIf you aren't already, your addon should require `statamic/cms` as a Composer dependency. Otherwise, the `vendor/statamic/cms` directory won't exist.\n:::\n\n### vite.config.js\nHere's `vite.config.js`, which configures Vite itself.\n\n- The Laravel Vite plugin defaults to the `public` directory to place the compiled code because it's intended to be used in your app. We've changed it to `resources/dist` as we think it's a nicer convention when using in an addon. Of course, you may customize it. Whichever directory you choose, you'll need to make sure it exists.\n- The `statamic` plugin allows you to import Statamic's Vue components and CSS files.\n\n```js\nimport { defineConfig } from 'vite';\nimport laravel from 'laravel-vite-plugin';\nimport statamic from '@statamic/cms/vite-plugin';\n\nexport default defineConfig({\n    plugins: [\n        laravel({\n            input: [\n                'resources/js/addon.js',\n                'resources/css/addon.css'\n            ],\n            publicDirectory: 'resources/dist',\n        }),\n        statamic(),\n    ],\n});\n```\n\n### addon.js\nThe `addon.js` file is responsible for registering Vue components or hooks.\n\n- Wrap any component registrations inside a `Statamic.booting()` callback to ensure components are registered _after_ Statamic has booted.\n- Use `Statamic.$components.register()` (or `Statamic.$inertia.register()` for Inertia.js pages) to register components.\n\n``` js\n// import YourComponent from './components/YourComponent.vue';\n\nStatamic.booting(() => {\n    // Statamic.$components.register('component-name', YourComponent);\n});\n```\n\n### addon.css\nTrue to its name, the `addon.css` file is responsible for your addon's CSS.\n\n```css\n/** Your custom styles go here */\n```\n\n#### Tailwind CSS\n\nIf you want to use [Tailwind CSS](https://tailwindcss.com) in your addon's components, you'll need to install & configure Tailwind.\n\n1. First, install `tailwindcss` and `@tailwindcss/vite`:\n    ```sh\n    npm install tailwindcss @tailwindcss/vite\n    ```\n\n2. Add the Tailwind Vite plugin to your `vite.config.js` file:\n\n    ```js\n    import { defineConfig } from 'vite';\n    import laravel from 'laravel-vite-plugin';\n    import statamic from '@statamic/cms/vite-plugin';\n    import tailwindcss from '@tailwindcss/vite'; // [tl! ++ **]\n\n    export default defineConfig({\n        plugins: [\n            laravel({\n                input: [\n                    'resources/js/addon.js',\n                    'resources/css/addon.css'\n                ],\n                publicDirectory: 'resources/dist',\n            }),\n            statamic(),\n            tailwindcss(),  // [tl! ++ **]\n        ],\n    });\n    ```\n\n3. In your addon's CSS file, import Statamic's `tailwind.css` file:\n    ```css\n    @import \"@statamic/cms/tailwind.css\";\n    ```\n\n    You don't need to `@import \"tailwindcss\"`, as it'll be imported by Statamic's `tailwind.css` file.\n\n### Overriding Control Panel CSS\n\nWe organize our CSS using [cascade layers](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers#issues_cascade_layers_can_solve) to help manage styling priorities and avoid conflicts. Internally, we define layers in this order: `@layer base, addon-theme, addon-utilities, components, utilities, ui, ui-states;`.\n\n#### Overriding Control Panel CSS using Tailwind\n\nIf you use Tailwind, any CSS you add to your addon will automatically go into the `addon-utilities` layer.\n\nWe intentionally place addon layers below Statamic’s core layers. This prevents addon styles from unintentionally overriding Statamic's core styles—for instance, an addon's `bg-something` class won’t interfere with Statamic's `dark:bg-something`, which could otherwise cause visual issues like incorrect background colors.\n\nIf you do need to explicitly override Statamic’s styles, you can use the Tailwind `!` prefix (e.g., `!lg:grid-cols-3`) to force your class to take priority, which is `!important` under the hood. However, use this technique sparingly, as it will override Statamic’s intended default styling and could introduce unintended side effects.\n\n#### Overriding Control Panel CSS using plain CSS\n\nIf you don't use Tailwind, you should still use the `addon-utilities` layer by default, to avoid conflicts with Statamic's core styles.\n\nIf you want to override something specific in the CP CSS and your rule is clashing with a core style, there are a couple of different approaches you can take.\n\n1. You can simply write CSS outside cascade layers, which will have higher specificity than Statamic's styles.\n2. You can also use the `!important` flag inside `@layer addon-utilities { ... }` to override Statamic's styles.\n\n### Service Provider\nHere's `ServiceProvider.php`, which is the PHP entry point to your addon. You should add a `$vite` property which mirrors the paths in your `vite.config.js` file.\n\n```php\nclass ServiceProvider extends AddonServiceProvider\n{\n    protected $vite = [ // [tl! ++:start]\n        'input' => [\n            'resources/js/addon.js',\n            'resources/css/addon.css',\n        ],\n        'publicDirectory' => 'resources/dist',\n    ]; // [tl! ++:end]\n}\n```\n\n:::tip\nIf you use the `php please make:fieldtype` command, these files will be created automatically for you.\n:::\n\n## Development\n\nIf you visit the Control Panel before running any commands, you will be greeted with a `Vite manifest not found` error. You'll need to install dependencies (the first time only) and start the development server.\n\n```bash\ncd addons/your/addon\nnpm install\nnpm run dev\n```\n\nNow that the Vite server is running, the error in the Statamic CP should be gone once you refresh.\n\nTo use Hot Module Reloading (HMR) or the [Vue Devtools](https://devtools.vuejs.org) browser extension, you need to publish a special \"dev build\", which can be done via the `vendor:publish` command:\n\n```bash\nphp artisan vendor:publish --tag=statamic-cp-dev\n```\n\nAlternatively, it can be symlinked:\n\n```bash\nln -s /path/to/vendor/statamic/cms/resources/dist-dev public/vendor/statamic/cp-dev\n```\n\nStatamic will use the dev build as long as `APP_DEBUG=true` in your `.env` and the `public/vendor/statamic/cp-dev` directory exists. You **shouldn't** commit these or use this on production.\n\n:::tip\nIf you're using Herd or Valet with a secured site, your JS might not be loading correctly due to access control checks. You'll need Vite know about your Laravel site in `vite.config.js`.\n\n```js\nexport default defineConfig({\n    plugins: [\n        laravel({\n            detectTls: 'yoursite.test', // [tl!++]\n            input: [\n```\n:::\n\nTo avoid needing to run the development script every time you visit the Control Panel, you may wish to build your CSS & JS.\n\n```bash\nnpm run build\n```\n\nYou may need to symlink your addon's `resources/dist` directory the first time so it points to your addon's directory:\n\n```bash\nln -s ./addons/your/addon/resources/dist public/vendor/package\n```\n\n## Deployment\n\nWhen you're ready to deploy your addon, either to your own application or getting it ready to go into the marketplace, you should compile the production assets.\n\nMake sure that the Vite dev server is not running, then run:\n\n```bash\nnpm run build\n```\n\nThe files will be compiled into `resources/dist`.\n\nIf you'd like to test that everything is working you can run `php artisan vendor:publish` in your app and choose your addon's tag. The compiled assets should be copied into `public/vendor/your-addon` and they should be loaded in the Control Panel.\n\n## Configuring Vue\n\nYou may use the `Statamic.configuring()` hook to register Vue plugins and global properties before the Control Panel is mounted.\n\n```js\n// addon.js\n\nStatamic.configuring(() => {\n    Statamic.$app.use(PerfectScrollbarPlugin);\n\n    Object.assign(Statamic.$app.config.globalProperties, {\n        $something: something,\n    });\n});\n```\n"
  },
  {
    "path": "content/collections/pages/vue-2-to-3.md",
    "content": "---\nid: c3553d05-d1a8-453a-a59b-7e67dd2412a4\nblueprint: page\ntitle: 'Upgrade from Vue 2 to Vue 3'\nintro: 'A guide for upgrading Vue 2 to 3.'\ntemplate: page\n---\n## Overview\n\nAs part of the Statamic 6 release, Vue was upgraded to version 3. \n\n\n## Vite\n\n`package.json`:\n\n```json\n{\n    \"devDependencies\": {\n        \"@vitejs/plugin-vue2\": \"^6.0.1\", // [tl! --]\n        \"@statamic/cms\": \"file:./vendor/statamic/cms/resources/dist-package\" // [tl! ++]\n    }\n}\n```\n\n`vite.config.js`:\n\n```js\nimport vue from '@vitejs/plugin-vue2'; // [tl! --]\nimport laravel from 'laravel-vite-plugin';\nimport statamic from '@statamic/cms/vite-plugin'; // [tl! ++]\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n    plugins: [\n        statamic(), // [tl! ++]\n        laravel({\n            refresh: true,\n            input: ['resources/js/cp.js']\n        }),\n    ],\n});\n```\n\n## Laravel Mix\n\nIf you are still using Laravel Mix, you will need to switch to Vite.\n\n## Composition API\n\nConverting your components to the Composition API **is not required**.\n\nHowever, since it is now supported, you might love it. We recommend it. It could greatly clean up your components. Check out this example component written using the Options API:\n\n```vue\n<script>\nimport { FooComponent, BarComponent } from './components';\nimport { Button, Card } from '@statamic/cms/ui';\n\nexport default {\n    components: {\n        FooComponent,\n        BarComponent,\n        Button,\n        Card\n    },\n    data() {\n        return {\n            firstName: 'John',\n            lastName: 'Smith',\n        }   \n    },\n    computed: {\n        fullName() {\n            return `${this.firstName} ${this.lastName}`;\n        }  \n    },\n    methods: {\n        changeName(first, last) {\n            this.firstName = first;\n            this.lastName = last;\n        }\n    },\n    watch: {\n        fullName(newName) {\n            console.log(`Name changed to ${newName}`);\n        }\n    }\n}\n</script>\n```\n\nAnd now converted to the Composition API:\n\n```vue\n<script setup>\nimport { FooComponent, BarComponent } from './components';\nimport { Button, Card } from '@statamic/cms/ui';\nimport { ref, computed, watch } from 'vue';\n\nconst firstName = ref('John');\nconst lastName = ref('Smith');\nconst fullName = computed(() => `${firstName.value} ${lastName.value}`);\n\nfunction changeName(first, last) {\n    firstName.value = first;\n    lastName.value = last;\n}\n\nwatch(fullName, (newName) => console.log(`Name changed to ${newName}`));\n</script>\n```\n\n## Imports\n\nYou should be importing components and other files from `@statamic/cms`. You may have been previously importing explicit files straight from the vendor directory. This is not supported.\n\n```js\nimport Something from '../../../vendor/statamic/cms/resources/js/Something.vue'; // [tl! --]\nimport { Something } from '@statamic/cms'; // [tl! ++]\n```\n\n\n## Fieldtypes\n\n### Mixins\n\nMixins will now need to be explicitly imported. Assuming you've updated `vite.config.js` explained above, you should be able to add the following to your fieldtype:\n\n```js\nimport { FieldtypeMixin as Fieldtype } from '@statamic/cms'; // [tl! ++]\n\nexport default {\n    mixins: [Fieldtype],\n    data() {\n        return {\n            //\n        }\n    }\n}\n```\n\n### Events\n\nIf you are manually emitting an `input` event from within a fieldtype, you should change it to `update:value`.\n\n```js\nthis.$emit('input', foo); // [tl! --]\nthis.$emit('update:value', foo); // [tl! ++]\n```\n\n::: tip\nYou should be using `this.update()` if possible anyway.\n\n```js\nthis.$emit('input', foo); // [tl! --]\nthis.update(foo); // [tl! ++]\n```\n:::\n\n\n\n## Props, events, and v-model\n\nVue 3 changes how v-model works.\n\n\n### Fieldtypes\nTo avoid needing to change all references to the `value` prop, we've kept the prop as-is. If you are using `v-model` directly on a fieldtype component, you will need to specify `:value` now.\n\nNote that this is only if you are _using a fieldtype component_ from within another component.\n\n```vue\n<my-fieldtype\n    v-model=\"foo\" <!-- [tl! --] -->\n    v-model:value=\"foo\" <!-- [tl! ++] -->\n/>\n```\n\n### Components that no longer support v-model\n\nIf you were using `v-model`, you must change to the appropriate prop and event:\n\n```vue\n<my-component\n    v-model=\"foo\" <!--[tl! --]-->\n    :value=\"foo\"  <!--[tl! ++]-->\n    @input=\"foo = $event\" <!--[tl! ++]-->\n/>\n```\n\n| Component               | Prop      | Event       |\n|-------------------------|-----------|-------------|\n| `<slugify>`             | `to`     | `@slugified` |\n| `<publish-container>`   | `values` | `@updated`  |\n\n### Components that support v-model\nIf you were *not* using `v-model` and instead using the `value` prop and `input` event, you will need to change to `model-value` and `@update:model-value`.\n\n```vue\n<component\n  :value=\"foo\" <!--[tl! --]-->\n  @input=\"foo = $event\" <!--[tl! --]-->\n  :model-value=\"foo\" <!--[tl! ++]-->\n  @update:model-value=\"foo = $event\" <!--[tl! ++]-->\n/>\n```\n\n| Component      | Notes                                |\n|----------------|--------------------------------------|\n| `<form-group>` |                                      |\n| `<v-select>`   | This is from the vue-select package. |\n\n## Slots\n\nThe slot syntax changed. If you were previously using `slot-scope`, you should change to `v-slot`:\n\nDefault slot:\n\n```html\n<component><!-- [tl! --:start] -->\n    <div slot-scope=\"{ ... }\"></div>\n</component><!-- [tl! --:end] -->\n<component v-slot=\"{ ... }\"><!-- [tl! ++:start] -->\n    <div></div>\n</component><!-- [tl! ++:end] -->\n```\n\nNamed slots:\n\n```html\n<component>\n    <template slot=\"something\" slot-scope=\"{ ... }\"></template><!-- [tl! --] -->\n    <template v-slot:something=\"{ ... }\"></template><!-- [tl! ++] -->\n</component>\n```\n\nYou can also use the shorthand:\n\n```html\n<template v-slot:something=\"{ ... }\"><!-- [tl! --] -->\n<template #something=\"{ ... }\"><!-- [tl! ++] -->\n```\n\n## Bard\n\nSince the `$on`, `$off`, and `$once` methods have been removed from Vue 3, Bard events need to work differently. They have been moved into an event bus on the bard component.\n\nYou might be using these methods if you have a custom Bard toolbar button (via `this.bard`) or Prosemirror mark/node element (via `vm`).\n\n```js\nbard.$on(...); // [tl! --]\nbard.$off(...); // [tl! --]\nbard.$once(...); // [tl! --]\nbard.events.on(...); // [tl! ++]\nbard.events.off(...); // [tl! ++]\nbard.events.once(...); // [tl! ++]\n```\n\n## Components\n\nComponents should be registered using the `$components` API rather than directly through `Statamic`:\n\n```js\nStatamic.component('my-fieldtype', MyFieldtype); // [tl! --]\nStatamic.$components.register('my-fieldtype', MyFieldtype); // [tl! ++]\n```\n\n## Vuex to Pinia\n\n### Publish Components\nThe primary reason for Statamic including Vuex at all was for the Publish components. These no longer use Vuex (or Pinia). If you were using `$store` in your code it's probably for those.\n\n```js\nimport { publishContextKey } from '@statamic/cms/ui'; // [tl! ++]\n\nexport default {\n    inject: [ // [tl! --:start]\n        'storeName',\n    ],// [tl! --:end]\n    inject: { // [tl! ++:start]\n        publishContext: { from: publishContextKey },\n    },// [tl! ++:end]\n    methods: {\n        getValue(handle) {\n            return this.$store.state.publish[this.storeName].values[handle]; // [tl! --]\n            return this.publishContext.values.value[handle]; // values is a ref() so you need .value to unwrap it. [tl! ++]\n        },\n        setValue(handle, newValue) {\n            this.$store.dispatch(`publish/${this.storeName}/setFieldValue`), newValue); // [tl! --]\n            this.publishContext.setFieldValue(newValue); // [tl! ++]\n        }\n    }\n}\n```\n\nThe most common place for the store to be accessed like this is in a fieldtype, so they automatically get the publish context injected as `injectedPublishContainer` and have all the refs unwrapped as `publishContainer`.\n\n```js\nexport default {\n    mixins: [Fieldtype],\n    inject: [ // [tl! --:start]\n        'storeName',\n    ],// [tl! --:end]\n    methods: {\n        getValue(handle) {\n            return this.$store.state.publish[this.storeName].values[handle]; // [tl! --]\n            return this.publishContainer.values[handle]; // [tl! ++]\n        },\n        setValue(handle, newValue) {\n            this.$store.dispatch(`publish/${this.storeName}/setFieldValue`), newValue); // [tl! --]\n            this.publishContainer.setFieldValue(newValue); // [tl! ++]\n        }\n    }\n}\n````\n\n### Custom Stores\nIf you _were_ adding your own Vuex stores, you should switch to Pinia. Rather than registering to a global Vuex store, you define your own store and use it directly in your components.\n\nUse the provided `$pinia` helper rather than trying to import from your own version of Pinia.\n\n```js\n// In bootstrapping... [tl! --:start]\nStatamic.$store.registerModule(['publish', 'myStore'], {\n    state: { foo: 'bar' },\n    mutations: {\n        doSomething(payload) {\n            //\n        }\n    },\n    actions: {\n        doSomething(context, payload) {\n            context.commit('doSomething', payload);\n        }\n    }\n}); \n\n// In component...\nconst foo = this.$store.state.publish.myStore.foo;\nthis.$store.dispatch('publish/myStore/doSomething', 'example'); // [tl! --:end]\n\n\n// mystore.js... [tl! ++:start]\nconst useMyStore = Statamic.$pinia.defineStore('myStore', {\n    state: { foo: 'bar' },\n    actions: {\n        doSomething() {\n            //\n        }\n    }\n});\n\n// In component...\nimport { useMyStore } from './mystore';\nconst store = useMyStore();\nconst foo = store.foo;\nstore.doSomething('example'); // [tl! ++:end]\n```\n\n## Bard Tiptap API\n\nPreviously you would be able to access Tiptap components directly through the Bard API. They will now be provided to you when using the various callbacks. For example:\n\n```js\nconst { Node, Mark, Extension } = Statamic.$bard.tiptap.core; // [tl! --:start]\n\nStatamic.$bard.addExtension(() => {\n    return [\n        Node.create({...}),\n        Mark.create({...}),\n        Extension.create({...}),\n    ]\n}) // [tl! --:end]\n\nStatamic.$bard.addExtension(({ tiptap }) => { // [tl! ++:start]\n    const { Node, Mark, Extension } = tiptap.core;\n    \n    return [\n        Node.create({...}),\n        Mark.create({...}),\n        Extension.create({...}),\n    ]\n}) // [tl! ++:end]\n```\n\nIf you were importing/exporting the component, you should change to a \"factory\" function that accepts the Tiptap API and returns the component. For example:\n\n```js\nimport TextColor from './TextColor.js'; // [tl! --:start]\nStatamic.$bard.addExtension(() => TextColor);\n\n// TextColor.js\nconst { Mark } = Statamic.$bard.tiptap.core;\nexport default Mark.create({}); // [tl! --:end]\n\nimport TextColor from './TextColor.js'; // [tl! ++:start]\nStatamic.$bard.addExtension(({ tiptap }) => TextColor(tiptap));\n\n// TextColor.js\nexport default function (tiptap) {\n    const { Mark } = tiptap.core;\n    return Mark.create({});\n} // [tl! ++:end]\n```\n\n## Component Substitutions\n\nWith the introduction of the [UI component library](https://ui.statamic.dev), a number of components have been replaced by more modern versions.\n\n### Publish\n\nIf you were building a custom publish form using `publish-*` components, they have been replaced by newer equivalents through the UI component library.\n\nYou would have previously needed to set up \"prop- and event-ception\". Now the `PublishContainer` becomes the source of truth and you can define the child components as needed (or not!). \n\nBefore:\n\n```html\n<publish-container\n    :blueprint=\"blueprint\"\n    :values=\"values\"\n    :meta=\"meta\"\n    :errors=\"errors\"\n    @updated=\"values = $event\"\n    v-slot=\"{ setFieldValue, setFieldMeta }\"\n>\n    <publish-tabs\n        @updated=\"setFieldValue\"\n        @meta-updated=\"setFieldMeta\"\n    />\n</publish-container>\n```\n\nAfter:\n\n```vue\n<script>\nimport { PublishContainer } from '@statamic/cms/ui';\n</script>\n<template>\n    <PublishContainer\n        :blueprint=\"blueprint\"\n        :meta=\"meta\"\n        :errors=\"errors\"\n        v-model=\"values\"\n    />\n</template>\n```\n\nView the [Publish component docs page](https://ui.statamic.dev/?path=/docs/components-publishcontainer--docs) for more details.\n\n\n### Listing\n\nIf you were building custom listings using the `data-list` components, they have been replaced by newer equivalents.\n\nBefore, you probably grabbed our listing mixin, added the necessary properties to make it work, and added a bunch of components to your template, each of them with a bunch of props.\n\n```vue\n<script>\nimport Listing from '../../../../vendor/statamic/cms/resources/js/components/Listing.vue';\n\nexport default {\n    mixins: [Listing],\n    data() {\n        return {\n            requestUrl: '/cp/my-listing-url',\n        }\n    }\n}\n</script>\n<template>\n    <data-list\n      :rows=\"items\"\n      :columns=\"columns\"\n      :sort=\"false\"\n      :sort-column=\"sortColumn\"\n      :sort-direction=\"sortDirection\"\n      v-slot=\"{ hasSelections }\"\n    >\n        <data-list-filter-presets ... />\n        <data-list-search ... />\n        <data-list-filters ... />\n        <data-list-bulk-actions ... />\n        <data-list-table>\n            <template slot=\"cell-title\" slot-scope=\"{ row }\">...</template>\n            <template slot=\"cell-another\" slot-scope=\"{ row }\">...</template>\n            <template slot=\"actions\" slot-scope=\"{ row }\">...</template>\n        </data-list-table>\n        <data-list-pagination ... />\n    </data-list>\n</template>\n```\n\nAfter, you can use the new `Listing` component, pass in a URL, some props, and not worry about any nested components. The layout will figure itself out. \n\n```vue\n<script>\nimport { Listing } from '@statamic/cms/ui';\n\nexport default {\n    data() {\n        return {\n            requestUrl: '/cp/my-listing-url',\n        }\n    }\n}\n</script>\n<template>\n    <Listing\n        :url=\"requestUrl\"\n        :columns=\"columns\"\n        :filters=\"filters\"\n        :action-url=\"actionUrl\"\n    >\n        <template #cell-title=\"{ row }\">...</template>\n        <template #cell-another=\"{ row }\">...</template>\n        <template #prepended-row-actions=\"{ row }\"></template>\n    </Listing>\n</template>\n```\n\nView the [Listing component docs page](https://ui.statamic.dev/?path=/docs/layout-listing--docs) for more details.\n\n\n\n### Dropdown List\n\nThe `dropdown-list` component has been replaced by the `Dropdown` UI component.\n\n```html\n<dropdown-list>\n    <template v-slot:trigger>\n        <button>Click me</button>\n    </template>\n    <dropdown-item redirect=\"/somewhere\">Text</dropdown-item>\n</dropdown-list>\n```\n\n```vue\n<script>\nimport { Dropdown, DropdownMenu, DropdownItem } from '@statamic/cms';\n</script>\n<template>\n    <Dropdown>\n        <template #trigger>\n            <button>Click me</button>\n        </template>\n        <DropdownMenu>\n            <DropdownItem href=\"/somewhere\" text=\"Text\" />\n        </DropdownMenu>\n    </Dropdown>\n</template>\n```\n\nYou can also forgo the imports and just use `ui-dropdown`, `ui-dropdown-menu`, and `ui-dropdown-item` respectively.\n\n### Inputs\n\nInput components such as `<text-input>`, `<textarea-input>`, `<select-input>`, and `<toggle-input>` have been removed in favor of [Input](https://ui.statamic.dev/?path=/docs/forms-input--docs), [Textarea](https://ui.statamic.dev/?path=/docs/forms-textarea--docs), [Combobox](https://ui.statamic.dev/?path=/docs/forms-combobox--docs), [Switch](https://ui.statamic.dev/?path=/docs/forms-switch--docs) UI components respectively. \n\n```vue\n<script>\nimport { Input, Textarea, Combobox, Switch } from '@statamic/cms/ui'; // [tl! ++]\n</script>\n<template>\n    <text-input v-model=\"textValue\" /> <!-- [tl! --] -->\n    <textarea-input v-model=\"textValue\" /> <!-- [tl! --] -->\n    <select-input v-model=\"textValue\" /> <!-- [tl! --] -->\n    <toggle-input v-model=\"textValue\" /> <!-- [tl! --] -->\n    <Input v-model=\"textValue\" /> <!-- [tl! ++] -->\n    <Textarea v-model=\"textareaValue\" /> <!-- [tl! ++] -->\n    <Combobox v-model=\"comboboxValue\" /> <!-- [tl! ++] -->\n    <Switch v-model=\"switchValue\" /> <!-- [tl! ++] -->\n</template>\n```\n\n## Modals and Stacks\n\nPreviously, you had to use `v-if` to control the \"open state\" of Modals and Stacks. You should now use `v-model` to control the open state instead:\n\n```vue\n<!-- Using v-model -->\n<Modal v-model:open=\"isModalOpen\">\n<Stack v-model:open=\"isStackOpen\">\n\n<!-- Using a prop & event listener -->\n<Modal :open=\"isModalOpen\" @update:open=\"modalOpenUpdated\">\n<Stack :open=\"isStackOpen\" @update:open=\"stackOpenUpdated\">\n```\n\nAlternatively, you may provide a `trigger` slot where the modal/stack will maintain the open state internally:\n\n```vue\n<Stack>\n  <template #trigger>\n    <Button>Click Me!</Button>\n  </template>\n</Stack>\n```\n\nModals and Stacks now accept `title` and `icon` props which will be used to render the modal/stack header:\n\n```vue\n<Stack :title=\"__('How neat is that?')\" icon=\"playground\">\n```\n\n### Confirmation Modals\n\nThe Confirmation Modal component has also been updated. You should replace usage of `<confirmation-modal>` with either:\n\n- `<ui-confirmation-modal>`\n- `import { ConfirmationModal } from '@statamic/cms/ui'` and `<ConfirmationModal>`\n\nYou'll also need to adjust the open state logic from the previous `v-if` to the new `v-model:open` or `:open`:\n\n```html\n<confirmation-modal v-if=\"isConfirming\" ...> <!-- [tl! remove] -->\n<ui-confirmation-modal v-model:open=\"isConfirming\" ...> <!-- [tl! add] -->\n```\n\n## HMR and Vue Devtools\n\nTo use Hot Module Reloading (HMR) or the [Vue Devtools](https://devtools.vuejs.org) browser extension, you will need to publish a special \"dev build\" of Statamic.\n\nYou can do this via the `vendor:publish` command:\n\n```\nphp artisan vendor:publish --tag=statamic-cp-dev\n```\n\nAlternatively, it can be symlinked:\n\n```\nln -s /path/to/vendor/statamic/cms/resources/dist-dev public/vendor/statamic/cp-dev\n```\n\nStatamic will use the dev build as long as `APP_DEBUG=true` in your `.env` and the `public/vendor/statamic/cp-dev` directory exists. You **shouldn't** commit these or use this on production.\n\n## Removals\n\nA number of items have been removed. If you feel they shouldn't have been removed, please contact us and we can evaluate bringing them back.\n\n- We had a `String.includes()` polyfill that has been removed since it's widely supported now.\n- Underscore.js mixins `objMap`, `objFilter`, and `objReject` have been removed.\n- `resource_url` and `file_icon` methods are no longer available in Vue templates but are still available as global functions.\n- The deprecated `$slugify` function has been removed in favor of the `$slug` API.\n- The `v-focus` directive has been removed.\n- The `store`/`storeName` are no longer included field action payloads. \n- The `vue-select` package has been removed. If you're using `<v-select>` you should change to the [Combobox UI component](https://ui.statamic.dev/?path=/docs/forms-combobox--docs).\n"
  },
  {
    "path": "content/collections/pages/vue-components.md",
    "content": "---\nid: 63e7f70f-3371-4cba-b3fb-9d5a1b519c8b\ntitle: 'Vue Components'\nblueprint: link\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/white-labeling.md",
    "content": "---\ntitle: 'White Labeling'\nintro: 'White Labeling allows you to customize the logo, visible name, and basic theme of the CMS throughout the control panel.'\ntemplate: page\nblueprint: page\npro: true\nid: 5bd9426f-23cf-4196-9848-471dff67f5ea\n---\n\n## Configuration\n\nWhite Label options are available in `config/statamic/cp.php` or through corresponding [environment variables](configuration#environment-variables).\n\n:::warning\nKeep in mind that according to the license terms you can only rebrand for personal, internal, or client usage. You cannot resell Statamic under another name.\n:::\n\n### CP theme\n\nYou can channel your inner branding overlord in `config/statamic/cp.php`—just find the “White Labeling” section and reign supreme!\n\n<figure>\n    <img src=\"/img/white-label-login.webp\" alt=\"Statamic White Label Theme\" class=\"u-hide-in-dark-mode\">\n    <img src=\"/img/white-label-login-dark.webp\" alt=\"Statamic White Label Theme\" class=\"u-hide-in-light-mode\">\n    <figcaption>Here's the theme with a custom primary color and logo</figcaption>\n</figure>\n\n### Custom CMS name\n\nSet a custom name for the CMS.\n\n``` php\n'custom_cms_name' => env('STATAMIC_CUSTOM_CMS_NAME', 'Statamic'),\n```\n\n### Custom logo\n\nSwap out the logo with a URL to one of your own.\n\n``` php\n'custom_logo_url' => env('STATAMIC_CUSTOM_LOGO_URL', null),\n```\n\nYou may set different logos for inside and outside Control Panel (nav bar and login screen, respectively) by passing an array.\n\n``` php\n'custom_logo_url' => [\n    'nav' => '/logo-white.png',\n    'outside' => '/logo-dark.png'\n],\n```\n\nYou can also specify a different URL to be used when in Dark Mode:\n\n``` php\n'custom_logo_url' => '/logo-light-mode.png',\n\n'custom_dark_logo_url' => '/logo-dark-mode.png',\n```\n\n### Custom logo text\n\nDisplay a custom name in plain text in the control panel; automatically changes when in Dark Mode.\n\nWhen defined, logo image URLs will take precedence over logo text.\n\n``` php\n'custom_logo_text' => env('STATAMIC_CUSTOM_LOGO_TEXT', null),\n```\n\n### Custom favicon\n\nSwap out the favicon with a URL to one of your own.\n\n``` php\n'custom_favicon_url' => env('STATAMIC_CUSTOM_FAVICON_URL', null),\n```\n\n### Custom CSS\n\nSet the path to a CSS file and easily add your own styles to the control panel.\n\n``` php\n'custom_css_url' => env('STATAMIC_CUSTOM_CSS_URL', null),\n```\n\n### Support URL\n\nSet the location of the support link in the \"Useful Links\" header dropdown. Use `false` to remove it entirely.\n\n```php\n'support_url' => env('STATAMIC_SUPPORT_URL', 'https://statamic.com/support'),\n```\n\n### Documentation\n\nWhether to show links to Statamic documentation throughout the Control Panel.\n\n```php\n'link_to_docs' => env('STATAMIC_LINK_TO_DOCS', true),\n```\n"
  },
  {
    "path": "content/collections/pages/widgets.md",
    "content": "---\nid: c2ab2945-4dce-4875-b5fe-48a006dd8193\ntitle: Widgets\nredirect:\n  url: '@child'\n  status: 301\n---\n"
  },
  {
    "path": "content/collections/pages/yaml.md",
    "content": "---\ntitle: YAML\nintro: YAML is a data storage format designed to be human readable and easily manipulated by hand. It's interchangeable with JSON and in most cases easier to write. Statamic uses YAML extensively to store data, content, and settings.\ntemplate: page\nid: 93cf3f23-24c4-4722-a6e2-5b369e952a3b\nblueprint: page\n---\n## What is YAML?\n\n`<tangent>`YAML stands for \"YAML Ain't Markup Language\". It's a rare example of the elusive [recursive acronym][recursive-acronym]. At one point it stood for \"Yet Another Markup Language\" but semantically-oriented people quickly shut it down, denoting the fact that nothing was being marked up, but rather data was being structured. So on that fateful day (probably a Wednesday), YAML became self-referential. `</tangent>`.\n\nYAML complies with the JSON spec, making it easy to interchange it with nearly any native data format. It consists of key and value pairs delimited by a colon then a space.\n\n```yaml\nvariable_name: value\n```\n\nYAML is usually stored in `.yaml` or `.yml` files, but can often (and in Statamic's case) be found inside and at the top of other text files. This is referred to as \"Front Matter\" and looks like this:\n\n```md\n---\ntitle: Hello there! this is Front Matter!\n---\nLetters are strung together in a specific order down here.\n```\n\n## Strings\n\nA string is a single sequence of characters. It might be a single word or a huge chunk of HTML, but it's always a single element. These following variable definitions, in different languages and formats, are equivalent:\n\n```yaml\nschool: Flatside High\n```\n\n```php\n$school = \"Flatside High\";\n```\n\n```js\nvar school = \"Flatside High\";\n```\n\n### String escaping\n\nAs YAML is a **structured** data format, you will occasionally need to quote your strings to prevent rogue apostrophes, commas, and other reserved constructs from confusing the parser, allowing you to structure your data exactly as desired.\n\n:::tip\nYAML uses **2 spaces** for indentation. Not 3, not 4, not 12, but 2.\n:::\n\nYou can also use quotes to force or typecast another datatype as a string, For example, if your key or value is `10` but you want it to return a String and not an Integer, write `'10'` or `\"10\"`.\n\n```yaml\n# Probably broken\ncartoon: Rocko's Modern Life\n\n# Perfection\ncartoon: \"Rocko's Modern Life\"\n```\n\n### Preserving newlines\n\nYou can preserve the line breaks in your string block by using the `|` pipe symbol followed by a line break and indented content. This is useful if you're writing Markdown or preserving HTML line breaks.\n\n```yaml\nlyrics: |\n  When I wake up in the morning\n  And the alarm gives out a warning\n  And I don't think I'll ever make it on time\n  By the time I grab my books\n  And I give myself a look\n  I'm at the corner just in time to see the bus fly by\n```\n\n### Collapsing newlines\n\nCompletely ignore line breaks with a `>` character and indent the rest of the content.\n\n```yaml\ntest: >\n  These\n  lines will\n  be collapsed\n  into a single\n  paragraph.\n\n  Blank lines are\n  treated as\n  paragraph breaks.\n```\n\n## Numbers\n\nNumbers are represented as numerals without any \"string quotes\". Those quotes were meant to demonstrate what string quotes are, not [mock them](https://media.giphy.com/media/Kc7qzYMnOTcDb0aEw5/giphy.gif).\n\n```yaml\n# an integer\nnumber: 12\n\n# a float\nnumber: 26.2\n\n# a hexadecimal\nnumber: 0xC\n\n# an exponential number\nnumber: 1.2e+34\n```\n\n## Nulls\n\nNulls in YAML are expressed with `null` or `~`.\n\n## Booleans\n\nBooleans in YAML are expressed with `true` and `false`.\n\n## Collections\n\nYAML collections can be a sequence (or list). Arrays are lists of values. They can be formatted like a plain-text bulleted list or comma delimited inside brackets, similar to JSON.\n\n```yaml\n# These are both valid YAML arrays\nto_buy:\n  - sunglasses\n  - sandals\n  - surfboard\n\nto_sell: [aloe vera, winter coat, mittens]\n```\n\nTo render the values from a YAML array:\n\n\n### Element map\n\n```yaml\nantisocial:\n  facebook: http://facebook.com/statamic\n  twitter: http://twitter.com/statamic\n```\n\n```php\n$antisocial = [\n    \"facebook\" => \"http://facebook.com/statamic\",\n    \"twitter\" => \"http://twitter.com/statamic\"\n];\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ antisocial:facebook }}\">Facebook</a>\n<a href=\"{{ antisocial:twitter }}\">Twitter</a>\n```\n::tab blade\n```blade\n<a href=\"{{ $antisocial['facebook'] }}\">Facebook</a>\n<a href=\"{{ $antisocial['twitter'] }}\">Twitter</a>\n```\n::\n\n### Nesting mappings and sequences\n\nYou can create mappings of sequences, and those sequences can have mappings, and those mappings can have their own sequences and mappings, and so on and on and on until you choose to skip the rest of the paragraph and go onto the next.\n\nThis is a very common pattern in Statamic. Bard, Grid, and Replicator fieldtypes all use nested mappings and sequences.\n\n```yaml\nstudents:\n  -\n    name: Zach Slater\n    school: Bayside High\n  -\n    name: Jack McDade\n    school: Flatside High\n```\n\nLook at how pretty the data is.\n\n```php\n$students = [\n    0 => [\n        \"name\" => \"Zach Slater\",\n        \"school\" => \"Bayside High\"\n    ],\n    1 => [\n        \"name\" => \"Jack McDade\",\n        \"school\" => \"Flatside High\"\n    ]\n];\n```\n\n### Mixing and matching\n\nYou can build multidimensional arrays full of associative arrays, and vice versa.\n\n```yaml\ntrips:\n  vacation:\n    - Miami\n    - Malibu\n  work:\n    - Orlando\n    - San Fransisco\n```\n\n## Comments\n\nYou can comment out any line of YAML by prefixing it with a `#` hash symbol.\n\n```yaml\n# title: I Quit My Day Job!\ntitle: Another Monday\n```\n\n## Explicit Typing\n\nYAML autodetects the datatype of the entity. Sometimes you'll want to cast the datatype explicitly, like when a single word string looks like a number or boolean may need disambiguation by surrounding it with quotes or use of an explicit datatype tag.\n\n```yaml\na: 42                      # integer\n2: \"42\"                    # string, disambiguated by quotes\nd: 42.0                    # float\nl: !!float 42              # float via explicit data type\nm: !!str 42                # string, disambiguated by explicit type\nn: !!str Yes               # string via explicit type\no: Yes                     # string\np: Yes we have No whiskey  # string, disambiguated by context.\n```\n\n## Related reading\n\nSo there you have it &mdash; YAML in its many shapes and forms. To learn how to render all this lovely data in a template, check out the [Antlers][antlers] section.\n\nYou can also refer to the [Symfony YAML component][symfony-yaml] and [YAML format][yaml-format] documentation for even more technical and in-depth knowledge gains.\n\n**Bonus points** for reading the full [YAML 1.2 specification document][yaml-spec]. We've done it and it's as dry as a mouthful of cinnamon.\n\n[recursive-acronym]: https://en.wikipedia.org/wiki/Recursive_acronym\n[antlers]: /antlers\n[symfony-yaml]: https://symfony.com/doc/current/components/yaml.html\n[yaml-format]: https://symfony.com/doc/current/components/yaml/yaml_format.html\n[yaml-spec]: https://yaml.org/spec/1.2/spec.html\n"
  },
  {
    "path": "content/collections/pages.yaml",
    "content": "title: Pages\nicon: collections\ntemplate: page\nlayout: layout\nrevisions: false\nroute: '{parent_uri}/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\ninject:\n  blueprint: page\n  section: docs\nstructure:\n  root: true\n"
  },
  {
    "path": "content/collections/resource_apis/asset-container-repository.md",
    "content": "---\nid: 2ecb1176-6cf1-48cf-937e-baad66a002fa\nblueprint: resource_apis\ntitle: 'Asset Container Repository'\nclass: \\Statamic\\Facades\\AssetContainer\nnav_title: 'Asset Containers'\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - 391907fa-1312-4143-80e7-546d20f20d84\n  - 7277432d-bb25-458a-a3a2-a72976b44ad5\n  - 5b748a3f-be0e-41c1-8877-73f6b7ee1d0a\n  - d0c65546-74f1-4a15-89d5-1562a95ee2c6\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1634259827\n---\nTo work with the AssetContainer Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\AssetContainer;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all AssetContainers |\n| `find($id)` | Get AssetContainer by `id` |\n| `findByHandle($handle)` | Get AssetContainer by `handle` |\n| `findOrFail($id)` | Get AssetContainer by `id`. Throws an `AssetContainerNotFoundException` when the asset container cannot be found. |\n| `queryAssets()` | Query Builder for the AssetContainer's [Assets](/repositories/asset-repository) |\n| `make()` | Makes a new AssetContainer instance |\n\n:::tip\nThe `id` is the same as `handle` while using the default Stache driver.\n:::\n\n## Querying\n\nWhile the `AssetContainer` Repository does not have a Query Builder, you can still query for Assets _inside_ AssetContainers with the `queryAssets` method. This approach can be useful for retrieving Assets with an existing AssetContainer object.\n\n```php\n$videos = AssetContainer::find('videos');\n\n$videos->queryAssets()\n    ->where('series', 'stranger-things')\n    ->get();\n```\n\nWhen an asset container can't be found, the `AssetContainer::find()` method will return `null`. If you'd prefer an exception be thrown, you may use the `findOrFail` method:\n\n```php\nAssetContainer::findOrFail('videos');\n```\n\n## Creating\n\nStart by making an instance of an asset container with the `make` method. You can pass the handle into it.\n\n```php\n$container = AssetContainer::make('assets');\n```\n\nYou may call additional methods on the container to customize it further.\n\n```php\n$container\n  ->title('Assets')\n  ->allowDownloading(true)\n  ->allowMoving(true)\n  ->allowRenaming(true)\n  ->allowUploads(true)\n  ->createFolders(true)\n  ->searchIndex('assets');\n```\n\nFinally, save it.\n\n```php\n$container->save();\n```\n"
  },
  {
    "path": "content/collections/resource_apis/asset-repository.md",
    "content": "---\nid: 391907fa-1312-4143-80e7-546d20f20d84\nblueprint: resource_apis\ntitle: 'Asset Repository'\nclass: \\Statamic\\Facades\\Asset\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1634414521\nnav_title: Assets\n---\nTo work with the Asset Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Asset;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all Assets |\n| `find($filename)` | Get Asset by `filename` |\n| `findByPath($path)` | Get Asset by `path` |\n| `findByUrl($url)` | Get Asset by `url` |\n| `findOrFail($filename)` | Get Asset by `filename`. Throws an `AssetNotFoundException` when the asset cannot be found. |\n| `query()` | Query Builder |\n| `whereContainer($container)` | Find Assets by [AssetContainer](/repositories/asset-container-repository) |\n| `whereFolder($folder)` | Find Assets in a filesystem folder |\n| `make()` | Makes a new `Asset` instance |\n\n:::tip\nYou **must** specify the Asset Container when querying Assets.\n:::\n\n## Querying\n\n#### Examples {.popout}\n\n#### Get all assets in a container\n\n```php\nAsset::query()\n    ->where('container', 'main')\n    ->get();\n```\n\n#### Get all assets in a folder\n\n```php\nAsset::query()\n->where('container', 'main')\n    ->where('folder', 'team_photos')\n    ->get();\n```\n\n#### Get all assets without an alt tag\n\n```php\nAsset::query()\n    ->where('container', 'main')\n    ->where('alt', null)\n    ->get();\n```\n\n#### Get all Assets with \"thumbnail\" in the path.\n\n```php\nAsset::query()\n    ->where('container', 'main')\n    ->where('path', 'like', '%thumbnail%')\n    ->get();\n```\n\n#### Get the newest 10 assets from a container\n\n```php\nAsset::query()\n    ->where('container', 'main')\n    ->orderBy('last_modified', 'desc')\n    ->get();\n```\n\n## Creating\n\nStart by making an instance of an asset with the `make` method.\nYou need at least a path and the container before you can save an asset.\n\n```php\n$asset = Asset::make()->container('assets')->path('images/hat.jpg');\n```\n\nOr, if you have an asset container instance, you can use the `makeAsset` method.\n\n```php\n$asset = $container->makeAsset('images/hat.jpg');\n```\n\nOnce you have an asset instance, you can add data to it.\n\n```php\n$asset->data(['foo' => 'bar']);\n```\n\nFinally, save it. It'll return a boolean for whether it succeeded.\n\n```php\n$asset->save(); // true or false\n```\n\n:::tip\nSaving an asset instance will only store the metadata. It doesn't actually create the asset itself.\n\nYou should manually place the asset at the corresponding location, or you can use the `upload` method which accepts an `UploadedFile` instance.\n\n```php\n$file = $request->file('file');\n$asset->upload($file);\n```\n:::\n"
  },
  {
    "path": "content/collections/resource_apis/collection-repository.md",
    "content": "---\nid: 9c6a0b01-449e-49dd-8fa6-11b975d2726d\nblueprint: resource_apis\ntitle: 'Collection Repository'\nclass: \\Statamic\\Facades\\Collection\nnav_title: Collections\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - 44c3da60-ef47-408e-afc4-a33026c86f5d\n  - 045a6e54-c792-483a-a109-f07251a79e47\n  - 7202c698-942a-4dc0-b006-b982784efb03\n---\nTo work with the with `Collection` Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Collection;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all Collections |\n| `find($id)` | Get Collection by `id` |\n| `findByHandle($handle)` | Get Collection by `handle` |\n| `findByMount($mount)` | Get Collection by mounted entry `id` |\n| `findOrFail($id)` | Get Collection by `id`. Throws a `CollectionNotFoundException` when the collection cannot be found. |\n| `handleExists($handle)` | Check to see if `Collection` exists |\n| `handles()` | Get all `Collection` handles |\n| `queryEntries()` | Query Builder for [Entries](/repositories/entry-repository) |\n| `whereStructured()` | Get all Structured `Collections` |\n| `make()` | Makes a new `Collection` instance |\n\n:::tip\nThe `id` is the same as `handle` while using the default Stache driver.\n:::\n\n## Querying\n\nWhile the `Collection` Repository does not have a Query Builder, you can still query for Entries _inside_ Collections with the `queryEntries` method. This approach can be useful for retrieving Entries with an existing Collection object.\n\n```php\n$blog = Collection::find('blog');\n\n$blog->queryEntries()->get();\n```\n\nWhen a collection can't be found, the `Collection::find()` method will return `null`. If you'd prefer an exception be thrown, you may use the `findOrFail` method:\n\n```php\nCollection::findOrFail('blog');\n```\n\n## Creating\n\nStart by making an instance of a collection with the `make` method. You can pass the handle into it.\n\n```php\n$collection = Collection::make('assets');\n```\n\nYou may call additional methods on the collection to customize it further.\n\n```php\n$collection\n    ->title('Blog')\n    ->routes('/blog/{slug}') // a string, or array of strings per site\n    ->mount('blog-page') // id of an entry\n    ->dated(true)\n    ->ampable(false)\n    ->sites(['one', 'two']) // array of handles\n    ->template('template')\n    ->layout('layout')\n    ->cascade($data) // an array\n    ->searchIndex('blog') // index name\n    ->revisionsEnabled(false)\n    ->defaultPublishState('published') // or 'draft'\n    ->structureContents($structure) // an array\n    ->sortField('field') // the field to sort by default\n    ->sortDirection('asc') // or 'desc'\n    ->taxonomies(['tags']) // array of handles\n    ->propagate(true); // whether newly created entries propagate to other sites\n```\n\nFinally, save it.\n\n```php\n$collection->save();\n```\n"
  },
  {
    "path": "content/collections/resource_apis/entry-repository.md",
    "content": "---\nid: 4238bce4-a94b-4d07-96fa-ea77c1d8e48d\nblueprint: resource_apis\ntitle: 'Entry Repository'\nnav_title: Entries\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - acee879a-c832-449d-a714-c57ea5862717\n  - 9c6a0b01-449e-49dd-8fa6-11b975d2726d\n  - 7202c698-942a-4dc0-b006-b982784efb03\n  - 853b6690-c1fc-46bc-b865-e61a33d14563\n---\nTo work with the Entry Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Entry;\n```\n\n## Methods\n\n| Methods                         | Description                                                                           |\n| ------------------------------- | ------------------------------------------------------------------------------------- |\n| `all()`                         | Get all Entries                                                                       |\n| `find($id)`                     | Get Entry by `id`                                                                     |\n| `findByUri($uri, $site)`        | Get Entry by `uri`, optionally in a site                                              |\n| `findOrFail($id)`               | Get Entry by `id`. Throws an `EntryNotFoundException` when the entry cannot be found. |\n| `findOrMake($id)`               | Get Entry by `id` or make a new entry.                                                |\n| `findOr($id, $callback)`        | Get Entry by `id` or execute callback.                                                |\n| `query()`                       | Query Builder                                                                         |\n| `whereCollection($handle)`      | Get all Entries in a `Collection`                                                     |\n| `whereInCollection([$handles])` | Get all Entries in an array of `Collections`                                          |\n| `whereInId([$ids])`             | Get all entries in array of IDs. Entries returned in the same order as `$ids`         |\n| `make()`                        | Makes a new entry instance                                                            |\n\n## Querying\n\n#### Examples {.popout}\n\n#### Get a single entry by its id\n\n```php\nEntry::query()->where('id', 123)->first();\n\n// Or with the shorthand method\nEntry::find(123);\n```\n\nWhen an entry can't be found, the `Entry::find()` method will return `null`. If you'd prefer an exception be thrown, you may use the `findOrFail` method:\n\n```php\nEntry::findOrFail(123);\n```\n\n#### Get an entry by its URI\n\n```php\nEntry::query()->where('uri', 'blog/my-first-post')->first();\n\n// Or with the shorthand method\nEntry::findByUri('/blog/my-first-post');\n```\n\n:::tip\n**What is the difference between `URI` and `URL`?** `URL` includes the site root (e.g. `/fr/` in a multisite), if there is one, while `URI` is site agnostic and will not. As you may have surmised, when you only have a single site — they are identical.\n:::\n\n#### Get entries by IDs\n\nIf you already know the IDs of the entries you want to find, you can call the `whereInId` method. The entries will be returned in the same order you provided them.\n\n```php\nEntry::whereInId([3, 1, 2]);\n```\n\n#### Get all entries in a collection\n\n```php\nEntry::query()\n  ->where('collection', 'blog')\n  ->get();\n```\n\n#### Get an entry from a collection by its slug\n\n```php\nEntry::query()\n    ->where('collection', 'blog')\n    ->where('slug', 'my-first-post')\n    ->first();\n```\n\n#### Get an entry by its slug in a multi-site install\n\n```php\nEntry::query()\n    ->where('collection', 'team')\n    ->where('slug', 'director')\n    ->where('site', 'albuquerque')\n    ->get();\n```\n\n#### Get all Pre-Y2K news\n\n```php\nEntry::query()\n    ->where('collection', 'news')\n    ->where('date', '<', '2000') // [tl! ~~]\n    ->get();\n```\n\n#### Get all of today's news\n\n```php\nuse Illuminate\\Support\\Carbon;\n\nEntry::query()\n    ->where('collection', 'news')\n    ->where('date', Carbon::parse('today'))\n    ->get();\n```\n\n#### Get the last 12 months of news\n\n```php\nuse Illuminate\\Support\\Carbon;\n\nEntry::query()\n    ->where('collection', 'news')\n    ->where('date', '>=', Carbon::parse('now')->subYears(1))\n    ->get();\n```\n\n#### Find all entries authored by Jack\n\n```php\n$author = User::findByEmail('jack@statamic.com');\n\nEntry::query()\n    ->where('collection', 'news')\n    ->where('author', $author->id())\n    ->get();\n```\n\n#### Get all entries using a specific Blueprint\n\n```php\nEntry::query()\n    ->where('blueprint', 'editorial')\n    ->get();\n```\n\n#### Get all published entries\n\n```php\nEntry::query()\n  ->whereStatus('published')\n  ->get();\n```\n\n:::tip\n**What is the difference between querying against `published` and `status`?** Read more on [date behavior and published status](/collections#date-behavior-and-published-status)!\n:::\n\n#### Get all entries with taxonomy terms\n\nWhen you want to query all entries with a specific term, you should use the `whereTaxonomy` method:\n\n```php\nEntry::query()\n  ->where('collection', 'news')\n  ->whereTaxonomy('categories::laravel')\n  ->get();\n```\n\nIn the above example, `categories` is the taxonomy's handle and `laravel` is the term's slug.\n\nWhen you want to query all entries with **multiple** terms, you should instead use the `whereTaxonomyIn` method:\n\n```php\nEntry::query()\n  ->where('collection', 'news')\n  ->whereTaxonomyIn(['categories::laravel', 'categories::statamic'])\n  ->get();\n```\n\n:::warning\n**This only works when the taxonomy [is linked in your collection's config](/collections#taxonomies).** If it's not linked in the collection config, you should use the `whereJsonContains` method instead:\n\n```php\nEntry::query()\n  ->where('collection', 'news')\n  ->whereJsonContains('categories_field', ['laravel', 'statamic'])\n  ->get();\n```\n:::\n\n## Creating\n\nStart by making an instance of an entry with the `make` method.\nYou need at least a slug and the collection before you can save an entry.\n\n```php\n$entry = Entry::make()->collection('blog')->slug('my-entry');\n```\n\nYou may call additional methods on the entry to customize it further.\n\n```php\n$entry\n  ->date($carbon) // or string of Y-m-d or Y-m-d-Hi\n  ->published(true) // or false for a draft\n  ->locale('default') // the site handle. defaults to the default site.\n  ->blueprint('article') // set entry blueprint\n  ->data(['foo' => 'bar']) // an array of data (front-matter)\n  ->origin($origin); // another entry instance\n```\n\nFinally, save it. It'll return a boolean for whether it succeeded.\n\n```php\n$entry->save(); // true or false\n```\n\n### Setting an entry's parent\n\nWhen you're creating entries in [structured collections](/collections#ordering), you may find yourself needing to programatically set an entry's parent.\n\nParent & children pages are stored in structures, which live in \"trees\", rather than in the entry data. This means that in order to set an entry's parent, we will need to append it to the structure.\n\n```php\n// Create your entry as normal...\n$entry = Entry::make()\n    ->collection('pages')\n    ->slug('about')\n    ->data([\n        // ...\n    ]);\n\n\n// Before saving the entry, define an \"afterSave\" callback to append the\n// new entry to the tree, under the parent.\n$entry->afterSave(function ($entry) {\n    $parent = Entry::find('the_parent_entry');\n\n    $entry->collection()\n        ->structure()\n        ->in($entry->locale())\n        ->appendTo($parent->id(), $entry->id())\n        ->save();\n});\n\n// And finally, save the entry.\n$entry->save();\n```\n\n:::tip\nMake sure the parent entry exists in the tree by appending it in an \"afterSave\" callback with `->append($entry)` just like in the example above.\n:::\n\n### Localization\n\n#### Localizing an entry\n\nAfter an entry has been created, you can call the `makeLocalization` method to localize the entry into another site:\n\n```php\n$entry->makeLocalization('fr');\n```\n\n#### Getting an entry's localizations\n\nYou can use various methods to get an entry's localizations:\n\n```php\n// Check if the entry has been localized in a specific site...\n$entry->existsIn('fr');\n\n// Get the localized entry in a specific site...\n$entry->in('fr');\n\n// Get all \"descendants\" of the entry (those entries localized from the current entry)...\n$entry->descendants();\n\n// Get all \"ancestors\" of the entry (those entries the current entry is localized from)...\n$entry->ancestors();\n```\n"
  },
  {
    "path": "content/collections/resource_apis/form-repository.md",
    "content": "---\nid: bbea4454-efa2-4372-842b-b295376230f7\nblueprint: resource_apis\ntitle: 'Form Repository'\nnav_title: Forms\nrelated_entries:\n  - fdb45b84-3568-437d-84f7-e3c93b6da3e6\n  - e4f4f91e-a442-4e15-9e16-3b9880a25522\n  - d630ea15-d94f-4404-84d2-0926a898e672\n  - 02261135-24fa-4d2f-9bc5-a7d2f5e6a975\n---\nTo work with the Form Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Form;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all Forms |\n| `find($handle)` | Get Form by `handle` |\n| `findOrFail($handle)` | Get Form by `handle`. Throws a `FormNotFoundException` when the form cannot be found. |\n| `make()` | Makes a new `Form` instance |\n\n:::tip\nThe `handle` is the name of the form's YAML file.\n:::\n\n## Querying\n\n#### Examples {.popout}\n\n#### Get a single form by its handle\n\n```php\nForm::find('postbox');\n```\n\nWhen a form can't be found, the `Form::find()` method will return `null`. If you'd prefer an exception be thrown, you may use the `findOrFail` method:\n\n```php\nForm::findOrFail('postbox');\n```\n\n#### Get all forms from your site\n\n```php\nForm::all()\n```\n\n#### Get submissions to a form by its handle\n\n```php\nForm::find('postbox')->submissions();\n```\n\nThe `->submissions()` method will return a `Collection` of form submissions. You can use the [Form Submissions repository](/repositories/form-submission-repository) if you need to query form submissions further.\n\n#### Get the blueprint of a form\n\n```php\nForm::find('postbox')->blueprint();\n```\n\n\n## Creating\n\nStart by making an instance of a form with the `make` method.\nYou need at least a handle before you can save a form.\n\n```php\n$form = Form::make()->handle('feedback');\n```\n\nYou may call additional methods on the entry to customize it further.\n\n```php\n$form\n  ->handle('postbox')\n  ->honeypot('winnie-the-pooh')\n  ->title('The Hundred Acre Wood');\n```\n\nFinally, save it. It'll return a boolean for whether it succeeded.\n\n```php\n$form->save(); // true or false\n```\n"
  },
  {
    "path": "content/collections/resource_apis/form-submission-repository.md",
    "content": "---\nid: 02261135-24fa-4d2f-9bc5-a7d2f5e6a975\nblueprint: resource_apis\ntitle: 'Form Submission Repository'\nnav_title: Form Submissions\nrelated_entries:\n  - fdb45b84-3568-437d-84f7-e3c93b6da3e6\n  - e4f4f91e-a442-4e15-9e16-3b9880a25522\n  - bbea4454-efa2-4372-842b-b295376230f7\n---\nTo work with the Form Submissions Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\FormSubmission;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all form submissions. |\n| `whereForm($handle)` | Get submissions by form handle. |\n| `whereInForm($handles)` | Get submissions, across multiple forms. Accepts an array of form handles. |\n| `find($id)` | Get a form submission, by its submission ID. |\n| `make()` | Makes a new `Submission` instance |\n| `query()` | Query Builder |\n\n## Querying\n\n#### Examples {.popout}\n\n#### Get form submissions by form\n\n```php\nFormSubmission::whereForm('postbox');\n```\n\n#### Get form submissions, between multiple forms\n\n```php\nFormSubmission::whereInForm(['postbox', 'newsletter']);\n```\n\n#### Get a single submission to a form by its id\n\n```php\nFormSubmission::find($id);\n```\n\n#### Get form submissions, filtered by field\n\n```php\nFormSubmission::query()\n    ->where('form', 'postbox')\n    ->where('email', 'hoff@statamic.com')\n    ->get();\n```\n\n\n## Creating\n\nStart by making an instance of a form submission with the `make` method.\nYou need to pass in [a `Form` instance](/repositories/form-repository) before you can save a form submission.\n\n```php\n$form = \\Statamic\\Facades\\Form::find('postbox');\n\n$submission = FormSubmission::make()->form($form);\n```\n\nTo set submission data, you may call the `->data()` method and pass an array:\n\n```php\n$submission->data([\n    'name' => 'David Hasselhoff',\n    'email' => 'hoff@statamic.com',\n]);\n```\n\nFinally, save it. It'll return a boolean for whether it succeeded.\n\n```php\n$submission->save(); // true or false\n```\n"
  },
  {
    "path": "content/collections/resource_apis/global-repository.md",
    "content": "---\nid: 8159394f-8735-4659-844b-75a58d187007\nblueprint: resource_apis\ntitle: 'Global Repository'\nnav_title: Globals\nrelated_entries:\n  - 1e91dd54-c452-4e3b-8972-dba83c048d3d\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n---\nTo work with the GlobalSet Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\GlobalSet;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all GlobalSets |\n| `find($id)` | Get GlobalSets by `id` |\n| `findByHandle($handle)` | Get GlobalSets by `handle` |\n| `findOrFail($id)` | Get GlobalSets by `id`. Throws a `GlobalSetNotFoundException` when the global set cannot be found. |\n| `make()` | Makes a new `GlobalSet` instance |\n\n## Querying\n\nGlobals are a bit unique in that there is a single \"container\" – the Global Set – which contains common things like its title and handle. The actual variables are stored separately by site (even when not using the multi-site feature).\n\n``` php\n$set = GlobalSet::findByHandle('theme'); // returns a `GlobalSet`\n$variables = $set->in($site); // returns a `Variables`\n$variables->get('favicon'); // returns the value\n```\n\nOf course, you can chain this:\n\n```php\nGlobalSet::findByHandle('theme')->in($site)->get('favicon')\n```\n\n:::tip\n**You must specify your site** when working with GlobalSets, even if you only have **one**. You can use any of these methods:\n\n```php\n$set->in('siteHandle'); // A specific site handle from sites.php\n$set->inDefaultSite(); // The first site in sites.php\n$set->inCurrentSite(); // The site the user is currently visiting.\n```\n:::\n\n\n## Creating\n\nStart by making a global set instance with the `make` method. You can pass in the handle.\n\n```php\n$globals = GlobalSet::make('settings')->title('Settings');\n```\n\nThe variables are stored per-site in a `Variables` object. You can make one using the `makeLocalization` method, passing in the site handle. Then attach it to the set.\n\n```php\n$variables = $globals->makeLocalization('default');\n$variables->data($data); // array of values\n$globals->addLocalization($variables);\n```\n\nFinally, save it.\n\n```php\n$globals->save();\n```\n"
  },
  {
    "path": "content/collections/resource_apis/site-repository.md",
    "content": "---\nid: 668f4934-9dd9-4e5b-b24e-8fa6173336c2\nblueprint: resource_apis\ntitle: 'Site Repository'\nclass: \\Statamic\\Facades\\Site\nnav_title: 'Sites'\nrelated_entries:\n  - fb20f2e0-3881-43e6-8507-3308a18c54b0\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1634259827\n---\nTo work with the with Site Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Site;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all Sites |\n| `get($handle)` | Get Site by handle |\n| `findByUrl($url)` | Get site by a given URL |\n| `current()` | Returns the current site, determined by the current page URL. |\n| `setCurrent($handle)` | Sets the current site. |\n| `selected()` | Returns the site currently selected in the Control Panel |\n| `setSelected($handle)` | Sets the selected site. |\n| `default()` | Returns the \"default site\", which will be the first site in the `sites` config |\n| `authorized()` | Returns a collection of Site objects, where the user is authorized to view the site (see [Multisite Permissions](/multi-site#permissions)). |\n| `multiEnabled` | Returns a boolean indicating if multi-site is enabled. |\n| `hasMultiple()` | Returns a boolean indicating if multiple sites are configured. |\n| `setSites($sites)` | Accepts an array. Allows you to override the configured sites. |\n\n## Resolving the current site\n\nWhen you're using the `Site::current()` method, sometimes the current page URL will lead to the wrong site being returned (like when using Livewire). In this case, you can use the resolveCurrentUrlUsing to check against a different URL:\n\n```php\n// app/Providers/AppServiceProvider.php\n\nuse Statamic\\Facades\\Site;\n\nSite::resolveCurrentUrlUsing(fn () => Livewire::originalUrl());\n```\n"
  },
  {
    "path": "content/collections/resource_apis/taxonomy-repository.md",
    "content": "---\nid: 42d2d87c-5af6-4856-9ee0-9548439df772\nblueprint: resource_apis\ntitle: 'Taxonomy Repository'\nclass: \\Statamic\\Facades\\Taxonomies\nnav_title: Taxonomies\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - ba832b71-a567-491c-b1a3-3b3fae214703\n  - 6a18eac8-6139-419c-9d64-a2c960ccc3cd\n---\nTo work with the with `Taxonomy` Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Taxonomy;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all Taxonomies |\n| `find($id)` | Get Taxonomy by `id` |\n| `findByHandle($handle)` | Get Taxonomy by `handle` |\n| `findByUri($mount)` | Get Taxonomy by `uri` |\n| `findOrFail($id)` | Get Taxonomy by `id`. Throws a `TaxonomyNotFoundException` when the taxonomy cannot be found. |\n| `handleExists($handle)` | Check to see if `Taxonomy` exists |\n| `handles()` | Get all `Taxonomy` handles |\n| `queryTerms()` | Query Builder for [Terms](/content-queries/term-repository) |\n| `make()` | Makes a new `Taxonomy` instance |\n\n:::tip\nThe `id` is the same as `handle` while using the default Stache driver.\n:::\n\n## Querying\n\nWhile the `Taxonomy` Repository does not have a Query Builder, you can still query for Terms _inside_ Taxonomies with the `queryTerms` method. This approach can be useful for retrieving Terms with an existing Taxonomy object.\n\n```php\n$tags = Taxonomy::find('tags');\n\n$tags->queryTerms()->get();\n```\n\nWhen a taxonomy can't be found, the `Taxonomy::find()` method will return `null`. If you'd prefer an exception be thrown, you may use the `findOrFail` method:\n\n```php\nTaxonomy::findOrFail('tags');\n```\n\n## Creating\n\nStart by making an instance of a taxonomy with the `make` method. You can pass the handle into it.\n\n```php\n$taxonomy = Taxonomy::make('tags');\n```\n\nYou may call additional methods on the taxonomy to customize it further.\n\n```php\n$taxonomy\n    ->title('Tags')\n    ->cascade($data) // an array\n    ->revisionsEnabled(false)\n    ->searchIndex('tags')\n    ->defaultPublishState('published') // or 'draft'\n    ->sites(['one', 'two']) // array of handles\n```\n\nFinally, save it.\n\n```php\n$taxonomy->save();\n```\n"
  },
  {
    "path": "content/collections/resource_apis/term-repository.md",
    "content": "---\nid: 322d7330-0967-428c-9d15-5b289e920466\nblueprint: resource_apis\ntitle: 'Taxonomy Term Repository'\nnav_title: Terms\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - ba832b71-a567-491c-b1a3-3b3fae214703\n  - 6a18eac8-6139-419c-9d64-a2c960ccc3cd\n  - 42d2d87c-5af6-4856-9ee0-9548439df772\n---\n\nTo work with the Repository Term queries, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Term;\n```\n\n## Methods\n\n| Methods                  | Description                                                                       |\n| ------------------------ | --------------------------------------------------------------------------------- |\n| `all()`                  | Get all Terms                                                                     |\n| `find($id)`              | Get Term by `id`                                                                  |\n| `findByUri($uri)`        | Get Term by `uri`                                                                 |\n| `findOrFail($id)`        | Get Term by `id`. Throws a `TermNotFoundException` when the term cannot be found. |\n| `findOrMake($id)`        | Get Term by `id` or make a new term.                                              |\n| `findOr($id, $callback)` | Get Term by `id` or execute callback.                                             |\n| `query()`                | Query Builder                                                                     |\n| `make()`                 | Makes a new `Term` instance                                                       |\n\n## Querying\n\n#### Examples {.popout}\n\n#### Get a single term by its id\n\nWhen getting a single term by its ID, the value of the `$id` parameter should be `taxonomy_handle::term_id`.\n\n```php\nTerm::query()->where('id', 'tags::123')->first();\n\n// Or with the shorthand method\nTerm::find('tags::123');\n```\n\nWhen a term can't be found, the `Term::find()` method will return `null`. If you'd prefer an exception be thrown, you may use the `findOrFail` method:\n\n```php\nTerm::findOrFail('tags::123');\n```\n\n#### Get all tags\n\n```php\nTerm::query()\n    ->where('taxonomy', 'tags')\n    ->get();\n```\n\n#### Get all tags in a collection\n\n```php\nTerm::query()\n    ->where('collection', 'blog')\n    ->where('taxonomy', 'tags')\n    ->get();\n```\n\n#### Get all tags in multiple collections\n\n```php\nTerm::query()\n    ->where('taxonomy', 'tags')\n    ->whereIn('collection', ['blog', 'news'])\n    ->get();\n```\n\n#### Only include tags attached to entries\n\n```php\nTerm::query()\n    ->where('taxonomy', 'tags')\n    ->where('entries_count', '>=', 1);\n    ->get();\n```\n\n\n## Creating\n\nStart by making an instance of a term with the `make` method.\nYou need at least a slug and the taxonomy.\n\n```php\n$term = Term::make()->taxonomy('tags')->slug('my-term');\n```\n\nData for a term is stored on a per site basis, even if you only are using a single site.\n\nThe method expects a site handle and an array of key-value pairs.\n```php\n// Example for a single site\n$term->dataForLocale('default', [\n    'mandalorian_code' => 'This Is The Value',\n    //...\n]);\n```\n\nWhen using [multi-site](/multi-site), you can pass different data to each site.\n```php\n$term->dataForLocale('default', $data);\n$term->dataForLocale('fr', $frenchData);\n```\n\nYou may call additional methods on the term to customize it further.\n\n```php\n$term->blueprint('tag');\n```\n\nFinally, save it. It'll return a boolean for whether it succeeded.\n\n```php\n$term->save(); // true or false\n```\n"
  },
  {
    "path": "content/collections/resource_apis/user-group-repository.md",
    "content": "---\nid: 980c2496-c80a-44f2-8f28-39cfdeccc2c8\nblueprint: resource_apis\ntitle: 'User Group Repository'\nnav_title: 'User Groups'\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - 980c2496-c80a-44f2-8f28-39cfdeccc2c8\n---\nTo work with the with `UserGroup` Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\UserGroup;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all User Groups |\n| `find($id)` | Get User Group by `id` |\n| `queryUsers()` | Query Builder for Users in a Group |\n| `make()` | Makes a new `UserGroup` instance |\n\n## Querying\n\n#### Examples {.popout}\n\n#### Get a User Group\n\n``` php\nUserGroup::find('admin');\n```\n\n#### Get all users in a group\n\n``` php\nUserGroup::find('editors')\n    ->queryUsers()\n    ->get();\n```\n\n#### Find admins with an avatar\n``` php\nUserGroup::find('admin')\n    ->queryUsers()\n    ->where('avatar', true)\n    ->get();\n```\n\n## Creating\n\nStart by making an instance of a user group with the `make` method. You can pass the handle into it.\n\n```php\n$group = UserGroup::make('admin');\n```\n\nYou may call additional methods on the group to customize it further.\n\n```php\n$group\n    ->title('Administrators')\n    ->roles($roles); // array of role handles\n```\n\nFinally, save it.\n\n```php\n$group->save();\n```\n"
  },
  {
    "path": "content/collections/resource_apis/user-repository.md",
    "content": "---\nid: 1f89a175-5544-4151-9228-620f2c4f0925\nblueprint: resource_apis\ntitle: 'User Repository'\nnav_title: Users\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - 0f8102b9-c948-4264-8cb8-cbfbd0415a04\n  - 980c2496-c80a-44f2-8f28-39cfdeccc2c8\n  - c5f70315-b897-4037-a599-ef298539b988\n---\nTo work with the User Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\User;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all Users |\n| `current()` | Get current User |\n| `find($id)` | Get User by `id` |\n| `findByEmail($email)` | Get User by `email` |\n| `findByOAuthID($provider, $id)` | Get User by an ID from an OAuth provider  |\n| `findOrFail($id)` | Get User by `id`. Throws a `UserNotFoundException` when the user cannot be found. |\n| `query()` | Query Builder |\n| `make()` | Makes a new `User` instance |\n\n## Querying\n\n#### Get a user by ID\n\n```php\nUser::query()\n    ->where('id', 'abc123')\n    ->first();\n\n// Or with the shorthand method\nUser::find('abc123');\n```\n\nWhen a user can't be found, the `User::find()` method will return `null`. If you'd prefer an exception be thrown, you may use the `findOrFail` method:\n\n```php\nUser::findOrFail('abc123');\n```\n\n#### Get a user by email\n\n```php\nUser::query()\n    ->where('email', 'hulk@hogan.com')\n    ->first();\n\n// Or with the shorthand method\nUser::findByEmail('hulk@hogan.com');\n```\n\n#### Get a user by OAuth ID\n\n```php\nUser::findByOAuthId('github', '123');\n```\n\n#### Get all super users\n\n```php\nUser::query()->where('super', true)->get();\n```\n\n#### Get the currently logged in user\n\n```php\nUser::current();\n```\n\n## Creating\n\nStart by making an instance of a user with the `make` method.\nYou need at least an email before you can save a user.\n\n```php\n$user = User::make()->email('john@smith.com');\n```\n\nYou may call additional methods on the user to customize it further.\n\n```php\n$user\n  ->password('plaintext') // it will be hashed for you\n  ->data(['foo' => 'bar']) // an array of data (front-matter)\n  ->preferences($prefs) // array of preferences\n  ->roles($roles) // array of roles\n  ->groups($groups); // array of groups\n```\n\nFinally, save it.\n\n```php\n$user->save();\n```\n\n### Roles & Groups\n\nIn the example above, it demonstrates passing roles & groups as arrays to the `->roles()` and `->groups()` methods.\n\nHowever, Statamic also provides a few convenience methods for assigning/removing/checking individual roles & groups:\n\n```php\n$user->roles(); // Returns a collection of the user's roles\n$user->roles(['role_1', 'role_2']); // Sets the user's roles (overrides any existing roles)\n$user->assignRole('role_1'); // Assigns a role to the user\n$user->removeRole('role_1'); // Removes a role from the user\n$user->hasRole('role_2'); // Checks if the user has the provided role.\n\n$user->groups(); // Returns a collection of the user's groups\n$user->groups(['group_1', 'group_2']); // Sets the user's groups (overrides any existing groups)\n$user->addToGroup('group_1'); // Adds the user to a group\n$user->removeFromGroup('group_1'); // Removes the user from a group\n$user->isInGroup('group_2'); // Checks if the user is part of a group\n```\n"
  },
  {
    "path": "content/collections/resource_apis/user-role-repository.md",
    "content": "---\nid: c5f70315-b897-4037-a599-ef298539b988\nblueprint: resource_apis\ntitle: 'User Role Repository'\nnav_title: 'User Roles'\nrelated_entries:\n  - e7833062-e05c-42c9-ad35-dc5077f1f0b8\n  - 0f8102b9-c948-4264-8cb8-cbfbd0415a04\n  - 6b691e04-8f28-4eb2-8288-b61433883fe4\n---\nTo work with the with `Role` Repository, use the following Facade:\n\n```php\nuse Statamic\\Facades\\Role;\n```\n\n## Methods\n\n| Methods | Description |\n| ------- | ----------- |\n| `all()` | Get all User Roles |\n| `find($id)` | Get User Roles by `id` |\n| `make()` | Makes a new `Role` instance |\n\n:::tip\nThe `id` is the same as `handle` while using the default Stache driver.\n:::\n\n\n## Creating\n\nStart by making an instance of a user role with the `make` method. You can pass the handle into it.\n\n```php\n$role = Role::make('editors');\n```\n\nYou may call additional methods on the role to customize it further.\n\n```php\n$role\n    ->title('Editors')\n    ->permissions($permissions); // array of permissions\n```\n\nFinally, save it.\n\n```php\n$role->save();\n```\n"
  },
  {
    "path": "content/collections/resource_apis.yaml",
    "content": "title: 'Resource APIs'\nicon: programming-module-box-cube\ntemplate: page\nlayout: layout\nmount: 50dfaf8c-8ca4-4b58-ac84-e4c50099e42e\nrevisions: false\nroute: '/{mount}/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\n"
  },
  {
    "path": "content/collections/tags/404.md",
    "content": "---\ntitle: 404 (Not Found)\ndescription: Triggers a 404 response\nintro: |\n  Anytime this tag is rendered — whether in a template, partial, or content, Statamic will trigger a 404 status code and render your 404 template.\nid: 89f254ef-0312-429c-80bf-0a30a19edd0c\n---\n## Overview\n\nThe most common use cases for the 404 tag is for checking if a user is logged in or has performed an action before displaying a particular page.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if ! logged_in }}\n  {{ 404 }}\n{{ /if }}\n\n{{ if ! success }}\n  {{ 404 }}\n{{ /if }}\n```\n\n::tab blade\n```blade\n@if (! $logged_in }}\n  <statamic:not_found />\n@endif\n\n@if (! $success)\n  <statamic:not_found />\n@endif\n```\n\n:::tip\nIf you want to trigger Statamic's 404 logic manually, you can throw an instance of the `NotFound` exception:\n\n```php\n<?php\n\nuse Statamic\\Exceptions\\NotFoundHttpException;\n\n// Trigger Statamic's 404 logic.\nthrow new NotFoundHttpException();\n```\n:::\n::\n\nIf you'd rather perform a _redirect_ instead of throwing a 404, you can use the [redirect tag](/tags/redirect).\n\n:::tip\nThere's no need to wrap the rest of the template in an else condition.\n:::\n\n"
  },
  {
    "path": "content/collections/tags/asset.md",
    "content": "---\ntitle: Asset\ndescription: Retrieves an Asset by its URL\nintro: The Asset (singular tense) tag retrieves single assets by their URL.\nparameters:\n  -\n    name: url\n    type: string\n    description: >\n      The path to the file, relative to the web root.\nid: de348605-5489-4282-9257-bd9ffd92438e\n---\n## Overview\n\nThe Asset tag's primary purpose is to retrieve [Assets](/assets) by their URL.  Pass the URL into the `url` parameter and voila. To say it did any more than that would be a lie.\n\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ asset url=\"/img/brand/logo.png\" }}\n  <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /asset }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<statamic:asset\n  url=\"/assets/logo.png\"\n>\n\t<img src=\"{{ $url }}\" alt=\"{{ $alt }}\" />\n</statamic:asset>\n\n{{-- Using Fluent Tags --}}\n@if ($asset = Statamic::tag('asset')->src('/assets/logo.png')->fetch())\n\t<img src=\"{{ $asset->url }}\" alt=\"{{ $asset->alt }}\" />\n@endif\n```\n::\n"
  },
  {
    "path": "content/collections/tags/assets.md",
    "content": "---\nid: 5b748a3f-be0e-41c1-8877-73f6b7ee1d0a\ntitle: Assets\nintro: >\n    Used to retrieve [Assets](/assets) directly from a container where you can then loop, filter, and sort them in expected but exciting ways.\ndescription: Fetches Assets from a container\nparameters:\n  -\n    name: id|container|handle\n    type: string\n    description: |\n      Every [asset container](/assets/#containers) has a unique handle. Pass it in and win! Default: `assets`.\n  -\n    name: folder\n    type: string\n    description: |\n      Filter the resulting assets by specific folder. Default: none.\n  -\n    name: recursive\n    type: boolean\n    description: |\n      If you enable recursion, the tag will return all the assets in all the subdirectories that match your parameters. Default: `false`.\n  -\n    name: not_in\n    type: string\n    description: >\n      Filter by excluding from a subdirectory or subdirectories. You may use regex, and will be matched against the file path without a leading slash. For example: `not_in=\"img/(brand|logos)\"`\n  -\n    name: limit\n    type: integer\n    description: Limit the total results to a specific number.\n  -\n    name: offset\n    type: integer\n    description: Skip a specific number of results. Useful for if you want to pull the first one out as a hero image or something similar.\n  -\n    name: sort\n    type: string\n    description: >\n      Sort entries by any available asset variable, or `random`. Pipe-separate multiple fields for sub-sorting and specify sort direction of each field using a colon. Example: `sort=\"size\"` or `sort=\"size:asc|title:desc\"` to sort by size _then_ by title.\n  -\n    name: type\n    type: string\n    description: >\n      Filter by asset type. One of `audio`, `image`, `svg`, or `video`. Default: none.\n  -\n    name: query_scope\n    type: string\n    description: >\n      Apply a custom [query scope](/extending/query-scopes-and-filters). Reference it by its handle (usually the class name in snake case).\n---\n## Overview\n\nIf you ever find yourself needing to loop over all the assets in a container or folder instead of selecting them manually with the [assets fieldtype](/fieldtypes/assets), this tag was designed to make you smile.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ assets container=\"photoshoots\" }}\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /assets }}\n```\n\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<statamic:assets\n  container=\"photoshoots\"\n>\n\t<img src=\"{{ $url }}\" alt=\"{{ $alt }}\" />\n</statamic:assets>\n\n{{-- Using Fluent Tags --}}\n@php\n\t$assets = Statamic::tag('assets')->container('photoshoots')->fetch();\n@endphp\n\n@foreach ($assets as $asset)\n\t<img src=\"{{ $asset->url }}\" alt=\"{{ $asset->alt }}\" />\n@endforeach\n```\n::\n\nThis tag returns an array of [Asset](/assets) objects. You'll have access to all the data and meta data on each file.\n\n## Filtering\n\n### Conditions\n\nYou can filter assets using the same [conditions syntax][conditions] available on the Collection tag. This works against both built-in asset properties (`filename`, `extension`, `size`, etc.) and any custom fields defined on the container's blueprint.\n\n::tabs\n::tab antlers\n```antlers\n{{ assets container=\"photos\" extension:is=\"jpg\" alt:contains=\"sunset\" }}\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /assets }}\n```\n\n::tab blade\n```blade\n<statamic:assets\n    container=\"photos\"\n    extension:is=\"jpg\"\n    alt:contains=\"sunset\"\n>\n    <img src=\"{{ $url }}\" alt=\"{{ $alt }}\" />\n</statamic:assets>\n```\n::\n\nCommon conditions include `:is`, `:isnt`, `:contains`, `:starts_with`, and `:ends_with`. See the full list on the [conditions page][conditions].\n\n### Filter by Type\n\nLimit results to a single media type using the `type` parameter. Valid values are `audio`, `image`, `svg`, and `video`.\n\n```antlers\n{{ assets container=\"uploads\" type=\"image\" }}\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /assets }}\n```\n\n### Custom Query Scopes\n\nFor more complex filtering, create a [query scope](/extending/query-scopes-and-filters) and reference it with `query_scope`:\n\n```antlers\n{{ assets container=\"photos\" query_scope=\"recent_hero_shots\" }}\n    <img src=\"{{ url }}\" alt=\"{{ alt }}\" />\n{{ /assets }}\n```\n\n[conditions]: /conditions\n"
  },
  {
    "path": "content/collections/tags/cache.md",
    "content": "---\ntitle: Cache\ndescription: Caches a view chunk for performance gains\nintro: >\n  If you find that a particular chunk of your view logic is the cause of a performance hit — perhaps you're fetching and filtering huge amount of content, or pulling data from an API, caching that portion of your template can remove alleviate any slowdown.\nparameters:\n  -\n    name: for\n    type: string\n    description: 'The length of time to cache this section. Use plain English to specify the length, eg. `2 hours`, `5 minutes`, etc.'\n  -\n    name: key\n    type: string\n    description: 'The cache key to be used, if you''d like to manually invalidate this tag pair programmatically.'\n  -\n    name: scope\n    type: 'string'\n    description: 'Sets the [cache scope](#scope). Three possible values: `site`, `page` or `user`. Has no effect when using the `key` parameter.'\n  -\n    name: store\n    type: 'string'\n    description: 'Sets the [cache store](https://laravel.com/docs/cache#accessing-multiple-cache-stores) the cache tag uses to store and retrieve the cached values.'\nid: 1d0d2d1f-734b-4360-af7a-6792bf670bc7\n---\n## Overview\n\nAfter an initial render, markup inside a cache tag will be pulled from a cached, statically cached copy until invalidated.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cache for=\"5 minutes\" }}\n  {{ collection:stocks limit=\"5000\" }}\n    <!-- probably lots of stuff happening -->\n  {{ /collection:stocks }}\n{{ /cache }}\n```\n\n::tab blade\n```blade\n<statamic:cache\n  for=\"5 minutes\"\n>\n  <statamic:collection:stocks\n    limit=\"5000\"\n  >\n    <!-- probably lots of stuff happening -->\n  </statamic:collection:stocks>\n</statamic:cache>\n```\n::\n\nIt's worth noting that variables defined inside the cache tag won't be available outside of it.\n\n\n:::tip\nYou can disable the `cache` tag (temporarily) based on the environment. This is great for your local setup.\n\n``` env\nSTATAMIC_CACHE_TAGS_ENABLED=false\n```\n\n``` php\nreturn [\n   'cache_tags_enabled' => env('STATAMIC_CACHE_TAGS_ENABLED', true), // [tl! highlight]\n   ...\n];\n```\n:::\n\n## Exclusions\n\nYou may use the `nocache` tag inside a `cache` tag to keep that section dynamic.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cache }}\n  this will be cached\n  {{ nocache }} this will remain dynamic {{ /nocache }}\n  this will also be cached\n{{ /cache }}\n```\n::tab blade\n```blade\n<statamic:cache>\n  this will be cached\n  <statamic:nocache> this will remain dynamic </statamic:nocache>\n  this will also be cached\n</statamic:cache>\n```\n::\n\n[Read more about the nocache tag](/tags/nocache)\n\n## Invalidation\n\nCaching is handy to speed up parts of your site, but it's not very useful unless it's able to be updated at some stage. Here's how\nthe tag contents can be invalidated.\n\n### Time\n\nUsing the `for` parameter allows you to say how long the tag pair contents should be cached in time.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cache for=\"5 minutes\" }} ... {{ /cache }}\n```\n::tab blade\n```blade\n<statamic:cache\n  for=\"5 minute\"\n>\n  ...\n</statamic:cache>\n```\n::\n\n### Key\n\nBy specifying a `key`, you can invalidate it programmatically.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cache key=\"homepage_stocks\" }} ... {{ /cache }}\n```\n::tab blade\n```blade\n<statamic:cache\n  key=\"homepage_stocks\"\n>\n  ...\n</statamic:cache>\n```\n::\n\nFor example, you could listen for an entry in the `stocks` collection being saved and then bust the key.\n\n``` php\nuse Illuminate\\Support\\Facades\\Cache;\nuse Illuminate\\Support\\Facades\\Event;\nuse Statamic\\Events\\EntrySaved;\n\nclass EventServiceProvider\n{\n    public function boot()\n    {\n        Event::listen(function (EntrySaved $event) {\n            if ($event->entry->collectionHandle() === 'stocks') {\n                Cache::forget('homepage_stocks');\n            }\n        });\n    }\n}\n```\n\n:::warning\nInvalidating by `key` won't work if you're using tags. In that case, you should invalidate by flushing the tag.\n:::\n\n### Cache clear\n\nThe contents of your cache tags are stored in the application cache. Clear that, and you'll see fresh content next visit.\n\nYou can clear your cache using the Artisan command:\n\n``` shell\nphp artisan cache:clear\n```\n\n### Tag parameters and contents\n\nIt might be useful to know that if you aren't using the `key` parameter, a key is generated behind the scenes based on what\nparameters and values you've used, along with what's between the tag pair.\n\nSo, if you change your template or parameters, you'll see a fresh version next time you visit the page.\n\n\n## Scope\n\nThe `scope` parameter allows you cache the template chunk either across the whole site (the default behavior), for the current user, or per page.\n\nFor example, you might have a \"recent articles\" list on the sidebar that's the same on every page. Or, your footer navigation is probably the same on every page. You can use the `site` scope for those.\n\nHowever, your header navigation might have \"active\" states on it, so you'd want to make sure to cache it per page.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cache scope=\"page\" }}\n    {{ nav }} ... {{ /nav }}\n{{ /cache }}\n```\n::tab blade\n```blade\n<statamic:cache\n  scope=\"page\"\n>\n  <statamic:nav> ... </statamic:nav>\n</statamic:cache>\n```\n::\n\nOr if your navigation changes depending on the current user, you want to use the `user` scope:\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cache scope=\"user\" }}\n    {{ nav }} {{ user }} ... {{ /user }} {{ /nav }}\n{{ /cache }}\n```\n::tab blade\n```blade\n<statamic:cache\n  scope=\"user\"\n>\n  <statamic:nav>\n    {{ $user->name }}\n  </statamic:nav>\n</statamic:cache>\n```\n::\n\n:::tip\nThe `scope` parameter has no effect if you use the `key` parameter.\n:::\n\n\n## Static Caching\n\nYou're free to use the cache tag on top of [static caching](/static-caching).\n\nYou'll probably have static caching disabled during development so you can see your changes without having to continually clear anything.\n\nThe cache tag could be a nice compromise to speed up heavy areas for a few minutes at a time. Or, if you have some pages excluded from static caching then the cache tag could be useful there.\n\nOf course, if you *do* have static caching enabled, keep in mind that you aren't going to gain anything by using both at the same time.\n"
  },
  {
    "path": "content/collections/tags/children.md",
    "content": "---\nid: 85539849-c829-4ebc-a849-8b14fc8a4bf2\nblueprint: tag\ntitle: Children\ndescription: 'Fetch data from children of the current URL'\nintro: 'The Children tag allows you to loop over and fetch data from the current URL.'\nparameters:\n  -\n    name: of\n    type: string\n    description: >\n      The URL of the entry whose children you want to fetch. Defaults to the current URL.\n---\n## Overview\n\n:::warning\nNot to be confused with [the children variable when inside the Nav tag](/tags/nav#variables).\n:::\n\nThe children tag allows you to fetch data and loop over the children of the current page/URL. Can be handy when building a sub-nav, for example.\n\nYou can get the parent by using the [Parent tag](/tags/parent).\n\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ children }}\n    {{ title }}\n{{ /children }}\n```\n::tab blade\n```blade\n<statamic:children>\n  {{ $title }}\n</statamic:children>\n```\n::\n\n## Fetching children of another entry\n\nPass a URL to the `of` parameter to fetch children from somewhere other than the current page.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ children of=\"/blog\" }}\n    {{ title }}\n{{ /children }}\n```\n::tab blade\n```blade\n<statamic:children of=\"/blog\">\n  {{ $title }}\n</statamic:children>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/collection-count.md",
    "content": "---\ntitle: \"Collection:Count\"\ndescription: Fetches the number of entries in a collection\nintro: |\n  This tag is a clone of the [collection tag](/tags/collection) but with one big difference: it only returns the total number of entries that match your set of filters.\nparent_tag: 045a6e54-c792-483a-a109-f07251a79e47\nparameters:\n  -\n    name: in|from\n    type: string\n    description: >\n      The collection in which to count entries.\n  -\n    name: \"*\"\n    type: inherit\n    description: 'All parameters available on the [collection tag](/tags/collection) are available.'\nid: b888a242-ca4c-4a96-81ca-518bc5e3b085\n---\n## Overview\n\nThis tag's only purpose is to fetch the number of the entries in a collection that match your set of filtering parameters without the need for a tag pair/loop.\n\n## Example\n\n::tabs\n::tab antlers\n```antlers\nThere are {{ collection:count in=\"pogs\" }} pogs in this site.\n```\n\n::tab blade\n\n```blade\n{{-- Using Antlers Blade Components --}}\nThere are <collection:count in=\"pogs\" /> pogs in this site.\n\n{{-- Using Fluent Tags --}}\nThere are {{ Statamic::tag('collection:count')->in('pogs') }} pogs in this site.\n```\n::\n\n```html\nThere are 6201 pogs in this site.\n```\n\nYou could do the same thing inside a regular collection tag by aliasing the results to a single variable and using the [count modifier](/modifiers/count).\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:blog as=\"entries\" }}\nThere are {{ entries | count }} pogs in this site.\n{{ /collection:blog }}\n```\n\n::tab blade\n```blade\n<statamic:collection:blog\n  as=\"entries\"\n>\n{{-- This example calls the ->count() method on a Collection instance. --}}\nThere are {{ $entries->count() }} pogs in this site.\n\n{{-- This example uses the count modifier. --}}\nThere are {{ Statamic::modify($entries)->count()->fetch() }} pogs in this site.\n\n</statamic:collection:blog>\n```\n::"
  },
  {
    "path": "content/collections/tags/collection-next.md",
    "content": "---\ntitle: \"Collection:Next\"\ndescription:  Fetches the next entries in order.\nintro: If you're on a single entry page and want to show _next_ entries in order (publish date, alphabetical, or manual), this is the tag you're looking for.\nparameters:\n  -\n    name: in|collection\n    type: string\n    description: >\n      Explicitly define a collection. Defaults to whatever collection the current entry is in.\n  -\n    name: current\n    type: 'string'\n    description: >\n      Explicitly define a current entry by `id`. Defaults to the current entry in context.\n  -\n    name: collection params\n    type: inheritance\n    description: 'All [collection tag](/tags/collection#parameters) parameters are available.'\nvariables:\n  -\n    name: no_results\n    type: boolean\n    description: >\n      `true` if no results.\nparent_tag: 045a6e54-c792-483a-a109-f07251a79e47\nid: 2d66cda0-8765-4fe8-b902-a72de83bcbed\n---\n## Date Order\nThis tag relies on the native publish `date` field for date ordering.\n\n## Example\n\nThis will show the next 2 posts in a `blog` collection. It'll scope the entries loop into the `posts` tag pair. If there are no more entries, the no results text will be shown.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:next in=\"blog\" as=\"posts\" limit=\"2\" sort=\"date:asc\" }}\n\n  {{ if no_results }}\n    No more posts to read!\n  {{ /if }}\n\n  {{ posts }}\n    <div class=\"post\">\n      <a href=\"{{ url }}\">{{ title }}</a>\n    </div>\n  {{ /posts }}\n\n{{ /collection:next }}\n```\n::tab blade\n```blade\n<statamic:collection:next\n  in=\"blog\"\n  as=\"posts\"\n  limit=\"2\"\n  sort=\"date:asc\"\n>\n  @if ($no_results)\n    No more posts to read!\n  @endif\n\n  @foreach ($posts as $post)\n    <div class=\"post\">\n      <a href=\"{{ $post->url }}\">{{ $post->title }}</a>\n    </div>\n  @endforeach\n</statamic:collection:next>\n```\n::\n\n:::tip\nThis functions the same way as the [collection:previous](/tags/collection-previous) tag but in the opposite direction.\n:::\n"
  },
  {
    "path": "content/collections/tags/collection-previous.md",
    "content": "---\ntitle: \"Collection:Previous\"\ndescription:  Fetches the previous entries in order.\nintro: If you're on a single entry page and want to show _previous_ entries in order (publish date, alphabetical, or manual), this is the tag you're looking for.\nparent_tag: 045a6e54-c792-483a-a109-f07251a79e47\nparameters:\n  -\n    name: in|collection\n    type: string\n    description: >\n      Explicitly define a collection. Defaults to whatever collection the current entry is in.\n  -\n    name: current\n    type: 'string'\n    description: >\n      Explicitly define a current entry by `id`. Defaults to the current entry in context.\n  -\n    name: collection params\n    type: inheritance\n    description: 'All [collection tag](/tags/collection#parameters) parameters are available.'\nvariables:\n  -\n    name: no_results\n    type: boolean\n    description: >\n      `true` if no results.\nid: 741cf972-c0bd-4e3c-81e2-8cc8bea60737\n---\n## Date Order\nThis tag relies on the native publish `date` field for date ordering.\n\n## Example\n\nThis will show the next 2 posts in a `blog` collection. It'll scope the entries loop into the `posts` tag pair. If there are no more entries, the no results text will be shown.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:previous in=\"blog\" as=\"posts\" limit=\"2\" sort=\"date:asc\" }}\n\n  {{ if no_results }}\n    No more posts to read!\n  {{ /if }}\n\n  {{ posts }}\n    <div class=\"post\">\n      <a href=\"{{ url }}\">{{ title }}</a>\n    </div>\n  {{ /posts }}\n\n{{ /collection:previous }}\n```\n::tab blade\n```blade\n<statamic:collection:previous\n  in=\"blog\"\n  as=\"posts\"\n  limit=\"2\"\n  sort=\"date:asc\"\n>\n  @if ($no_results)\n    No more posts to read!\n  @endif\n\n  @foreach ($posts as $post)\n    <div class=\"post\">\n      <a href=\"{{ $post->url }}\">{{ $post->title }}</a>\n    </div>\n  @endforeach\n</statamic:collection:previous>\n```\n::\n\n:::tip\nThis functions the same way as the [collection:next](/tags/collection-next) tag but in the opposite direction.\n:::\n"
  },
  {
    "path": "content/collections/tags/collection.md",
    "content": "---\nid: 045a6e54-c792-483a-a109-f07251a79e47\nblueprint: tag\ntitle: Collection\nis_parent_tag: true\nintro: 'Entries are grouped into Collections and are fetched and filtered by this tag. A Collection could contain blog posts, products, or even a bag full of dad jokes. We don''t judge, and neither does the Collection Tag.'\ndescription: 'Fetches and filters entries in one or more collections.'\nparameters:\n  -\n    name: from|folder|use\n    type: string|array\n    description: 'The name of the collection(s). Pipe separate names to fetch entries from multiple collections. You may use `*` to get entries from all collections.'\n    required: false\n  -\n    name: not_from|not_folder|dont_use\n    type: string|array\n    description: 'When getting all collections with `*`, this parameter can accept a pipe delimited list of collections to exclude.'\n    required: false\n  -\n    name: collection\n    type: 'tag part'\n    description: 'The name of the collection when using the shorthand syntax. This is not actually a parameter, but part of the tag itself. For example, `{{ collection:blog }}`.'\n    required: false\n  -\n    name: show_future\n    type: 'boolean *false*'\n    description: 'Date-based entries from the future are excluded from results by default. Of course, if you want to show upcoming events or similar content, flip this switch.'\n    required: false\n  -\n    name: show_past\n    type: 'boolean *true*'\n    description: 'Just like `show_future`, but for entries in the past.'\n    required: false\n  -\n    name: since\n    type: string/var\n    description: 'Limits the date the earliest point in time from which date-based entries should be fetched. You can use plain English (PHP''s `strtotime` method will interpret. eg. `last sunday`, `january 15th, 2013`, `yesterday`) or the name any date variable.'\n    required: false\n  -\n    name: until\n    type: string/var\n    description: 'The inverse of `since`, but sets the _max_ date.'\n    required: false\n  -\n    name: sort\n    type: string\n    description: 'Sort entries by field name (or `random`). You may pipe-separate multiple fields for sub-sorting and specify sort direction of each field using a colon. For example, `sort=\"title\"` or `sort=\"date:asc|title:desc\"` to sort by date then by title. To sort manually, use `sort=\"order\"`. (Make sure to set max depth to 1 for your collection).'\n    required: false\n  -\n    name: limit\n    type: integer\n    description: 'Limit the total results returned.'\n    required: false\n  -\n    name: filter|query_scope\n    type: string\n    description: \"Apply a custom [query scope](/extending/query-scopes-and-filters) You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\"\n    required: false\n  -\n    name: offset\n    type: integer\n    description: 'Skip a specified number of results.'\n    required: false\n  -\n    name: taxonomy\n    type: mixed\n    description: 'A multitude of ways to filter by taxonomies. [More details](#taxonomies)'\n    required: false\n  -\n    name: paginate\n    type: 'boolean|int *false*'\n    description: 'Specify whether your entries should be paginated. You can pass `true` and also use the `limit` param, or just pass the limit directly in here.'\n    required: false\n  -\n    name: page_name\n    type: 'string *page*'\n    description: 'The query string variable used to store the page number (ie. `?page=`).'\n    required: false\n  -\n    name: on_each_side\n    type: 'int *3*'\n    description: When using pagination, this specifies the max number of links each side of the current page. The minimum value is `1`.\n  -\n    name: as\n    type: string\n    description: 'Alias your entries into a new variable loop.'\n    required: false\n  -\n    name: scope\n    type: string\n    description: 'Scope your entries with a variable prefix.'\n    required: false\n  -\n    name: locale|site\n    type: string\n    description: 'Show the retrieved content in the selected locale.'\n    required: false\n  -\n    name: redirects|links\n    type: 'boolean *false*'\n    description: 'By default, entries with redirects will be filtered out. Set this to `true` to include them.'\n    required: false\nvariables:\n  -\n    name: first\n    type: boolean\n    description: 'Is this the first item in the loop?'\n  -\n    name: last\n    type: boolean\n    description: 'Is this the last item in the loop?'\n  -\n    name: count\n    type: integer\n    description: 'The number/index of current iteration in the loop, starting from 1'\n  -\n    name: index\n    type: integer\n    description: 'The number/index of current iteration in the loop, starting from 0'\n  -\n    name: order\n    type: integer\n    description: 'The number/index of the item relative to the collection, not affected by any sort/filter parameters on the tag. Note: this is only available on collections where the order is set to number.'\n  -\n    name: no_results\n    type: boolean\n    description: 'Returns true if there are no results.'\n  -\n    name: total_results\n    type: integer\n    description: 'The total number of results in the loop when there are results. You should use `no_results` to check if any results exist.'\n  -\n    name: 'entry data'\n    type: mixed\n    description: 'Each result has access to all the variables inside that entry (`title`, `content`, etc).'\nvariables_content: |\n  The following variables are **Antlers-only**. See [Loop variables](/antlers#loop-variables) for details, or the [Blade equivalents](/blade#loop-variables) if you're writing Blade.\nrelated_entries:\n  - 7202c698-942a-4dc0-b006-b982784efb03\n  - 8ed04215-9f46-4000-bd67-c71b21b67d85\n  - 6177b316-0eed-4dec-83d1-e5a48a8e00b6\n  - 3c34ef5c-781e-4a22-a09b-25f58bdb58a8\n  - 74c47654-8c47-49b1-a616-ed940ce19977\n---\n## Overview\n\nThe collection tag is one of main workhorses of your Statamic [frontend](/frontend). It's like an Eloquent model in Laravel or \"The Loop\" in WordPress – it's how you get data from everywhere (_other than_ the current entry and global variables) into your [view](/views).\n\n## Example\n\nA basic example would be to loop through the entries in a blog collection and link to each individual blog post:\n\n::tabs\n::tab antlers\n```antlers\n<ul>\n{{ collection from=\"blog\" }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n{{ /collection }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n<statamic:collection\n    from=\"blog\"\n>\n    <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n</statamic:collection>\n</ul>\n```\n::\n\nYou can also use the shorthand syntax for this. We prefer this style ourselves.\n\n::tabs\n::tab antlers\n```antlers\n<ul>\n{{ collection:blog }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n{{ /collection:blog }}\n</ul>\n```\n\n::tab blade\n```blade\n<ul>\n<statamic:collection:blog>\n    <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n</statamic:collection:blog>\n</ul>\n```\n::\n\nIf you'd like to fetch entries from multiple collections, you'll need to use the standard syntax.\n\n::tabs\n::tab antlers\n```antlers\n{{ collection from=\"blog|events\" }}\n```\n::tab blade\n```blade\n<statamic:collection\n    from=\"blog|events\"\n>\n\n</statamic:collection>\n```\n::\n\nTo get entries from _all_ collections, use the wildcard `*`. You may also exclude collections when doing this.\n\n::tabs\n::tab antlers\n```antlers\n{{ collection from=\"*\" not_from=\"blog|events\" }}\n```\n::tab blade\n```blade\n<statamic:collection\n    from=\"*\"\n    not_from=\"blog|events\"\n>\n\n</statamic:collection>\n```\n::\n\n## Filtering\n\nThere are a number of ways to filter your collection. There's the conditions syntax for filtering by fields, taxonomy filter for using terms, and the custom filter class if you need extra control.\n\n### Conditions\n\nWant to get entries where the title has the words \"awesome\" and \"thing\", and \"joe\" is the author? You can write it how you'd say it:\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:blog title:contains=\"awesome\" title:contains=\"thing\" author:is=\"joe\" }}\n```\n\n::tab blade\n\n```blade\n<statamic:collection:blog\n    title:contains=\"awesome\"\n    title:contains=\"thing\"\n    author:is=\"joe\"\n>\n\n</statamic:collection:blog>\n```\n::\n\nThere are a bunch of conditions available to you, like `:is`, `:isnt`, `:contains`, `:starts_with`, and `:is_before`. There are many more than that. In fact, there's a whole page dedicated to [conditions - check them out][conditions].\n\n### Taxonomies\n\nFiltering by a taxonomy term (or terms) is done using the `taxonomy` parameter, similar to the conditions syntax mentioned above.\n\nTo show entries with the `harry-potter` term within the `tags` taxonomy, you could do this:\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:blog taxonomy:tags=\"harry-potter\" }}\n```\n\n::tab blade\n\n```blade\n<statamic:collection:blog\n    taxonomy:tags=\"harry-potter\"\n>\n\n</statamic:collection:blog>\n```\n::\n\nIt is important that the collection has been [configured to use this taxonomy](/collections#taxonomies) in order to filter the results based on the passed in term.\n\n:::tip\nThere are several different ways to use this filtering parameter. They are explained in depth on the [Conditions page](/conditions#taxonomy-conditions).\n:::\n\n### Published Status\n\nBy default, only `published` entries are included.  Entries can be queried against `any`, `draft`, `scheduled`, or `expired` status with [conditions](#conditions) on `status` like this:\n\n::tabs\n::tab antlers\n```antlers\n// Only include published entries\n{{ collection:blog status:is=\"published\" }}\n```\n\n::tab blade\n\n```blade\n<statamic:collection:blog\n    status:is=\"published\"\n>\n\n</statamic:collection:blog>\n```\n::\n\n:::tip\n**What is the difference between filtering against `published` and `status`?** Read more on [date behavior and published status](/collections#date-behavior-and-published-status)!\n:::\n\n\n### Custom Query Scopes\n\nDoing something custom or complicated? You can create [query scopes](/extending/query-scopes-and-filters) to narrow down those results with the `query_scope` or `filter` parameter:\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:blog query_scope=\"your_query_scope\" }}\n```\n\n::tab blade\n```blade\n<statamic:collection:blog\n    query_scope=\"your_query_scope\"\n>\n\n</statamic:collection:blog>\n```\n::\n\nYou should reference the query scope by its handle, which is usually the name of the class in snake case. For example: `YourQueryScope` would be `your_query_scope`.\n\n## Pagination\n\nTo enable pagination mode, add the `paginate` parameter with the number of entries in each page.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:blog paginate=\"10\" as=\"posts\" }}\n\n    {{ if no_results }}\n        <p>Aww, there are no results.</p>\n    {{ /if }}\n\n    {{ posts }}\n        <article>\n            {{ title }}\n        </article>\n    {{ /posts }}\n\n    {{ paginate }}\n        <a href=\"{{ prev_page }}\">⬅ Previous</a>\n\n        {{ current_page }} of {{ total_pages }} pages\n        (There are {{ total_items }} posts)\n\n        <a href=\"{{ next_page }}\">Next ➡</a>\n    {{ /paginate }}\n\n{{ /collection:blog }}\n```\n::tab blade\n```blade\n<statamic:collection:pages\n  paginate=\"10\"\n  as=\"posts\"\n>\n  @if ($no_results)\n    <p>Aww, there are no results.</p>\n  @endif\n\n  @foreach ($posts as $post)\n    <article>\n      {{ $post->title }}\n    </article>\n  @endforeach\n\n  @if ($paginate['total_pages'] > 1)\n    <a href=\"{{ $paginate['prev_page'] }}\">⬅ Previous</a>\n\n    {{ $paginate['current_page'] }} of {{ $paginate['total_pages'] }} pages\n    (There are {{ $paginate['total_items'] }} posts)\n\n    <a href=\"{{ $paginate['next_page'] }}\">Next ➡</a>\n  @endif\n</statamic:collection:pages>\n```\n::\n\nIn pagination mode, your entries will be scoped (in the example, we're scoping them into the `posts` tag pair). Use that tag pair to loop over the entries in that page.\n\nThe `paginate` variable will become available to you. This is an array containing data about the paginated set.\n\n| Variable | Description |\n|----------|-------------|\n| `next_page` |\tThe URL to the next paginated page.\n| `prev_page` |\tThe URL to the previous paginated page.\n| `total_items` |\tThe total number of entries.\n| `total_pages` |\tThe number of paginated pages.\n| `current_page` |\tThe current paginated page. (ie. the x in the ?page=x param)\n| `auto_links` |\tOutputs an HTML list of paginated links.\n| `links` |\tContains data for you to construct a custom list of links.\n| `links:all` |\tAn array of all the pages. You can loop over this and output {{ url }} and {{ page }}.\n| `links:segments` |\tAn array of data for you to create a segmented list of links.\n\n<br>\n\n### Pagination Examples\n\nThe `auto_links` tag is designed to be your friend. It'll save you more than a few keystrokes, and even more headaches. It will output an HTML list of links for you. With a large number of pages, it will create segments so that you don't end up with hundreds of numbers.\n\nIt's clever enough to work out a comfortable range of numbers to display, and it'll also throw in the prev/next arrow for good measure.\n\nMaybe the default markup isn't for you and you want total control. You're a maverick. That's cool, we roll that way sometimes too. That's where the `links:all` or `links:segments` array variables come in. These give you all the data you need to recreate your own set of links.\n\n- The `links:all` array is _all_ the pages with `url` and `page` variables.\n\n- The `links:segments` array will contain the segments mentioned above. You'll be able to access `first`, `slider`, and `last`, which are the 3 segments.\n\nHere's the `auto_links` output, recreated using the other tags, for you mavericks out there:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ paginate }}\n    <ul class=\"pagination\">\n        {{ if prev_page }}\n            <li><a href=\"{{ prev_page }}\">&laquo;</a></li>\n        {{ else }}\n            <li class=\"disabled\"><span>&laquo;</span></li>\n        {{ /if }}\n\n        {{ links:segments }}\n\n            {{ first }}\n                {{ if page == current_page }}\n                    <li class=\"active\"><span>{{ page }}</span></li>\n                {{ else }}\n                    <li><a href=\"{{ url }}\">{{ page }}</a></li>\n                {{ /if }}\n            {{ /first }}\n\n            {{ if slider }}\n                <li class=\"disabled\"><span>...</span></li>\n            {{ /if }}\n\n            {{ slider }}\n                {{ if page == current_page }}\n                    <li class=\"active\"><span>{{ page }}</span></li>\n                {{ else }}\n                    <li><a href=\"{{ url }}\">{{ page }}</a></li>\n                {{ /if }}\n            {{ /slider }}\n\n            {{ if slider || (!slider && last) }}\n                <li class=\"disabled\"><span>...</span></li>\n            {{ /if }}\n\n            {{ last }}\n                {{ if page == current_page }}\n                    <li class=\"active\"><span>{{ page }}</span></li>\n                {{ else }}\n                    <li><a href=\"{{ url }}\">{{ page }}</a></li>\n                {{ /if }}\n            {{ /last }}\n\n        {{ /links:segments }}\n\n        {{ if next_page }}\n            <li><a href=\"{{ next_page }}\">&raquo;</a></li>\n        {{ else }}\n            <li class=\"disabled\"><span>&raquo;</span></li>\n        {{ /if }}\n    </ul>\n{{ /paginate }}\n```\n::tab blade\n```blade\n<statamic:collection:blog\n  paginate=\"10\"\n  as=\"posts\"\n>\n  @if ($no_results)\n    <p>Aww, there are no results.</p>\n  @endif\n\n  @foreach ($posts as $post)\n    <article>\n      {{ $post->title }}\n    </article>\n  @endforeach\n\n  @if ($paginate['total_pages'] > 1)\n    @php\n        $hasSlider = count($paginate['links']['segments']['slider']) > 0;\n        $hasLast = count($paginate['links']['segments']['last']) > 0;\n    @endphp\n\n    <ul class=\"pagination\">\n      @if ($paginate['prev_page'])\n        <li><a href=\"{{ $paginate['prev_page'] }}\">&laquo;</a></li>\n      @else\n        <li class=\"disabled\"><span>&laquo;</span></li>\n      @endif\n\n      @foreach (Arr::get($paginate, 'links.segments.first', []) as $segment)\n        @if ($segment['page'] == $paginate['current_page'])\n          <li class=\"active\"><span>{{ $segment['page'] }}</span></li>\n        @else\n          <li><a href=\"{{ $segment['url'] }}\">{{ $segment['page'] }}</a></li>\n        @endif\n      @endforeach\n\n      @if ($hasSlider)\n        <li class=\"disabled\"><span>...</span></li>\n      @endif\n\n      @foreach (Arr::get($paginate, 'links.segments.slider', []) as $segment)\n        @if ($segment['page'] == $paginate['current_page'])\n          <li class=\"active\"><span>{{ $segment['page'] }}</span></li>\n        @else\n          <li><a href=\"{{ $segment['url'] }}\">{{ $segment['page'] }}</a></li>\n        @endif\n      @endforeach\n\n      @if ($hasSlider || $hasLast)\n        <li class=\"disabled\"><span>...</span></li>\n      @endif\n\n      @foreach (Arr::get($paginate, 'links.segments.last', []) as $segment)\n        @if ($segment['page'] == $paginate['current_page'])\n          <li class=\"active\"><span>{{ $segment['page'] }}</span></li>\n        @else\n          <li><a href=\"{{ $segment['url'] }}\">{{ $segment['page'] }}</a></li>\n        @endif\n      @endforeach\n\n      @if ($paginate['next_page'])\n        <li><a href=\"{{ $paginate['next_page'] }}\">&raquo;</a></li>\n      @else\n        <li class=\"disabled\"><span>&raquo;</span></li>\n      @endif\n    </ul>\n  @endif\n</statamic:collection:blog>\n```\n::\n\n## Aliasing {#alias}\n\nOften times you'd like to have some extra markup around your list of entries, but only if there are results. Like a `<ul>` element, for example. You can do this by _aliasing_ the results into a new variable tag pair. This actually creates a copy of your data as a new variable. It goes like this:\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:blog as=\"posts\" }}\n    <ul>\n      {{ posts }}\n        <li>\n            <a href=\"{{ url }}\">{{ title }}</a>\n        </li>\n      {{ /posts }}\n    <ul>\n{{ /collection:blog }}\n```\n::tab blade\n```blade\n<statamic:collection:blog\n  as=\"posts\"\n>\n  <ul>\n    @foreach ($posts as $post)\n    <li>\n      <a href=\"{{ $post->url }}\">{{ $post->title }}</a>\n    </li>\n    @endforeach\n  </ul>\n</statamic:collection:blog>\n```\n::\n\n## Scoping {#scope}\n\nSometimes not all of your entries have the same set of variables. And sometimes the page that you're on (while listing entries in a Collection, for example) may have those very same variables on the page-level scope. Statamic assumes you'd like to fallback to the parent scope's data to plug any holes. This logic has pros and cons, and you can [read more about scoping and the Cascade here](/cascade).\n\nYou can assign a _scope_ prefix to your entries so you can be sure to get the data you want. Define your scope and then prefix all of your variables with it.\n\n```yaml\n# Page data\nfeatured_image: /img/totes-adorbs-kitteh.jpg\n```\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:blog scope=\"post\" }}\n  <div class=\"block\">\n    <img src=\"{{ post:featured_image }}\">\n  </div>\n{{ /collection:blog }}\n```\n\n::tab blade\n```blade\n<statamic:collection:blog\n    scope=\"post\"\n>\n  <div class=\"block\">\n    <img src=\"{{ $post->featured_image }}\">\n  </div>\n</statamic:collection:blog>\n```\n::\n\nYou can also add your scope down into your [alias](#alias) loop. Yep, we thought of that too.\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:blog as=\"posts\" }}\n  {{ posts scope=\"post\" }}\n    <div class=\"block\">\n      <img src=\"{{ post:featured_image }}\">\n    </div>\n  {{ /posts }}\n{{ /collection:blog }}\n```\n::tab blade\n```blade\n<statamic:collection:blog\n    as=\"posts\"\n>\n  @foreach ($posts as $post)\n    <div class=\"block\">\n      <img src=\"{{ $post->featured_image }}\">\n    </div>\n  @endforeach\n</statamic:collection:blog>\n```\n::\n\nCombining both an Alias and a Scope on a Collection Tag doesn't make a whole lot of sense. You shouldn't do that.\n\n## Grouping\n\nTo group entries – by date or any other field – you should use [aliasing](#alias) (explained above) as well as the [`group_by`](/modifiers/group_by) modifier.\nThere's no \"grouping\" feature on the collection tag itself.\n\nFor example, if you want to group some dated entries by month/year, you could do this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection:articles as=\"entries\" }}\n    {{ entries group_by=\"date|F Y\" }}\n        {{ groups }}\n            <h3>{{ group }}</h3>\n            {{ items }}\n                {{ title }} <br>\n            {{ /items }}\n        {{ /groups }}\n    {{ /entries }}\n{{ /collection:articles }}\n```\n::tab blade\n```blade\n<statamic:collection:articles\n  as=\"entries\"\n>\n  @php\n  $groupedEntries = $entries->groupBy(fn($entry) => $entry->date?->format('F Y'));\n  @endphp\n\n  @foreach ($groupedEntries as $group => $items)\n    <h3>{{ $group }}</h3>\n\n    @foreach ($items as $entry)\n      {{ $entry->title }}\n    @endforeach\n  @endforeach\n</statamic:collection:articles>\n```\n::\n\n[conditions]: /conditions\n"
  },
  {
    "path": "content/collections/tags/cookie.md",
    "content": "---\ntitle: Cookie\ndescription: 'Get, set, check, and forget cookies.'\nintro: 'Cookies provide a client-side way to store information about the user across requests. The cookie tag will let you get, set, and forget cookie data.'\nis_parent_tag: false\nid: c15836c2-808d-4260-9d01-e5a569da5b7a\n---\n## Retrieving Cookie Data\n\nYou can use `{{ cookie }}` as a tag pair to access any cookie data that has been set.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cookie }}\n    {{ oreo }}\n{{ /cookie }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:cookie>\n  {{ $oreo ?? '' }}\n</s:cookie>\n\n{{-- Using the Cookie facade --}}\n{{ Cookie::get('oreo') }}\n```\n::\n\n```.output\nYum yum yum.\n```\n\nYou can also retrieve single variables with a single tag syntax.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cookie:oreo }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:cookie:oreo />\n\n{{-- Using the Cookie facade --}}\n{{ Cookie::get('oreo') }}\n```\n::\n\n## Checking\n\nYou can check if a cookie exists with [cookie:has](/tags/session-has).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {cookie:has key=\"has_voted\"} === true }}\n  You already voted. Thank you!\n{{ /if }}\n```\n::tab blade\n```blade\n{{-- Using the Cookie facade --}}\n@if (Cookie::has('has_voted') === true)\n  You already voted. Thank you!\n@endif\n```\n::\n\n## Aliasing\n\nIf you need extra markup around your cookie data, you can _alias_ a new child array variable.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cookie as=\"snack\" }}\n  {{ snack }}\n    {{ message }}\n  {{ /snack }}\n{{ /cookie }}\n```\n::tab blade\n```blade\n{{-- Retrieving the message using the Cookie facade --}}\n{{ Cookie::get('message') }}\n\n{{-- Aliasing using Antlers Blade Components --}}\n<s:cookie as=\"snack\">\n  {{ $snack['oreo'] ?? '' }}\n</s:cookie>\n```\n::\n\n## Setting\n\nYou can set a cookie with `cookie:set`:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cookie:set my_key=\"my_value\" }}\n```\n::tab blade\n```blade\n{{-- Using Antler Blade Components --}}\n<s:cookie:set my_key=\"my_value\" />\n\n{{-- Using the cookie helper --}}\n<?php\n  cookie()->queue(cookie('my_key', 'my_value'));\n?>\n```\n::\n\nYou can optionally set a number of minutes the cookie is valid for (60 by default):\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cookie:set my_key=\"my_value\" minutes=\"3600\" }}\n```\n::tab blade\n```blade\n{{-- Using Antler Blade Components --}}\n<s:cookie:set\n  my_key=\"my_value\"\n  minutes=\"3600\"\n/>\n\n{{-- Using the cookie helper --}}\n<?php\n  cookie()->queue(cookie('my_key', 'my_value', 3600));\n?>\n```\n::\n\nYou can set multiple cookies at once and reference interpolated data (references to variables).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cookie:set likes=\"hats\" :visited=\"url\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:cookie:set\n  likes=\"hats\"\n  :visited=\"$url\"\n/>\n\n{{-- Using the cookie helper --}}\n<?php\n  cookie()->queue(cookie('likes', 'hats'));\n  cookie()->queue(cookie('visited', $url));\n?>\n```\n::\n\n:::tip\nWhen you set a cookie, it will only be available on the _next_ request.\n:::\n\n## Forgetting\n\nYou can remove cookies by passing the names of the variables into the `keys` parameter. Pass multiple keys by delimiting them with a pipe.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ cookie:forget keys=\"likes|referral\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:cookie:forget\n  keys=\"likes|referral\"\n/>\n\n{{-- Using the Cookie helper --}}\n<?php\n  cookie()->queue(cookie()->forget('likes'));\n  cookie()->queue(cookie()->forget('referral'));\n?>\n```\n::\n\n## Accessing Cookies in JavaScript\n\nBy default, in Laravel, cookies are encrypted so you will not be able to access the values of any data you set outside of PHP. To exclude specifc cookies from encryption you need to use the `encryptCookies` method in your application's `bootstrap/app.php` file:\n\n```php\n->withMiddleware(function (Middleware $middleware) {\n    $middleware->encryptCookies(except: [\n        'cookie_name',\n    ]);\n})\n```\n\nIf your app is older, you may need to add the exception in `app/Http/Middleware/EncryptCookies.php` instead:\n\n```php\n/**\n * The names of the cookies that should not be encrypted.\n *\n * @var array\n */\nprotected $except = [\n    'cookie_name',\n];\n```"
  },
  {
    "path": "content/collections/tags/dictionary.md",
    "content": "---\ntitle: Dictionary\nintro: Allows you to loop through options from a Dictionary.\ndescription: Allows you to loop through options from a Dictionary.\nid: 603bb573-5ebf-4851-935e-713f1caaa431\nparameters:\n  -\n    name: handle\n    type: string\n    description: 'The handle of the dictionary you wish to get options from.'\n    required: true\n  -\n    name: sort\n    type: string\n    description: 'Sort options by field name (or `random`). You may pipe-separate multiple fields for sub-sorting and specify sort direction of each field using a colon. For example, `sort=\"name\"` or `sort=\"date:asc|name:desc\"` to sort by date then by name.'\n    required: false\n  -\n    name: limit\n    type: integer\n    description: 'Limit the total results returned.'\n    required: false\n  -\n    name: filter|query_scope\n    type: string\n    description: \"Apply a custom [query scope](/extending/query-scopes-and-filters) You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\"\n    required: false\n  -\n    name: offset\n    type: integer\n    description: 'Skip a specified number of results.'\n    required: false\n  -\n    name: paginate\n    type: 'boolean|int *false*'\n    description: 'Specify whether your options should be paginated. You can pass `true` and also use the `limit` param, or just pass the limit directly in here.'\n    required: false\n  -\n    name: page_name\n    type: 'string *page*'\n    description: 'The query string variable used to store the page number (ie. `?page=`).'\n    required: false\n  -\n    name: on_each_side\n    type: 'int *3*'\n    description: When using pagination, this specifies the max number of links each side of the current page. The minimum value is `1`.\n  -\n    name: as\n    type: string\n    description: 'Alias your options into a new variable loop.'\n    required: false\n  -\n    name: scope\n    type: string\n    description: 'Scope your options with a variable prefix.'\n    required: false\n  -\n    name: '*'\n    type: string\n    description: 'Any additional parameters will be set as config options on the Dictionary.'\n---\n\nThis tag allows you to loop through options from a [Dictionary](/fieldtypes/dictionary).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary handle=\"countries\" }}\n    {{ label }} {{ value }}\n{{ /dictionary }}\n```\n::tab blade\n```blade\n<s:dictionary handle=\"countries\">\n  {{ $label }} {{ $value }}\n</s:dictionary>\n```\n::\n\nYou can also use the shorthand syntax for this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary:countries }}\n    {{ label }} {{ value }}\n{{ /dictionary:countries }}\n```\n::tab blade\n```blade\n<s:dictionary:countries>\n  {{ $label }} {{ $value }}\n</s:dictionary:countries>\n```\n::\n\nYou can also output any additional data provided by the Dictionary, like `emoji` or `region` in the case of the Countries dictionary:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary:countries }}\n    {{ emoji }} {{ name }} in {{ region }}\n{{ /dictionary:countries }}\n```\n::tab blade\n```blade\n<s:dictionary:countries>\n  {{ $emoji }} {{ $name }} in {{ $region }}\n</s:dictionary:countries>\n```\n::\n\n## Searching\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary:countries search=\"Aus\" }}\n    {{ label }} {{ value }}\n{{ /dictionary:countries }}\n```\n::tab blade\n```blade\n<s:dictionary:countries search=\"Aus\">\n  {{ $label }} {{ $value }}\n</s:dictionary:countries>\n```\n::\n\n```html\n🇦🇺 Australia AUS\n🇦🇹 Austria AUT\n```\n\n## Conditions\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary:countries region:is=\"Europe\" }}\n    {{ label }} {{ value }}\n{{ /dictionary:countries }}\n```\n::tab blade\n```blade\n<s:dictionary:countries region:is=\"Europe\">\n  {{ $label }} {{ $value }}\n</s:dictionary:countries>\n```\n::\n\nThere are a bunch of conditions available to you, like `:is`, `:isnt`, `:contains`, `:starts_with`, and `:is_before`. There are many more than that. In fact, there's a whole page dedicated to [conditions - check them out](/conditions).\n\n## Paginating\n\nTo enable pagination mode, add the `paginate` parameter with the number of options in each page.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary:countries paginate=\"10\" as=\"countries\" }}\n    {{ countries }}\n        {{ label }}<br>\n    {{ /countries }}\n\n    {{ paginate }}\n        <a href=\"{{ prev_page }}\">⬅ Previous</a>\n\n        {{ current_page }} of {{ total_pages }} pages\n        (There are {{ total_items }} pages)\n\n        <a href=\"{{ next_page }}\">Next ➡</a>\n    {{ /paginate }}\n{{ /dictionary:countries }}\n```\n::tab blade\n```blade\n<s:dictionary:countries paginate=\"10\" as=\"countries\">\n  @foreach ($countries as $country)\n    {{ $country['label'] }}<br>\n  @endforeach\n\n  @if ($paginate['total_pages'] > 1)\n    <a href=\"{{ $paginate['prev_page'] }}\">⬅ Previous</a>\n\n    {{ $paginate['current_page'] }} of {{ $paginate['total_pages'] }} pages\n    (There are {{ $paginate['total_items'] }} pages)\n\n    <a href=\"{{ $paginate['next_page'] }}\">Next ➡</a>\n  @endif\n</s:dictionary:countries>\n```\n::\n\nIn pagination mode, your options will be scoped (in the example, we're scoping them into the `countries` tag pair). Use that tag pair to loop over the options in that page.\n\nThe `paginate` variable will become available to you. This is an array containing data about the paginated set.\n\n| Variable | Description |\n|----------|-------------|\n| `next_page` |\tThe URL to the next paginated page.\n| `prev_page` |\tThe URL to the previous paginated page.\n| `total_items` |\tThe total number of options.\n| `total_pages` |\tThe number of paginated pages.\n| `current_page` |\tThe current paginated page. (ie. the x in the ?page=x param)\n| `auto_links` |\tOutputs an HTML list of paginated links.\n| `links` |\tContains data for you to construct a custom list of links.\n| `links:all` |\tAn array of all the pages. You can loop over this and output {{ url }} and {{ page }}.\n| `links:segments` |\tAn array of data for you to create a segmented list of links.\n\n<br>\n\n## Query Scopes\n\nDoing something custom or complicated? You can create [query scopes](/extending/query-scopes-and-filters) to narrow down those results with the `query_scope` or `filter` parameter:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary:countries query_scope=\"your_query_scope\" }}\n```\n::tab blade\n```blade\n<s:dictionary:countries query_scope=\"your_query_scope\">\n\n</s:dictionary:countries>\n```\n::\n\nYou should reference the query scope by its handle, which is usually the name of the class in snake case. For example: `YourQueryScope` would be `your_query_scope`.\n\n## Files\n\nOne of the powerful things you can do with the Files dictionary is pull in options from a JSON, YAML or CSV file.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dictionary:file filename=\"products.json\" label=\"Name\" value=\"Code\" paginate=\"5\" }}\n```\n::tab blade\n```blade\n<s:dictionary:file\n  filename=\"products.json\"\n  label=\"Name\"\n  value=\"Code\"\n  paginate=\"5\"\n>\n\n</s:dictionary:file>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/dump.md",
    "content": "---\ntitle: Dump\ndescription: Debugs variables in current view context\nintro: The dump tag is used for debugging data inside your current view context.\nid: 32bc9a50-3b12-11e6-bdf4-0800200c9a66\n---\n## Overview\nThis tag is useful for debugging. It will render the raw data of the variables in your current context (page, variable loop, etc).\n\nDropping it in a template or layout will show you all the data that's been injected into the view layer.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dump }}\n```\n::tab blade\n```blade\n<statamic:dump />\n```\n::\n\n:::tip\nThe `dump` tag will output dumps when `APP_DEBUG` is `true`.\n:::\n\nIf you need to dump something when debug mode is disabled, you can use the `force` parameter:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ dump force=\"true\" }}\n```\n::tab blade\n```blade\n<statamic:dump force=\"true\" />\n```\n::\n\n\nDropping it inside a loop will dump all the data _just for that loop context_.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ gallery }}\n  {{ dump }}\n{{ /gallery }}\n```\n::tab blade\n```blade\n@foreach ($gallery as $item)\n  {{-- Using the Statamic tag --}}\n  <statamic:dump />\n  \n  {{-- Using Blade Directives --}}\n  @dd($item)\n@endforeach\n```\n::\n\n:::tip\nYou can also use the [dump modifier](/modifiers/dump) to achieve a similar effect.\n:::\n"
  },
  {
    "path": "content/collections/tags/foreach.md",
    "content": "---\ntitle: Foreach\ndescription: Loops through keys in a named key/value array\nintro: >\n  Loop through and render values from an array of named keys and values without needing to know the keys.\nparameters:\n  -\n    name: as\n    type: string\n    description: |\n      Optionally rename the `key|value` variables. See the above example.\n  -\n    name: array\n    type: string|array\n    description: |\n      The name of the array to loop over, or a reference to the array itself. See [dynamic variables](#dynamic-variables).\n  -\n    name: limit\n    type: int\n    description: |\n      Limits the number of items returned in the loop.\nid: 34b03d03-a113-467f-a1c9-65bc6b446220\n---\n## Overview\n\nNormally when you have data stored in a named array format perhaps created by the [array fieldtype](/fieldtypes/array), you would need to know the keys to render data in your view.\n\nUsing the `foreach` tag you can pass in variable name as the second argument in the tag name and loop through the data using `{{ key }}` and `{{ value }}`.\n\n```yaml\ncompany_info:\n  address_1: 123 Hollywood Blvd\n  address_2: Suite 404\n  city: Beverly Hills\n  state: California\n  zip: 90210\n\nsong_reviews:\n  Never Gonna Give You Up: 5/5\n  My Heart Will Go On: 3/5\n```\n::tabs\n\n::tab antlers\n```antlers\n{{ foreach:company_info }}\n  {{ key }}: {{ value }}<br>\n{{ /foreach:company_info }}\n\n<ul>\n  {{ foreach:song_reviews as=\"song|rating\" }}\n    <li>{{ song }}: {{ rating }}</li>\n  {{ /foreach:song_reviews }}\n</ul>\n```\n::tab blade\n\nWhen authoring Blade templates, you should use the `@foreach` directive instead.\n\n```blade\n@foreach ($company_info as $key => $value)\n  <li>{{ $key }}: {{ $value }}<br>\n@endforeach\n\n<ul>\n  @foreach ($song_reviews as $song => $rating)\n     <li>{{ $song }}: {{ $rating }}</li>\n  @endforeach\n</ul>\n```\n::\n\n```html\nAddress 1: 123 Hollywood Blvd<br>\nAddress 2: Suite 404<br>\nCity: Beverly Hills<br>\nState: California<br>\nZip Code: 90210\n\n<ul>\n  <li>Never Gonna Give You Up: 5/5</li>\n  <li>My Heart Will Go On: 3/5</li>\n</ul>\n```\n\n:::tip\nPHP reserves the word `foreach`, so this tag is _technically_ an alias of `iterate` under the hood. If you're spelunking through the source code, that's where you'll find it.\n:::\n\n## Dynamic Variables\n\nInstead of using the shorthand `{{ foreach:variable_name }}` syntax, you may pass in the array's name manually.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ foreach array=\"song_reviews\" }}\n    ...\n{{ /foreach }}\n```\n::tab blade\n```blade\n@foreach ($song_reviews as $review)\n  ...\n@endforeach\n```\n::\n\nIf you have a more complicated array location, you can use a dynamic parameter to pass the array itself.\n\n```yaml\nreviews:\n  songs: [...]\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ foreach :array=\"reviews:songs\" }}\n    ...\n{{ /foreach }}\n```\n::tab blade\n```blade\n@foreach ($reviews['songs'] as $song)\n  ...\n@endforeach\n```\n::\n"
  },
  {
    "path": "content/collections/tags/form-create.md",
    "content": "---\ntitle: \"Form:Create\"\nid: aa96fcf1-510c-404b-9b63-cea8942e1bf8\ndescription: Manages markup and success/error handling for forms.\nintro: Statamic [forms](/forms) serve to collect, report, and reuse user submitted data. This tag handles the HTML markup, redirect behavior, and success/error states and messages for these forms.\nparameters:\n  -\n    name: handle|is|in|form|formset\n    type: string\n    description: >\n      Specify the name of the form. Only required if you do _not_ use the `form:set` tag, or don't have a `form` defined in the current context.\n  -\n    name: redirect\n    type: string\n    description: >\n      The location your user will be taken after a successful form submission. If left blank, the user will stay on the same page.\n  -\n    name: error_redirect\n    type: string\n    description: >\n      The location your user will be taken after a failed form submission. If left blank, the user will stay on the same page.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When `true`, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL. For example, `?redirect=/thanks`\n  -\n    name: csrf\n    type: boolean\n    description: When `false`, the hidden `name=\"_token\"` attribute won't be added to the form so you can use other ways of providing the token. Defaults to `true`.\n  -\n    name: files\n    type: boolean\n    description: When `true`, the `enctype=\"multipart/form-data\"` attribute will be rendered on your `<form>` tag for file uploads.\n  -\n    name: js\n    type: string\n    description: Enable [conditional fields](#conditional-fields) using one of the provided JS drivers.\n  -\n    name: HTML Attributes\n    type: string\n    description: >\n      Set HTML attributes as if you were on an HTML element. For example, `class=\"required\" id=\"contact-form\"`.\nvariables:\n  -\n    name: fields\n    type: array\n    description: >\n      An array of available fields for [dynamic rendering](#dynamically-rendering-fields).\n  -\n    name: errors\n    type: array\n    description: |\n      An indexed array of any validation errors upon submission. For example: `{{ errors }}{{ value }}{{ /errors }}`\n  -\n    name: error\n    type: array\n    description: |\n      An array of validation errors indexed by **field name**. For example: `{{ error:email }}`\n  -\n    name: old\n    type: array\n    description: An array of submitted values from the previous request. Used for re-populating fields if there are validation errors.\n  -\n    name: success\n    type: string\n    description: A success message, usually used in a condition to check of a form submission was successful. `{{ if success }} Hurray! {{ /if }}`\n  -\n    name: submission_created\n    type: boolean\n    description: A success boolean, which differs from `success` in that it will actually return falsey when the [honeypot](/forms#honeypot) is filled. This can be useful when you want to show a fake success message for honeypot spam, but want to prevent analytics tracking code from being rendered.\n---\n## Overview\n\nHere we'll be creating a form to submit an entry in a `contact` form.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:create in=\"contact\" }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}\n        </div>\n    {{ /if }}\n\n    <label>Email</label>\n    <input type=\"text\" name=\"email\" value=\"{{ old:email }}\" />\n\n    <label>Message</label>\n    <textarea name=\"message\" rows=\"5\">{{ old:message }}</textarea>\n\n    <button>Submit</button>\n\n{{ /form:create }}\n```\n::tab blade\n```blade\n<s:form:create in=\"contact\">\n\n    @if (count($errors) > 0)\n        <div class=\"bg-red-300 text-white p-2\">\n            @foreach ($errors as $error)\n                {{ $error }}<br>\n            @endforeach\n        </div>\n    @endif\n\n    @if ($success)\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ $success }}\n        </div>\n    @endif\n\n    <label>Email</label>\n    <input type=\"text\" name=\"email\" value=\"{{ old('email') }}\" />\n\n    <label>Message</label>\n    <textarea name=\"message\" rows=\"5\">{{ old('message') }}</textarea>\n\n    <button>Submit</button>\n\n</s:form:create>\n```\n::\n\nYou can also use the shorthand syntax for `form:create in=\"contact\"`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact }}\n    ...\n{{ /form:contact }}\n```\n::tab blade\n```blade\n<s:form:contact>\n    ...\n</s:form:contact>\n```\n::\n\nUsing this tag, Statamic will automatically take care of opening your form with a proper [CSRF token](https://laravel.com/docs/csrf).\n\n```html\n<form method=\"POST\" action=\"https://website.example/!/forms/contact\">\n    <input type=\"hidden\" name=\"_token\" value=\"cN03woeRj5Q0GtlOj7GydsZcRwlyp9VLzfpwDFJZ\">\n    ...\n</form>\n```\n\nIt also provides helpers for [dynamically rendering](#dynamic-rendering) sections and fields, [conditionally rendering](#conditional-fields) fields, etc.\n\n## Dynamic Rendering\n\n### Dynamically Rendering via Form Fieldtype\n\nWhen you need to render a form that's selected via the [Form Fieldtype](/fieldtypes/form) you can use this pattern:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:create :in=\"form_fieldtype:handle\" }}\n    ...\n{{ /form:create }}\n```\n::tab blade\n```blade\n<s:form:create :in=\"$form_fieldtype->handle\">\n    ...\n</s:form:create>\n```\n::\n\nThis way you can let Control Panel users select which form should be used on an entry.\n\n### Dynamically Rendering Fields\n\nInstead of hardcoding individual fields, you may loop through the `fields` array using the [form:fields](/tags/form-fields) tag to render your blueprint's fields in a dynamic fashion.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact }}\n\n    {{ form:fields }}\n        <div class=\"p-2\">\n            <label>\n                {{ display }}\n                {{ if validate | contains:required }}\n                    <sup class=\"text-red\">*</sup>\n                {{ /if }}\n            </label>\n            <div class=\"p-1\">{{ field }}</div>\n            {{ if error }}\n                <p class=\"text-gray-500\">{{ error }}</p>\n            {{ /if }}\n        </div>\n    {{ /form:fields }}\n\n    <button>Submit</button>\n\n{{ /form:contact }}\n```\n::tab blade\n```blade\n<s:form:contact>\n\n    <s:form:fields>\n        <div class=\"p-2\">\n            <label>\n                {{ $field['display'] }}\n                @if (in_array('required', $field['validate'] ?? []))\n                    <sup class=\"text-red\">*</sup>\n                @endif\n            </label>\n            <div class=\"p-1\">{{ $field['field'] }}</div>\n            @if ($field['error'])\n                <p class=\"text-gray-500\">{{ $field['error'] }}</p>\n            @endif\n        </div>\n    </s:form:fields>\n\n    <button>Submit</button>\n\n</s:form:contact>\n```\n::\n\nEach item in this `fields` array contains the following data:\n\n#### Fields Array Variables\n\n| Variable | Type | Description |\n|---|---| --- |\n| `display` | string | User-friendly field label configured in blueprint |\n| `instructions` | string | User-friendly instructions label configured in blueprint |\n| `field` | string | [Pre-rendered field HTML](#pre-rendered-field-html) based on the fieldtype |\n| `type` | string | Name of the [fieldtype](/fieldtypes) |\n| `handle` | string | Blueprint handle for the field |\n| `name` | string | Input name for submitting the field |\n| `value` | mixed | Field value, which respects both `old` and `default` |\n| `default` | mixed | Default field value as assigned by blueprint |\n| `old` | mixed | Old field value from an unsuccessful submission |\n| `error` | string | Error message from an unsuccessful submission |\n| `validate` | array | Contains an array of validation rules |\n| `width` | string | Width of the field assigned in the blueprint |\n\n\n### Pre-rendered Field HTML\n\nUsing the `field` variable will intelligently render inputs as inputs, textareas as textareas, and snozzberries as snozzberries.\n\nYou can customize these pre-rendered snippets by running `php artisan vendor:publish --tag=statamic-forms`. It will expose editable templates snippets in your `views/vendor/statamic/forms/fields` directory that will be used by each fieldtype.\n\n:::tip\nBy default, pre-rendered snippets are implemented in Antlers. If you'd prefer to use Blade, you can grab some [ready-to-go Blade snippets](/blade-form-fields) to use as a starting point.\n:::\n\nThis approach, combined with the [blueprint editor](/blueprints), will give you something very similar to a traditional \"Form Builder\" from other platforms.\n\n**Example**\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:fields }}\n    <div class=\"mb-2\">\n        <label class=\"block\">{{ display }}</label>\n        {{ field }}\n    </div>\n{{ /form:fields }}\n```\n::tab blade\n```blade\n<s:form:fields>\n    <div class=\"mb-2\">\n        <label class=\"block\">{{ $field['display'] }}</label>\n        {{ $field['field'] }}\n    </div>\n</s:form:fields>\n```\n::\n\n```output\n<div class=\"mb-2\">\n    <label class=\"block\">Name</label>\n    <input type=\"text\" name=\"name\" value=\"\">\n</div>\n<div class=\"mb-2\">\n    <label class=\"block\">Email Address</label>\n    <input type=\"text\" name=\"email\" value=\"\">\n</div>\n<div class=\"mb-2\">\n    <label class=\"block\">Note</label>\n    <textarea name=\"message\"></textarea>\n</div>\n```\n\n### Dynamically Rendering Sections\n\nIf you have defined multiple sections in your form's blueprint, you can loop over these `sections` in a dynamic fashion as well.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact }}\n\n    {{ sections }}\n        <fieldset>\n            <legend>{{ display }}</legend>\n            {{ form:fields }}\n                ...\n            {{ /form:fields }}\n        </fieldset>\n    {{ /sections }}\n\n    <button>Submit</button>\n\n{{ /form:contact }}\n```\n::tab blade\n```blade\n<s:form:contact>\n\n    @foreach($sections as $section)\n        <fieldset>\n            <legend>{{ $section['display'] }}</legend>\n            <s:form:fields :section=\"$section\">\n                ...\n            </s:form:fields>\n        </fieldset>\n    @endforeach\n\n    <button>Submit</button>\n\n</s:form:contact>\n```\n::\n\nEach item in the `sections` array contains the following data configurable in the form's blueprint.\n\n| Variable | Type | Description |\n|---|---| --- |\n| `display` | string | User-friendly section label |\n| `instructions` | string | User-friendly section instructions |\n| `fields` | array | An array of [fields](#dynamically-rendering-fields) defined within that section |\n\n\n## Conditional Fields\n\nYou may conditionally show and hide fields by utilizing the [conditional fields](/conditional-fields#overview) settings in your form's blueprint editor. Once configured, by including the necessary front-end scripts and enabling JavaScript on the `form:create` tag, all of the conditional logic will Just Work™.\n\nStatamic includes an [Alpine.js](https://alpinejs.dev/) driver or you can build your own [custom JS driver](#custom-js-drivers) to wire up whichever framework you prefer.\n\n### Including the Scripts\n\nFor our Alpine.js example, the first step is to include Alpine, as well as Statamic's front-end `helpers.js` script:\n\n```html\n<script src=\"https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js\" defer></script>\n<script src=\"/vendor/statamic/frontend/js/helpers.js\"></script>\n```\n\nThese can be added to your [layout](/views#layouts) just before your `</body>` tag. Alternatively, you could also work these into your webpack/mix build, but this is the simplest way.\n\n### Enabling the JS Driver\n\nThe next step is to enable the Alpine JS driver via the `js=\"alpine\"` parameter.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact js=\"alpine\" }}\n    ...\n{{ /form:contact }}\n```\n::tab blade\n```blade\n<s:form:contact js=\"alpine\">\n    ...\n</s:form:contact>\n```\n::\n\nThis will generate an Alpine component, with automatic `x-data` handling that will respect old input when there are validation errors, etc.\n\n### Wiring Up the Fields\n\nFinally, you will need to wire up the fields. With Alpine, this is done using `x-model` on the input to keep it in sync with the component, as well as an `x-if` to conditionally render the input and its label.\n\n::tabs\n\n::tab antlers\n```antlers\n<template x-if=\"{{ show_field['name'] }}\">\n    <div class=\"p-2\">\n        <label>Name</label>\n        <input type=\"text\" name=\"name\" value=\"{{ old:name }}\" x-model=\"name\" />\n    </div>\n</template>\n```\n::tab blade\n```blade\n<template x-if=\"{{ $show_field['name'] }}\">\n    <div class=\"p-2\">\n        <label>Name</label>\n        <input type=\"text\" name=\"name\" value=\"{{ old('name') }}\" x-model=\"name\" />\n    </div>\n</template>\n```\n::\n\nThe `x-model` should reference the field's handle, and the `x-if` should reference the appropriate `show_field` JS generated by Statamic; In this case, `x-model=\"name\"` and `x-if=\"{{ show_field['name'] }}\"` respectively.\n\nFor nested fields, you can get `show_field` JS by passing the whole dotted handle as the key, ie) `x-if=\"{{ show_field['field_group.nested_field']}}\"`.\n\n### Wiring Up Dynamically Rendered Fields\n\nIf you are [dynamically rendering your fields](#dynamic-rendering) using the `fields` loop, your template might look something like this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:fields }}\n    <template x-if=\"{{ show_field }}\">\n        <div class=\"p-2\">\n            <label>{{ display }}</label>\n            <div class=\"p-1\">{{ field }}</div>\n        </div>\n    </template>\n{{ /form:fields }}\n```\n::tab blade\n```blade\n<s:form:fields>\n    <template x-if=\"{{ $field['show_field'] }}\">\n        <div class=\"p-2\">\n            <label>{{ $field['display'] }}</label>\n            <div class=\"p-1\">{{ $field['field'] }}</div>\n        </div>\n    </template>\n</s:form:fields>\n```\n::\n\nThe pre-rendered `{{ field }}` input will automatically render `x-model` for you, but you'll still need to wrap your input and its label with an `x-if=\"{{ show_field }}`, as shown above.\n\n### Scoping Your Alpine Data\n\nIf you are using other Alpine components in your form or on your page, the included Alpine driver allows you to scope the generated `x-data` to prevent conflicts with your other components. To do this, provide a scope key when enabling the JS driver.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact js=\"alpine:contact_form\" }}\n    ...\n{{ /form:contact }}\n```\n::tab blade\n```blade\n<s:form:contact js=\"alpine:contact_form\">\n    ...\n</s:form:contact>\n```\n::\n\nThe above will nest your form fields in a `contact_form` object within the generated `x-data`.\n\nIf you are hardcoding your inputs, you will need adjust your `x-model` to follow suit.\n\n::tabs\n\n::tab antlers\n```antlers\n<template x-if=\"{{ show_field:name }}\">\n    <div class=\"p-2\">\n        <label>Name</label>\n        <input type=\"text\" name=\"name\" value=\"{{ old:name }}\" x-model=\"contact_form.name\" />\n    </div>\n</template>\n```\n::tab blade\n```blade\n<template x-if=\"{{ $show_field['name'] }}\">\n    <div class=\"p-2\">\n        <label>Name</label>\n        <input type=\"text\" name=\"name\" value=\"{{ old('name') }}\" x-model=\"contact_form.name\" />\n    </div>\n</template>\n```\n::\n\nIf you are [dynamically rendering your fields](#dynamic-rendering) using the `fields` loop, this is once again handled for you.\n\n\n## Custom JS Drivers\n\nShould you need to work with another JS framework for handling [conditional fields](#conditional-fields) and form state in realtime, we've provided a few tools to help you build your own JS driver.\n\n### Creating the Driver\n\nTo write a custom JS form driver, create a class and extend `Statamic\\Forms\\JsDrivers\\AbstractJsDriver`.\n\n```php\n<?php\n\nnamespace App\\Forms;\n\nuse Statamic\\Forms\\JsDrivers\\AbstractJsDriver;\nuse Statamic\\Statamic;\n\nclass RadJs extends AbstractJsDriver\n{\n    public function addToFormAttributes()\n    {\n        return [\n            'r-data' => Statamic::modify($this->getInitialFormData())->toJson()->entities(),\n        ];\n    }\n\n    public function addToRenderableFieldAttributes($field)\n    {\n        $attributes = [];\n\n        if ($field->fieldtype()->hasJsDriverDataBinding()) {\n            $attributes['r-model'] => $field->handle(),\n        }\n\n        return $attributes;\n    }\n\n    public function addToRenderableFieldData($field, $data)\n    {\n        $conditions = Statamic::modify($field->conditions())->toJson()->entities();\n\n        return [\n            'show_field' => 'Statamic.$conditions.showField('.$conditions.', $data)',\n        ];\n    }\n}\n```\n\nIn this above example, we provide `r-data` and `r-model` attributes for a fictional framework called `Rad.js`, as well as `show_field` [conditional logic](#the-helpersjs-script) for each renderable field.\n\n:::tip\nFor a more real-world example, here is how you could create [a custom driver for Vue.js](https://gist.github.com/jesseleite/3507f7ad3dd062b9e5f7592c899bf297). Of course, you should also check out our built-in [Alpine.js driver](https://github.com/statamic/cms/blob/13721c5738c3fe43ce5b2161595a6d42016e7594/src/Forms/JsDrivers/Alpine.php).\n:::\n\n### Registering the Driver\n\nTo register your custom JS form driver class, simply call its static `register()` method from within a service provider.\n\n``` php\npublic function register()\n{\n    \\App\\Forms\\RadJs::register();\n}\n```\n\n### Driver Requirements\n\nThe only true requirement of your custom driver is that you return `show_field` javascript from the `addToRenderableFieldData()` method, so that the user can wire up `show_field` [conditional logic](#the-helpersjs-script) as per [the documentation above](#wiring-up-the-fields).\n\n### Available Methods and Properties\n\nTake a look at the [AbstractJsDriver](https://github.com/statamic/cms/blob/feature/frontend-form-conditions/src/Forms/JsDrivers/AbstractJsDriver.php) class to see what is available to you, but here is a list of available methods and properties at a glance:\n\n#### Definable Render Methods\n\n- Define an `addToFormData($data)` method in your class to add to the available data within the `form:create` tag pair.\n- Define an `addToFormAttributes()` method in your class to add custom HTML attributes to your `<form>` element.\n- Define an `addToRenderableFieldData($field, $data)` method in your class to add to the available data for each field within the `fields` loop.\n- Define an `addToRenderableFieldAttributes($field)` method in your class to add custom HTML attributes to each pre-rendered `field` field input within the `fields` loop.\n- Define a `render($html)` method to control the overall rendering of your form component HTML.\n\n#### Callable Helper Methods\n\n- Call `$this->getInitialFormData()` to get the initial form field values from the server, while respecting old input when there are validation errors, etc.\n\n#### Driver Properties\n\n- The `$this->form` property gives you access to the relevant `Statamic\\Forms\\Form` object anywhere within your driver class.\n- The `$this->options` property gives you access to the passed [driver options](#driver-options).\n\n### Driver Options\n\nYou can also pass comma-delimited options into the `js` parameter like so:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:contact js=\"radjs:foo:bar\" }}\n    ...\n{{ /form:contact }}\n```\n::tab blade\n```blade\n<s:form:contact js=\"radjs:foo:bar\">\n    ...\n</s:form:contact>\n```\n::\n\nWithin your driver class, you'll be able to access `$this->options` to retrieve an array of options (ie. `['foo', 'bar']` in the example above).\n\n### The Helpers.js Script\n\nThe `Statamic.$conditions.showField(conditions, data)` helper is available when including the `helpers.js` script:\n\n```html\n<script src=\"/vendor/statamic/frontend/js/helpers.js\"></script>\n```\n\nThe `conditions` parameter accepts your field's conditions, typically generated using `$field->conditions()`.\n\nThe `data` parameter accept's an object containing your form's values, typically stored somewhere within your form's javascript state.\n\nThis JS helper will evaluate your [field conditions](/conditional-fields#overview) in realtime against your form's field values to determine whether or not the field in question should be shown.\n"
  },
  {
    "path": "content/collections/tags/form-errors.md",
    "content": "---\ntitle: \"Form:Errors\"\nid: eff214d5-cd61-4318-b351-14f765bbe4fc\nintro: >\n  If a form submission encounters a validation error, you can use this tag to loop through the error messages and show your user where everything went south.\ndescription: Provides access to form errors.\nparameters:\n  -\n    name: handle|is|in|form|formset\n    type: string\n    description: >\n      Specify the name of the form. Only required if you do _not_ use the `form:set` tag, or don't have a `form` defined in the current context.\nvariables:\n  -\n    name: value\n    type: string\n    description: >\n      This tag contains a primitive array. In each iteration, the `{{ value }}` will output a different error message. See the example above.\n---\n## Example\n\nThis tag can be used both as a conditional and as the data itself.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:set is=\"contact\" }}\n    {{ if {form:errors} }}\n        <p>Oops, here's what went wrong:</p>\n        <ul>\n            {{ form:errors }}\n                <li>{{ value }}</li>\n            {{ /form:errors }}\n        </ul>\n    {{ /if }}\n\n    {{ form:create }}\n        ...\n    {{ /form:create }}\n{{ /form:set }}\n```\n\n:::tip\n`form:errors` is a Tag, not a variable. Be sure to wrap it with single braces when inside a condition.\n:::\n\n::tab blade\n```blade\n<s:form:set\n  is=\"contact\"\n>\n  <s:form:errors\n    as=\"errors\"\n  >\n    @if (count($errors) > 0)\n      <p>Oops, here's what went wrong:</p>\n      <ul>\n        @foreach($errors as $error)\n          <li>{{ $error['value'] }}</li>\n        @endforeach\n      </ul>\n    @endif\n  </s:form:errors>\n\n  <s:form:create>\n    ...\n  </s:form:create>\n</s:form:set>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/form-fields.md",
    "content": "---\ntitle: \"Form:Fields\"\nid: 14d5eff2-1cd6-4318-1b35-67bc514be4ff\nintro: >\n  To be used within the [form:create](/tags/form-create#dynamically-rendering-fields) tag when dynamically rendering fields.\ndescription: Recursive fields loop helper.\nparameters:\n  -\n    name: get\n    type: string\n    description: >\n      Get and render one specific field by its handle.\n  -\n    name: only\n    type: string\n    description: >\n      Loop over only the specified field handles (pipe separated).\n  -\n    name: except\n    type: string\n    description: >\n      Loop over all except the specified field handles (pipe separated).\n---\n## Example\nThis tag can recursively loop over the `fields` array context provided within the [form:create](/tags/form-create#dynamically-rendering-fields) tag pair.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:fields }}\n    <div class=\"p-2\">\n        <label>{{ display }}</label>\n        <div class=\"p-1\">{{ field }}</div>\n        {{ if error }}\n            <p class=\"text-gray-500\">{{ error }}</p>\n        {{ /if }}\n    </div>\n{{ /form:fields }}\n```\n::tab blade\n```blade\n<s:form:fields>\n    <div class=\"p-2\">\n        <label>{{ $field['display'] }}</label>\n        <div class=\"p-1\">{{ $field['field'] }}</div>\n        @if ($field['error'])\n            <p class=\"text-gray-500\">{{ $field['error'] }}</p>\n        @endif\n    </div>\n</s:form:fields>\n```\n::\n\n:::tip\nLearn more about which [fields array variables](/tags/form-create#fields-array-variables) are available to this fields loop!\n:::\n"
  },
  {
    "path": "content/collections/tags/form-set.md",
    "content": "---\ntitle: \"Form:Set\"\nid: 0b9590a7-f8b3-4a11-92b5-60d6d43cf869\ndescription: Wraps other form tags to group them by the same formset.\nintro: This is a \"convenience\" wrapper tag that will set all _other_ form tags to use the same formset.\nparameters:\n  -\n    name: handle|is|in|form|formset\n    type: string\n    description: >\n      Specify the name of the form.\n---\n## Overview\n\nEach `form` tag needs to know which formset it is handling. As a convenience, rather than re-specifying the same formset parameter over and over, we can use an enclosing `{{ form:set }}` tag pair to apply it everywhere, automatically.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:set is=\"contact\" }}\n\n    {{ if {form:errors} }}\n      {{ form:errors }}...{{ /form:errors }}\n    {{ /if }}\n\n    {{ if {form:success} }}...{{ /if }}\n\n    {{ form:create }}...{{ /form:create }}\n\n{{ /form:set }}\n```\n::tab blade\n```blade\n<statamic:form:set\n  is=\"contact\"\n>\n  <statamic:form:errors\n    as=\"errors\"\n  >\n    @foreach ($errors as $error)\n      {{ $error['value'] }}\n    @endforeach\n  </statamic:form:errors>\n  \n  <statamic:form:success>\n    ...\n  </statamic:form:success>\n  \n  <statamic:form:create>\n    ...\n  </statamic:form:create>\n</statamic:form:set>\n```\n::\n\nIn this example, if we didn't use the `form:set` wrapper tag, we would need to add `in=\"contact\"` to each of the\n`form:something` tags.\n"
  },
  {
    "path": "content/collections/tags/form-submission.md",
    "content": "---\ntitle: \"Form:Submission\"\nid: 8720d9ed-3a5f-4c60-af74-7b82933146a2\ndescription: Accesses the data from a successful submission.\nintro: If you want to show the user the data they submitted — whether as a confirmation or to pre-populate or personalize some content — this is the easiest way to do it.\nparameters:\n  -\n    name: handle|is|in|form|formset\n    type: string\n    description: >\n      Specify the name of the form. Only required if you do _not_ use the `form:set` tag, or don't have a `form` defined in the current context.\nvariables:\n  -\n    name: submission data\n    type: array\n    description: >\n      All the fields that were entered in the submission are available.\n---\n## Example\n\nHere we'll output a small thank-you note once there's a successful submission, otherwise show the form itself.\n\n::tabs\n\n::tab antlers\n\nThe `{{ name }}` and `{{ rating }}` variables correspond to input fields of the same name.\n\n```antlers\n{{ form:set is=\"feedback\" }}\n    {{ if {form:success} }}\n\n        {{ form:submission }}\n            Thanks for your feedback, {{ name }}.\n            We appreciate the {{ rating }} star rating you gave us.\n        {{ /form:submission }}\n\n    {{ else }}\n\n        {{ form:create }} ... {{ /form:create }}\n\n    {{ /if }}\n{{ /form:set }}\n```\n::tab blade\n\nThe `{{ $submission['name'] }}` and `{{ $submission['rating'] }}` variables correspond to input fields of the same name.\n\n```blade\n<s:form:set\n  is=\"feedback\"\n>\n  @if (Statamic::tag('form:success')->in('feedback')->fetch())\n    <s:form:submission\n      as=\"submission\"\n    >\n      Thanks for your feedback, {{ $submission['name'] }}.\n      We appreciate the {{ $submission['rating'] }} star rating you gave us.\n    </s:form:submission>\n  @else\n    <s:form:create>\n      ...\n    </s:form:create>\n  @endif\n</s:form:set>\n```\n\n:::tip\nThe `$success` variable is added by the `<s:form:set></s:form:set>` tag pair. It is a shortcut for the following:\n\n```blade\n@if (Statamic::tag('form:success')->in('feedback')->fetch())\n  ...\n@endif\n```\n:::\n\n::\n"
  },
  {
    "path": "content/collections/tags/form-submissions.md",
    "content": "---\ntitle: \"Form:Submissions\"\nid: afa2740e-2cf7-4ada-a92e-4fc92e827351\ndescription: Fetches data from form submissions.\nintro: This is how you fetch data and display data from form submissions. It works very much like the [collection](/tags/collection) tag.\nparameters:\n  -\n    name: handle|is|in|form|formset\n    type: string\n    description: >\n      Specify the name of the form. Only required if you do _not_ use the `form:set` tag, or don't have a `form` defined in the current context.\n  -\n    name: sort\n    type: string\n    description: 'Sort form submissions by field name (or `random`). You may pipe-separate multiple fields for sub-sorting and specify sort direction of each field using a colon. For example, `sort=\"name\"` or `sort=\"date:asc|name:desc\"` to sort by date then by name.'\n    required: false\n  -\n    name: limit\n    type: integer\n    description: 'Limit the total results returned.'\n    required: false\n  -\n    name: filter|query_scope\n    type: string\n    description: \"Apply a custom [query scope](/extending/query-scopes-and-filters) You should specify the query scope's handle, which is usually the name of the class in snake case. For example: `MyAwesomeScope` would be `my_awesome_scope`.\"\n    required: false\n  -\n    name: offset\n    type: integer\n    description: 'Skip a specified number of results.'\n    required: false\n  -\n    name: paginate\n    type: 'boolean|int *false*'\n    description: 'Specify whether your form submissions should be paginated. You can pass `true` and also use the `limit` param, or just pass the limit directly in here.'\n    required: false\n  -\n    name: page_name\n    type: 'string *page*'\n    description: 'The query string variable used to store the page number (ie. `?page=`).'\n    required: false\n  -\n    name: on_each_side\n    type: 'int *3*'\n    description: When using pagination, this specifies the max number of links each side of the current page. The minimum value is `1`.\n  -\n    name: as\n    type: string\n    description: 'Alias your form submissions into a new variable loop.'\n    required: false\n  -\n    name: scope\n    type: string\n    description: 'Scope your form submissions with a variable prefix.'\n    required: false\nvariables:\n  -\n    name: submission data\n    type: mixed\n    description: Each submission has access to all the submitted data.\n  -\n    name: date\n    type: Carbon object\n    description: Along with the submission data, all submissions will contain the date they were submitted.\n  -\n    name: no_results\n    type: boolean\n    description: >\n      `true` if no results.\n  -\n    name: total_results\n    type: integer\n    description: The total number of results, if any.\n---\n\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" }}\n    <div>\n        Submitted on {{ date }}<br>\n        Name: {{ name }}<br>\n        Rating: {{ rating }}<br>\n        Comment: {{ comment }}\n    </div>\n{{ /form:submissions }}\n```\n::tab blade\n```blade\n{{-- Without aliasing. --}}\n<s:form:submissions\n  in=\"feedback\"\n>\n  <div>\n    Submitted on {{ $date }}<br>\n    Name: {{ $name }}<br>\n    Rating: {{ $rating }}<br>\n    Comment: {{ $comment }}\n  </div>\n</s:form:submissions>\n\n{{-- With aliasing --}}\n<s:form:submissions\n  in=\"feedback\"\n  as=\"submissions\"\n>\n  @foreach ($submissions as $submission)\n    <div>\n      Submitted on {{ $submission->date }}<br>\n      Name: {{ $submission->name }}<br>\n      Rating: {{ $submission->rating }}<br>\n      Comment: {{ $submission->comment }}\n    </div>\n  @endforeach\n</s:form:submissions>\n```\n::\n\n## Filtering\n\nThere are a number of ways to filter your submissions. There's the conditions syntax for filtering by fields and the custom filter class if you need extra control.\n\n### Conditions\n\nWant to get entries where the title has the words \"awesome\" and \"thing\", and \"joe\" is the author? You can write it how you'd say it:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" name:contains=\"David\" email:contains=\"gmail.com\" }}\n```\n::tab blade\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  name:contains=\"David\"\n  email:contains=\"gmail.com\"\n>\n\n</s:form:submissions>\n```\n::\n\nThere are a bunch of conditions available to you, like `:is`, `:isnt`, `:contains`, `:starts_with`, and `:is_before`. There are many more than that. In fact, there's a whole page dedicated to [conditions - check them out][conditions].\n\n### Custom Query Scopes\n\nDoing something custom or complicated? You can create [query scopes](/extending/query-scopes-and-filters) to narrow down those results with the `query_scope` or `filter` parameter:\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" query_scope=\"your_query_scope\" }}\n```\n::tab blade\n\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  query_scope=\"your_query_scope\"\n>\n\n</s:form:submissions>\n```\n::\n\nYou should reference the query scope by its handle, which is usually the name of the class in snake case. For example: `YourQueryScope` would be `your_query_scope`.\n\n## Pagination\n\n\nTo enable pagination mode, add the `paginate` parameter with the number of submissions in each page.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" paginate=\"10\" as=\"comments\" }}\n\n    {{ if no_results }}\n        <p>Aww, there are no results.</p>\n    {{ /if }}\n\n    {{ comments }}\n        <article>\n            Feedback from {{ name }}\n        </article>\n    {{ /comments }}\n\n    {{ paginate }}\n        <a href=\"{{ prev_page }}\">⬅ Previous</a>\n\n        {{ current_page }} of {{ total_pages }} pages\n        (There are {{ total_items }} submissions)\n\n        <a href=\"{{ next_page }}\">Next ➡</a>\n    {{ /paginate }}\n\n{{ /form:submissions }}\n```\n::tab blade\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  paginate=\"10\"\n  as=\"comments\"\n>\n  @if ($no_results)\n    <p>Aww, there are now results.</p>\n  @endif\n\n  @foreach ($comments as $comment)\n    <article>\n      Feedback from {{ $comment->name }}\n    </article>\n  @endforeach\n\n  @if ($paginate['total_pages'] > 1)\n    <a href=\"{{ $paginate['prev_page'] }}\">⬅ Previous</a>\n\n    {{ $paginate['current_page'] }} of {{ $paginate['total_pages'] }} pages\n    (There are {{ $paginate['total_items'] }} submissions)\n\n    <a href=\"{{ $paginate['next_page'] }}\">Next ➡</a>\n  @endif\n</s:form:submissions>\n```\n::\n\nIn pagination mode, your submissions will be scoped (in the example, we're scoping them into the `comments` tag pair). Use that tag pair to loop over the entries in that page.\n\nThe `paginate` variable will become available to you. This is an array containing data about the paginated set.\n\n| Variable | Description |\n|----------|-------------|\n| `next_page` |\tThe URL to the next paginated page.\n| `prev_page` |\tThe URL to the previous paginated page.\n| `total_items` | The total number of submissions.\n| `total_pages` |  number of paginated pages.\n| `current_page` | The current paginated page. (ie. the x in the ?page=x param)\n| `auto_links` | Outputs an HTML list of paginated links.\n| `links` |\tContains data for you to construct a custom list of links.\n| `links:all` |\tAn array of all the pages. You can loop over this and output {{ url }} and {{ page }}.\n| `links:segments` | An array of data for you to create a segmented list of links.\n\n<br>\n\n### Pagination Examples\n\nThe `auto_links` tag is designed to be your friend. It'll save you more than a few keystrokes, and even more headaches. It will output an HTML list of links for you. With a large number of pages, it will create segments so that you don't end up with hundreds of numbers.\n\nIt's clever enough to work out a comfortable range of numbers to display, and it'll also throw in the prev/next arrow for good measure.\n\nMaybe the default markup isn't for you and you want total control. You're a maverick. That's cool, we roll that way sometimes too. That's where the `links:all` or `links:segments` array variables come in. These give you all the data you need to recreate your own set of links.\n\n- The `links:all` array is _all_ the pages with `url` and `page` variables.\n\n- The `links:segments` array will contain the segments mentioned above. You'll be able to access `first`, `slider`, and `last`, which are the 3 segments.\n\nHere's the `auto_links` output, recreated using the other tags, for you mavericks out there:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ paginate }}\n    <ul class=\"pagination\">\n        {{ if prev_page }}\n            <li><a href=\"{{ prev_page }}\">&laquo;</a></li>\n        {{ else }}\n            <li class=\"disabled\"><span>&laquo;</span></li>\n        {{ /if }}\n\n        {{ links:segments }}\n\n            {{ first }}\n                {{ if page == current_page }}\n                    <li class=\"active\"><span>{{ page }}</span></li>\n                {{ else }}\n                    <li><a href=\"{{ url }}\">{{ page }}</a></li>\n                {{ /if }}\n            {{ /first }}\n\n            {{ if slider }}\n                <li class=\"disabled\"><span>...</span></li>\n            {{ /if }}\n\n            {{ slider }}\n                {{ if page == current_page }}\n                    <li class=\"active\"><span>{{ page }}</span></li>\n                {{ else }}\n                    <li><a href=\"{{ url }}\">{{ page }}</a></li>\n                {{ /if }}\n            {{ /slider }}\n\n            {{ if slider || (!slider && last) }}\n                <li class=\"disabled\"><span>...</span></li>\n            {{ /if }}\n\n            {{ last }}\n                {{ if page == current_page }}\n                    <li class=\"active\"><span>{{ page }}</span></li>\n                {{ else }}\n                    <li><a href=\"{{ url }}\">{{ page }}</a></li>\n                {{ /if }}\n            {{ /last }}\n\n        {{ /links:segments }}\n\n        {{ if next_page }}\n            <li><a href=\"{{ next_page }}\">&raquo;</a></li>\n        {{ else }}\n            <li class=\"disabled\"><span>&raquo;</span></li>\n        {{ /if }}\n    </ul>\n{{ /paginate }}\n```\n::tab blade\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  paginate=\"10\"\n  as=\"comments\"\n>\n  // ...\n\n  @if ($paginate['total_pages'] > 1)\n    @php\n        $hasSlider = count($paginate['links']['segments']['slider']) > 0;\n        $hasLast = count($paginate['links']['segments']['last']) > 0;\n    @endphp\n\n    <ul class=\"pagination\">\n      @if ($paginate['prev_page'])\n        <li><a href=\"{{ $paginate['prev_page'] }}\">&laquo;</a></li>\n      @else\n        <li class=\"disabled\"><span>&laquo;</span></li>\n      @endif\n\n      @foreach (Arr::get($paginate, 'links.segments.first', []) as $segment)\n        @if ($segment['page'] == $paginate['current_page'])\n          <li class=\"active\"><span>{{ $segment['page'] }}</span></li>\n        @else\n          <li><a href=\"{{ $segment['url'] }}\">{{ $segment['page'] }}</a></li>\n        @endif\n      @endforeach\n\n      @if ($hasSlider)\n        <li class=\"disabled\"><span>...</span></li>\n      @endif\n\n      @foreach (Arr::get($paginate, 'links.segments.slider', []) as $segment)\n        @if ($segment['page'] == $paginate['current_page'])\n          <li class=\"active\"><span>{{ $segment['page'] }}</span></li>\n        @else\n          <li><a href=\"{{ $segment['url'] }}\">{{ $segment['page'] }}</a></li>\n        @endif\n      @endforeach\n\n      @if ($hasSlider || $hasLast)\n        <li class=\"disabled\"><span>...</span></li>\n      @endif\n\n      @foreach (Arr::get($paginate, 'links.segments.last', []) as $segment)\n        @if ($segment['page'] == $paginate['current_page'])\n          <li class=\"active\"><span>{{ $segment['page'] }}</span></li>\n        @else\n          <li><a href=\"{{ $segment['url'] }}\">{{ $segment['page'] }}</a></li>\n        @endif\n      @endforeach\n\n      @if ($paginate['next_page'])\n        <li><a href=\"{{ $paginate['next_page'] }}\">&raquo;</a></li>\n      @else\n        <li class=\"disabled\"><span>&raquo;</span></li>\n      @endif\n    </ul>\n  @endif\n</s:form:submissions>\n```\n::\n\n## Aliasing {#alias}\n\nOften times you'd like to have some extra markup around your list of submissions, but only if there are results. Like a `<ul>` element, for example. You can do this by _aliasing_ the results into a new variable tag pair. This actually creates a copy of your data as a new variable. It goes like this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" as=\"comments\" }}\n    <ul>\n      {{ comments }}\n        <li>\n            Feedback from {{ name }}\n        </li>\n      {{ /comments }}\n    <ul>\n{{ /form:submissions }}\n```\n::tab blade\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  as=\"comments\"\n>\n  <ul>\n    @foreach ($comments as $comment)\n    <li>\n      Feedback from {{ $comment->name }}\n    </li>\n    @endforeach\n  </ul>\n</s:form:submissions>\n```\n::\n\n## Scoping {#scope}\n\nSometimes not all of your submissions have the same set of variables. And sometimes the page that you're on may have those very same variables on the page-level scope. Statamic assumes you'd like to fallback to the parent scope's data to plug any holes. This logic has pros and cons, and you can [read more about scoping and the Cascade here](/cascade).\n\nYou can assign a _scope_ prefix to your submissions so you can be sure to get the data you want. Define your scope and then prefix all of your variables with it.\n\n```yaml\n# Page data\nfeatured_image: /img/totes-adorbs-kitteh.jpg\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" scope=\"comment\" }}\n  <div class=\"block\">\n    {{ comment:name }}\n  </div>\n{{ /form:submissions }}\n```\n::tab blade\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  scope=\"comment\"\n>\n   <div class=\"block\">\n     {{ $comment->name }}\n   </div>   \n</s:form:submissions>\n```\n::\n\nYou can also add your scope down into your [alias](#alias) loop. Yep, we thought of that too.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" as=\"comments\" }}\n  {{ comments scope=\"comment\" }}\n    <div class=\"block\">\n      {{ comment:name }}\n    </div>\n  {{ /comments }}\n{{ /form:submissions }}\n```\n::tab blade\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  as=\"comments\"\n>\n  @foreach ($comments as $comment)\n  <div class=\"block\">\n    {{ $comment->name }}  \n  </div>\n  @endforeach\n</s:form:submissions>\n```\n::\n\nCombining both an Alias and a Scope on the Form Submissions Tag doesn't make a whole lot of sense. You shouldn't do that.\n\n## Grouping\n\nTo group submissions – by date or any other field – you should use [aliasing](#alias) (explained above) as well as the [`group_by`](/modifiers/group_by) modifier.\nThere's no \"grouping\" feature on the submissions tag itself.\n\nFor example, if you want to group some dated submissions by month/year, you could do this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ form:submissions in=\"feedback\" as=\"comments\" }}\n  {{ comments group_by=\"date|F Y\" }}\n    {{ groups }}\n        <h3>{{ group }}</h3>\n        {{ items }}\n            {{ title }} <br>\n        {{ /items }}\n    {{ /groups }}\n  {{ /comments }}\n{{ /form:submissions }}\n```\n::tab blade\n```blade\n<s:form:submissions\n  in=\"feedback\"\n  as=\"comments\"\n>\n  @php\n  $groupedSubmissions = $comments->groupBy(fn($$comment) => $entry->comment?->format('F Y'));\n  @endphp\n\n  @foreach ($groupedSubmissions as $group => $items)\n    <h3>{{ $group }}</h3>\n\n    @foreach ($items as $comment)\n      {{ $comment->title }}\n    @endforeach\n  @endforeach\n</s:form:submissions>\n```\n::\n\n\n[conditions]: /conditions\n"
  },
  {
    "path": "content/collections/tags/form-success.md",
    "content": "---\ntitle: \"Form:Success\"\nid: e7430255-6237-4cc8-96c2-e8338758851f\noverview: Returns true after a successful form submission.\nintro: >\n  This is how you check if a form was successful _outside_ of a [form:create](/tags/form-create) tag.\nparameters:\n  -\n    name: handle|is|in|form|formset\n    type: string\n    description: >\n      Specify the name of the form. Only required if you do _not_ use the `form:set` tag, or don't have a `form` defined in the current context.\n---\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {form:success in=\"contact\"} }}\n    <p>Thanks for filling out the survey! Sorry it was so long.</p>\n{{ /if }}\n```\n\n:::tip\n`form:success` is a Tag, not a variable. Be sure to wrap it with single braces when inside a condition.\n:::\n\n::tab blade\n```blade\n{{-- Using Fluent Tags --}}\n@if (Statamic::tag('form:success')->in('contact')->fetch())\n  <p>Thanks for filling out the survey! Sorry it was so long.</p>\n@endif\n\n{{-- Using Antlers Blade Components --}}\n<s:form:success\n  in=\"contact\"\n>\n  <p>Thanks for filling out the survey! Sorry it was so long.</p>\n</s:form:success>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/form.md",
    "content": "---\ntitle: Form\nis_parent_tag: true\ndescription: Handles the frontend side of your forms.\nintro: Statamic forms serve to collect, report, and reuse user submitted data. There are a number of different tags available, depending what you need to do.\nid: e4f4f91e-a442-4e15-9e16-3b9880a25522\n---\n## Form Tags\n- [form:create](/tags/form-create)\n- [form:errors](/tags/form-errors)\n- [form:set](/tags/form-set)\n- [form:submission](/tags/form-submission)\n- [form:submissions](/tags/form-submissions)\n"
  },
  {
    "path": "content/collections/tags/get_content.md",
    "content": "---\ntitle: Get Content\ndescription: Fetches content by URL or ID\nintro: |\n  It gets content from other entries! Specify a URI or ID and fetch all the data attached to it.\nparameters:\n  -\n    name: from\n    type: string\n    description: Pass a URI (e.g. `/about`), an ID (e.g. `123`), a pipe delimited list of them (e.g. `123|456`), or a reference to a variable containing them (e.g. `:from=\"ids\"`), and all retrieved data will be available inside the tag pair.\n  -\n    name: site|locale\n    type: string\n    description: Show the retrieved content in the selected site.\nid: a33dd9d3-f2f0-4114-b19d-1126361c327e\n---\n## Overview\n\nThis tag lets you fetch data from other entries. It's useful if you need to hard-code some dynamic content in your template.\n\n:::tip\nIf you're using a fieldtype like [entries](/fieldtypes/entries) to select which entries you'd like to render, then you don't even need this tag. You can loop over the selections like this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ your_entries_field }}\n  {{ title }}, {{ url }}, {{ whatever }}\n{{ /your_entries_field }}\n```\n::tab blade\n```blade\n@foreach ($your_entries_field as $entry)\n  {{ $entry->title }}, {{ $entry->url }}, {{ $entry->whatever }}\n@endforeach\n```\n::\n:::\n\nFor example, you might want to output some company information from your home page:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_content from=\"/about/company\" }}\n  {{ staff }}\n    <div class=\"w-1/3\">\n      <img src=\"{{ headshot }}\" alt=\"{{ name }}\">\n      <p>{{ name }}, {{ job_title }}</p>\n    </div>\n  {{ /staff }}\n{{ /get_content }}\n```\n::tab blade\n```blade\n<s:get_content from=\"/about/company\">\n  @foreach ($staff as $member)\n    <div class=\"w-1/3\">\n      <img src=\"{{ $member->headshot }}\" alt=\"{{ $member->name }}\">\n      <p>{{ $member->name }}, {{ $member->job_title }}</p>\n    </div>\n  @endforeach\n</s:get_content>\n```\n::\n\n\n## Shorthand\n\nYou may also use a shorthand syntax, where the second tag argument refers to a variable that contains a URI or ID.\n\nThe data:\n\n```yaml\nrelated_by_uri: /about\nrelated_by_id: 123-321-abc-defg123\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_content:related_by_uri }}\n  {{ title }}\n{{ /get_content:related_by_uri }}\n\n{{ get_content:related_by_id }}\n  {{ title }}\n{{ /get_content:related_by_id }}\n```\n::tab blade\n```blade\n<s:get_content:related_by_uri>\n  {{ $title }}\n</s:get_content:related_by_uri>\n\n<s:get_content:related_by_id>\n  {{ $title }}\n</s:get_content:related_by_id>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/get_error.md",
    "content": "---\nid: e7f25633-f573-48f4-9f7c-6097c3681100\nblueprint: tag\ntitle: 'Get Error'\nintro: 'The singular version of the \"Get Errors\" tag'\ndescription: 'Gets a validation error'\n---\nSee the [Get Errors](/tags/get_errors) tag.\n"
  },
  {
    "path": "content/collections/tags/get_errors.md",
    "content": "---\nid: e8956d9b-bba6-4270-b410-cba1046435bd\nblueprint: tag\ntitle: Get Errors\nintro: 'This tag allows you to interact with a Laravel `ViewErrorBag` object to output validation errors.'\ndescription: 'Gets validation errors'\nparameters:\n  -\n    name: bag\n    type: string\n    description: 'The error bag. Defaults to `default`. You may have differently named bags if you have multiple forms on a page.'\n---\n## List all validation errors\n\nThe most common use case is to list all the validation errors. You can do this with the `all` tag.\n\nIf there are no errors, the tag will output nothing at all, so you can put your wrapping html around the inner `messages` tag pair.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_errors:all }}\n<div class=\"errors\">\n  <p>Oops, something went wrong!</p>\n  <ul>\n    {{ messages }}\n      <li>{{ message }}</li>\n    {{ /messages }}\n  </ul>\n</div>\n{{ /get_errors:all }}\n```\n::tab blade\n```blade\n<s:get_errors:all>\n<div class=\"errors\">\n  <p>Oops, something went wrong!</p>\n  <ul>\n    @foreach ($messages as $message)\n      <li>{{ $message['message'] }}</li>\n    @endforeach\n  </ul>\n</div>\n</s:get_errors:all>\n```\n::\n\n## List errors for a specific field\n\nYou can replace `all` with a field's handle to get errors for just that field.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_errors:fieldname }}\n<div class=\"errors\">\n  <p>Oops, something went wrong!</p>\n  <ul>\n    {{ messages }}\n      <li>{{ message }}</li>\n    {{ /messages }}\n  </ul>\n</div>\n{{ /get_errors:fieldname }}\n```\n::tab blade\n```blade\n<s:get_errors:fieldname>\n  <p>Oops, something went wrong!</p>\n  <ul>\n    @foreach ($messages as $message)\n      <li>{{ $message['message'] }}</li>\n    @endforeach\n  </ul>\n</s:get_errors:fieldname>\n```\n::\n\n## Get the first error for a specific field\nUseful for outputting inline errors, you can use the `get_error` tag.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_error:fieldname }}\n    <div class=\"inline-error\">{{ message }}</div>\n{{ /get_error:fieldname }}\n```\n::tab blade\n```blade\n<s:get_error:fieldname>\n  <div class=\"inline-error\">{{ $message }}</div>\n</s:get_error:fieldname>\n```\n::\n\n:::tip Note\nThat's `get_error` (singular), not `get_errors`.\n:::\n\n\n## Getting all errors, grouped by field\n\nUsing the standalone tag, you can loop through fields and then their errors.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_errors }}\n  <div class=\"errors\">\n    Oops!\n    {{ fields }}\n      <p>{{ field }}</p>\n      <ul>\n        {{ messages }}\n          <li>{{ message }}</li>\n        {{ /messages }}\n      </ul>\n    {{ /fields }}\n  </div>\n{{ /get_errors }}\n```\n::tab blade\n```blade\n<s:get_errors>\n  <div class=\"errors\">\n    Oops!\n    @foreach ($fields as $field)\n      <p>{{ $field['field'] }}</p>\n      <ul>\n        @foreach ($field['messages'] as $message)\n          <li>{{ $message['message'] }}</li>\n        @endforeach\n      </ul>\n    @endforeach\n  </div>\n</s:get_errors>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/get_files.md",
    "content": "---\ntitle: Get Files\ndescription: Retrieves and filters local files\nintro: The ultimate 🇨🇭Swiss Army Knife doing-stuff-with-files feature. With the `get_files` tag you can scan and display data on files in _any_ directories inside your local filesystem.\nparameters:\n  -\n    name: in|from\n    type: string\n    description: >\n      The directory to find files in relative to the `public/` directory.\n      Example: `in=\"img/brand\"`\n  -\n    name: depth\n    type: integer\n    description: >\n      The depth of subdirectories to recursively look through. Default: `1` (no recursion).\n  -\n    name: not_in\n    type: string\n    description: >\n      Filter by excluding from a subdirectory or subdirectories. You may use regex, and will be matched against the file path without a leading slash. For example: `not_in=\"img/(brand|logos)\"`\n  -\n    name: file_size\n    type: string\n    description: >\n      Filter by file size using one of the following comparison operators. >, >=, <, <=, ==, !=.\n      For example: `file_size=\"< 500K\"`\n  -\n    name: ext|extension\n    type: string\n    description: >\n      Filter by file extension. You may pipe delimit multiple extensions. Example: `ext=\"jpg|png\"`.\n  -\n    name: include|match\n    type: regex\n    description: >\n      Filter files by a regular expression. Matches will be **kept**.\n  -\n    name: exclude\n    type: regex\n    description: >\n      Exclude files by a regular expression. Matches will be **removed**.\n  -\n    name: file_date\n    type: string\n    description: >\n      Filter by last modified dates. The target value can be any date supported by PHP’s [strtotime](http://www.php.net/manual/en/datetime.formats.php) function.\n  -\n    name: limit\n    type: integer\n    description: Limit the total results returned.\n  -\n    name: offset\n    type: integer\n    description: The number of entries the results should by offset by.\n  -\n    name: sort\n    type: string\n    description: Sort the listing by `type` (file extension), `size`, `last_modified`, or `random`.\nvariables:\n  -\n    name: file\n    type: string\n    description: The filename path relative to your project root.\n  -\n    name: filename\n    type: string\n    description: The filename part of the path. The `neon` in `path/to/neon.jpg`\n  -\n    name: basename\n    type: string\n    description: The basename part of the path. The `neon.jpg` in `path/to/neon.jpg`\n  -\n    name: extension\n    type: string\n    description: |\n      The file extension. Example: `jpg` or `zip`.\n  -\n    name: size\n    type: string\n    description: |\n      The file size in a human readable format. Example: `1.24 MB`.\n  -\n    name: size_bytes|size_b\n    type: integer\n    description: The file size in bytes.\n  -\n    name: size_kilobytes|size_kb\n    type: integer\n    description: The file size in kilobytes.\n  -\n    name: size_megabytes|size_mb\n    type: integer\n    description: The file size in megabytes.\n  -\n    name: size_gigabytes|size_gb\n    type: integer\n    description: The file size in gigabytes.\n  -\n    name: is_image\n    type: boolean\n    description: Whether the file is an image.\n  -\n    name: last_modified\n    type: Carbon\n    description: The last modified date of the file.\nid: c7f3def7-8db8-4e6a-b14f-b2981b2333a5\n---\n## Overview\n\nWhat you do with this tag is up to you. It's that odd, multi-tool you don't have a use for..._until you need it desperately_. If you need to do stuff with listing files and their meta data, this might be the thing you're looking for.\n\nDon't forget about [assets](/assets) though, they're a much more robust and controlled aspect of Statamic.\n\n## Example\n\nHere's a few examples of what you can do with the `get_files` tag.\n\n### List non-asset images in the site's design resources\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_files in=\"public/img/brand\" }}\n  <img src=\"{{ file }}\" class=\"w-1/3\">\n{{ /get_files }}\n```\n::tab blade\n```blade\n<s:get_files in=\"public/img/brand\">\n  <img src=\"{{ $file }}\" class=\"w-1/3\">\n</s:get_files>\n```\n::\n\n### List the zip files in a web-inaccessible directory\n\n::tabs\n\n::tab antlers\n```antlers\n<table>\n  <thead>\n    <tr>\n      <th>File</th>\n      <th>Size</th>\n      <th>Last Modified</th>\n    </tr>\n  </thead>\n  <tbody>\n  {{ get_files in=\"secure/downloads\" }}\n    <tr>\n      <td>{{ basename }}</td>\n      <td>{{ size }}</td>\n      <td>{{ last_modified }}</td>\n    </tr>\n  {{ /get_files }}\n  </tbody>\n</table>\n```\n::tab blade\n```blade\n<table>\n  <thead>\n    <tr>\n      <th>File</th>\n      <th>Size</th>\n      <th>Last Modified</th>\n    </tr>\n  </thead>\n  <tbody>\n  <s:get_files in=\"secure/downloads\">\n    <tr>\n      <td>{{ $basename }}</td>\n      <td>{{ $size }}</td>\n      <td>{{ $last_modified }}</td>\n    </tr>\n  </s:get_files>\n  </tbody>\n</table>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/get_site.md",
    "content": "---\nid: 6761887c-a062-488f-a43e-87b22326215f\nblueprint: tag\ntitle: 'Get Site'\nintro: \"It gets a site's config, given it's handle.\"\ndescription: \"Fetches a site's config, given it's handle.\"\n---\n## Overview\n\nThis tag lets you get a site's config. It's useful if you need to display information, like site names or URLs, outside of the context of that site.\n\nFor example, you might want to output a site's name & logo in your footer:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_site:english }}\n    <a href=\"{{ permalink }}\">\n        Go to {{ name }}\n    </a>\n{{ /get_site:english }}\n```\n::tab blade\n```blade\n<s:get_site:english>\n  <a href=\"{{ $permalink }}\">\n    Go to {{ $name }}\n  </a>\n</s:get_site:english>\n```\n\nYou can also alias the result, if you need to:\n\n```blade\n<s:get_site:english as=\"the_site\">\n  {{ $the_site->name }}\n</s:get_site:english>\n```\n::\n\nIf you need to, you can pass the site handle dynamically:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_site :handle=\"another_sites_handle\" }}\n    <!-- ... -->\n{{ /get_site }}\n```\n::tab blade\n```blade\n<s:get_site :handle=\"$another_sites_handle\">\n  <!-- ... -->\n</s:get_site>\n```\n::\n\nYou can also use it as a single tag:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get_site:english:permalink }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:get_site:english:permalink />\n\n{{-- Using Fluent Tags --}}\n{{ Statamic::tag('get_site:english:permalink') }}\n```\n::\n"
  },
  {
    "path": "content/collections/tags/glide-batch.md",
    "content": "---\ntitle: \"Glide:Batch\"\ndescription: Manipulates a whole batch of `<img>` tags\nintro: Use `glide:batch` to manipulate a whole batch of `<img>` tags with [Glide](/tags/glide).\nparameters:\n  -\n    name: glide parameters\n    type: mixed\n    description: |\n      All of the manipulation parameters listed on the [Glide tag](/tags/glide#parameters).\nid: 5173c6fb-8c28-4cb1-9d2e-b7c902f96308\n---\n## Overview\n\nWraps content containing `<img>` tags and each will be manipulated with your desired Glide parameters.\n\nThis tag is useful when uniformly resizing all images inside a chunk of HTML or the contents of Markdown or other fields. If you wrap the tag around Antlers variables, be sure to use **page scope:** `{{ page:content }}` instead of `{{ content }}`.\n\n## Example\n\n``` markdown\nI went exploring today and here are some photos I took and I was too lazy to use an Asset fieldtype so here they all are plop ok\n\n![Bears](/images/bears.jpg)\n![Beets](/images/beets.jpg)\n![Battlestar](/images/galactica.jpg)\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide:batch width=\"600\" height=\"400\" fit=\"crop\" }}\n  {{ page:content }}\n{{ /glide:batch }}\n```\n::tab blade\n```blade\n<s:glide width=\"600\" height=\"400\" fit=\"crop\">\n  {!! $page->content !!}\n</s:glide>\n```\n::\n\n```html\n<p>I went exploring today and here are some photos I took and I was too lazy to use an Asset fieldtype so here they all are plop ok</p>\n\n<img src=\"/img/assets/bears.jpg?w=600&h=400&fit=crop\" title=\"Bears\" />\n<img src=\"/img/assets/beats.jpg?w=600&h=400&fit=crop\" title=\"Beats\" />\n<img src=\"/img/assets/galactica.jpg?w=600&h=400&fit=crop\" title=\"Battlestar\" />\n```\n"
  },
  {
    "path": "content/collections/tags/glide-data-url.md",
    "content": "---\ntitle: \"Glide:Data_URL\"\ndescription: Manipulates an image and returns a data URL\nintro: Use `glide:data_url` to output a manipulated image as a Data URL.\nparameters:\n  -\n    name: src|path|id\n    type: string\n    description: >\n      The URL of the image when _not_ using the shorthand syntax. (Use the shorthand syntax if you can, it's nicer.)\n      This also accepts asset IDs, if using [private assets](/assets#private-assets), for example.\n  -\n    name: tag\n    type: boolean *false*\n    description: When set to true, this will output an `<img>` tag with the URL in the `src` attribute, rather than just the URL.\n  -\n    name: alt\n    type: string\n    description: When using the `tag` parameter, this will insert the given text into the `alt` attribute.\n  -\n    name: preset\n    type: string\n    description: >\n      Use a preset of pre-configured parameters. [Learn more](#presets).\nid: 41698c83-5ca0-43eb-88ad-96343dea12ef\n---\n## Overview\n\nThe `{{ glide:data_url }}` tag works in a very similar way to the standard [Glide tag](/tags/glide), except it outputs the manipulated as a [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs).\n\n## Example\n\nYou may pass any parameters you'd [normally pass to the Glide tag](/tags/glide#parameters), like `width`, `height` and `format`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide:data_url src=\"image.jpg\" width=\"1280\" height=\"800\" format=\"webp\" }}\n```\n::tab blade\n```blade\n{{\n  Statamic::tag('glide:data_url')\n    ->src('image.jpg')\n    ->width(1280)\n    ->height(800)\n    ->format('webp')\n}}\n```\n::\n\n```output\ndata:image/webp;base64,R0lGODlhAgACAJEAALwWrOQqhOwqhP///yH5BAEAAAMALAAAAAACAAIAAAIDBBIFADs=\n```\n"
  },
  {
    "path": "content/collections/tags/glide.md",
    "content": "---\ntitle: Glide\ndescription: Manipulates images on the fly\nintro: The Glide tag makes it easy to manipulate images on the fly – from resizing and cropping to adjustments (like sharpness and contrast) and image effects (like pixelate and sepia).\ntemplate: tags.glide\nblueprint: tag-glide\nparameters:\n  -\n    name: src|path|id\n    type: string\n    description: >\n      The URL of the image when _not_ using the shorthand syntax. (Use the shorthand syntax if you can, it's nicer.)\n      This also accepts asset IDs, if using [private assets](/assets#private-assets), for example.\n  -\n    name: field\n    type: tag part\n    required: true\n    description: 'The name of the field containing the asset ID or image path when using the shorthand syntax. This is not actually a parameter, but part of the tag itself. For example, `{{ glide:hero_image }}`.'\n  -\n    name: tag\n    type: boolean *false*\n    description: When set to true, this will output an `<img>` tag with the URL in the `src` attribute, rather than just the URL.\n  -\n    name: alt\n    type: string\n    description: When using the `tag` parameter, this will insert the given text into the `alt` attribute.\n  -\n    name: absolute\n    type: 'boolean *false*'\n    description: >\n      When set to `true`, this tag will output the full URL rather than the default relative URL.\n  -\n    name: preset\n    type: string\n    description: >\n      Use a preset of pre-configured parameters. [Learn more](#presets).\nshape:\n  -\n    name: width\n    type: integer\n    description: >\n      Sets the width of the image, in pixels.\n  -\n    name: height\n    type: integer\n    description: >\n      Sets the height of the image, in pixels.\n  -\n    name: square\n    type: integer\n    description: >\n      A shortcut for setting `width` and `height` to the same value. For example `square=\"250\"` is a shortcut for `width=\"250\" height=\"250\"`.\n  -\n    name: fit\n    type: string\n    description: >\n      See the [Glide docs](https://glide.thephpleague.com/3.0/api/crop/#fit-fitcrop-x-y---crop-based-on-focal-point) on this parameter. In addition to the\n      Glide's fit options, Statamic also supports `crop_focal` to automatically fit/crop to a predefined focal point.\n      See the [_Focal Crop_](#focal-point-cropping) section for more details.\n  -\n    name: crop\n    type: string\n    description: >\n      Crops the image to specific dimensions prior to any other resize operations. Required format: `width,height,x,y`.\n  -\n    name: orient\n    type: mixed\n    description: >\n      Rotates the image. Accepts `auto`, `0`, `90`, `180` or `270`. The `auto` option uses Exif data to automatically orient images correctly. Default: `auto`.\n  -\n    name: flip\n    type: string\n    description: 'Flips the image. Accepts `v`, `h` and `both`.'\n  -\n    name: quality\n    type: integer\n    description: >\n      Defines the quality of the image. Use values between `0` and `100`. Only relevant if the format is `jpg`, `pjpg` or `webp`. Default: `90`.\n  -\n    name: dpr\n    type: integer\n    description: >\n      Defines the device pixel ratio. This makes it possible to display images at the correct pixel density on a variety of devices. For example the following would output an image of 400px `{{ glide:image width=\"200\" dpr=\"2\" square }}`. Default: `1`. The maximum value that can be set for dpr is `8`.\n  -\n    name: format\n    type: string\n    description: >\n      Encodes the image to a specific format. Accepts `jpg`, `pjpg` (progressive jpeg), `png`, `gif`, `webp` or `avif`. If using the imagick image manipulation driver, glide can additionally handle `tif`, `bmp` and `psd`. The default format: `jpg`\n  -\n    name: border\n    type: string\n    description: >\n      Adds a border to the image. Required format: `width,color,method` (comma-separated).\n      Width is in pixels or a relative dimension; color follows Glide's [color formats](https://glide.thephpleague.com/3.0/api/colors/) (defaults to `ffffff` if omitted);\n      method is `overlay` (default), `shrink`, or `expand`. [See Glide border docs](https://glide.thephpleague.com/3.0/api/border/).\n\nfilters:\n  -\n    name: bg\n    type: string\n    description: >\n      Sets a background color for transparent images.\n  -\n    name: blur\n    type: integer\n    description: >\n      Adds a blur effect to the image. Use values between `0` and `100`.\n  -\n    name: brightness\n    type: string\n    description: >\n      Adjusts the image brightness. Use values between `-100` and `+100`, where `0` represents no change.\n  -\n    name: contrast\n    type: string\n    description: >\n        Adjusts the image contrast. Use values between `-100` and `+100`, where `0` represents no change.\n  -\n    name: gamma\n    type: float\n    description: >\n      Adjusts the image gamma. Use values between `0.1` and `9.99`.\n  -\n    name: sharpen\n    type: integer\n    description: >\n      Sharpen the image. Use values between `0` and `100`.\n  -\n    name: pixelate\n    type: integer\n    description: >\n      Applies a pixelation effect to the image. Use values between `0` and `100`.\n  -\n    name: filter\n    type: string\n    description: >\n      Applies a filter effect to the image. Accepts `greyscale` or `sepia`.\nother:\n  -\n    name: preset\n    type: string\n    description: >\n      Applies a [preset manipulation](/image-manipulation#presets) as defined in the config.\n  -\n    name: mark\n    type: string\n    description: >\n      The source of a [watermark](#watermarks).\n  -\n    name: markw\n    type: string\n    description: The width of the watermark.\n  -\n    name: markh\n    type: string\n    description: The height of the watermark.\n  -\n    name: markfit\n    type: string\n    description: The fit of the watermark. [See Glide docs](https://glide.thephpleague.com/3.0/api/watermarks/#fit-markfit)\n  -\n    name: markx\n    type: string\n    description: How far the watermark is away from the left and right edges.\n  -\n    name: marky\n    type: string\n    description: How far the watermark is away from the top and bottom edges.\n  -\n    name: markpad\n    type: string\n    description: How far the watermark is away from all edges. A shortcut for using markx and marky.\n  -\n    name: markpos\n    type: string\n    description: Sets where the watermark is positioned. Accepts `top-left`, `top`, `top-right`, `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right`. Default is `bottom-right`.\n  -\n    name: markalpha\n    type: integer\n    description: >\n      Sets the opacity of the watermark. Use values between `0` and `100`, where `100` is fully opaque and `0` is fully transparent. Default: `100`. [See Glide docs](https://glide.thephpleague.com/3.0/api/watermarks/#alpha-markalpha).\nvariables:\n  -\n    name: url\n    type: string\n    description: The URL of the resized image.\n  -\n    name: width\n    type: integer\n    description: The width of the resized image.\n  -\n    name: height\n    type: integer\n    description: The height of the resized image.\n  -\n    name: asset data\n    type: mixed\n    description: If your source was an asset, you will also have all of its fields available. e.g. `alt`\nid: b70a3d9a-6605-446e-b278-de99ba561fe0\n---\n## Setting up Glide\nGlide is ready to go out of the box with no setup required. However, you may customize its behavior. You can read about it on the [image manipulation](/image-manipulation) page.\n\n## Basic Usage\n\nManipulate images by passing an image [source](#sources) and adding the desired [parameters](#parameters) like `height`, `crop`, or `quality`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide src=\"image.jpg\" width=\"1280\" height=\"800\" }}\n```\n::tab blade\n```blade\n<s:glide src=\"image.jpg\" width=\"1280\" height=\"800\" />\n```\n::\n\n\n```output\n/img/image.jpg?w=1280&h=800\n```\n\nThe Glide tag outputs a URL of the transformed image, so you'll likely want to use it as the `src` for an `<img />` HTML tag.\n\n::tabs\n\n::tab antlers\n```antlers\n<img src=\"{{ glide ... }}\" />\n```\n::tab blade\n```blade\n{{-- Using fluent tags --}}\n<img\n  src=\"{{ Statamic::tag('glide')->src('test.png')->width(1280)->height(800)->fetch() }}\"\n/>\n\n{{-- Using Antlers Blade Components tag pair --}}\n<s:glide src=\"test.png\" width=\"1280\" height=\"800\">\n  <img src=\"{{ $url }}\" />\n</s:glide>\n```\n::\n\n## Sources\n\nThere are a number of options when it comes to what you can use as an image source.\n\n### Asset\nYou can pass along an asset object by using an asset field by reference:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide :src=\"asset_field\" w=\"100\" }} // /img/asset/6f75d8as?w=100\n```\n::tab blade\n```blade\n<s:glide :src=\"$asset_field\" w=\"100\" /> // /img/asset/6f75d8as?w=100\n```\n::\n\n### Relative path/URL\nYou can pass a string of a path located within your `public` directory.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide src=\"image.jpg\" w=\"100\" }} // img/image.jpg?w=100\n```\n::tab blade\n```blade\n<s:glide src=\"image.jpg\" w=\"100\" /> // img/image.jpg?w=100\n```\n::\n\n### External URL\nYou can pass a string of a URL on another site.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide src=\"http://anothersite.com/image.jpg\" w=\"100\" }} // /img/http/6f75d8as?w=100\n```\n::tab blade\n```blade\n<s:glide src=\"http://anothersite.com/image.jpg\" w=\"100\" /> // /img/http/6f75d8as?w=100\n```\n::\n\n\n## Tag Pair\n\nIf you use the tag as a pair, you'll have access to `url`, `height`, and `width` variables inside to do with as you wish.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide src=\"image.jpg\" width=\"600\" }}\n    <img src=\"{{ url }}\" width=\"{{ width }}\" height=\"{{ height }}\">\n{{ /glide }}\n```\n::tab blade\n```blade\n<s:glide src=\"image.jpg\" width=\"600\">\n  <img src=\"{{ $url }}\" width=\"{{ $width }}\" height=\"{{ $height }}\">\n</s:glide>\n```\n::\n\nYou may also use the tag pair to loop over multiple sources. For example, you may provide it with an `assets` field.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide :src=\"multiple_assets\" width=\"600\" }}\n   ...\n{{ /glide }}\n```\n::tab blade\n```blade\n<s:glide :src=\"multiple_assets\" width=\"600\">\n  {{ $url }}\n</s:glide>\n\n{{-- Aliasing the results --}}\n<s:glide :src=\"$multiple_assets\" width=\"600\" as=\"assets\">\n  @foreach ($assets as $asset)\n    {{ $asset['url'] }}\n  @endforeach\n</s:glide>\n```\n::\n\n:::tip\nThe tag pair is also available as `{{ glide:generate }}`. You may need to use this version if you're using [Blade](#usage-in-blade).\n:::\n\n:::tip\nNormally, the Glide tag only generates a URL. The image itself is generated when the URL is visited. When using the tag pair, your Glide images will be generated when the page is rendered. This will result in an initial longer load time.\n:::\n\n\n## Shorthand Tag\n\nRather than using the `src` parameter, you may choose to use a variable name as the second part of the tag.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide:my_var w=\"100\" }}\n```\n::tab blade\n```blade\n<s:glide:my_var w=\"100\" />\n```\n::\n\nYou may also use the shorthand as a tag pair:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide:my_var }} ... {{ /glide:my_var }}\n```\n::tab blade\n```blade\n<s:glide:my_var>\n  ...\n</s:glide:my_var>\n```\n::\n\n\n## Watermarks\n\nYou may use Glide's [watermarking feature](https://glide.thephpleague.com/3.0/api/watermarks/) by passing in a [source](#sources) to the `mark` parameter, and then manipulate it using the various watermark parameters (`markw`, `markh`, `markfit`, `markalpha`, etc).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ glide src=\"image.jpg\" mark=\"watermark.jpg\" markw=\"30\" }}\n```\n::tab blade\n```blade\n<s:glide src=\"image.jpg\" mark=\"watermark.jpg\" markw=\"30\" />\n```\n::\n\n:::tip\nYou don't need to worry about setting up a watermark filesystem yourself. Statamic will take care of that automatically based on the source you provide.\n:::\n\n## Usage in Blade\n\nTo use the Glide tag within Blade, you can also use the `generate` tag, which follows the same rules as the [tag pair](#tag-pair).\n\n```blade\n@foreach (Statamic::tag('glide:generate')->src($source)->width(100) as $image)\n  <img src=\"{{ $image['url'] }}\" width=\"{{ $image['width'] }}\" />\n@endforeach\n```\n\nYou should use a `@foreach` loop even if you are only providing a single source.\n\n## Focal Point Cropping\n\nFocal point cropping helps ensure the important bits of an image stay in the bounds of any crops you perform.\n\n### Using the focal point picker\n\nYou can set focal points and zoom-levels for your images in the control panel using the asset editor. Use `fit=\"crop_focal\"` while cropping to use an asset's saved focal point, if it has one.\n\n<figure>\n  <img src=\"/img/focal-point-picker-v6.webp\" alt=\"The Focal Point Picker\" class=\"u-hide-in-dark-mode\">\n  <img src=\"/img/focal-point-picker-v6-dark.webp\" alt=\"The Focal Point Picker\" class=\"u-hide-in-light-mode\">\n  <figcaption>The focal point picker. Make sure to keep those eyes in the shot! They're delightfully menacing.</figcaption>\n</figure>\n\n### Manually setting focal points\n\nWhen setting focal points manually, specify two offset percentages: `crop-x%-y%`.\n\nFor example, `fit=\"crop-75-50\"` would crop the image and make sure that the point at 75% across and 50% down would be the focal point.\n\nIf an asset doesn't have a focal point set it will simply crop from the center.\n\n_Note: All Glide generated images are cropped at their focal point, unless you disable the _Auto Crop_ setting. This happens even when you don't specify a `fit` parameter. You may override this behavior per-image/tag by specifying the `fit` parameter as described above._\n\n``` php\n// config/statamic/assets.php\n\n'auto_crop' => true,\n```\n\n\n### focus_css\n\nYou may use the [`focus_css`](/variables/focus_css) variable to get an asset's focal point in a format suitable for the `background-position` CSS property. \n\n::tabs\n\n::tab antlers\n```antlers\n{{ focus_css }}\n```\n::tab blade\n```blade\n{{ $focus_css }}\n```\n::\n\n```html\n50% 30%\n```\n\n## Unsupported formats\n\nGlide will resize whatever images it supports, like jpgs or pngs. If you pass an unsupported type to Glide, like an svg, it'll just return the unmodified URL.\n\n``` yaml\nimages:\n  - image.jpg\n  - image.svg\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ images }}\n  <img src=\"{{ glide:url width=\"600\" }}\" />\n{{ /images }}\n```\n::tab blade\n```blade\n@foreach ($images as $image)\n  <img\n    src=\"{{ Statamic::tag('glide')->src($image['url'])->width(600)->fetch() }}\"\n  />\n@endforeach\n```\n::\n\n```html\n<img src=\"/img/image.jpg?w=600\" />\n<img src=\"/assets/image.svg\" />\n```\n"
  },
  {
    "path": "content/collections/tags/increment.md",
    "content": "---\ntitle: Increment\ndescription: \"Creates incrementing indexes inside loops\"\nintro: \"Each time an increment tag is parsed, an index is incremented by one and displayed.\"\nparameters:\n  -\n    name: from\n    type: integer\n    description: |\n      The number to start incrementing from. Default: `0`;\n  -\n    name: by\n    type: integer\n    description: |\n      The size to increment by. Default: `1`.\nid: b33aa176-06e6-411d-a4b7-0a514f697d78\n---\n## Overview\n\nMost loops already have an `index` variable that will display which iteration the loop is on. However, there are cases were you may need to start another counter, begin counting from a particular number, or increment by a step size other than `1`. For those reasons, this tag exists.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ items }}\n  {{ increment }}\n{{ /items }}\n```\n::tab blade\n```blade\n@foreach ($items as $item)\n  <s:increment />\n\n  -- or --\n  \n  {{ Statamic::tag('increment') }}\n@endforeach\n```\n::\n\n```html\n0 1 2 3 4 5\n```\n\n:::tip\nA counter will only be incremented if its parsed. You can wrap it inside an `if` condition if you want it to be conditionally incremented.\n:::\n\n## Multiple Counters\n\nYou can have multiple counters going at once in your view by giving each a unique name as the second tag argument.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ items }}\n  {{ increment:again }}\n{{ /items }}\n```\n::tab blade\n```blade\n@foreach ($items as $item)\n  <s:increment:again />\n@endforeach\n```\n::\n\n## Customize the Counters\n\nCustomize the counts using the `from` and `by` parameters.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ items }}\n  {{ increment from=\"0\" by=\"200\" }}\n{{ /items }}\n```\n::tab blade\n```blade\n@foreach ($items as $item)\n  <s:increment from=\"0\" by-\"200\" />\n@endforeach\n```\n::\n\n```html\n0 200 400 600 800 100\n```\n"
  },
  {
    "path": "content/collections/tags/installed.md",
    "content": "---\ntitle: Installed\nintro: Determine whether or not a Composer package is installed.\ndescription: Determine whether or not a Composer package is installed.\nid: d2d3c660-c7be-11eb-9345-0800200c9a66\n---\nA common use case for this tag is if you are building a reusable site (like a Starter Kit) and you'd like\nto do something differently depending on whether a package is installed.\n\nUse this as a single tag within an `if` statement:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {installed:statamic/seo-pro} }}\n    {{ seo_pro:meta }}\n{{ else }}\n    {{ partial:seo }}\n{{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::tag('installed:statamic/seo-pro')->fetch())\n  <s:seo_pro:meta />\n@else\n  <s:partial:seo />\n@endif\n```\n::\n\nOr as a tag pair. If the package doesn't exist, then nothing between the tag will be output:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ installed:statamic/seo-pro }}\n    {{ seo_pro:meta }}\n{{ /installed:statamic/seo-pro }}\n```\n::tab blade\n```blade\n<s:installed:statamic/seo-pro>\n  <s:seo_pro:meta />\n</s:installed:statamic/seo-pro>\n```\n::\n\n:::tip\nYou can pass any Composer package name into this tag. It's not limited to Statamic addons.\n:::\n"
  },
  {
    "path": "content/collections/tags/link.md",
    "content": "---\ntitle: Link\ndescription: 'Generates URLs'\nintro:  Generate fully qualified URLs with the option to include your domain.\nparameters:\n  -\n    name: to|src\n    type: string\n    description: |\n      Generate a URL to this relative path string.\n  -\n    name: absolute\n    type: boolean\n    description: |\n      Make the URL absolute if it isn't already. Default: `false`.\n  -\n    name: id\n    type: string\n    description: |\n      ID of the entry to link to.\n  -\n    name: in\n    type: string\n    description: |\n      Handle of the site you want to link to (only when using Multi-Site).\nid: ce8211b3-7e33-46ae-85ff-fe8880dafe11\n---\n## Overview\nYou can create a fully qualified URL to any resource, asset, or page on your site using a path relative string.\n\nFor example, if you had a link to `<a href=\"fanny-packs\">`, it would be broken if you left that relative area of the site. By using the link tag you can ensure it's relative to your site root, and include the domain if needed.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ link to=\"fanny-packs\" }}\n{{ link to=\"fanny-packs\" absolute=\"true\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:link to=\"fanny-packs\" />\n<s:link to=\"fanny-packs\" absolute=\"true\" />>\n\n{{-- Using Fluent Tags --}}\n{{ Statamic::tag('link')->to('fanny-packs') }}\n{{ Statamic::tag('link')->to('fanny-packs')->absolute(true) }}\n```\n::\n\n```html\n/fanny-packs\nhttps://example.com/fanny-packs\n```\n\n## Link to Entries\n\nYou can also link to entries using their ID directly:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ link id=\"1715c9a8-0662-4ca7-b9ea-1ad642431fae\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:link id=\"1715c9a8-0662-4ca7-b9ea-1ad642431fae\" />\n\n{{-- Using Fluent Tags --}}\n{{ Statamic::tag('link')->id('1715c9a8-0662-4ca7-b9ea-1ad642431fae') }}\n```\n::\n\n``` output\n/the-pages-slug\n```\n\nWhen using Multi-Site, the URL automatically links to the entry on the current site. If you want to link to a specific site instead, you can add the `in` parameter:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ link id=\"1715c9a8-0662-4ca7-b9ea-1ad642431fae\" in=\"spanish\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:link id=\"1715c9a8-0662-4ca7-b9ea-1ad642431fae\" in=\"spanish\" />\n\n{{-- Using Fluent Tags --}}\n{{ Statamic::tag('link')->id('1715c9a8-0662-4ca7-b9ea-1ad642431fae')->in('spanish') }}\n```\n```\n::\n\n``` output\n/es/el-asdf-de-la-pagina\n```\n"
  },
  {
    "path": "content/collections/tags/locales-count.md",
    "content": "---\nid: 5442a3d6-db7b-41d3-8ab2-2514f8a11eff\nblueprint: tag\ntitle: 'Locales:Count'\nintro: 'Get the number of localizations.'\n---\nThis tag shares everything from the [locales tag](/tags/locales), except that instead of looping over the results, it will just tell you how many there are.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ locales:count }}\n{{ locales:count self=\"false\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:locales:count />\n<s:locales:count self=\"false\" />\n\n{{-- Using Fluent Tags --}}\n{{ Statamic::tag('locales:count') }}\n{{ Statamic::tag('locales:count')->self(false) }}\n```\n::\n\n```output\n3\n2\n```\n"
  },
  {
    "path": "content/collections/tags/locales.md",
    "content": "---\ntitle: Locales\nid: ec775716-e573-4a0e-b6e6-23ca1d7b3bbd\nintro: Create links to localized content.\noverview: If you need to generate links to your site's content in other languages (using [multi-site](/multi-site)), you've come to the right place.\nparameters:\n  -\n    name: id\n    type: string\n    description: |\n      The ID of the content you want to localize. If left blank, the content will be taken from the context.\n  -\n    name: sort\n    type: string\n    description: |\n      Sort by one of the keys in your `sites.php`'s `sites` array. (eg. `name` or `full`). If left blank, the order in the file will be maintained. Only applicable in the tag pair.\n  -\n   name: current_first\n   type: boolean *true*\n   description: |\n     When true, this ensures that the current site locale will be first in the list. Only applicable in the tag pair.\n  -\n   name: self\n   type: boolean *true*\n   description: |\n     When true, it includes the given entry's locale in the list. Only applicable in the tag pair.\n  -\n   name: all\n   type: boolean *false*\n   description: |\n     When true, all of the sites will be displayed, even if the entry isn't localized into that site. When the entry is missing, the values (e.g. `url`) will fall back to the site. Only applicable in the tag pair.\nvariables:\n  -\n    name: current\n    type: string\n    description: >\n      The `handle` of the current locale.\n  -\n    name: is_current\n    type: boolean\n    description: >\n      `true` if the given locale in the loop is the current one.\n  -\n    name: locale:handle\n    type: string\n    description: |\n      The system handle for any given locale as set in `config/statamic/sites.php`.\n  -\n    name: locale:name\n    type: string\n    description: |\n      The user-friendly name for any given locale as set in `config/statamic/sites.php`.\n  -\n    name: locale:full\n    type: string\n    description: |\n      The full, 4 character system locale (e.g. `en_US`) for any given locale as set in `config/statamic/sites.php`.\n  -\n    name: locale:short\n    type: string\n    description: |\n      The short 2 character system locale (e.g. `en`) for any given locale as set in `config/statamic/sites.php`.\n  -\n    name: locale:url\n    type: string\n    description: The URL as defined in in `sites.php`.\n  -\n    name: locale:permalink\n    type: string\n    description: The absolute URL of the site.\n  -\n    name: exists\n    type: boolean\n    description: Whether the entry has been localized into the site. It will be `false` if the entry hasn't been localized at all, or if it's a draft.\n  -\n    name: entry data\n    type: mixed\n    description: |\n      Each result has access to all the variables inside that entry (`title`, `content`, etc).\n---\n## Overview\n\nThis tag is used to access all the locales any given entry or term is available in. It's most commonly used as a language switcher.\n\nEach locale's system data, is [configured](/multi-site#configuration) in `resources/sites.yaml`.\n## Examples\n\n### Iterating over locales {#iterating}\n\nLoop through in each locale to get URLs to translated versions of an entry or taxonomy term.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ locales }}\n    <li><a href=\"{{ permalink }}\">View in {{ locale:name }}</a></li>\n{{ /locales }}\n</ul>\n```\n::tab blade\n<ul>\n<s:locales>\n  <li><a href=\"{{ $locale['permalink'] }}\">View in {{ $locale['name'] }}</a></li>\n</s:locales>\n</ul>\n::\n\n### Targeting a locale {#targeting}\n\nYou can also specify a locale directly instead of looping through them all.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ locales:fr }}\n    <a href=\"{{ permalink }}\">View in French</a>\n{{ /locales:fr }}\n```\n::tab blade\n```blade\n<s:locales:fr>\n  <a href=\"{{ $locale['permalink'] }}\">View in French</a>\n</s:locales:fr>\n```\n::\n\n### Excluding the entry's locale {#excluding}\n\nYou can choose to not show the locale belonging to the entry.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ locales self=\"false\" }}\n    <a href=\"{{ permalink }}\">View in {{ locale:name }}</a>\n{{ /locales }}\n```\n::tab blade\n```blade\n<s:locales self=\"false\">\n  <a href=\"{{ $locale['permalink'] }}\">View in {{ $locale['name'] }}</a>\n</s:locales>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/loop.md",
    "content": "---\ntitle: Loop\ndescription: 'Loop a specified number of times'\nintro: |\n  Create and iterate through a loop a specific number of times or between a range.\n\nparameters:\n  -\n    name: times\n    type: int\n    description: |\n      Number of times to loop.\n\n  -\n    name: from\n    type: int\n    description: |\n      Number to start looping from. Default `1`.\n\n  -\n    name: to\n    type: int\n    description: 'Number to stop looping at.'\nid: 0c59949c-2a78-4f83-94c3-164736140f03\n---\n## Overview\n\nCreate and iterate through a loop a specific number of times using the `times` parameter, or through a range with the `from` and `to` parameters. Do not use both. It'll make a weird mess.\n\n## Examples\n\n### Count to 10\n\n::tabs\n\n::tab antlers\n```antlers\n{{ loop times=\"10\" }}\n  {{ value }}\n{{ /loop }}\n```\n::tab blade\n```blade\n<s:loop times=\"10\">\n  {{ $value }}\n</s:loop>\n```\n::\n\n### Looping a variable number of times\n\n\n::tabs\n\n::tab antlers\n```antlers\n---\nnumber: \"5\"\ntext: \"Pow\"\n---\n\n{{ loop :times=\"number\" }}\n    {{ text }}\n{{ /loop }}\n\n// Output: PowPowPowPowPow\n```\n::tab blade\n```blade\n<?php\n  $number = 5;\n  $text = 'Pow';\n?>\n\n<s:loop :times=\"$number\">\n  {{ $text }}\n</s:loop>\n\n// Output: PowPowPowPowPow\n```\n::\n\n### Populate a select field with years\n\n::tabs\n\n::tab antlers\n```antlers\n<select name=\"year\">\n   {{ loop from=\"1900\" to=\"2020\" }}\n      <option value=\"{{ value }}\">{{ value }}</option>\n   {{ /loop }}\n</select>\n```\n::tab blade\n```blade\n<select name=\"year\">\n  <s:loop from=\"1900\" to=\"2020\">\n    <option value=\"{{ $value }}\">{{ $value }}</option>\n  </s:loop>\n</select>\n```\n::"
  },
  {
    "path": "content/collections/tags/markdown-indent.md",
    "content": "---\ntitle: \"Markdown:Indent\"\nid: d26ec250-460e-11e7-9598-0800200c9a66\ndescription: Transforms inline Markdown while ignoring whitespace\nintro: This tag is used for transforming Markdown while ignoring whitespace in your view files.\n---\n## Example {#example}\n\n::tabs\n\n::tab antlers\n```antlers\n<article class=\"mx-auto max-w-lg\">\n  {{ markdown:indent }}\n    # My Favorite Nickelodeon Shows\n\n    - Kenan & Kel\n    - All That\n    - Double Dare\n    - Wild & Crazy Kids\n    - Legends of the Hidden Temple\n  {{ /markdown:indent }}\n</article>\n```\n::tab blade\n```blade\n<article class=\"mx-auto max-w-lg\">\n  <s:markdown:indent>\n    # My Favorite Nickelodeon Shows\n\n    - Kenan & Kel\n    - All That\n    - Double Dare\n    - Wild & Crazy Kids\n    - Legends of the Hidden Temple\n  </s:markdown:indent>\n</article>\n```\n::\n\n```html\n<article class=\"mx-auto max-w-lg\">\n  <h1>My Favorite Nickelodeon Shows</h1>\n  <ul>\n    <li>Kenan & Kel</li>\n    <li>All That</li>\n    <li>Double Dare</li>\n    <li>Wild & Crazy Kids</li>\n    <li>Legends of the Hidden Temple</li>\n  </ul>\n</article>\n```\n"
  },
  {
    "path": "content/collections/tags/markdown.md",
    "content": "---\ntitle: Markdown\nid: 3f0e8d63-f6ca-4a8e-86dd-8361aa328807\ndescription: Transforms inline Markdown written in your template.\nintro: Transform Markdown content written inline in your template. For when you just can't be bothered to write HTML or make another variable.\n---\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n<article class=\"mx-auto max-w-lg\">\n{{ markdown }}\n# My Favorite Nickelodeon Shows\n\n- Kenan & Kel\n- All That\n- Double Dare\n- Wild & Crazy Kids\n- Legends of the Hidden Temple\n{{ /markdown }}\n</article>\n```\n::tab blade\n```blade\n<article class=\"mx-auto max-w-lg\">\n<s:markdown>\n# My Favorite Nickelodeon Shows\n\n- Kenan & Kel\n- All That\n- Double Dare\n- Wild & Crazy Kids\n- Legends of the Hidden Temple\n</s:markdown>\n</article>\n```\n::\n\n```html\n<article class=\"mx-auto max-w-lg\">\n<h1>My Favorite Nickelodeon Shows</h1>\n<ul>\n  <li>Kenan & Kel</li>\n  <li>All That</li>\n  <li>Double Dare</li>\n  <li>Wild & Crazy Kids</li>\n  <li>Legends of the Hidden Temple</li>\n</ul>\n</article>\n```\n\n:::tip\nMarkdown considers indentation to be a code block. You'll need to keep your content flush left or use the [markdown:indent](/tags/markdown-indent) tag.\n:::\n"
  },
  {
    "path": "content/collections/tags/mix.md",
    "content": "---\ntitle: Mix\ndescription: Returns the path to a versioned Mix file\nintro: The Mix tag is used in tandem with Laravel Mix to return the path to versioned CSS and JavaScript files.\nparameters:\n  -\n    name: src\n    type: string\n    description: >\n        The path to the versioned file, relative to your `public/` directory.\n  -\n    name: in\n    type: string\n    description: The location of your mix manifest file relative to the `public/` directory.\nid: b8936f37-a237-4fad-bf70-a6421ab413ae\n---\n## Overview\n[Laravel Mix][mix] is an Webpack API wrapper for compiling and building CSS and JavaScript files. The mix tag returns the path to a versioned [Mix][mix] file. Don't worry, if you're not using versioning it will return the path to the _non_-versioned file.\n\n[Webpack][webpack] and asset compilation can be a pretty complicated task and Laravel Mix does a really good job of simplifying it as far as it can possibly go.\n\n::tabs\n\n::tab antlers\n```antlers\n// CSS\n<link href=\"{{ mix src='/css/tailwind.css' }}\" rel=\"stylesheet\">\n\n// JavaScript\n<script src=\"{{ mix src='/js/app.js' }}\"></script>\n```\n::tab blade\n```blade\n// CSS\n<link href=\"{{ Statamic::tag('mix')->src('/css/tailwind.css') }}\" rel=\"stylesheet\">\n\n// JavaScript\n<script src=\"{{ Statamic::tag('mix')->src('/js/app.js') }}\"></script>\n```\n::\n\n## Default Directory\n\nBy default Mix will assume that the `mix-manifest.json` file that points to the proper file locations is in your `public/` directory. If your file is configured to build elsewhere you can point to it with the `in` parameter.\n\n::tabs\n\n::tab antlers\n```antlers\n<link href=\"{{ mix src='/css/tailwind.css' in='assets' }}\" rel=\"stylesheet\">\n```\n::tab blade\n```blade\n<link href=\"{{ Statamic::tag('mix')->src('/css/tailwind.css')->in('assets') }}\" rel=\"stylesheet\">\n```\n::\n\n## Related Reading\n\n- [Laravel Mix docs][mix]\n- [Laravel Mix FAQs](https://laravel-mix.com/docs/4.0/faq)\n- [Using TailwindCSS with Laravel Mix](https://tailwindcss.com/docs/installation/#laravel-mix)\n- [Webpack][webpack]\n\n[mix]: https://laravel.com/docs/mix\n[webpack]: https://webpack.js.org/\n"
  },
  {
    "path": "content/collections/tags/mount_url.md",
    "content": "---\nid: f8e00858-1991-44ae-bc47-b87779e7a31d\nblueprint: tag\ntitle: Mount URL\nintro: 'The Mount URL tag is used to return the URL to a collection''s mount entry.'\nparameters:\n  -\n    name: handle\n    type: string\n    required: true\n    description: 'Specify the name of the collection.'\n---\n## Overview\n\nThis tag lets you get the URL to a collection's mount entry.\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ mount_url handle=\"blog\" }}\">Read Our Blog</a>\n```\n::tab blade\n```blade\n<a href=\"{{ Statamic::tag('mount_url')->handle('blog') }}\">Read Our Blog</a>\n```\n::\n\n## Shorthand\n\nYou may also use a shorthand syntax, where the second tag argument is the collection handle.\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ mount_url:blog }}\">Read Our Blog</a>\n```\n::tab blade\n```blade\n<a href=\"{{ Statamic::tag('mount_url:blog') }}\">Read Our Blog</a>\n```\n::"
  },
  {
    "path": "content/collections/tags/nav-breadcrumbs.md",
    "content": "---\ntitle: \"Nav:Breadcrumbs\"\nparent_tag: ed746608-87f9-448f-bf57-051da132fef7\nintro: >\n  Breadcrumbs are a common form of site navigation designed to give the user a view of where the current page is in the parent/child hierarchy. Much like the crumbs left by a certain little German boy — they lead from wherever you are, all the way back home.\ndescription: Display breadcrumb-style navigation links to your current page.\nparameters:\n  -\n    name: include_home\n    type: 'boolean'\n    description: >\n      Include the home page as the first breadcrumb. Default: `true`.\n  -\n    name: reverse\n    type: 'boolean'\n    description: Reverse the order of links.\n  -\n    name: trim\n    type: 'boolean *true*'\n    description: >\n      Trim the whitespace from each iteration\n      of the loop. Default: `true`.\nvariables:\n  -\n    name: is_current\n    type: boolean\n    description: >\n      `true` if current page is the URL being viewed. Useful for adding\n      active state classes in your HTML.\n  -\n    name: data/content\n    type: mixed\n    description: >\n      All data and variables are available for each item in the list.\nid: 485f1703-fc6f-4d0f-94f2-e84ae625e1b7\n---\n## Overview\n\nThis tag looks at the current URL and look for any entries that match each segment. Let's say you visit `/italian/articles/dance`. The logic works like this:\n\n1. Looks for an entry with a URL of /italian/articles/dance.\n2. Pops the last segment off (`dance`) and look for an entry with a url of `/italian/articles`\n3. Do the same for `/italian`\n4. If `include_home` is set to `true`, look an entry with a url of `/`.\n\nIf any of the URLs don't match an entry in the current site, they will be skipped, so be sure to create translations for parent pages you if you're working on a multisite.\n\n:::tip\nBreadcrumbs don't follow structures, they follow the current URL hierarchy.\n:::\n\n## Example\n\nHere's an example of what breadcrumbs might look like, as well as a code example in use.\n\n<figure>\n    <div class=\"flex font-mono space-x-4 mx-4\">\n      <div>Home</div>\n      <div>&rarr;</div>\n      <div>Blog</div>\n      <div>&rarr;</div>\n      <div class=\"text-pink-hot font-bold\">How to Dress Like David Hasselhoff</div>\n    </div>\n    <figcaption>These crumbs are delicious.</figcaption>\n</figure>\n\n::tabs\n\n::tab antlers\n```antlers\n<ul class=\"breadcrumbs\">\n    {{ nav:breadcrumbs }}\n    <li{{ if is_current }} class=\"current\"{{ /if }}>\n        <a href=\"{{ url }}\">{{ title }}</a>\n    </li>\n    {{ /nav:breadcrumbs }}\n</ul>\n```\n::tab blade\n```blade\n<ul class=\"breadcrumbs\">\n  <s:nav:breadcrumbs>\n  <li\n    @if ($is_current) class=\"current\" @endif\n  >\n    <a href=\"{{ $url }}\">{{ $title }}</a>\n  </li>\n  </s:nav:breadcrumbs>\n</ul>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/nav.md",
    "content": "---\ntitle: Nav\nintro: >\n  The nav tags are designed to help your users navigate through your hierarchy of navigations and collections.\ndescription: Creates site navigations.\nid: ed746608-87f9-448f-bf57-051da132fef7\nis_parent_tag: true\nvideo: https://youtu.be/POgIsLeWGGQ\nparameters:\n  -\n    name: handle\n    type: string\n    description: 'The navigation or collection to use (e.g. `{{ nav handle=\"collection::my_pages_collection_handle\" }}`). Not necessary if you''re using the shorthand tag (e.g. `{{ nav:links }}`)'\n  -\n    name: from\n    type: string\n    description: \"Specify the URI of the entry to be used as the starting point for your navigation. If unspecified, it'll start from the top. Note: this parameter is only supported for orderable collections.\"\n  -\n    name: show_unpublished\n    type: 'boolean *false*'\n    description: \"Unpublished content is, by it's very nature, unpublished. That is, unless you show it by turning on this parameter.\"\n  -\n    name: include_home\n    type: 'boolean *false*'\n    description: >\n      You can choose to turn off the home page in the tree, opting to start the crumbs from the first level nav item. Doesn't do\n      anything if you're using the `from` parameter.\n  -\n    name: include_parents\n    type: 'boolean *true*'\n    description: >\n      Prevents the `parent` key from being returned on nav items.\n  -\n    name: max_depth\n    type: 'int'\n    description: >\n      This limits how deep the tag will render. If you are using [recursion](#multi-level), this could be useful to stop at a certain point. It also helps with [performance](#performance). By default there is no max.\n  -\n    name: select\n    type: array\n    description: >\n      Limits the fields that will be made available to the tag. Selecting fewer fields will improve performance. By default all variables will be selected. See [performance](#performance).\n  -\n     name: as\n     type: string\n     description: >\n      Alias your nav items into a new variable loop. It's worth noting that the `*recursive children*` variable won't be available when using this parameter.\n     required: false\nvariables:\n  -\n    name: is_published\n    type: boolean\n    description: Whether or not the page is published.\n  -\n    name: is_page\n    type: boolean\n    description: >\n      Whether or not the page is in fact a\n      page. If you are using the `entries`\n      parameter to show entries, a \"page\" may\n      potentially be an entry.\n  -\n    name: is_entry\n    type: boolean\n    description: >\n      The inverse of `is_page`. Outputs\n      whether the \"page\" is an entry.\n  -\n    name: has_entries\n    type: boolean\n    description: >\n      Whether the current page has entries\n      mounted to it.\n  -\n    name: children\n    type: array\n    description: >\n      An array of child pages. Use this as a\n      tag pair to iterate over any child\n      pages.\n  -\n    name: parent\n    type: array\n    description: \"An array containing the current page's parent. Use this as a tag pair to output variables from the parent's page data.\"\n  -\n    name: is_parent\n    type: boolean\n    description: >\n      Whether the current page is a parent of\n      the URL being viewed. Useful for\n      outputting active states.\n  -\n    name: is_current\n    type: boolean\n    description: >\n      Whether the current page is the URL\n      being viewed. Also useful for outputting\n      active states.\n  -\n    name: is_external\n    type: boolean\n    description: >\n      Whether the current nav URL is an external link . Useful for outputting\n      `target=_\"blank\"` in menu templates.\n  -\n    name: depth\n    type: integer\n    description: The depth of the page within the nav structure.\n  -\n    name: page data\n    type: mixed\n    description: >\n      Each page being iterated has access to\n      all the variables inside that page. This\n      includes things like `title`, `content`,\n      etc.\n  -\n    name: '\\*recursive children\\*'\n    type: wizardry\n    description: >\n      Recursively output the entire contents\n      of the `nav` tag pair.\n---\n## Overview\n\nThe various Nav tags work together to allow you to easily traverse your content upways and downways, sideways, slantways, longways, backways, squareways, frontways, and any other ways that you can think of.\n\nThis tag is designed to be used for top-level and multi-level navs.\n\n## Navs or Collections\n\nThe nav tag supports both [navigations](/navigation) or multi-depth [collections](/collections).\n\nYou specify what kind you need by using the second tag part:\n\n::tabs\n\n::tab antlers\n```antlers\n// The \"links\" nav\n{{ nav:links }} ... {{ /nav:links }}\n```\n::tab blade\n```blade\n// The \"links\" nav\n<s:nav:links>\n  ...\n</s:nav:links>\n```\n::\n\n::tabs\n\n::tab antlers\n```antlers\n// The \"pages\" collection\n{{ nav:collection:pages }} ... {{ /nav:collection:pages }}\n```\n::tab blade\n```blade\n// The \"pages\" collection\n<s:nav:collection:pages>\n  ...\n</s:nav:collection:pages>\n```\n::\n\nIf you use the tag on its own without a second part, it will assume you want the `pages` collection. That's a super common thing to do, and the `statamic/statamic` repo comes bundled with it.\n\n::tabs\n\n::tab antlers\n```antlers\n// Also the \"pages\" collection\n{{ nav }} ... {{ /nav }}\n```\n::tab blade\n```blade\n// Also the \"pages\" collection\n<s:nav>\n  ...\n</s:nav>\n```\n::\n\nYou can also specify the navigation using the `handle` parameter:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ nav handle=\"links\" }} ... {{ /nav }}\n```\n::tab blade\n```blade\n<s:nav\n  handle=\"links\"\n>\n  ...\n</s:nav>\n```\n::\n\n:::tip\nThe `{{ nav }}` tag will only output entries for [**Orderable** collections](collections#ordering). If you need navigation for a non-ordered collection, you may wish to use the [`collection`](/tags/collection) tag instead.\n:::\n\n## Basic Example\n\nA single level nav, much like something you'd have at the top of your site, can be built by looping through all the items in the nav and using their `title` and `url` variables in your HTML. Add a \"current\" state by checking for `is_current` and `is_parent`, and you're probably good to go.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n  {{ nav include_home=\"true\" }}\n    <li>\n      <a href=\"{{ url }}\"{{ if is_current || is_parent }} class=\"current\"{{ /if }}>\n        {{ title }}\n      </a>\n    </li>\n  {{ /nav }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n  <s:nav include_home=\"true\">\n    <li>\n      <a\n        href=\"{{ $url }}\"\n        @if ($is_current || $is_parent) class=\"current\" @endif\n      >\n        {{ $title }}\n      </a>\n    </li>\n  </s:nav>\n</ul>\n```\n::\n\n## Show the children of the current page\n\n:::tip\nA simpler way is to use the [children tag](/tags/children) for this.\n:::\n\nUse the `uri` to get the children of the current page.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n    {{ nav :from=\"uri\" }}\n        {{ unless no_results }}\n            <li>\n                <a href=\"{{ url }}\">{{ title }}</a>\n            </li>\n        {{ /unless }}\n    {{ /nav }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n  <s:nav :from=\"uri\" as=\"pages\">\n    @foreach ($pages as $page)\n      <li>\n        <a href=\"{{ $page['url'] }}\">{{ $page['title'] }}</a>\n      </li>\n    @endforeach\n  </s:nav>\n</ul>\n```\n::\n\n## Multi-level Nav Example Recursion {#multi-level}\n\nBuilding an infinitely deep nav is possible by using recursion.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n   {{ nav :from=\"segment_1\" }}\n      <li>\n         <a href=\"{{ url }}\"{{ if is_current || is_parent }} class=\"on\"{{ /if }}>{{ title }}</a>\n         {{ if is_current || is_parent }}\n            {{ if children }}\n               <ul>{{ *recursive children* }}</ul>\n            {{ /if }}\n         {{ /if }}\n      </li>\n   {{ /nav }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n   <s:nav :from=\"$segment_1\">\n      <li>\n         <a\n           href=\"{{ $url }}\"\n           @if ($is_current || $is_parent) class=\"current\" @endif\n         >\n           {{ $title }}\n         </a>\n         @if ($is_current || $is_parent)\n            @if (count($children) > 0)\n               <ul>@recursive_children</ul>\n            @endif\n         @endif\n      </li>\n   </s:nav>\n</ul>\n```\n::\n\nThe `{{ *recursive children* }}` tag will repeat the contents of the entire `{{ nav }}` tag using child elements, if they exist. As long as there are children to display, and we’re still on either the current or parent page of those children, the nav tag will traverse deeper. If your scoped variables have trouble making it through to the next recursion, you can glue them to children like this: `{{ *recursive children:my_scoped_variable* }}`.\n\nTake the time to wrap your brain around this concept and learn to wield it and a powerful Jedi will you be.\n\n:::tip\nThe Jedi have blessed us all with [even more recursive nav examples](/tips/recursive-nav-examples), so that you may have the high ground next time you're fighting that losing nav battle on Mustafar.\n:::\n\n## Performance\n\nYou may improve performance of the `nav` tag in two ways:\n\n1. Set the `max_depth` parameter appropriately.\n   If you only need one level, set `max_depth=\"1\"`.\n2. Select the fields that you'll be using.\n   If you're only going to be using `{{ title }}` and `{{ url }}` in the loop, set `select=\"title|url\"`.\n"
  },
  {
    "path": "content/collections/tags/nocache.md",
    "content": "---\nid: 221fadae-5284-42a6-a1df-aad326e5fa70\nblueprint: tag\ntitle: 'No Cache'\ndescription: 'Keeps a chunk of your template dynamic inside of an otherwise cached area.'\nintro: When using a broader caching solution (like [static caching](/static-caching) or the [cache tag](/tags/cache)) you may want to keep a chunk of your template dynamic.\n---\n## Overview\n\nWhen using [Static Caching](/static-caching) or the [cache tag](/tags/cache), the rendered contents will be remembered for the next request. That's great for performance, but if you have something in there that you'd like to keep dynamic (such as authenticated user data, randomized elements, or listings), you'd normally have to disable static caching for the whole page.\n\nWith the `nocache` tag, you can keep the entirety of the page cached (when using Static Caching) or chunks of the page cached (when using the cache tag), with specific parts remaining dynamic:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ nav }} ... {{ /nav }}\n\n{{ nocache }} {{# [tl! focus:6] #}}\n    {{ if logged_in }}\n        Welcome back, {{ current_user:name }}\n    {{ else }}\n        Hello, Guest!\n    {{ /if }}\n{{ /nocache }}\n\n{{ content }}\n```\n::tab blade\n```blade\n<statamic:nav> ... </statamic:nav>\n\n<statamic:nocache> {{-- [tl! focus:6] --}}\n   @if ($logged_in)\n      Welcome back, {{ $current_user->name }}   \n   @else\n      Hello, guest!\n   @endif\n</statamic:nocache>\n\n{!! $content !!}\n```\n::\n\n## Forms\n\nBefore the `nocache` tag existed, a common reason to exclude a page from static caching would be if there was a form on there.\n\nForms contain a CSRF token for security, which is unique to each user visiting. If you submit the form using someone elses token (which could happen when static caching is enabled) the form won't work.\n\nCSRF tokens are now updated automatically.\n\nHowever, if your form has logic in it, like validation or success messages, you'll need to keep the `form:create` dynamic by wrapping in `nocache` tags.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ nocache }} {{# [tl!++] #}}\n    {{ form:create }}\n        ...\n    {{ /form:create }}\n{{ /nocache }} {{# [tl!++] #}}\n```\n::tab blade\n```blade\n<statamic:nocache> {{-- [tl!++] --}}\n   <statamic:form:create>\n      ...\n   </statamic:form:create>\n</statamic:nocache> {{-- [tl!++] --}}\n```\n::\n\n## Blade\n\nYou can use the \"no cache\" feature in Blade too, although you should use the dedicated Blade directive or the Statamic Elements tag instead of trying to use `Statamic::tag`.\n\n::tabs\n\n::tab nocache Directive\n\nTo keep a part of your template dynamic, move it into a partial then use the `@nocache` directive just like you would use the `@include` directive.\n\n```blade\n@include('mypartial') {{-- this will be cached --}}\n@nocache('mypartial') {{-- this will be dynamic --}}\n```\n::tab Antlers Blade Components\n\nIf you'd like to make a section of the current template dynamic without extracting a dedicated partial, you may use the `<statamic:nocache>` tag:\n\n```blade\n<statamic:nocache>\n  ...\n</statamic:nocache>\n```\n::\n\n## Caveats\n\nMost of the time you can probably get away with wrapping something in a `nocache` tag and it will just work! However there are some situations where you will need to be more aware of how the tag works in order to get predictable results.\n\n\n### When to wrap\n\nFor example, let's say you have a collection of stocks. Each entry contains information about the stock, except for the price. You have a custom tag that will get the up-to-the-second stock price using an API.\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:stocks }}\n    <li>\n        {{ company }}\n        {{ symbol }}\n        {{ get_price :of=\"symbol\" }}\n    </li>\n{{ /collection:stocks }}\n```\n::tab blade\n```blade\n<statamic:collection:stocks>\n   <li>\n      {{ $company }}\n      {{ $symbol }}\n      <statamic:get_price :of=\"$symbol\" />\n   </li>\n</statamic:collection:stocks>\n```\n::\n\nIf you turned on static caching now, then the whole listing would be cached, including the prices.\n\nIn this example, you should put a `nocache` _inside_ the loop.\n\n::tabs\n::tab antlers\n```antlers\n{{ collection:stocks }}\n    <li>\n        {{ company }}\n        {{ symbol }}\n        {{ nocache }} {{# [tl!++] #}}\n            {{ get_price :of=\"symbol\" }}\n        {{ /nocache }} {{# [tl!++] #}}\n    </li>\n{{ /collection:stocks }}\n```\n::tab blade\n```blade\n<statamic:collection:stocks>\n   <li>\n      {{ $company }}\n      {{ $symbol }}\n      <statamic:nocache> {{-- [tl!++] --}}\n        <statamic:get_price :of=\"$symbol\" />\n      </statamic:nocache> {{-- [tl!++] --}}\n   </li>\n</statamic:collection:stocks>\n```\n::\n\nBy putting it inside the loop, it means that the `collection:stocks` tag wouldn't need to re-query for entries on each subsequent request.\n\nYou'd then also rely on [static caching invalidation rules](/static-caching#invalidation) to invalidate the entire page when one of your stock entries change.\n\nAn example where you would want to put a `nocache` tag _around_ a loop would be when re-querying every request would be important. For example, a randomized section:\n\n::tabs\n::tab antlers\n```antlers\n{{ nocache }}\n    {{ collection:blog featured:is=\"true\" sort=\"random\" }}\n        ...\n    {{ /collection:blog }}\n{{ /nocache }}\n```\n::tab blade\n```blade\n<statamic:nocache>\n   <statamic:collection:blog\n     featured:is=\"true\"\n     sort=\"random\"\n   >\n     ...\n   </statamic:collection:blog>\n</statamic:nocache>\n```\n::\n\n### Context\n\nThe `nocache` tag will remember the \"context\", which are the variables available at that point in the template.\n\nHere we'll use a loop to illustrate what happens:\n\n```yaml\ntitle: Movie Reviews\nreviews:\n  - name: Top Gun\n    rating: 58%\n  - name: Citizen Kane\n    rating: 99%\n  - name: Jack and Jill\n    rating: 3%\n```\n\n::tabs\n::tab antlers\n```antlers\n{{ reviews }}\n    <div class=\"movie\">\n        {{ nocache }}\n            {{ name }} {{ rating }} {{ title }}\n        {{ /nocache }}\n    </div>\n{{ /reviews }}\n```\n::tab blade\n\n```blade\n@foreach ($reviews as $review)\n  <div class=\"movie\">\n    <statamic:nocache>\n      {{ $review->name }} {{ $review->rating}} {{ $review->title }}\n    </statamic:nocache>\n  </div>\n@endforeach\n```\n::\n\n```\n<div class=\"movie\"> Top Gun 58% Movie Reviews </div>\n<div class=\"movie\"> Citizen Kane 99% Movie Reviews </div>\n<div class=\"movie\"> Jack and Jill 3% Movie Reviews </div>\n```\n\nWithin each of those nocache tags, you'd have access to the iteration specific variables:\n  - `name`, `rating` (e.g. `name` would be `Top Gun`, then `Citizen Kane`, etc)\n  - special loop variables (e.g. `first`, `last`, `count`)\n  - any cascading variables (e.g. from outer tags, or top level page variables like `now`, `page`, etc)\n\nAll of the [top level variables](/variables) will be filtered out and replaced with fresh versions on subsequent requests.\n\nNow lets say you update `title` to `Ratings` and Top Gun's `rating` to `60%`. The first line out of output will now look like this:\n\n```\n<div class=\"movie\"> Top Gun 58% Ratings </div>\n```\n\nOnly the `title`'s change would be reflected, since the `reviews` value was remembered. If you wanted this to be dynamic, you should wrap the loop rather than putting it inside the loop, as explained in [when to wrap](#when-to-wrap).\n\n::tabs\n::tab antlers\n```antlers\n{{ nocache }} {{# [tl!++] #}}\n    {{ reviews }}\n        <div class=\"movie\">\n            {{ nocache }} {{# [tl!--] #}}\n                {{ name }} {{ rating }} {{ title }}\n            {{ /nocache }} {{# [tl!--] #}}\n        </div>\n    {{ /reviews }}\n{{ /nocache }} {{# [tl!++] #}}\n```\n::tab blade\n```blade\n<statamic:nocache> {{-- [tl!++] --}}\n    @foreach ($reviews as $review)\n        <div class=\"movie\">\n            <statamic:nocache> {{-- [tl!--] --}}\n                {{ $review->name }} {{ $review->rating }} {{ $review->title }}\n            </statamic:nocache> {{-- [tl!--] --}}\n        </div>\n    @endforeach\n</statamic:nocache> {{-- [tl!++] --}}\n```\n::\n\n```\n<div class=\"movie\"> Top Gun 60% Ratings </div>\n```\n\n#### Performance\nSometimes, you may notice the contents of the `nocache` taking a while to load. This is often caused by Statamic hydrating all of the variables from the context.\n\nTo improve performance, you can explicitly select the variables you need:\n\n::tabs\n::tab antlers\n```antlers\n{{ nocache select=\"this|that\" }}\n```\n::tab blade\n```blade\n<statamic:nocache\n  select=\"this|that\"\n>\n</statamic:nocache>\n```\n::\n\nAlternatively, you can use the `@auto` placeholder for Statamic to extract the variables from the template (this is similar to the `@shallow` placeholder in the nav tag):\n\n::tabs\n::tab antlers\n```antlers\n{{ nocache select=\"@auto\" }}\n```\n::tab blade\n```blade\n<statamic:nocache\n  select=\"@auto\"\n>\n\n</statamic:nocache>\n```\n::\n\n:::tip\nIt's worth noting, the `nocache` tag won't be able to extract variables used inside partials or PHP files like custom tags. You will need to explicitly define the variables you need in these cases.\n:::\n\nYou can also combine them to extract the variables from the template and add additional ones:\n\n::tabs\n::tab antlers\n```antlers\n{{ nocache select=\"@auto|this|that\" }}\n```\n::tab blade\n```blade\n<statamic:nocache\n  select=\"@auto|this|that\"\n>\n\n</statamic:nocache>\n```\n::\n\n## Full Measure Static Caching\n\nWhen using the `file` static cache driver (aka. \"full measure\") the pages will be stored as plain `html` files on your server.\n\nOn any pages that use a `nocache` tag, a small snippet of JavaScript will be injected just before the closing `</body>` tag.\n\nThe nocache fragments will be retrieved from the server using an AJAX request. Because of this, there may be a slight delay before the fragments are replaced. This is similar to a \"FOUC\" or \"flash of unstyled content\". In this case, there will be empty `<span>` tags until they are replaced by the real fragments.\n\n### JavaScript position\n\nBy default, the nocache JavaScript is injected just before the closing `</body>` tag so replacements happen after the full HTML has parsed. If you need the script to load earlier — for example, when using Livewire — you can move it into the `<head>` via the `nocache_js_position` option:\n\n```php\n// config/statamic/static_caching.php\n\n'nocache_js_position' => 'head', // 'body' (default) or 'head'\n```\n\nWhen set to `head`, the script is inserted before the first `<link>`, `<script>`, or the closing `</head>` tag (whichever comes first).\n\nYou can optionally define the inner html of these `span` tags, if you wanted to have a \"loading\" state, for example.\n\n```php\nuse Statamic\\Facades\\StaticCache;\n\nStaticCache::nocachePlaceholder('Loading...');\n// or\nStaticCache::nocachePlaceholder('<svg>...</svg>');\n```\n\nYou may wish to run some additional Javascript code once the nocache fragments on the page have been replaced, to enable this a custom event is dispatched that you may register an event listener for.\n\n```js\ndocument.addEventListener('statamic:nocache.replaced', (event) => {\n    alert('nocache fragments have been replaced!');\n});\n```\n\n## Database\n\nBehind the scenes, the `nocache` tag needs to store some data somewhere. This includes the contents of the template chunks, the available variables at that point of the template, which pages it's being used on, etc.\n\nBy default, these are stored in the cache. However, with increased traffic or site size, you may eventually run into resource usage issues. In this case, it's possible to store the nocache data in a database.\n\n1. Configure the database driver in `config/statamic/static_caching.php`:\n    ```php\n    'nocache' => 'database'\n    ``` \n2. Generate the migration:\n    ```sh\n    php please nocache:migration\n    ```\n3. Run the migration:\n    ```sh\n    php artisan migrate\n    ```\n4. Clear the static cache. Things may be out of sync if you have previously cached pages but the nocache regions don't exist in the DB yet.\n    ```sh\n    php please static:clear\n    ```\n"
  },
  {
    "path": "content/collections/tags/oauth.md",
    "content": "---\ntitle: OAuth\ndescription: Generate OAuth login URLs.\nintro: If you're using [OAuth](/oauth) to manage user authentication, you may find you need to generate login URLs at some point. Here's how you do it.\nparameters:\n  -\n    name: provider\n    type: string|tagpart\n    description: |\n      The provider to be used. You may either specify as a parameter or as a tagpart for shorthand: `{{ oauth provider=\"github\" }}` or `{{ oauth:github }}`\n  -\n    name: redirect\n    type: string\n    description: The URL to be taken to after authenticating. This will be appending onto the generated URL as a query parameter.\nid: f7676fe0-abb3-4a05-8530-6d23a9b5130d\n---\n## Examples\n\nHere's the regular/parameter syntax in action, especially useful if the provider name comes from variable.\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ oauth provider=\"github\" }}\">Sign In with Github</a>\n```\n::tab blade\n```blade\n<a href=\"{{ Statamic::tag('oauth')->provider('github') }}\">Sign In with Github</a>\n```\n::\n\n```output\n<a href=\"/oauth/github\">Sign In with Github</a>\n```\n\nAnd the shorthand version.\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ oauth:github }}\">Sign In with Github</a>\n```\n::tab blade\n```blade\n<a href=\"{{ Statamic::tag('oauth:github') }}\">Sign In with Github</a>\n```\n::\n\n```html\n<a href=\"/oauth/github\">Sign In with Github</a>\n```\n\nAnd now with a redirect:\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ oauth:github redirect=\"/account\" }}\">Sign In with Github</a>\n```\n::tab blade\n```blade\n<a href=\"{{ Statamic::tag('oauth:github')->redirect('/account') }}\">Sign In with Github</a>\n```\n::\n\n```html\n<a href=\"/oauth/github?redirect=/account\">Sign In with Github</a>\n```\n"
  },
  {
    "path": "content/collections/tags/obfuscate.md",
    "content": "---\ntitle: Obfuscate\ndescription: Obfuscates content to foil screen-scraping nightmare bots from hell\nintro: Whenever you need to obfuscate something, reach for the obfuscate tag. It's an obfuscating good time.\nid: 161f6255-465d-41bd-8028-d6aba01cebbf\n\n---\n## Overview\n\nThis tag obfuscates content.\n\nObfuscation is a method of encoding content so that the **source code** is hard or impossible to understand. This is most often used on email addresses to prevent spambots from scraping them and signing the poor souls up for an endless chain of pharmaceutical drug emails.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ obfuscate }}\n  djjazzyjeff@angelfire.com\n{{ /obfuscate }}\n```\n::tab\n```blade\n<s:obfuscate>\n  djjazzyjeff@angelfire.com\n</s:obfuscate>\n```\n::\n\n```html\n<!-- Users see this -->\ndjjazzyjeff@angelfire.com\n\n<!-- Source code looks like this -->\n&#x64;&#106;j&#x61;z&#x7a;&#121;je&#102;&#102;&#x40;&#97;&#110;&#103;&#x65;&#x6c;f&#x69;&#114;&#x65;&#x2e;&#x63;&#x6f;&#x6d;\n```\n\n## Related Delights\n\n- [Obfuscate modifier](/modifiers/obfuscate)\n- [How to pronounce obfuscate](https://www.youtube.com/watch?v=zaEg0gziFiU)\n- [How to pronounce Schenectady](https://www.youtube.com/watch?v=e6IO_x3L53c)\n"
  },
  {
    "path": "content/collections/tags/parent.md",
    "content": "---\ntitle: Parent\ndescription: Fetches data from a parent entry\nintro: The Parent tag fetches data from the \"parent\" page — the URL one level above the current one. For example, the parent of this very URL (`/tags/parent`) is `/tags`, and the parent title is \"Tags\".\nid: 932ae2b5-0ff0-40e3-b8a4-1c71784917e4\n---\n## Overview\n\nThis is a simple utility tag that makes it easy to fetch data from the entry one page above the current entry. It's useful for creating section headers, simple breadcrumbs, and so on.\n\n## Parent URL\nBy itself the tag returns the parent's URL.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ parent }}\n// Would return \"/tags\"\n```\n::tab blade\n```blade\n<s:parent />\n```\n::\n\n## Single Variables\nYou can fetch single variables from the parent entry by passing them as the second tag argument.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ parent:title }}\n// Would return \"Tags\"\n```\n::tab blade\n\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:parent:title />\n\n{{-- Using Fluent Tags --}}\n{{ Statamic::tag('parent:title') }}\n```\n\n::\n\n## Tag Pair\n\nAs a tag pair, it will have access to all the parent's data:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ parent }}\n  Go back to <a href=\"{{ url }}\">{{ title }}</a>.\n{{ /parent }}\n```\n::tab blade\n```blade\n<s:parent>\n    Go back to <a href=\"{{ $url }}\">{{ $title }}</a>.\n</s:parent>\n\n{{-- You also use \"as\" to alias the parent variable. --}}\n<s:parent as=\"parent\">\n    Go back to <a href=\"{{ $parent['url'] }}\">{{ $parent['title'] }}</a>.\n</s:parent>\n```\n::\n\n```html\nGo back to <a href=\"/tags\">Tags</a>.\n```\n"
  },
  {
    "path": "content/collections/tags/partial-exists.md",
    "content": "---\nid: 75bf08fb-59ba-4148-91ca-5199efa241cf\ntitle: 'Partial:Exists'\ndescription: 'Checks if a partial exists.'\nintro: 'Checks if a partial exists.'\nparameters:\n  -\n    name: src\n    type: string\n    description: 'You can pass the name of the partial with a parameter instead of tag argument. Example: `src=\"cards/author_bio\"` or `:src=\"var_name\"`.'\n---\n## Overview\n\nYou can use this tag to check if a partial exists. Useful if you have some sort of dynamic loop.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {partial:exists src=\"mypartial\" } }}\n    It exists\n{{ else }}\n    It doesn't.\n{{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::tag('partial:exists')->src('mypartial')->fetch())\n  It exists\n@else\n  It doesn't.\n@endif\n```\n::\n\n## Related Reading\n\nThis tag goes hand in hand with the [`partial`](/tags/partial) tag.\nYou may be interested in the [`partial:if_exists`](/tags/partial-if-exists) tag to simplify your template.\n"
  },
  {
    "path": "content/collections/tags/partial-if-exists.md",
    "content": "---\nid: 29a6e9dd-283e-4463-a414-d115dcde8451\nblueprint: tag-glide\ntitle: 'Partial:If_Exists'\ndescription: 'Renders a partial if it exists.'\nintro: 'Renders a partial if it exists.'\nparameters:\n  -\n    name: src\n    type: string\n    description: 'You can pass the name of the partial with a parameter instead of tag argument. Example: `src=\"cards/author_bio\"` or `:src=\"var_name\"`.'\n  -\n    name: '*'\n    type: mixed\n    description: 'Any parameter you create will be passed through to the partial as a variable.'\n---\n## Overview\n\nYou can use this tag to output a partial if it exists. Useful if you have some sort of dynamic loop.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ partial:if_exists src=\"mypartial\" }}\n```\n::tab blade\n```blade\n<s:partial:if_exists src=\"mypartial\" />\n```\n::\n\nPractically identical to the [`partial`](/tags/partial) except if the view doesn't exist it will just output\nnothing instead of throwing a \"view not found\" exception.\n\n## Related Reading\n\nThis tag goes hand in hand with the [`partial`](/tags/partial) tag.\nYou may be interested in the [`partial:exists`](/tags/partial-exists) tag if you need to do a more\ncomplicated conditional check in your template.\n"
  },
  {
    "path": "content/collections/tags/partial.md",
    "content": "---\ntitle: Partial\ndescription: Renders a partial view\nintro: Partials are reusable [views](/views) that may find themselves in any number of other layouts, templates, and other partials.\nvideo: https://youtu.be/Ddz6mD-jT7E\nparameters:\n  -\n    name: src\n    type: string\n    description: |\n      You can pass the name of the partial with a parameter instead of tag argument. Example: `src=\"cards/author_bio\"` or `:src=\"var_name\"`.\n  -\n    name: when\n    type: string\n    description: |\n      Render a partial only if a condition is met.\n  -\n    name: unless\n    type: string\n    description: |\n      The converse of `when`.\n  -\n    name: handle_prefix\n    type: string\n    description: |\n      A prefix to prepend to variable names when looking up data. For example, if you have a variable named `hero_title` and use `handle_prefix=\"hero_\"`, you can reference it as `{{ title }}`.\n  -\n    name: \"*\"\n    type: mixed\n    description: |\n      Any parameter you create will be passed through to the partial as a variable.\nid: 1f683992-401e-44f6-8506-7967005778a5\n---\n## Overview\n\nYou can use any view as a partial by using this here partial tag.\n\n::tabs\n\n::tab antlers\n```antlers\n// This will import /resources/views/blog/_card.antlers.html\n{{ partial:blog/card }}\n```\n::tab blade\n```blade\n// This will import /resources/views/blog/_card.antlers.html\n<s:partial:blog/card />\n```\n::\n\n:::best-practice\nWe recommend prefixing any views intended to be _only_ used as partials with an underscore, `_like-this.antlers.html` and reference them `{{ partial:like-this }}. The underscore is not necessary in the partial tag definition.\n:::\n\n## Passing Data\n\nYou can pass additional data into a partial via on-the-fly parameters. The parameter name will become a variable inside the partial. The only reserved parameter is `src`, so you can name your variables just about anything.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ partial:list header=\"favorite ice cream flavors\" :items=\"flavors\" }}\n\n// Inside `list.antlers.html`\n<h2>These are my {{ header }}</h2>\n{{ items | ul }}\n```\n::tab blade\n```blade\n<s:partial:list\n  header=\"favorite ice cream flavors\"\n  :items=\"$flavors\"\n/>\n```\n\n```blade\n{{-- Inside `list.blade.php` --}}\n\n<h2>These are my {{ $header }}</h2>\n{!! Statamic::modify($items)->ul() !!}\n```\n::\n\n```html\n<h2>These are my favorite ice cream flavors</h2>\n<ul>\n  <li>Chocolate Chip Cookie Dough</li>\n  <li>Mint Chocolate Chip</li>\n  <li>Neon Mind Melter</li>\n</ul>\n```\n\nNote that the `:items` parameter is prefixed by a colon, meaning it will pass the _value_ of a `flavors` variable, if it exists.\n\n\n:::best-practice\nTo set default values for parameters inside your partials, you can [add YAML front-matter](/variables/#view-frontmatter) to the top of your Antlers partials.\n\nIn the example below, the partial has two front-matter variables (`author` and `image`). When the partial is called, the `author` parameter is provided. When the partial is outputted, the `author` parameter is used, and the value for the `image` variable falls back to the partial's front-matter.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ partial:card author=\"David Hasselhoff\" }}\n```\n\n```antlers\n---\nauthor: Jack McDade\nimage: https://example.com/placeholder.png\n---\n\n<img src=\"{{ view:image }}\">\n<p>Written by {{ view:author }}</p>\n```\n::tab blade\n```blade\n<s:partial:card author=\"David Hasselhoff\" />\n```\n\n```blade\n@frontmatter([\n  'author' => 'Jack McDade',\n  'image'  => 'https://example.com/placeholder.png',\n])\n\n<img src=\"{{ $view['image'] }}\">\n<p>Written by {{ $view['author'] }}</p>\n```\n::\n\n```html\n<img src=\"https://example.com/placeholder.png\"> <!-- No image was provided, so falling back to the front-matter. -->\n<p>Written by David Hasselhoff</p> <!-- Author parameter provided, so using that. -->\n```\n\nThis technique is preferrable over defining custom variables inside your partial.\n\n:::\n\n\n## Slots\n\nSometimes you might need to pass a large chunk of content into a partial. Jamming a bunch of HTML through a parameter would be like trying to shove a pizza through a donut. A hilarious YouTube video but a bad idea on a Friday night.\n\nThe solution is to use the partial tag as a pair. Everything inside will be passed into the partial in the `{{ slot }}` variable.\n\n::tabs\n::tab antlers\n```antlers\n// Your main view\n{{ partial:modal title=\"Confirmation\" }}\n  <div class=\"flex items-center\">\n    <img src=\"/img/warning.svg\" />\n    <p>Are you sure you want to delete your entire collection of WWE wrestling figures?</p>\n  </div>\n{{ /partial:modal }}\n\n// _modal.antlers.html\n<div class=\"bg-white rounded shadow\">\n  <h2 class=\"p-2 border-b\">{{ title }}</h2>\n  {{ slot }}\n  <button @click=\"confirm\" class=\"button\">Do it</button>\n</div>\n```\n::tab blade\n```blade\n// Your main view\n<s:partial:modal title=\"Confirmation\">\n  <div class=\"flex items-center\">\n    <img src=\"/img/warning.svg\" />\n    <p>Are you sure you want to delete your entire collection of WWE wrestling figures?</p>\n  </div>\n</s:partial:modal>\n\n// _modal.blade.php\n<div class=\"bg-white rounded shadow\">\n  <h2 class=\"p-2 border-b\">{{ $title }}</h2>\n  {!! $slot !!}\n  <button @click=\"confirm\" class=\"button\">Do it</button>\n</div>\n```\n::\n\n```output\n<div class=\"bg-white rounded shadow\">\n  <h2 class=\"p-2 border-b\">Confirmation</h2>\n    <div class=\"flex items-center\">\n      <img src=\"/img/warning.svg\" />\n      <p>Are you sure you want to delete your entire collection of WWE wrestling figures?</p>\n  </div>\n  <button @click=\"confirm\" class=\"button\">Do it</button>\n</div>\n```\n\nYou can also name your slots using the `name` parameter.\n\n\n## Conditional Rendering\n\nYou can render a partial only if a condition is met.\n\n::tabs\n::tab antlers\n```antlers\n{{ partial:components/subtitle :when=\"subtitle\" }}\n    {{ subtitle }}\n{{ /partial:components/subtitle }}\n```\n::tab blade\n```blade\n<s:partial:components/subtitle :when=\"isset($subtitle)\">\n  {{ $subtitle }}\n</s:partial:components/subtitle>\n```\n::\n\nAlso supports the converse using `:unless`.\n\n## Using With Modifiers\n\nBecause the `partial` tag is a tag and not a variable, you can't pipe it through [modifiers](/modifiers) directly. To apply modifiers to a partial's rendered output, wrap it in a [sub-expression](/antlers#sub-expressions) using curly braces.\n\n```antlers\n{{ { partial:component } | spaceless }}\n```\n\nEverything inside the `{ ... }` is parsed first, and the result is then passed through the modifier chain — handy for things like `spaceless`, `markdown`, `trim`, or any other modifier you'd want to run on a partial's output.\n\n## Related Reading\n\nIf you haven't read up on [views](/views) yet, you should. It's considered fundamental knowledge, like knowing that seals are just dog mermaids. 🐕 🧜‍♀️\n\nYou may also be interested in the [`partial:exists`](/tags/partial-exists) or [`partial:if_exists`](/tags/partial-if-exists) tags.\n"
  },
  {
    "path": "content/collections/tags/protect-password_form.md",
    "content": "---\nid: 2a2ec438-4274-4de7-9261-94221507e6c6\ntitle: 'Protect:Password_Form'\nintro: 'This tag is used to create a custom content [password protection](/protecting-content#password) form.'\nparameters:\n  -\n    name: HTML Attributes\n    type: string\n    description: >\n      Set HTML attributes as if you were on an HTML element. For example, `class=\"required\" id=\"contact-form\"`.\nvariables:\n  -\n    name: invalid_token\n    type: boolean\n    description: |\n      Returns `true` when the token is missing or invalid. Functionally the same as the `no_token` variable.\n  -\n    name: errors\n    type: array\n    description: |\n      An indexed array of any validation errors upon submission. For example: `{{ errors }}{{ value }}{{ /errors }}`\n  -\n    name: error\n    type: array\n    description: |\n      An array of validation errors indexed by **field name**. For example: `{{ error:email }}`\n  -\n    name: old\n    type: array\n    description: An array of submitted values from the previous request. Used for re-populating fields if there are validation errors.\n  -\n    name: success\n    type: string\n    description: A success message that can be used in a condition to check if the password was valid. `{{ if success }} Welcome to Narnia! {{ /if }}`\nrelated_entries:\n  - 75be125b-7d92-496c-ac5d-7098560d3d44\n---\n## Overview\n\nThe HTML of the form itself is up to you. The only requirement is to name the password input `password` and wrap the form with the tag pair.\n\nAny variables from the protected entry will also be available in the password form.\n\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ protect:password_form }}\n    {{ if invalid_token }}\n        No token has been provided.\n    {{ else }}\n\n        {{ if error }}\n            <div class=\"error\">{{ error }}</div>\n        {{ /if }}\n\n        <input type=\"password\" name=\"password\" />\n\n        {{ errors:password }}\n            <div class=\"inline-error\">{{ value }}</div>\n        {{ /errors:password }}\n\n        <button>Submit</button>\n\n    {{ /if }}\n{{ /protect:password_form }}\n```\n::tab blade\n```blade\n<s:protect:password_form>\n  @if ($no_token)\n    No token has been provided.\n  @else\n    @if ($error)\n      <div class=\"error\">{{ $error }}</div>\n    @endif\n\n    <input type=\"password\" name=\"password\" />\n\n    @if (isset($errors['password']))\n      @foreach ($errors['password'] as $error)\n        <div class=\"inline-error\">{{ $error }}</div>\n      @endforeach\n    @endif\n\n    <button>Submit</button>\n  @endif\n</s:protect:password_form>\n```\n::\n\n### Tokens\n\nWhen visiting a password protected page, Statamic generates a token and appends it to the form’s URL. Without this token, the form cannot function correctly. This is to combat brute-forcing and bots.\n\nIn the example above, you can see the `invalid_token` boolean will be populated for you. This may happen if you visit the form URL directly.\n"
  },
  {
    "path": "content/collections/tags/redirect.md",
    "content": "---\ntitle: Redirect\ndescription: Redirects visitor to another URL\nintro: |\n  Anytime this tag is rendered — whether in a template, partial, or content, Statamic will redirect the visitor to the specified URL.\nparameters:\n  -\n    name: url\n    type: string\n    description: Destination URL\n  -\n    name: to\n    type: string\n    description: Alias of `url`\n  -\n    name: route\n    type: string\n    description: Instead of entering a URL, you can specify a route name. Any other parameters will be passed along as route parameters.\n  -\n    name: response\n    type: integer\n    description: 'The HTTP response code to use. Default: `302` (temporary).'\nid: 444d1109-ed96-4162-b86d-24b39f569220\n---\n## Redirecting to URLs\n\nLet's redirect visitors to the homepage if they're not logged in.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if ! logged_in }}\n  {{ redirect to=\"/\" }}\n{{ /if }}\n```\n::tab blade\n```blade\n@if (! $logged_in)\n  <s:redirect to=\"/\" />\n@endif\n```\n::\n\nHow about RickRolling visitors if it's April Fool's Day?\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if (now|format:m-d) == \"04-01\" }}\n  {{ redirect to=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\" }}\n{{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::modify($now)->format('m-d')->fetch() == '04-01')\n  <s:redirect to=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\" />\n@endif\n```\n\n::\n\n\n## Named Routes\n\nYou may redirect to named routes with the `route` parameter. Anything else will be passed along as route parameters.\n\n```php\nRoute::get('products/{product}/{size}', fn($product, $size) => ...)\n     ->name('products.show');\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ redirect route=\"products.show\" product=\"socks\" size=\"large\" }}\n// /products/socks/large\n```\n::tab blade\n```blade\n<s:redirect\n  route=\"products.show\"\n  product=\"socks\"\n  size=\"large\"\n/>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/route.md",
    "content": "---\ntitle: Route\ndescription: 'Generate URLs to your named routes'\nintro: 'The route tag allows you to generate the full URL for a given named route, including any parameters.'\nvariables:\n  -\n    name: name\n    type: string\n    description: 'The route''s name'\n  -\n    name: route params\n    type: various\n    description: parameters the route requires\nid: d99ec742-2947-4b72-ba39-af51a9fed626\n---\n## Overview\nThis tag is equivalent to the `route()` helper in [Laravel](https://laravel.com/docs/urls#urls-for-named-routes). Useful for outputting the correct urls for all your glorious routes.\n\n## Example\n\n``` php\n  Route::put('bacon/{bacon}', 'BaconController@update')->name('bacon.update');\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<form action=\"{{ route:bacon.update :bacon=\"id\" }}\">\n   ... yummy bacon goodness\n</form>\n```\n::tab blade\n```blade\n<form action=\"{{ route('bacon.update', ['bacon' => $id]) }}\">\n   ... yummy bacon goodness\n</form>\n```\n::\n\n```html\n<form action=\"/bacon/6\">\n   ... yummy bacon goodness\n</form>\n```\n"
  },
  {
    "path": "content/collections/tags/scope.md",
    "content": "---\nid: d4f29394-ac2f-4f73-bc33-5ec081324e2e\nblueprint: tag\ntitle: Scope\nintro: 'Used to push all of the \"root\", or page scope data into an array to be used however you see fit.'\nparameters:\n  -\n    name: scope\n    type: 'tag part'\n    description: 'The name of the scope while. This is not a parameter, but part of the tag itself. For example, `{{ scope:plop }}`.'\n    required: false\n  -\n    name: handle_prefix\n    type: string\n    description: 'A prefix to prepend to variable names when looking up data. For example, if you have a variable named `hero_title` and use `handle_prefix=\"hero_\"`, you can reference it as `{{ title }}`.'\n    required: false\n---\n## Overview\n\nMost commonly this take is used to avoid any kind of variable collisions or to confirm data to a particular naming mechanism. Scoping is most often done on the Tag level (e.g. the [Collection Tag](/tags/collection/#scope)), but gives you another level of control and flexibility.\n\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n---\ntitle: Grimmace Shake\n---\n\n{{ scope:stuff }}\n<h1>{{ stuff:title }}</h1>\n{{ /scope:stuff }}\n```\n::tab blade\n```blade\n<?php\n  $title = 'Grimmace Shake';\n?>\n\n<s:scope:stuff>\n  <h1>{{ $stuff['title'] }}</h1>\n</s:scope:stuff>\n```\n::\n\nThis will output:\n\n```html\n<h1>Grimmace Shake</h1>\n```\n\n## Handle Prefix\n\nThe `handle_prefix` parameter lets you access prefixed variables without needing to include the prefix. This is useful when working with [imported fieldsets](/blueprints#importing-fieldsets) that use a prefix.\n\nFor example, if you have a fieldset imported with the prefix `hero_`, you might have variables like `hero_title` and `hero_description`. Using `handle_prefix`, you can reference them without the prefix:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ scope handle_prefix=\"hero_\" }}\n    {{ if title }}\n        <h1>{{ title }}</h1>\n        <p>{{ description }}</p>\n    {{ /if }}\n{{ /scope }}\n```\n::tab blade\n```blade\n<s:scope handle_prefix=\"hero_\">\n    @if ($title)\n        <h1>{{ $title }}</h1>\n        <p>{{ $description }}</p>\n    @endif\n</s:scope>\n```\n::\n\nIn this example, `{{ title }}` will resolve to the value of `hero_title`, and `{{ description }}` will resolve to `hero_description`.\n"
  },
  {
    "path": "content/collections/tags/search.md",
    "content": "---\ntitle: Search\ndescription: Performs searches and displays matching results\nintro: This is how you do search. This is the tag you're looking for.\nid: fe8ec156-447d-4f03-974f-0251a8c53244\nparameters:\n  -\n    name: index\n    type: string\n    description: >\n      The search index to query. Default: `default`.\n  -\n    name: query\n    type: 'string'\n    description: >\n      The query string parameter used for the search term. Default: `q`.\n  -\n    name: site\n    type: string\n    required: false\n    description: >\n      The site(s) you wish to search. Pass a single site handle (`one`), multiple handles pipe-separated (`one|two`), or a wildcard (`*`) to search all sites. Default: the current site.\n  -\n    name: limit\n    type: integer\n    description: Limit the total results returned.\n  -\n    name: offset\n    type: integer\n    description: Skip the first `n` number of results.\n  -\n    name: as\n    type: string\n    description: >\n      Alias your results into a new variable loop.\n  -\n    name: supplement_data\n    type: string\n    description: >\n      When `true` will include all non-indexed content field. See [supplementing data](#supplementing-data) Default: `true`.\n  -\n    name: for\n    type: string\n    required: false\n    description: 'The term to be searched for. Overrides the `query` parameter.'\n  -\n    name: paginate\n    type: 'boolean|int *false*'\n    description: 'Specify whether your results should be paginated. You can pass `true` and also use the `limit` param, or just pass the limit directly in here.'\n    required: false\n  -\n    name: page_name\n    type: 'string *page*'\n    description: 'The query string variable used to store the page number (ie. `?page=`).'\n    required: false\n  -\n    name: on_each_side\n    type: 'int *3*'\n    description: When using pagination, this specifies the max number of links each side of the current page. The minimum value is `1`.\n  -\n    name: chunk\n    type: int\n    description: 'Chunking results can significantly reduce memory usage when loading lots of results. Specify how many results should be included in each \"chunk\".'\nvariables:\n  -\n    name: no_results\n    type: boolean\n    description: If there are no results.\n  -\n    name: first\n    type: boolean\n    description: If this is the first item in the loop.\n  -\n    name: last\n    type: boolean\n    description: If this is the last item in the loop.\n  -\n    name: count\n    type: integer\n    description: >\n      The number of current iteration in the\n      loop.\n  -\n    name: index\n    type: integer\n    description: >\n      The zero-based count of the current\n      iteration in the loop.\n  -\n    name: total_results\n    type: integer\n    description: >\n      The number of results in the loop. \n      When you're paginating results, this will be the number of results on the current page. If you need to get the total number of results across all pages, you can use `{{ paginate:total_items }}`.\n  -\n    name: search_score\n    type: float\n    description: >\n      The internal relevance score that\n      Statamic given to this result. Helpful\n      for debugging, but useless to the\n      public. Only applies when using the local driver.\n  -\n    name: result_type\n    type: string\n    description: The type of result. e.g. `entry`, `term`, `asset`, etc.\n  -\n    name: search_snippets\n    type: array\n    description: >\n      .\n  -\n    name: _highlightResult\n    type: array\n    description: >\n      Available when using the [Algolia driver](https://www.algolia.com/doc/api-client/php/search#fields). Displays a field with the search term automatically highlighted. Example: `{{ _highlightResult:myfield:value }}`\nvariables_content: |\n  The following variables are **Antlers-only**. See [Loop variables](/antlers#loop-variables) for details, or the [Blade equivalents](/blade#loop-variables) if you're writing Blade.\n---\n## Overview\n\nAn overview on how to _configure_ search, indexing, and the query form can be found in the [Search Docs](/search).\n\n\n## Example\n\nOn a search result page, you can loop through the results of the search like they were entries. You'll have access to all the data of all the content of your search results returned so you can format them any way you wish.\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ search:results }}\n\n  {{ if no_results }}\n    <h2>No results.</h2>\n  {{ else }}\n\n    <a href=\"{{ url }}\" class=\"result\">\n      <h2>{{ title }}</h2>\n      <p>{{ content | truncate:240 }}</p>\n    </a>\n\n  {{ /if }}\n\n{{ /search:results }}\n```\n::tab blade\n```blade\n<s:search:results as=\"results\">\n  @forelse($results as $result)\n    <a href=\"{{ $result->url }}\" class=\"result\">\n      <h2>{{ $result->title }}</h2>\n      <p>{{ Statamic::modify($result->content)->truncate(240) }}</p>\n    </a>\n  @empty\n    <h2>No results.</h2>\n  @endforelse\n</s:search:results>\n```\n\n:::tip\nWhen using Blade, make sure to alias your search results if you want to use variables like `$no_results`!\n:::\n\n::\n\n## Search Forms\n\nThe search form itself — that text box users type into, is a normal, every day HTML form with a `search` input that submits to a URL containing a `search:results` tag in the template. Nice and simple.\n\n```\n<form action=\"/search/results\">\n    <input type=\"search\" name=\"q\" placeholder=\"Search\">\n    <button type=\"submit\">Make it so!</button>\n</form>\n```\n\n## Multiple Sites\n\nOn [multi-site](/multi-site) installations, you can search within a specific site, a subset of sites, or across all of them.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ search:results site=\"one\" }}\n\n{{ search:results site=\"one|two\" }}\n\n{{ search:results site=\"*\" }}\n```\n::tab blade\n```blade\n<s:search:results site=\"one\" />\n\n<s:search:results site=\"one|two\" />\n\n<s:search:results site=\"*\" />\n```\n::\n\nIf you omit the `site` parameter, results will be scoped to the current site.\n\n:::tip\nWhen using `supplement_data=\"false\"` with multiple sites, make sure the `site` field is indexed — otherwise results will be filtered out. See [Supplementing Data](#supplementing-data) below.\n:::\n\n## Supplementing Data\n\nBy default, data will be supplemented. This means that while your search indexes can remain lean by only including the fields you actually\nwant to be searchable, the tag will convert your results into full objects (entries, terms, etc.) which allow you to use any of their fields.\n\nThere is an overhead associated with this though, so if all you need is to display values that are in the index, you may disable supplementing.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ search:results supplement_data=\"false\" }}\n```\n::tab blade\n```blade\n<s:search:results supplement_data=\"false\">\n  ...\n</s:search:results>\n```\n::\n\nThis has a few caveats:\n\n- Only fields that you've indexed will be available.\n- The search tag will filter out any unpublished items by default. If you haven't indexed the `status` field, you will get no results. Either\n  index the `status` field, or add `status:is=\"\"` to your tag to prevent the filtering.\n- When using multiple sites, the search tag will filter items for the current site. If you haven't indexed the `site` field, you will get no results. Either\n  index the `site` field, or add `site:is=\"\"` to your tag to prevent the filtering.\n- You won't be able to use variables like `result_type` and `search_score`.\n\n## Contextual Keyword Snippets\n\nThis feature works slightly differently depending on the driver you're using.\n\n\n### Comb / Local\n::tabs\n::tab antlers\n```\n{{ search:results }}\n  {{ search_snippets:title | implode(' … ') | mark }}\n{{ /search:results }}\n```\n::tab blade\n```blade\n<s:search:results as=\"results\">\n  @foreach ($results as $result)\n    {!! Statamic::modify($result->search_snippets['title'])->implode(' … ')->mark() !!}\n  @endforeach\n</s:search:results>\n```\n::\n\n### Algolia\nHighlights are typically always available via `search_highlights`. The more powerful feature, [snippets](https://www.algolia.com/doc/api-reference/api-parameters/attributesToSnippet/), are available via `search_snippets` if you configure your index to use them. For example:\n\n```php\n'indexes' => [\n    'default' => [\n        'driver' => 'algolia',\n        'settings' => [ // [tl! **:start]\n            'attributesToSnippet' => [\n                'title:40',\n                'teaser:40',\n            ],\n            'highlightPreTag' => '<mark>',\n            'highlightPostTag' => '</mark>',\n        ], // [tl! **:end]\n    ],\n]\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ search:results }}\n  {{ search_snippets:title:value }}\n  or\n  {{ search_highlights:title:value }}\n{{ /search:results }}\n```\n::tab blade\n```blade\n<s:search:results as=\"results\">\n  @foreach ($results as $result)\n    {{ $result->search_snippets['title']['value'] }}\n    or\n    {{ $result->search_highlights['title']['value'] }}\n  @endforeach\n</s:search:results>\n```\n\n:::tip\nCalling `$result->searchSnippets()` without any arguments returns an array with all search snippets!\n:::\n::\n"
  },
  {
    "path": "content/collections/tags/section.md",
    "content": "---\ntitle: Section\ndescription: Extracts markup to be rendered elsewhere with yield.\nintro: 'The section tag is a useful way to abstract and reuse your views by extracting a section of markup that can then be rendered elsewhere with a [yield tag](/tags/yield).'\nid: 21481d1a-ee1b-4acd-b5ad-65dc7fcec976\n---\n## Overview\n\nMost commonly this section/yield approach is used to create a global area in your layout that can be changed by your templates. This eliminates the need for any brittle and messy logic.\n\n**Cheatsheet:**\n\n- <span class=\"text-red-600 font-bold\">No thank you:</span> `{{ if template == \"news\" }} hardcode something {{ /if }}`\n- <span class=\"text-green-700 font-bold\">Yes please:</span> `{{ section:something }}` + `{{ yield:something }}`\n\n## Example\n\nIn the example below, everything within the `section:sidebar` tag will _not_ be rendered in the template, but rather in the layout.\n\n::tabs\n\n::tab antlers\n```antlers\n// The Template\n\n<h1>{{ title }}</h1>\n{{ content }}\n\n{{ section:sidebar }}\n  <h2>About the Author</h2>\n  <div>\n    {{ author:name }}\n  </div>\n  {{ author:bio }}\n{{ /section:sidebar }}\n```\n\n```antlers\n// The Layout\n<html>\n  <head>\n    <title>{{ title }} | {{ site_name }}</title>\n  </head>\n  <body>\n    <article>\n      {{ template_content }}\n    </article>\n    <aside>\n      {{ yield:sidebar }}\n    </aside>\n  </body>\n</html>\n```\n\n::tab blade\n```blade\n// The Template\n@extends('layout')\n\n<h1>{{ $title }}</h1>\n{!! $content !!}\n\n@section('sidebar')\n  <h2>About the Author</h2>\n  <div>\n    {{ $author['name'] }}\n  </div>\n  {{ $author['bio'] }}\n@endsection\n```\n\n```blade\n// The Layout\n<html>\n  <head>\n    <title>{{ $title }} | {{ $site_name }}</title>\n  </head>\n  <body>\n    <article>\n      {!! $template_content !!}\n    </article>\n    <aside>\n      @yield('sidebar')\n    </aside>\n  </body>\n</html>\n```\n::\n\n## Related Reading\n\nIf you haven't read up on [templates and layouts](/views), you should. It's relevant.\n\n\n[yield_tag]: /tags/yield\n"
  },
  {
    "path": "content/collections/tags/session-dump.md",
    "content": "---\ntitle: 'Session:Dump'\ndescription: 'Peek into the user session for debugging purposes.'\nintro: The contents of the user session can be dumped to the browser. You never know when you need to peek inside the black box.\nid: d1ef36ac-7d21-40b2-a7fc-b53a0be3c79c\n---\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:dump }}\n```\n::tab blade\n```blade\n{{-- Using session() helper and PHP --}}\n@php(dump(session()->all()))\n\n{{-- Using Antlers Blade Components --}}\n<s:session:dump />\n```\n::\n\n<figure>\n    <img src=\"/img/session-dump.png\" alt=\"Screenshot of the output of a session:dump tag.\">\n    <figcaption>If you're lucky, your dump can look like this.</figcaption>\n</figure>\n"
  },
  {
    "path": "content/collections/tags/session-flash.md",
    "content": "---\ntitle: 'Session:Flash'\ndescription: 'Store data for a single request.'\nintro: Flash data is session data that is only kept for a single request. It is most often used for success/failure messages that automatically disappear after a page refresh.\nid: 29957a36-a15a-4fd0-9342-b829b6235fea\n---\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:flash message=\"You did it!\" }}\n```\n::tab blade\n```blade\n{{-- Using PHP --}}\n@php(session()->flash('message', 'You did it!'))\n\n{{-- Using Antlers Blade Components --}}\n<s:session:flash message=\"You did it!\" />\n```\n::\n\nThe next (and only next) request will then have that variable available.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:message }} // You did it!\n```\n::tab blade\n```blade\n{{-- Using PHP --}}\n@php(session()->get('message'))\n\n{{-- Using Antlers Blade Components --}}\n<s:session:message />\n```\n::\n\n## Multiple Variables\n\nYou can set multiple variables at once and reference interpolated data (references to variables).\n\n::tabs\n::tab antlers\n```antlers\n{{ session:flash success=\"true\" :clicked=\"order_button\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:session:flash\n  success=\"true\"\n  :clicked=\"$order_button\"\n/>\n\n{{-- Using PHP --}}\n<?php\n  session()->flash('success');\n  session()->flash('clicked', $order_button);\n?>\n```\n::\n\n\n## Setting Array Data\n\nArray data can be set with dot notation.\n\n::tabs\n::tab antlers\n```antlers\n{{ session:flash likes.snow_cones=\"true\" likes.italian_ice=\"false\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:session:flash\n  likes.snow_cones=\"true\"\n  likes.italian_ice=\"false\"\n/>\n\n{{-- Using PHP --}}\n<?php\n  session()->flash('likes.snow_cones');\n  session()->flash('likes.italian_ice', false);\n?>\n```\n::\n\n"
  },
  {
    "path": "content/collections/tags/session-flush.md",
    "content": "---\ntitle: 'Session:Flush'\ndescription: 'Clears the entire user session.'\nintro: The flush tag will wipe the entire user session. This will also sign a user out if they're signed in.\nid: 1f522665-9fc2-4c9f-9594-04a518c51b39\n---\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:flush }}\n```\n::tab blade\n```blade\n{{-- Using Statamic Antlers Components --}}\n<s:session:flush />\n\n{{-- Using PHP --}}\n@php(session()->flush())\n```\n::\n\nThat's all there is to it. How you use it is up to you. You may want to tuck this away behind an `if` statement or a unique URL.\n\n**Did you know?** In Australia the session flushes the other way. 🇦🇺\n"
  },
  {
    "path": "content/collections/tags/session-forget.md",
    "content": "---\ntitle: 'Session:Forget'\ndescription: 'Remove data from the user session.'\nintro: Remove variables from the user session by passing the names of the variables into the `keys` parameter.\nid: be024503-9796-4f2f-9c75-548e2ea09cec\n---\n## Example\n\nPass multiple keys by delimiting them with a pipe.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:forget keys=\"likes|referral\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:session:forget keys=\"likes|referral\" />\n\n{{-- Using the session() helper --}}\n@php(session()->forget(['likes', 'referral']))\n```\n::\n\n:::tip\nThe **entire** session can be wiped with the [session:flush](/tags/session-flush) tag. Always wipe and flush, folks.\n\nThis is the humor you came for and the software you (maybe) paid for.\n:::\n"
  },
  {
    "path": "content/collections/tags/session-has.md",
    "content": "---\nid: 36342e2a-aa6d-4e4b-898e-77e78c958470\nblueprint: tag\ntitle: 'Session:Has'\ndescription: 'Check if data exists in the user session.'\nintro: 'This tag checks the session to see if any given keys exist in a user session.'\nrelated_entries:\n  - c15836c2-808d-4260-9d01-e5a569da5b5a\n  - 90796c5b-6b11-4b02-9e6b-fd70211c825a\n---\n## Example\n\nThis is a very simple tag designed to be used in a condition statement. You may check to see if a key has been set (most likely by the [session:set](/tags/session-set) tag) in the session. It returns either `true` or `false`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {session:has key=\"entered_survey\"} }}\n    <p>You already filled out the survey. Thanks for your feedback!</p>\n{{ /if }}\n```\n::tab blade\n```blade\n{{-- We can use the session() helper in Blade --}}\n@if (session()->has('entered_survery'))\n  <p>You have already filled out the survey. Thanks for your feedback!</p>\n@endif\n```\n::\n"
  },
  {
    "path": "content/collections/tags/session-set.md",
    "content": "---\ntitle: 'Session:Set'\ndescription: 'Store and persist data in the user session.'\nintro: Data set in the session will be available in all future requests until such time that the session is cleared over time (sessions eventually expire) or intentionally. Session variables can be retrieved with the main [session](/tags/session) tag.\nid: 90796c5b-6b11-4b02-9e6b-fd70211c825a\n---\n## Example\n\nThis can be used for many different things. For example, you could set a variable on the form success page if a user has filled out a special survey form.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:set entered_survey=\"true\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:session:set entered_survey=\"true\" />\n\n{{-- Using session() helper --}}\n@php(session()->put('entered_survey', true))\n```\n::\n\nLater you could decide to show a message instead of the form if the user has already filled it out.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session }}\n    {{ if entered_survey }}\n        <p>You already filled out the form.</p>\n    {{ /if }}\n{{ /session }}\n```\n::tab blade\n```blade\n@if (session()->get('entered_survey'))\n  <p>You already filled out the form.</p>\n@endif\n```\n::\n\n## Multiple Variables\n\nYou can set multiple variables at once and reference interpolated data (references to variables).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:set likes=\"hats\" :visited=\"url\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:session:set likes=\"hats\" :visited=\"url\" />\n```\n::\n\n## Tag Pair\n\nThis tag is also available as a pair, which can be used to immediately display set data.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:set likes=\"boomboxes\" }}\n    <p>You like {{ likes }}, huh?</p>\n{{ /session:set }}\n```\n::tab blade\n```blade\n<s:session:set likes=\"boomboxes\">\n  <p>You like {{ $likes }}, huh?</p>\n</s:session:set>\n```\n::\n\n## Setting Array Data\n\nArray data can be set with dot notation.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:set likes.books=\"true\" likes.hats=\"true\" }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:session:set likes.books=\"true\" likes.hats=\"true\" />\n```\n::\n\n## Forgetting Data\n\nData can be removed from the session [session:forget](/tags/session-forget), and the entire session flushed with [session:flush](/tags/session-flush). 🚽\n"
  },
  {
    "path": "content/collections/tags/session.md",
    "content": "---\ntitle: Session\ndescription: 'Get, set, check, and forget data in your user''s session.'\nintro: 'Sessions provide a stateless way to store information about the user across requests. The session tag will let you get, set, and forget session data.'\nis_parent_tag: true\nid: c15836c2-808d-4260-9d01-e5a569da5b5a\n---\n## Retrieving Session Data\n\nYou can use `{{ session }}` as a tag pair to access all the data inside your user's session.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session }}\n    {{ message }}\n{{ /session }}\n```\n::tab blade\n```blade\n<s:session>\n  {{ $message }}\n</s:session>\n```\n::\n\n```.output\nWelcome to the session.\n```\n\nYou can also retrieve single variables with a single tag syntax.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session:message }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:session:message />\n\n{{-- Using session() helper --}}\n{{ session()->get('message') }}\n```\n::\n\n## Setting and Forgetting\n\nYou can set data with [session:set](/tags/session-set), flash data with [session:flash](/tags/session-flash), forget it with [session:forget](/tags/session-forget), and flush the entire session with [session:flush](/tags/session-flush).\n\n## Checking\n\nYou can check if data is set in a session with [session:has](/tags/session-has).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {session:has key=\"has_voted\"} === true }}\n  You already voted. Thank you!\n{{ /if }}\n```\n::tab blade\n```blade\n@if (session()->has('has_voted') === true)\n  You already voted. Thank you!\n@endif\n```\n::\n\n## Debugging\n\nIf you want to peek into the session and check the data, do so with with [session:dump](/tags/session-dump) or the [debug bar](/debugging#debug-bar).\n\n## Aliasing\n\nIf you need extra markup around your session data, you can _alias_ a new child array variable.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ session as=\"sesh\" }}\n  {{ sesh }}\n    {{ message }}\n  {{ /sesh }}\n{{ /session }}\n```\n::tab blade\n```blade\n{{-- Using the session() helper and PHP --}}\n@php($sesh = session()->all())\n```\n::\n\n"
  },
  {
    "path": "content/collections/tags/svg.md",
    "content": "---\ntitle: SVG\ndescription: 'Renders inline SVGs'\nintro: |\n  The SVG tag renders inline SVGs and lets you easily set attributes on the `<svg>` element.\n\nparameters:\n  -\n    name: src\n    type: string\n    description: |\n      The svg filename relative to root. The `.svg` extension is optional. Intelligently looks through (in this order):\n      - `resources/svg`\n      - `resources`\n      - `public/svg`\n      - `public`\n  -\n    name: sanitize\n    type: boolean\n    description: Determines whether the SVG should be sanitized before being output. Defaults to `true`.\n  -\n    name: allow_attrs\n    type: array\n    description: >\n      Set an array of allowable attributes to bypass sanitization. Example: `allow_attrs=\"from|to\"`.\n  -\n    name: '*'\n    type: string\n    description: >\n      Any additional parameter will be set as attributes on the `<svg>` element. For example `class=\"fill-current\"` will set `<svg class=\"fill-current\" ...>`.\nid: 4f54ed6a-4a80-4ee4-899b-cbae5cd3b73c\n---\n## Overview\n\nWorking with inline SVGs gives you the ability to cache them along with your markup and style them with CSS. It's one of the best reasons to work with SVG images.\n\nTo make dev life easier, this tag collapses whitespace automatically and can set attributes like `class` or `height/width` for you. It looks in `resources` and `public` automatically to keep your [src](#parameters) parameter nice and short, like a summer haircut.\n\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n// Using resources/svg/circle.svg\n{{ svg src=\"circle\" class=\"fill-current text-teal\" }}\n\n// Using public/img/icons/square\n{{ svg src=\"img/icons/square\" class=\"fill-current text-mint\" }}\n\n// Using a variable `promo_graphic` (defined in your blueprint)\n{{ svg :src=\"promo_graphic\" class=\"fill-current text-orange\" }}\n```\n::tab blade\n```blade\n// Using resources/svg/circle.svg\n<s:svg src=\"circle\" class=\"fill-current text-teal\" />\n\n// Using public/img/icons/square\n<s:svg src=\"img/icons/square\" class=\"fill-current text-mint\" />\n\n// Using a variable `promo_graphic` (defined in your blueprint)\n<s:svg :src=\"$promo_graphic\" class=\"fill-current text-orange\" />\n```\n::\n\n```html\n<svg class=\"fill-current text-teal\" viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n  <circle cx=\"50\" cy=\"50\" r=\"50\"/>\n</svg>\n\n<svg class=\"fill-current text-mint\" viewBox=\"0 0 220 100\" xmlns=\"http://www.w3.org/2000/svg\">\n  <rect width=\"100\" height=\"100\" />\n</svg>\n```\n\n## Sanitization\n\nAll SVGs are sanitized on upload into the control panel _and_ on output for to pretect your site from malicious code or other forms of potential compromise. You can [learn more about what's possible](https://www.cloudflare.com/threat-intelligence/research/report/svgs-the-hackers-canvas/) for hackers to attempt with SVGs.\n\nHowever, this sanitization may prove to be more aggressive than is beneficial for you. If you are in complete control of your uploads and trust your control panel users, you can disable sanitzation on upload in your assets config file like this:\n\n``` php\n// config/statamic/assets.php\n'svg_sanitization_on_upload' => false,\n```\n\nCombine that setting with the `sanitize=\"false\"` or `allow_attrs` parameters documented below to allow those additional SVG attributes and elements to render on your frontend.\n\n## Additional Reading\n\n- [SVG Properties and CSS](https://css-tricks.com/svg-properties-and-css/)\n- Tailwind has numerous SVG helpers, like [fill](https://tailwindcss.com/docs/fill) and [stroke](https://tailwindcss.com/docs/stroke), and a video on [working with SVG Icons](https://tailwindcss.com/course/working-with-svg-icons)\n"
  },
  {
    "path": "content/collections/tags/switch.md",
    "content": "---\ntitle: Switch\ndescription: Loops a given set of values repeatedly\nintro: >\n  Each time a switch tag is rendered it will return the next value from its `between` parameter until it reaches the end where it will start all over again.\nparameters:\n  -\n    name: between\n    type: array\n    description: >\n      A set of values to iterate over, using a pipe-separated string.\nid: 8b558556-a08b-4134-b77d-102b4fb34060\n---\n## Overview\n\nThe switch tag is most often used to write HTML classes in your markup to help style lists and grids of items. While CSS has gained a lot of features in recent years with CSS selectors like `nth-of-type`, the switch tag is more relevant than ever, especially in combination with utility frameworks like [TailwindCSS](https://tailwindcss.com).\n\n## Examples\n\nHere are a few ideas on what you can do with the switch tag.\n\n### Set alternating background color for table rows\n\n::tabs\n\n::tab antlers\n```antlers\n<table>\n  {{ collection:shows }}\n    <tr class=\"{{ switch between='bg-white|bg-grey-100' }}\">\n      <th>{{ title }}</th>\n      <td>{{ rating }}</td>\n    <tr>\n  {{ /collection:shows }}\n</table>\n```\n::tab blade\n```blade\n<table>\n  <s:collection:shows>\n    <tr class=\"{{ Statamic::tag('switch')->between('bg-white|bg-grey-100') }}\">\n     <th>{{ $title }}</th>\n     <td>{{ $rating }}</td>\n    </tr>\n  </s:collection:shows>\n</table>\n```\n::\n\n### Reverse every other pair of items with `flex-direction: row-reverse`\n\n::tabs\n\n::tab antlers\n```antlers\n{{ features }}\n  <div class=\"flex {{ switch between='flex-row|flex-row-reverse' }}\">\n    <div class=\"w-1/2 px-4 m-2\">\n      <h2>{{ feature_name }}</h2>\n      <div>{{ description }}</div>\n    </div>\n    <img src=\"{{ feature_screenshot }}\" class=\"w-1/2 m-2\">\n  </div>\n{{ /features }}\n```\n::tab blade\n```blade\n@foreach ($features as $feature)\n  <div class=\"flex {{ Statamic::tag('switch')->between('flex-row|flex-row-reverse') }}\">\n    <div class=\"w-1/2 px-4 m-2\">\n      <h2>{{ $feature->feature_name }}</h2>\n      <div>{{ $feature->description }}</div>\n    </div>\n    <img src=\"{{ $feature->feature_screenshot }}\" class=\"w-1/2 m-2\">\n  </div>\n@endforeach\n```\n::\n\n## Multiple Instances\n\nYou can have multiple instances of the switch tag in a single view and they won't collide with each other as long as the your set of parameters is unique.\n\nIf you want to have multiple, identical switch tags you can add an extra parameter to keep track of which is which.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ switch between=\"even|odd\" for=\"gallery\" }}\n{{ switch between=\"even|odd\" for=\"footer\" }}\n```\n::tab blade\n```blade\n{{ Statamic::tag('switch')->between('even|odd')->for('gallery') }}\n{{ Statamic::tag('switch')->between('even|odd')->for('footer') }}\n```\n::\n\n## Repeating Values\n\nIf you have a lot of redundancy in your `between` parameter, you can simplify it by passing in the number of times you want an element to be repeated.\n\nFor example, if you'd like to set the background of every 10th element to purple, you could set the first value to white 9 times followed by purple 1 time.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ switch between=\"bg-white:9|bg-purple\" }}\n```\n::tab blade\n```blade\n{{ Statamic::tag('switch')->between('bg-white:9|bg-purple') }}\n```\n::\n\n:::tip\nIf you're using [TailwindCSS's JIT mode](https://tailwindcss.com/docs/just-in-time-mode) or you purge your CSS on production, you might notice any classes **only in your switch tag** are missing. Add spaces around the pipes to make sure JIT picks them up.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ switch between=\"bg-white | bg-purple\" }}\n```\n::tab blade\n```blade\n{{ Statamic::tag('switch')->between('bg-white | bg-purple') }}\n```\n::\n\n:::\n"
  },
  {
    "path": "content/collections/tags/taxonomy-count.md",
    "content": "---\ntitle: \"Taxonomy:Count\"\ndescription: Fetches the number of terms in a taxonomy\nintro: |\n  This tag is a clone of the [taxonomy tag](/tags/taxonomy) but with one big difference: it only returns the total number of terms that match your set of filters.\nparent_tag: ba832b71-a567-491c-b1a3-3b3fae214703\nparameters:\n  -\n    name: in|from\n    type: string\n    description: >\n      The taxonomy in which to count terms.\n  -\n    name: \"*\"\n    type: inherit\n    description: 'All parameters available on the [taxonomy tag](/tags/taxonomy) are available.'\nid: b32c0902-9642-4625-b4bc-de68fa8dfee2\n---\n## Overview\n\nThis tag's only purpose is to fetch the number of the terms in a taxonomy that match your set of filtering parameters without the need for a tag pair/loop.\n\n## Example\n\n```\nThere are {{ taxonomy:count in=\"tags\" }} tags in this site.\n```\n\n```html\nThere are 6201 tags in this site.\n```\n\nYou could do the same thing inside a regular taxonomy tag by aliasing the results to a single variable and using the [count modifier](/modifiers/count).\n\n```\n{{ taxonomy:tags as=\"terms\" }}\nThere are {{ terms | count }} tags in this site.\n{{ /taxonomy:tags }}\n```\n"
  },
  {
    "path": "content/collections/tags/taxonomy.md",
    "content": "---\ntitle: Taxonomy\noverview: Fetch and filter Taxonomy terms.\nintro: Taxonomy terms are grouped into taxonomies and are fetched and filtered by this tag. A taxonomy could contain tags, categories, or sock colors.\ndescription: Fetches and filters terms in one or more taxonomies.\nparameters:\n  -\n    name: taxonomy\n    type: tag part\n    description: 'The taxonomy to use. This is not actually a parameter, but part of the tag itself. For example, `{{ taxonomy:categories }}`'\n  -\n    name: taxonomy|is|use|from|folder\n    type: string\n    description: >\n      When using the verbose syntax, this is how you specify which taxonomy to use.\n  -\n    name: min_count\n    type: 'integer *0*'\n    description: >\n      The minimum number of entries a taxonomy term\n      must have to show up in the list.\n  -\n    name: collection\n    type: string\n    description: >\n      Filter the listing by terms that\n      only appear in the specified collection.\n      You may pipe-separate multiple\n      collections.\n  -\n    name: sort\n    type: 'string *title*'\n    description: >\n      Sort terms by a field. By default it will be sorted by the title. Also available is `entries_count:desc` if you wanted to sort by the most popular terms.\n  -\n    name: filter\n    type: wizardry\n    description: >\n      Filter the listing by either a custom\n      class or using a special syntax, both of\n      which are outlined in more detail within the [Filtering](#filtering) section.\nvariables:\n  -\n    name: first\n    type: boolean\n    description: If this is the first item in the loop.\n  -\n    name: last\n    type: boolean\n    description: If this is the last item in the loop.\n  -\n    name: count\n    type: integer\n    description: >\n      The number of current iteration in the\n      loop.\n  -\n    name: index\n    type: integer\n    description: >\n      The zero-based count of the current\n      iteration in the loop.\n  -\n    name: total_results\n    type: integer\n    description: The number of results in the loop.\n  -\n    name: entries_count\n    type: integer\n    description: >\n      The number of entries taxonomized by this term.\n  -\n    name: taxonomy data\n    type: mixed\n    description: >\n      Each taxonomy being iterated has access\n      to all the variables inside that\n      taxonomy. This includes things like\n      `title`, `content`, etc.\n  -\n    name: entries\n    type: query builder\n    description: >\n      If you use this as a tag pair, you can loop through entries associated with the term. See [entries](#entries) above.\nvariables_content: |\n  The following variables are **Antlers-only**. See [Loop variables](/antlers#loop-variables) for details, or the [Blade equivalents](/blade#loop-variables) if you're writing Blade.\nid: ba832b71-a567-491c-b1a3-3b3fae214703\n---\n## Example {#example}\n\n\nA basic example would be to loop through the terms in a tags taxonomy and link to each individual tag:\n\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ taxonomy from=\"tags\" }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n{{ /taxonomy }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n<s:taxonomy from=\"tags\">\n  <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n</s:taxonomy>\n</ul>\n```\n::\n\nYou can also use the shorthand syntax for this. We prefer this style ourselves.\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ taxonomy:tags }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n{{ /taxonomy:tags }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n<s:taxonomy:tags>\n  <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n</s:taxonomy:tags>\n</ul>\n```\n::\n\nIf you'd like to fetch tags from multiple taxonomies, you'll need to use the standard syntax.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ taxonomy from=\"tags|categories\" }}\n```\n::tab blade\n```blade\n<s:taxonomy from=\"tags|categories\">\n\n</s:taxonomy>\n```\n::\n\nTo get terms from _all_ taxonomies, use the wildcard `*`. You may also exclude taxonomies when doing this.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ taxonomy from=\"*\" not_from=\"tags\" }}\n```\n::tab blade\n```blade\n<s:taxonomy from=\"*\" not_from=\"tags\">\n\n</s:taxonomy>\n```\n::\n\n## Entries\n\nThe `taxonomy` tag allows you to iterate over taxonomy terms, but in each iteration, you also have access to all the corresponding content.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ taxonomy:categories }}\n  <h2>{{ title }}</h2>\n  <ul>\n    {{ entries }}\n      <li><a href=\"{{ url }}\">{{ title }}</a></li>\n    {{ /entries }}\n  </ul>\n{{ /taxonomy:categories }}\n```\n::tab blade\n```blade\n<s:taxonomy:categories>\n  <h2>{{ $title }}</h2>\n  <ul>\n    @foreach ($entries as $entry)\n      <li><a href=\"{{ $entry->url }}\">{{ $entry->title }}</a></li>\n    @endforeach\n  </ul>\n</s:taxonomy:categories>\n```\n::\n\n```html\n<h2>News</h2>\n<ul>\n  <li><a href=\"/blog/breaking\">A breaking story!</a></li>\n  <li><a href=\"/blog/so-interesting\">An interesting article</a></li>\n</ul>\n\n<h2>Events</h2>\n<ul>\n  <li><a href=\"/events/walk-in-the-park\">A walk in the park</a></li>\n  <li><a href=\"/events/summer-camp\">Summer camp</a></li>\n</ul>\n```\n\nYou're free to use filtering or sorting parameters on the `entries` pair that you'd find on the [collection tag](/tags/collection).\n\n> To use the `entries` tag, the Taxonomy must already be [attached to the Collection](/taxonomies#collections).\n\n## Filtering\n\nThere are a couple of ways to filter your taxonomy terms. There's the conditions syntax for filtering by fields, or the custom filter class if you need extra control.\n\n### Conditions\n\nWant to get entries where the title has the words \"awesome\" and \"thing\", and \"joe\" is the author? You can write it how you'd say it:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ taxonomy:tags title:contains=\"awesome\" title:contains=\"thing\" author:is=\"joe\" }}\n```\n::tab blade\n```blade\n<s:taxonomy:tags\n  title:contains=\"awesome\"\n  title:contains=\"thing\"\n  author:is=\"joe\"\n>\n</s:taxonomy:tags>\n```\n::\n\nThere are a bunch of conditions available to you, like `:is`, `:isnt`, `:contains`, `:starts_with`, and `:is_before`. There are many more than that. In fact, there's a whole page dedicated to [conditions - check them out][conditions].\n\n\n### Custom Query Scopes\n\nDoing something custom or complicated? You can create [query scopes](/extending/query-scopes-and-filters) to narrow down those results.\n\n\n\n[conditions]: /conditions\n[custom_filters]: /addons/classes/filters\n"
  },
  {
    "path": "content/collections/tags/trans.md",
    "content": "---\ntitle: Translate\nintro: |\n  Retrieve a string from a language file in the current locale.\nparameters:\n  -\n    name: key\n    type: tagpart|string\n    description: 'The key of the translation string to find. Include both the filename and string key delimited with dots. Can be used as a tag part or a `key` parameter. If your key contains a namespace, you should use the key parameter instead of the tag part.'\n  -\n    name: locale|site\n    type: string\n    description: 'The locale to be used when translating.'\n  -\n    name: 'any parameters'\n    type: string\n    description: 'Any additional parameters will be treated as parameters that should be replaced in the string.'\n  -\n    name: count\n    type: 'integer *1*'\n    description: 'When using `trans_choice`, this is the number that defines the pluralization.'\n  -\n    name: fallback\n    type: string\n    description: 'A fallback string or translation key to use when the requested key does not exist.'\nid: 8ff99539-8b1a-4380-adf7-bdad979f8afd\n---\nThis tag is the equivalent of the [trans and trans_choice methods](https://laravel.com/docs/localization) provided by Laravel.\n\n:::tip\nThere's also a [modifier](/modifiers/trans) version that you may prefer.\n:::\n\n## Usage\n\nGet the `bar` string from the `lang/en/foo.php` translation file (where `en` is the current locale).\n\n```php\n<?php\nreturn [\n    'bar' => 'Bar!',\n    'welcome' => 'Welcome, :name!',\n    'apples' => 'There is one apple|There are :count apples',\n];\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ trans:foo.bar }} or {{ trans key=\"foo.bar\" }}\n```\n::tab blade\n```blade\n{{ trans('foo.bar') or {{ __('foo.bar') }}\n```\n::\n\n```html\nBar!\n```\n\n## Replacements\n\nAny additional tag parameters will be treated as parameters that should be replaced in the string.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ trans:foo.welcome name=\"Bob\" }}\n```\n::tab blade\n```blade\n{{ trans('foo.welcome', ['name' => 'Bob']) }}\n```\n::\n\n```html\nWelcome, Bob!\n```\n\n## Pluralization\n\nTo pluralize, use the `trans_choice` tag with a `count` parameter.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ trans_choice:foo.apples count=\"2\" }}\n```\n::tab blade\n```blade\n{{ trans_choice('foo.apples', 2) }}\n```\n::\n\n```html\nThere are 2 apples\n```\n\n## Fallback\n\nProvide a `fallback` parameter to use when the translation key doesn't exist. The fallback can be either a literal string or another translation key.\n\n```antlers\n{{ trans key=\"messages.does_not_exist\" fallback=\"Literal fallback\" }}\n```\n\n```html\nLiteral fallback\n```\n\nIf the fallback itself is a valid translation key, that translation will be used instead.\n\n```antlers\n{{ trans key=\"messages.does_not_exist\" fallback=\"messages.fallback_key\" }}\n```\n\n```html\nFallback from existing key\n```\n\nParameter replacements are also applied to the fallback.\n\n```antlers\n{{ trans key=\"messages.does_not_exist\" name=\"Bob\" fallback=\"Hello, :name\" }}\n```\n\n```html\nHello, Bob\n```\n"
  },
  {
    "path": "content/collections/tags/user-can.md",
    "content": "---\ntitle: User:Can\ndescription: Checks if a user has a specific permission\nintro: Anything inside the `user:can` tag will only be rendered if the user has the specified permission.\nparameters:\n  -\n    name: permission|do\n    type: string\n    description: >\n      The permissions to check against. You can use the parameter `permission` or `do`, depending on you feel about the grammar of each case. Specify multiple permissions by pipe separating them: `{{ user:can do=\"things|stuff\" }}`.\nid: 649f1eb3-cd60-46ec-ba07-38e2a4747952\n---\n## Overview\n\nUser tags are designed for sites that have areas or features behind a login. The `{{ user:can }}` tag is used to check if the currently logged in user has a one or more specific permissions.\n\n## Example\n\nLet's say we want a link to edit the current entry in the control panel if the user has the `edit faq entries` permission.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:can do=\"edit faq entries\" }}\n    <a href=\"{{ edit_url }}\">Edit this Page</a>\n{{ /user:can }}\n```\n::tab blade\n```blade\n{{-- Using user methods --}}\n@if (auth()->user()?->can('edit blog entries'))\n  ...\n@endif\n\n{{-- Using Fluent Tags --}}\n@if (Statamic::tag('user:can')->do('edit blog entries')->fetch())\n  ...\n@endif\n\n{{-- Using Antlers Blade Components --}}\n<s:user:can\n  do=\"edit blog entries\"\n>\n  ...\n</s:user:can>\n```\n::\n\n## Super Users\n\n[Super users](/users#super-users) are granted permission for any Statamic-related abilities. When a permission doesn't exist in Statamic, it'll fall back to [traditional Laravel authorization](https://laravel.com/docs/master/authorization#main-content).\n\n## Passing Arguments to Gates\n\nWhen falling back to Laravel authorization, you can pass additional arguments to your gate by adding extra parameters to the tag. Any parameter besides `do`/`permission` will be forwarded to the gate in the order it's defined.\n\n```php\nGate::define('view secret', function ($user, $secret) {\n    return $user->hasAccessToSecret($secret);\n});\n```\n\n```antlers\n{{ user:can do=\"view secret\" secret=\"foo\" }}\n    ...\n{{ /user:can }}\n```\n\n## Can’t\n\nWe also support the negative use case using `{{ user:cant }}` tags.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:cant do=\"anything\" }}\n  <p>Aww, I'm sure that's not true! 😊</p>\n{{ /user:cant }}\n```\n::tab blade\n```blade\n{{-- Using user methods --}}\n@if (auth()->user()?->cant('do anything'))\n  <p>Aww, I'm sure that's not true! 😊</p>\n@endif\n\n{{-- Using Fluent Tags --}}\n@if (Statamic::tag('user:cant')->do('anything')->fetch())\n  <p>Aww, I'm sure that's not true! 😊</p>\n@endif\n\n{{-- Using Antlers Blade Components --}}\n<s:user:cant\n  do=\"anything\"\n>\n  <p>Aww, I'm sure that's not true! 😊</p>\n</s:user:cant>\n```\n::\n\n## Permissions List\n\nCheck out the the complete [list of user permissions](/users#permissions).\n"
  },
  {
    "path": "content/collections/tags/user-delete_passkey_form.md",
    "content": "---\nid: d0ed4ac0-536a-47a1-965b-202b52baddb2\nblueprint: tag\ntitle: 'User:Delete_Passkey_Form'\ndescription: 'Creates a form to delete a passkey'\nintro: 'As the tag name suggests, it allows you to delete a passkey.'\nparameters:\n  -\n    name: id\n    type: string\n    description: 'The passkey ID to delete. Required.'\n  -\n    name: redirect\n    type: string\n    description: Where the user should be taken after successfully deleting a passkey.\n  -\n    name: HTML Attributes\n    type:\n    description: 'Set HTML attributes as if you were in an HTML element. For example, `class=\"delete-form\"`.'\nrelated_entries:\n    - 38323438-4719-4a7b-ba5a-8abfe0d7dfc0\n    - 7a958307-4cdb-47f3-a689-0c7de57e3ff7\n    - 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a\n---\n## Overview\n\nThe `user:delete_passkey_form` tag renders a form to delete a passkey.\n\n### Example\n\nThe tag is typically used inside a [`{{ user:passkeys }}`](/tags/user-passkeys) loop:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:passkeys as=\"passkeys\" }}\n    {{ passkeys }}\n        <div>\n            {{ name }}\n\n            {{ user:delete_passkey_form :id=\"id\" }}\n                <button type=\"submit\">Delete</button>\n            {{ /user:delete_passkey_form }}\n        </div>\n    {{ /passkeys }}\n{{ /user:passkeys }}\n```\n::tab blade\n```blade\n<s:user:passkeys as=\"passkeys\">\n    @foreach ($passkeys as $passkey)\n        <div>\n            {{ $passkey['name'] }}\n\n            <s:user:delete_passkey_form :id=\"$passkey['id']\">\n                <button type=\"submit\">Delete</button>\n            </s:user:delete_passkey_form>\n        </div>\n    @endforeach\n</s:user:passkeys>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-disable_two_factor_form.md",
    "content": "---\ntitle: User:Disable_Two_Factor_Form\ndescription: Renders a form to disable 2FA on the user's account\nintro: Allow users to turn off two-factor authentication. If their role requires 2FA, they'll be prompted to set it up again.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: Where the user should be taken after disabling 2FA.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When set to true, the `redirect` parameter will get overridden by a `redirect` query parameter in the URL.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"disable-form\"`.\nvariables:\n  -\n    name: success\n    type: string\n    description: A success message.\nid: 8f3d4a9b-0c2e-4f7a-3b6d-1e4f5a8b0c2d\n---\n## Overview\n\nThe `user:disable_two_factor_form` tag renders a form that allows authenticated users to disable two-factor authentication on their account. This removes the 2FA requirement and deletes their recovery codes.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. No input fields are required—just a submit button.\n\n:::tip\nThis form requires the user to be authenticated with 2FA enabled and an [elevated session](/tags/user-elevated_session_form). If the session isn't elevated, the user will be redirected to confirm their identity first.\n:::\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:disable_two_factor_form redirect=\"/account\" }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}\n        </div>\n    {{ /if }}\n\n    <p>Are you sure you want to disable two-factor authentication?</p>\n    <button type=\"submit\">Disable Two-Factor Authentication</button>\n\n{{ /user:disable_two_factor_form }}\n```\n::tab blade\n```blade\n<s:user:disable_two_factor_form redirect=\"/account\">\n    @if ($success)\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ $success }}\n        </div>\n    @endif\n\n    <p>Are you sure you want to disable two-factor authentication?</p>\n    <button type=\"submit\">Disable Two-Factor Authentication</button>\n</s:user:disable_two_factor_form>\n```\n::\n\n## Enforced 2FA\n\nIf the user belongs to a role that has 2FA enforced (configured via `two_factor_enforced_roles` in your config), they can't really stay signed in with 2FA off — so after the form is submitted, Statamic ignores the `redirect` parameter and sends them to the setup page instead. That destination is pulled from the `statamic.users.two_factor_setup_url` config key in `config/statamic/users.php`, falling back to Statamic's built-in setup route if that's left `null`.\n"
  },
  {
    "path": "content/collections/tags/user-elevated_session_form.md",
    "content": "---\ntitle: User:Elevated_Session_Form\ndescription: Creates a form to confirm user identity for elevated sessions\nintro: If you want to protect sensitive frontend actions with re-authentication, this tag renders the form for users to confirm their identity.\nparameters:\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"confirm-form\"`.\nvariables:\n  -\n    name: method\n    type: string\n    description: |\n      The authentication method required. One of: `password_confirmation`, `verification_code`, or `passkey`.\n  -\n    name: allow_passkey\n    type: boolean\n    description: |\n      Whether the user has passkeys available as an authentication option.\n  -\n    name: resend_code_url\n    type: string\n    description: |\n      URL to resend the verification code. Only relevant when `method` is `verification_code`.\n  -\n    name: passkey_options_url\n    type: string\n    description: |\n      URL to fetch WebAuthn assertion options for passkey authentication.\n  -\n    name: submit_url\n    type: string\n    description: |\n      The URL where the form submits to. Useful when implementing passkey authentication via JavaScript.\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\n  -\n    name: old\n    type: array\n    description: An array of previously submitted values.\nrelated_entries:\n  - 5eab02e3-c76b-4f44-a304-6a78877d099f\nid: 45cca7b8-63e1-4a26-bd61-6ae9cfb4a3ce\n---\n## Overview\n\n[Elevated Sessions](/control-panel/elevated-sessions) allow you to prompt users for their password or a verification code before being able to take certain actions.\n\nThe `user:elevated_session_form` tag renders a form allowing authenticated users to confirm their identity and start an elevated session. Useful for protecting sensitive actions that require re-authentication.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. The rest of the form markup is up to you.\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:elevated_session_form }}\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    {{ if method == \"password_confirmation\" }}\n        <label>Password</label>\n        <input type=\"password\" name=\"password\" />\n    {{ /if }}\n\n    {{ if method == \"verification_code\" }}\n        <p>A verification code has been sent to your email.</p>\n        <label>Verification Code</label>\n        <input type=\"text\" name=\"verification_code\" />\n        <a href=\"{{ resend_code_url }}\">Resend code</a>\n    {{ /if }}\n\n    <button type=\"submit\">Confirm</button>\n{{ /user:elevated_session_form }}\n```\n::tab blade\n```blade\n<s:user:elevated_session_form>\n  @if ($errors)\n    <div class=\"bg-red-300 text-white p-2\">\n      @foreach ($errors as $error)\n        {{ $error }}<br>\n      @endforeach\n    </div>\n  @endif\n\n  @if ($method === 'password_confirmation')\n    <label>Password</label>\n    <input type=\"password\" name=\"password\" />\n  @endif\n\n  @if ($method === 'verification_code')\n    <p>A verification code has been sent to your email.</p>\n    <label>Verification Code</label>\n    <input type=\"text\" name=\"verification_code\" />\n    <a href=\"{{ $resend_code_url }}\">Resend code</a>\n  @endif\n\n  <button type=\"submit\">Confirm</button>\n</s:user:elevated_session_form>\n```\n::\n\n## Authentication Methods\n\nThe `method` variable indicates how the user should confirm their identity:\n\n- **`password_confirmation`** - User should enter their password.\n- **`verification_code`** - User doesn't have a password, so they should enter the verification code sent to their email.\n- **`passkey`** - User requires a passkey to login. Passkey authentication requires JavaScript - see the below section for more details.\n\n### Passkeys\n\nPasskey authentication requires JavaScript. Include the frontend helpers script and use the `passkey_options_url` and `submit_url` variables:\n\n```antlers\n{{ user:elevated_session_form }}\n    {{ if method == \"password_confirmation\" }}\n        <input type=\"password\" name=\"password\" />\n        <button type=\"submit\">Confirm with Password</button>\n    {{ /if }}\n\n    {{ if method == \"verification_code\" }}\n        <p>A verification code has been sent to your email.</p>\n        <label>Verification Code</label>\n        <input type=\"text\" name=\"verification_code\" />\n        <a href=\"{{ resend_code_url }}\">Resend code</a>\n        <button type=\"submit\">Confirm with Verification Code</button>\n    {{ /if }}\n\n    {{ if allow_passkey }}\n        <button type=\"button\" id=\"passkey-confirm\">Confirm with Passkey</button> {{# [tl! focus:start] #}}\n\n        <script src=\"/vendor/statamic/frontend/js/helpers.js\"></script>\n        <script>\n            Statamic.$passkeys.configure({\n                optionsUrl: '{{ passkey_options_url }}',\n                verifyUrl: '{{ submit_url }}',\n                onSuccess: (data) => window.location = data.redirect || '/',\n                onError: (error) => alert(error.message)\n            });\n\n            document.getElementById('passkey-confirm').addEventListener('click', () => {\n                Statamic.$passkeys.authenticate();\n            });\n        </script> {{# [tl! focus:end] #}}\n    {{ /if }}\n{{ /user:elevated_session_form }}\n```\n\n## Protecting routes\n\nTo require an elevated session on your routes, apply the middleware:\n\n```php\nuse Statamic\\Http\\Middleware\\RequireElevatedSession;\n\nRoute::get('/account/sensitive-action', SensitiveActionController::class)\n    ->middleware(['auth', RequireElevatedSession::class]);\n```\n\nWhen users access a protected route without an elevated session, they'll be redirected to confirm their identity. After successful confirmation, they're redirected back to the original URL.\n\n## Configuration\n\nIn `config/statamic/users.php`:\n\n```php\n// Duration in minutes before the elevated session expires\n'elevated_session_duration' => 15,\n\n// URL to a custom elevated session page.\n// When null, it'll fallback to a Statamic-powered page.\n'elevated_sessions_url' => null,\n```\n"
  },
  {
    "path": "content/collections/tags/user-forgot_password_form.md",
    "content": "---\ntitle: User:Forgot_Password_Form\ndescription: Creates a \"Forgot Password\" form\nintro: This tag is used to create a \"forgot my password\" form for your users.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: >\n      Where the user should be taken after requesting a password reset.\n  -\n    name: error_redirect\n    type: string\n    description: >\n      The same as `redirect`, but for failed password reset requests.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When set to true, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL.\n  -\n    name: reset_url\n    type: string\n    description: >\n      The URL containing your Reset Password\n      Form. A link to this URL will be\n      included in the email, along with the\n      appropriate query parameters.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"forgot-password-form\"`.\nvariables:\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\n  -\n    name: old\n    type: array\n    description: An array of previously submitted values.\n  -\n    name: success\n    type: string\n    description: A success message.\n  -\n    name: email_sent\n    type: string\n    description: An alias of the `success` variable.\nid: 3e69f12e-72ac-4f1a-9847-fa08d651e750\n---\n## Overview\n\nUsers will enter their email address in the form and, upon submitting, an email will be sent with a link to create a new password.\n\nUnless you set a `redirect` parameter, the user will be redirected back to the **same page** after submitting, and the `success` variable will allow you to show a success message.\n\n## Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:forgot_password_form reset_url=\"/reset-password\" }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}<br>\n        </div>\n    {{ /if }}\n\n    <label>Email</label>\n    <input type=\"text\" name=\"email\" value=\"{{ old:email }}\" />\n\n    <button type=\"submit\">Send Reset Email</button>\n\n{{ /user:forgot_password_form }}\n```\n::tab blade\n```blade\n<s:user:forgot_password_form\n  reset_url=\"/reset-password\"\n>\n  @if ($errors)\n    <div class=\"bg-red-300 text-white p-2\">\n      @foreach ($errors as $error)\n        {{ $error }}<br>\n      @endforeach\n    </div>\n  @endif\n\n  @if ($success)\n    <div class=\"bg-green-300 text-white p-2\">\n      {{ $success }}<br>\n    </div>\n  @endif\n\n  <label>Email</label>\n  <input type=\"text\" name=\"email\" value=\"{{ old('email') }}\" />\n\n  <button type=\"submit\">Send Reset Email</button>\n</s:user:forgot_password_form>\n```\n::\n\nThe email will contain a link to the URL specified in the `reset_url` parameter, along with extra query parameters. On that URL you must have a [user:reset_password_form](/tags/user-reset_password_form) tag to finish the task and let the user set their new password.\n\n:::tip\nThe user needs to be logged out for this tag to do anything. You may want to wrap the form in `{{ if logged_out }}{{ /if }}`.\n:::\n\n## The email {#email}\n\nOnce the form is submitted, an email will be sent containing the URL for resetting the password.\n\nThis email is bundled with Statamic and will work for most people out of the box. However, if you'd like to customize it, you can.\n\nThe template is named `user-reset` and should contain a `{{ reset_url }}` variable, which is the generated reset URL.\n\n[custom-emails]: /tips/emails#templates\n"
  },
  {
    "path": "content/collections/tags/user-groups.md",
    "content": "---\nid: 878f0dd7-2d31-479c-b58d-bc60685fa7d3\nblueprint: tag\ntitle: User_Groups\ndescription: 'Fetch and iterate over User Groups and their data.'\nintro: 'The `user_groups` tag is used to return any groups you have added to collate the users on your site.'\nparameters:\n  -\n    name: handle\n    type: string\n    description: 'The handle(s) of the groups you want to return. You may specify multiple groups by pipe separating them: `{{ user_groups handle=\"jocks|geeks\" }}`.'\n    required: false\nvariables:\n  -\n    name: handle\n    type: string\n    description: The group's unique identifier.\n  -\n    name: title\n    type: string\n    description: The group's display title.\n---\n## Overview\n\nThe User Groups tag fetches lists of the user groups on your site so you can do whatever you want with them.\n\nA simple example is to loop through all the groups and list them by handle:\n\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ user_groups }}\n    <li>{{ handle }}</li>\n{{ /user_groups }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n<s:user_groups>\n  <li>{{ $handle }}</li>\n</s:user_groups>\n</ul>\n\n{{-- Aliasing the groups. --}}\n<s:user_groups\n  as=\"groups\"\n>\n  ...\n\n  @foreach ($groups as $group)\n    ...\n  @endforeach\n\n  ...\n</s:user_groups>\n```\n::\n\n## Filtering\n\nIf you only want a specific group or groups, you can pass their handle(s) using the `handle` parameter.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user_groups handle=\"group_1|group_2\" }}\n  // cool stuff goes here\n{{ /user_groups }}\n```\n::tab blade\n```blade\n<s:user_groups\n  handle=\"group_1|group_2\"\n>\n  // cool stuff goes here\n</s:user_groups>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-in.md",
    "content": "---\ntitle: User:In\ndescription: Checks if a user is in a specific user group\nintro: Anything inside the `user:in` tag will only be rendered if the user is in the specified group.\nparameters:\n  -\n    name: group|groups\n    type: string\n    description: |\n      The group or groups to filter by. You may specify multiple groups by pipe separating them: `{{ user:in groups=\"jocks|geeks\" }}`.\nid: 57184c18-28d3-433f-b6ee-0e4539f6b504\n---\n## Overview\nUser tags are designed for sites that have areas or features behind a login. The `{{ user:in }}` tag is used to check if the currently logged in user is in a specific user group.\n\n## Example\n\nLet's say we want show a list of downloadable PDFs if the user is in a `coaches` group.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:in group=\"coaches\" }}\n<ul>\n  {{ assets in=\"pdf\" }}\n    <li><a href=\"{{ url }}\">{{ title }}</a></li>\n  {{ /assets }}\n</ul>\n{{ /user:in }}\n```\n::tab blade\n```blade\n<s:user:in group=\"coaches\">\n<ul>\n  <s:assets in=\"pdf\">\n    <li><a href=\"{{ $url }}\">{{ $title }}</a></li>\n  </s:assets>\n</ul>\n</s:user:in>\n```\n::\n\n## Not In\n\nWe also support the negative use case using the `{{ user:not_in }}` tag.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:not_in group=\"coaches\" }}\n  <p>Hello, sportsball players!</p>\n{{ /user:not_in }}\n```\n::tab blade\n```blade\n<s:user:not_in group=\"coaches\">\n  <p>Hello, sportsball players!</p>\n</s:user:not_in>\n```\n::\n\n## Super Users\n\nWhile [super users](/users#super-users) have [permission](/users#permissions) to do everything, they are not automatically in all groups. Keep this in mind when testing your template logic.\n"
  },
  {
    "path": "content/collections/tags/user-is.md",
    "content": "---\ntitle: User:Is\ndescription: Checks if a user has a specific role\nintro: Anything inside the `user:is` tag will only be rendered if the user has a specific role.\nparameters:\n  -\n    name: role|roles\n    type: string\n    description: 'The role(s) to check against. You may specify multiple roles by pipe separating them: `{{ user:is roles=\"writer|editor\" }}`.'\nid: 8c7f38bb-ee6f-43ee-b775-4eeae0a87bf3\n---\n## Overview\n\nUser tags are designed for sites that have areas or features behind a login. The `user:is` tag is used to check if the currently logged in user has a one or more specific [roles](/users#permissions).\n\n## Example\n\nWe want to show some content on a page especially for `authors`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:is role=\"author\" }}\n<div class=\"markdown\">\n    {{ content }}\n</div>\n{{ /user:is }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:user:is role=\"author\">\n  <div class=\"markdown\">\n    {{ $content }}\n  </div>\n</s:user:is>\n\n{{-- Using Fluent Tags --}}\n@if (Statamic::tag('user:is')->role('author')->fetch())\n  ...\n@endif\n```\n::\n\n### Isn't\n\nWe also support the negative use case using `user:isnt` tags.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:isnt role=\"author\" }}\n    <a href=\"/apply\">Apply to be an author!</a>\n{{ /user:isnt }}\n```\n::tab blade\n```blade\n{{-- Using Antlers Blade Components --}}\n<s:user:isnt role=\"author\">\n  <a href=\"/apply\">Apply to be an author!</a>\n</s:user:isnt>\n\n{{-- Using Fluent Tags --}}\n@if (Statamic::tag('user:isnt')->role('author')->fetch())\n  ...\n@endif\n```\n::\n\n## Super Users\n\nWhile [super users](/users#super-users) have [permission](/users#permissions) to do everything, they do not automatically inherit all roles. Keep this in mind when testing your template logic.\n"
  },
  {
    "path": "content/collections/tags/user-login_form.md",
    "content": "---\ntitle: User:Login_Form\ndescription: Creates user login forms\nintro: If you want to build a login form for your users, this is the easiest way to do it.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: Where the user should be taken after successfully logging in.\n  -\n    name: error_redirect\n    type: string\n    description: >\n      The same as `redirect`, but for failed logins.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When set to true, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"login-form\"`.\nvariables:\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\n  -\n    name: old\n    type: array\n    description: An array of previously submitted values.\n  -\n    name: success\n    type: string\n    description: A success message.\n  -\n    name: passkey_options_url\n    type: string\n    description: URL to fetch WebAuthn assertion options for passkey login.\n  -\n    name: passkey_verify_url\n    type: string\n    description: URL to verify passkey login.\nid: 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a\n---\n## Overview\n\nUser tags are designed for sites that have areas or features behind a login. The `user:login_form` tag helps you build that login form.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. The rest of the form markup is up to you as long as you have an `email` and `password` input field.\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:login_form }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}<br>\n        </div>\n    {{ /if }}\n\n    <label>Email</label>\n    <input type=\"email\" name=\"email\" value=\"{{ old:email }}\" />\n\n    <label>Password</label>\n    <input type=\"password\" name=\"password\" value=\"{{ old:password }}\" />\n\n    <button type=\"submit\">Log in</button>\n\n{{ /user:login_form }}\n```\n::tab blade\n```blade\n<s:user:login_form>\n  @if ($errors)\n    <div class=\"bg-red-300 text-white p-2\">\n      @foreach ($errors as $error)\n        {{ $error }}<br>\n      @endforeach\n    </div>\n  @endif\n\n  @if ($success)\n    <div class=\"bg-green-300 text-white p-2\">\n      {{ $success }}<br>\n    </div>\n  @endif\n\n  <label>Email</label>\n  <input type=\"email\" name=\"email\" value=\"{{ old('email') }}\" />\n\n  <label>Password</label>\n  <input type=\"password\" name=\"password\" value=\"{{ old('password') }}\" />\n\n  <button type=\"submit\">Log in</button>\n</s:user:login_form>\n```\n::\n\n## Precognition\n\nThe login endpoint supports [Laravel Precognition](https://laravel.com/docs/precognition) for live, server-driven validation. See [Precognition for User Forms](/forms#user-forms) for setup and a full example.\n\n## Passkeys\n\nYou can add passkey authentication to your login form using Statamic's frontend JavaScript helpers.\n\n1. First, include the helpers script on your page:\n    ```html\n    <script src=\"/vendor/statamic/frontend/js/helpers.js\"></script>\n    ```\n\n2. Use the provided variables to add a passkey login option:\n    ```antlers\n    {{ user:login_form }}\n        <input type=\"email\" name=\"email\" value=\"{{ old:email }}\" />\n        <input type=\"password\" name=\"password\" value=\"{{ old:password }}\" />\n        <button type=\"submit\">Log in with Password</button>\n\n        <button type=\"button\" id=\"passkey-login\">Login with Passkey</button> {{# [tl! focus:start] #}}\n\n        <script>\n            Statamic.$passkeys.configure({\n                optionsUrl: '{{ passkey_options_url }}',\n                verifyUrl: '{{ passkey_verify_url }}',\n                onSuccess: (data) => window.location = data.redirect || '/',\n                onError: (error) => alert(error.message)\n            });\n\n            document.getElementById('passkey-login').addEventListener('click', () => {\n                Statamic.$passkeys.authenticate();\n            });\n\n            // Enable browser autofill for passkeys\n            Statamic.$passkeys.initAutofill();\n        </script> {{# [tl! focus:end] #}}\n    {{ /user:login_form }}\n    ```\n3. Add `autocomplete=\"username webauthn\"` to your email input for browser autofill to work.\n\nFor more information on managing passkeys on the frontend, see the following docs:\n- [`{{ user:passkeys }}`](/tags/user-passkeys)\n- [`{{ user:passkey_form }}`](/tags/user-passkey_form)\n- [`{{ user:delete_passkey_form }}`](/tags/user-delete_passkey_form)\n\n## Two-Factor Authentication\n\nWhen a user with two-factor authentication (2FA) enabled submits the login form, Statamic will redirect them to a challenge page so they can enter a code from their authenticator app. If the user belongs to a role that requires 2FA but hasn't set it up yet, they'll be redirected to the setup page instead.\n\nYou can customize where each of these redirects goes using the `two_factor_challenge_url` and `two_factor_setup_url` config keys in `config/statamic/users.php`. Leave them `null` to use Statamic's built-in pages.\n\n```php\n// config/statamic/users.php\n\n'two_factor_challenge_url' => '/account/2fa/challenge',\n'two_factor_setup_url' => '/account/2fa/setup',\n```\n\nWhen rolling your own frontend pages, use the following tags to render the forms:\n\n- [`{{ user:two_factor_challenge_form }}`](/tags/user-two_factor_challenge_form) — the code verification form during login\n- [`{{ user:two_factor_enable_form }}`](/tags/user-two_factor_enable_form) — step 1 of setup, generates the secret\n- [`{{ user:two_factor_setup_form }}`](/tags/user-two_factor_setup_form) — step 2 of setup, displays the QR code and confirms the code\n"
  },
  {
    "path": "content/collections/tags/user-logout.md",
    "content": "---\ntitle: User:Logout\ndescription: Logs a user out and redirects them elsewhere\nparameters:\n  -\n    name: redirect\n    type: string\n    description: >\n      Where the user should be redirected\n      after logging out. Defaults to the home\n      page.\nid: 3604bfe9-89be-42af-8163-4b378279026a\n---\n\n## Example {#example}\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if should_logout_for_whatever_reason }}\n  {{ user:logout redirect=\"/somewhere\" }}\n{{ /if }}\n```\n::tab blade\n```blade\n@if ($should_logout_for_whatever_reason)\n  <s:user:logout redirect=\"/somewhere\" />\n@endif\n```\n::\n\nThis will immediately log a user out and redirect to `/somewhere` if the condition is met.\n\nIf you'd like to just output a link, use the [`user:logout_url`](/tags/user-logout_url) tag.\n"
  },
  {
    "path": "content/collections/tags/user-logout_url.md",
    "content": "---\ntitle: User:Logout_URL\ndescription: Generates a user logout URL\nparameters:\n  -\n    name: redirect\n    type: string\n    description: >\n       Where the user should be redirected\n       after logging out. Defaults to the home\n       page.\nid: 232b878c-18da-4d01-80f3-a85fcdf65ed8\n---\n## Example {#example}\n\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ user:logout_url }}\">Log out</a>\n```\n::tab blade\n```blade\n<a href=\"{{ Statamic::tag('user:logout_url')->fetch() }}\">Log out</a>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-passkey_form.md",
    "content": "---\nid: 7a958307-4cdb-47f3-a689-0c7de57e3ff7\nblueprint: tag\ntitle: 'User:Passkey_Form'\ndescription: 'Creates a passkey registration form'\nintro: 'Allows authenticated users to set up passkeys'\nvariables:\n  -\n    name: passkey_option_url\n    type: string\n    description: 'URL to fetch WebAuthn attestation options for creating a new passkey.'\n  -\n    name: passkey_verify_url\n    type: string\n    description: 'URL to store the new passkey after registration.'\nrelated_entries:\n    - 38323438-4719-4a7b-ba5a-8abfe0d7dfc0\n    - d0ed4ac0-536a-47a1-965b-202b52baddb2\n    - 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a\n---\n## Overview\n\nThe `user:passkey_form` tag provides the necessary URLs to set up passkeys for authenticated users.\n\n### JavaScript helpers\n\nYou'll need to include the frontend helpers script on your page:\n\n```html\n<script src=\"/vendor/statamic/frontend/js/helpers.js\"></script>\n```\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:passkey_form }}\n    <input type=\"text\" id=\"passkey-name\" placeholder=\"Passkey name (e.g., My Laptop)\">\n    <button type=\"button\" id=\"create-passkey\">Create Passkey</button>\n\n    <script>\n        document.getElementById('create-passkey').addEventListener('click', () => {\n            const name = document.getElementById('passkey-name').value || 'My Passkey';\n\n            Statamic.$passkeys.register({\n                optionsUrl: '{{ passkey_option_url }}',\n                verifyUrl: '{{ passkey_verify_url }}',\n                name: name,\n                onSuccess: () => location.reload(),\n                onError: (error) => alert(error.message),\n                // csrfToken (optional)\n            });\n        });\n    </script>\n{{ /user:passkey_form }}\n```\n::tab blade\n```blade\n<s:user:passkey_form>\n    <input type=\"text\" id=\"passkey-name\" placeholder=\"Passkey name (e.g., My Laptop)\">\n    <button type=\"button\" id=\"create-passkey\">Create Passkey</button>\n\n    <script>\n        document.getElementById('create-passkey').addEventListener('click', () => {\n            const name = document.getElementById('passkey-name').value || 'My Passkey';\n\n            Statamic.$passkeys.register({\n                optionsUrl: '{{ $passkey_option_url }}',\n                verifyUrl: '{{ $passkey_verify_url }}',\n                name: name,\n                onSuccess: () => location.reload(),\n                onError: (error) => alert(error.message),\n                // csrfToken (optional)\n            });\n        });\n    </script>\n</s:user:passkey_form>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-passkeys.md",
    "content": "---\nid: 38323438-4719-4a7b-ba5a-8abfe0d7dfc0\nblueprint: tag\ntitle: 'User:Passkeys'\ndescription: 'Lists the current user''s passkeys'\nintro: 'Loop through the authenticated user''s registered passkeys.'\nvariables:\n  -\n    name: id\n    type: string\n    description: 'The passkey identifier.'\n  -\n    name: name\n    type: string\n    description: 'The user-defined passkey name.'\n  -\n    name: last_login\n    type: Carbon\n    description: 'The last time the passkey was used for login, or null if never used.'\nrelated_entries:\n    - 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a\n    - 7a958307-4cdb-47f3-a689-0c7de57e3ff7\n    - d0ed4ac0-536a-47a1-965b-202b52baddb2\n---\n## Overview\n\nThe `user:passkeys` tag loops through the user's passkeys. Useful for building a passkey management page where users can view and delete their passkeys.\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:passkeys as=\"passkeys\" }}\n    {{ if passkeys }}\n        <h3>Your Passkeys</h3>\n        <ul>\n            {{ passkeys }}\n                <li>\n                    <strong>{{ name }}</strong>\n                    {{ if last_login }}\n                        <span>Last used: {{ last_login format=\"M j, Y g:i A\" }}</span>\n                    {{ else }}\n                        <span>Never used</span>\n                    {{ /if }}\n\n                    {{ user:delete_passkey_form :id=\"id\" }}\n                        <button type=\"submit\">Delete</button>\n                    {{ /user:delete_passkey_form }}\n                </li>\n            {{ /passkeys }}\n        </ul>\n    {{ else }}\n        <p>You haven't set up any passkeys yet.</p>\n    {{ /if }}\n{{ /user:passkeys }}\n```\n::tab blade\n```blade\n<s:user:passkeys as=\"passkeys\">\n    @if ($passkeys)\n        <h3>Your Passkeys</h3>\n        <ul>\n            @foreach ($passkeys as $passkey)\n                <li>\n                    <strong>{{ $passkey['name'] }}</strong>\n                    @if ($passkey['last_login'])\n                        <span>Last used: {{ $passkey['last_login']->format('M j, Y g:i A') }}</span>\n                    @else\n                        <span>Never used</span>\n                    @endif\n\n                    <s:user:delete_passkey_form :id=\"$passkey['id']\">\n                        <button type=\"submit\">Delete</button>\n                    </s:user:delete_passkey_form>\n                </li>\n            @endforeach\n        </ul>\n    @else\n        <p>You haven't set up any passkeys yet.</p>\n    @endif\n</s:user:passkeys>\n```\n::\n\n## Aliasing\n\nYou can use the `as` parameter to alias the passkeys into a variable, which allows you to use `{{ if passkeys }}` to check if there are any passkeys before rendering.\n\n```antlers\n{{ user:passkeys as=\"passkeys\" }}\n    {{ if passkeys }}\n        {{# Render passkeys list #}}\n    {{ /if }}\n{{ /user:passkeys }}\n```\n"
  },
  {
    "path": "content/collections/tags/user-password_form.md",
    "content": "---\nid: d592bbbf-9bf4-4043-80bb-158e0da497f7\nblueprint: tag\ntitle: 'User:Password_Form'\ndescription: 'Creates a user password update form'\nparameters:\n  -\n    name: redirect\n    type: string\n    description: |\n      Where the user should be taken after successfully saving.\n  -\n    name: error_redirect\n    type: string\n    description: |\n      The same as `redirect`, but for failed a failed submission.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: 'When set to true, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL.'\n  -\n    name: 'HTML Attributes'\n    type: null\n    description: |\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"profile-form\"`.\nvariables:\n  -\n    name: fields\n    type: array\n    description: |\n      An array of available fields for [dynamic rendering](#dynamic-rendering).\n  -\n    name: errors\n    type: array\n    description: 'An array of validation errors.'\n  -\n    name: error\n    type: array\n    description: 'An array of validation errors indexed by field names. Suitable for targeting fields. eg. `{{ error:email }}`'\n  -\n    name: old\n    type: array\n    description: 'An array of previously submitted values.'\n  -\n    name: success\n    type: string\n    description: 'A success message.'\n---\n## Overview\n\nUser tags are designed for sites that have areas or features behind a login. The `user:password_form` tag allows your users to change their password.\n\nThe tag will render the opening and closing `<form>` HTML elements for you.\n\n### Example\n\nA basic user password form, with validation errors.\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:password_form }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}<br>\n        </div>\n    {{ /if }}\n\n    <label>Current Password</label>\n    <input type=\"password\" name=\"current_password\" />\n\n    <label>New Password</label>\n    <input type=\"password\" name=\"password\" />\n\n    <label>Confirm Password</label>\n    <input type=\"password\" name=\"password_confirmation\" />\n\n    <button type=\"submit\">Save</button>\n\n{{ /user:password_form }}\n```\n::tab blade\n```blade\n<s:user:password_form>\n  @if ($errors)\n    <div class=\"bg-red-300 text-white p-2\">\n      @foreach ($errors as $error)\n        {{ $error }}<br>\n      @endforeach\n    </div>\n  @endif\n\n  @if ($success)\n    <div class=\"bg-green-300 text-white p-2\">\n      {{ $success }}<br>\n    </div>\n  @endif\n\n  <label>Current Password</label>\n  <input type=\"password\" name=\"current_password\" />\n\n  <label>New Password</label>\n  <input type=\"password\" name=\"password\" />\n\n  <label>Confirm Password</label>\n  <input type=\"password\" name=\"password_confirmation\" />\n\n  <button type=\"submit\">Save</button>\n</s:user:password_form>\n```\n::\n\n## Dynamic Rendering\n\nInstead of hardcoding individual fields, you may loop through the `fields` array to render fields more dynamically.\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ fields }}\n    <div class=\"p-2\">\n        <label>{{ display }}</label>\n        <div class=\"p-1\">{{ field }}</div>\n        {{ if error }}\n            <p class=\"text-gray-500\">{{ error }}</p>\n        {{ /if }}\n    </div>\n{{ /fields }}\n```\n::tab blade\n```blade\n<s:user:password_form>\n\n  @foreach ($fields as $field)\n    <div class=\"p-2\">\n      <label>{{ $field['display'] }}</label>\n      <div class=\"p-1\">{!! $field['field'] !!}</div>\n      @if ($field['error'])\n        <p class=\"text-gray-500\">{{ $field['error'] }}</p>\n      @endif\n    </div>\n  @endforeach\n\n</s:user:password_form>\n```\n::\n\nEach item in the `fields` array contains `type`, `display` and `handle`, which are configurable from the `user` blueprint.\n\nYou will also find the field's `old` input on unsuccessful submission, as well as an `error` message when relevant.\n\nFinally, the `field` value contains a pre-rendered form input.  Using this will intelligently render inputs as inputs, textareas as textareas, and snozzberries as snozzberries.  You can customize these pre-rendered templates by running `php artisan vendor:publish --tag=statamic-forms`, which will expose editable templates in your `views/vendor/statamic/forms/fields` folder.\n\n## Precognition\n\nThe password update endpoint supports [Laravel Precognition](https://laravel.com/docs/precognition) — useful for catching mismatched confirmations or weak passwords before submitting. See [Precognition for User Forms](/forms#user-forms) for setup and a full example. Post to `/!/auth/password` with `current_password`, `password`, and `password_confirmation` in the `$form` data object.\n\n"
  },
  {
    "path": "content/collections/tags/user-profile.md",
    "content": "---\ntitle: User:Profile\ndescription: Fetches user data.\nintro: Fetching user data made easy.\nparameters:\n  -\n    name: id\n    type: string\n    description: |\n      Fetch user by ID.\n  -\n    name: email\n    type: string\n    description: |\n      Fetch user by email.\n  -\n    name: field\n    type: string\n    description: |\n      Fetch user by a specific field value. Used with `value` below.\n  -\n    name: value\n    type: string\n    description: |\n      Value of above `field` to fetch user by.\nvariables:\n  -\n    name: user data\n    type: mixed\n    description: >\n      All user data (except password) is\n      available.\n  -\n    name: no_results\n    type: boolean\n    description: >\n      `true` if user cannot be found or is logged out.\n  -\n    name: 'is_[role]'\n    type: boolean\n    description: >\n      `true` if user is assigned a given role. For example, `is_admin` or\n      `is_banned`.\n  -\n    name: 'in_[group]'\n    type: boolean\n    description: >\n      `true` if user is in a given group. For example, `in_admin` or\n      `in_editors`.\nid: 3be76d15-dee7-4619-a4cb-4a343e93c677\n---\n## Overview\nThe `user:profile` tag has access to all of a user's basic data. Passwords and hashes are _not_ available through this tag.\n\n:::tip\nThis will default to the currently logged in user if none are specified.\n:::\n\n\n## Shorthand\n\nYou can use `user` and drop the `:profile` bit off if you prefer.\n\n## Examples\n\nTo output the currently logged in user's details, you can do this:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user }}\n  The current user's name is {{ name }}.\n{{ /user }}\n```\n::tab blade\n```blade\n<s:user>\n  The current user's name is {{ $name }}.\n</s:user>\n\n{{-- Aliasing the user. --}}\n<s:user\n  as=\"user\"\n>\n  The current user's name is {{ $user->name }}.\n</s:user>\n```\n::\n\nOr perhaps you'd like to show user profile pages. Assuming your users have a `username` field, you could create a wildcard route like this:\n\n```php\nRoute::statamic('users/{username}', 'users.show');\n```\n\nThen when visiting `/users/chuck`, for example, you could display Chuck's details like this:\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:profile field=\"username\" :value=\"segment_2\" }}\n  {{ first_name }} {{ last_name }}\n{{ /user:profile }}\n```\n::tab blade\n```blade\n<s:user:profile\n  field=\"username\"\n  :value=\"$segment_2 ?? null\"\n>\n  {{ $first_name }} {{ $last_name }}\n</s:user:profile>\n```\n::\n\nOr to find a user by email:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:profile :email=\"email\" }}\n  {{ first_name }} {{ last_name }}\n{{ /user:profile }}\n```\n::tab blade\n```blade\n<s:user:profile\n  :email=\"$email\"\n>\n  {{ $first_name }} {{ $last_name }}\n</s:user:profile>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-profile_form.md",
    "content": "---\nid: 7906225a-9460-4759-8677-20a63a6204b0\nblueprint: tag\ntitle: 'User:Profile_Form'\ndescription: 'Creates user profile edit forms'\nparameters:\n  -\n    name: redirect\n    type: string\n    description: |\n      Where the user should be taken after successfully saving.\n  -\n    name: error_redirect\n    type: string\n    description: |\n      The same as `redirect`, but for failed form submissions.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: 'When set to true, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL.'\n  -\n    name: 'HTML Attributes'\n    type: null\n    description: |\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"profile-form\"`.\nvariables:\n  -\n    name: fields\n    type: array\n    description: |\n      An array of available fields for [dynamic rendering](#dynamic-rendering).\n  -\n    name: errors\n    type: array\n    description: 'An array of validation errors.'\n  -\n    name: error\n    type: array\n    description: 'An array of validation errors indexed by field names. Suitable for targeting fields. eg. `{{ error:email }}`'\n  -\n    name: old\n    type: array\n    description: 'An array of previously submitted values.'\n  -\n    name: success\n    type: string\n    description: 'A success message.'\n---\n## Overview\n\nUser tags are designed for sites that have areas or features behind a login. The `user:profile_form` tag helps you build a user profile edit form for existing users based on your user [Blueprint](/blueprints).\n\nThe tag will render the opening and closing `<form>` HTML elements for you.\n\n### Example\n\nA basic profile edit form, with validation errors.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:profile_form }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}<br>\n        </div>\n    {{ /if }}\n\n    <label>Email</label>\n    <input type=\"email\" name=\"email\" value=\"{{ old:email }}\" />\n\n    <label>Bio</label>\n    <textarea name=\"bio\">{{ old:bio }}</textarea>\n\n    <label>Job Title</label>\n    <input type=\"text\" name=\"title\" value=\"{{ old:title }}\" />\n\n    <button type=\"submit\">Save</button>\n\n{{ /user:profile_form }}\n```\n::tab blade\n```blade\n\n```\n::\n\n## Blueprints Fields\n\nYou may add edit fields for any that exist in the `user.yaml` blueprint.\n\nAny submitted data that does _not_ exist in the blueprint will be completely ignored.\n\nAdditional fields will be validated as per your blueprint `validate` rules.\n\n:::tip Note\nThe `user:profile_form` tag doesn't currently support uploading Assets.\n:::\n\n## Dynamic Rendering\n\nInstead of hardcoding individual fields, you may loop through the `fields` array to render fields more dynamically.\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ fields }}\n    <div class=\"p-2\">\n        <label>{{ display }}</label>\n        <div class=\"p-1\">{{ field }}</div>\n        {{ if error }}\n            <p class=\"text-gray-500\">{{ error }}</p>\n        {{ /if }}\n    </div>\n{{ /fields }}\n```\n::tab blade\n```blade\n<s:user:profile_form>\n\n  @foreach ($fields as $field)\n    <div class=\"p-2\">\n      <label>{{ $field['display'] }}</label>\n      <div class=\"p-1\">{!! $field['field'] !!}</div>\n      @if ($field['error'])\n        <p class=\"text-gray-500\">{{ $field['error'] }}</p>\n      @endif\n    </div>\n  @endforeach\n\n</s:user:profile_form>\n```\n::\n\nEach item in the `fields` array contains `type`, `display` and `handle`, which are configurable from the `user` blueprint.\n\nYou will also find the field's `old` input on unsuccessful submission, as well as an `error` message when relevant.\n\nFinally, the `field` value contains a pre-rendered form input.  Using this will intelligently render inputs as inputs, textareas as textareas, and snozzberries as snozzberries.  You can customize these pre-rendered templates by running `php artisan vendor:publish --tag=statamic-forms`, which will expose editable templates in your `views/vendor/statamic/forms/fields` folder.\n\n## Precognition\n\nThe profile update endpoint supports [Laravel Precognition](https://laravel.com/docs/precognition) for live, server-driven validation against your user blueprint's `validate` rules. See [Precognition for User Forms](/forms#user-forms) for setup and a full example. Post to `/!/auth/profile` and seed the `$form` data object with the user's current values.\n\n"
  },
  {
    "path": "content/collections/tags/user-register_form.md",
    "content": "---\ntitle: User:Register_Form\ndescription: Creates user registration forms\nparameters:\n  -\n    name: redirect\n    type: string\n    description: >\n      Where the user should be taken after\n      successfully registering.\n  -\n    name: error_redirect\n    type: string\n    description: >\n      The same as `redirect`, but for failed registrations.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When set to true, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"registration-form\"`.\nvariables:\n  -\n    name: fields\n    type: array\n    description: >\n      An array of available fields for [dynamic rendering](#dynamic-rendering).\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\n  -\n    name: error\n    type: array\n    description: An array of validation errors indexed by field names. Suitable for targeting fields. eg. `{{ error:email }}`\n  -\n    name: old\n    type: array\n    description: An array of previously submitted values.\n  -\n    name: success\n    type: string\n    description: A success message.\nid: 9323ebd8-c36c-4f0f-b73a-cb5ac4544e72\n---\n## Overview\n\nUser tags are designed for sites that have areas or features behind a login. The `user:registration_form` tag helps you build a public registration form for new users.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. The rest of the form markup is up to you as long as you have `email`, `password`, and `password_confirmation` input fields.\n\n### Example\n\nA basic registration form, with validation errors.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:register_form }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}<br>\n        </div>\n    {{ /if }}\n\n    <label>Email</label>\n    <input type=\"email\" name=\"email\" value=\"{{ old:email }}\" />\n\n    <label>Password</label>\n    <input type=\"password\" name=\"password\" />\n\n    <label>Password Confirmation</label>\n    <input type=\"password\" name=\"password_confirmation\" />\n\n    <button>Register</button>\n\n{{ /user:register_form }}\n```\n::tab blade\n```blade\n<s:user:register_form>\n  @if ($errors)\n    <div class=\"bg-red-300 text-white p-2\">\n      @foreach ($errors as $error)\n        {{ $error }}<br>\n      @endforeach\n    </div>\n  @endif\n\n  @if ($success)\n    <div class=\"bg-green-300 text-white p-2\">\n      {{ $success }}<br>\n    </div>\n  @endif\n\n  <label>Email</label>\n  <input type=\"email\" name=\"email\" value=\"{{ old('email') }}\" />\n\n  <label>Password</label>\n  <input type=\"password\" name=\"password\" />\n\n  <label>Password Confirmation</label>\n  <input type=\"password\" name=\"password_confirmation\" />\n\n  <button>Register</button>\n</s:user:register_form>\n```\n::\n\n## Password Rules\n\nYou may also customize your password rules by explicitly setting a `password` field in your `user.yaml` blueprint.\n\n```yaml\n-\n  handle: password\n  field:\n    type: text\n    display: Password\n    input: password\n    validate: 'min:8|alpha_num'\n```\n\n## Additional Fields\n\nYou are allowed to add any additional fields to your registration form, and they will be added to the user's account provided that they exist in the `user.yaml` blueprint.\n\nAny submitted data that does _not_ exist in the blueprint will be completely ignored.\n\nAdditional fields will be validated as per your blueprint `validate` rules.\n\n## Dynamic Rendering\n\nInstead of hardcoding individual fields, you may loop through the `fields` array to render fields more dynamically.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ fields }}\n    <div class=\"p-2\">\n        <label>{{ display }}</label>\n        <div class=\"p-1\">{{ field }}</div>\n        {{ if error }}\n            <p class=\"text-gray-500\">{{ error }}</p>\n        {{ /if }}\n    </div>\n{{ /fields }}\n```\n::tab blade\n```blade\n<s:user:register_form>\n\n  @foreach ($fields as $field)\n    <div class=\"p-2\">\n      <label>{{ $field['display'] }}</label>\n      <div class=\"p-1\">{!! $field['field'] !!}</div>\n\n      @if ($field['error'])\n        <p class=\"text-gray-500\">{{ $field['error'] }}</p>\n      @endif\n    </div>\n  @endforeach\n\n</s:user:register_form>\n```\n::\n\nEach item in the `fields` array contains `type`, `display` and `handle`, which are configurable from the `user` blueprint.\n\nYou will also find the field's `old` input on unsuccessful submission, as well as an `error` message when relevant.\n\nFinally, the `field` value contains a pre-rendered form input.  Using this will intelligently render inputs as inputs, textareas as textareas, and snozzberries as snozzberries.  You can customize these pre-rendered templates by running `php artisan vendor:publish --tag=statamic-forms`, which will expose editable templates in your `views/vendor/statamic/forms/fields` folder.\n\n## New User Roles\n\nMost of the time, new members will need some roles assigned to them so that they can do different things on your site. You can configure the default `new_user_roles` in your `config/statamic/users.php` config. When a user successfully registers as a member, their account will automatically be assigned the roles in this list.\n\nIt’s best to remember that these are _starting_ roles for the user. You can later either manually add roles to users in their files, update their account through the Control Panel, or have add-ons automatically add or remove roles as needed when users perform certain tasks.\n\n## Precognition\n\nThe registration endpoint supports [Laravel Precognition](https://laravel.com/docs/precognition) — handy for telling users their chosen email is already taken before they hit submit. See [Precognition for User Forms](/forms#user-forms) for setup and a full example. Post to `/!/auth/register` and include any blueprint fields you want validated live in the data object passed to `$form`.\n\n## Honeypot\n\nIf you want to protect your registration form from spam bots you can specify the handle of a [honeypot field](/forms#honeypot) in `config/statamic/users.php` using the `registration_form_honeypot_field` key.\n\n"
  },
  {
    "path": "content/collections/tags/user-reset_password_form.md",
    "content": "---\ntitle: User:Reset_Password_Form\ndescription: Creates a \"Create a New Password\" form\nintro: This tag is used to to set a new password _after_ a user has received the reset link from the \"forgot my password\" form.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: >\n      The URL the user will be taken after the\n      form is successfully submitted. Leaving\n      this blank will keep the user on the\n      same page.\n  -\n    name: error_redirect\n    type: string\n    description: >\n      The same as `redirect`, but for failed validation errors.\nvariables:\n  -\n    name: success\n    type: boolean\n    description: \"This will be `true` if the form has been submitted successfully. If you don't use the `redirect` parameter, you can keep your users on the same page and show a success message.\"\n  -\n    name: url_invalid\n    type: boolean\n    description: This will be `true` if the `code` query parameter is missing/incorrect, or if the `user` query parameter is invalid.\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\nid: e39fad1d-8b31-4dba-b32e-a0048084d178\n---\n## Overview\n\nAfter a user has put their email address into the [user:forgot_password_form](/tags/user-forgot_password_form), they'll arrive here to reset their password. This is the form used to create a _new_ password. That's really all there is to it.\n\n## Example\n\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:reset_password_form }}\n\n    {{ if success }}\n\n        <p class=\"alert alert-success\">Password has been reset.</p>\n\n    {{ elseif url_invalid }}\n\n        <p class=\"alert alert-danger\">This reset URL is invalid.</p>\n\n    {{ else }}\n\n        {{ if errors }}\n            <div class=\"alert alert-danger\">\n                {{ errors }}\n                    {{ value }}<br>\n                {{ /errors }}\n            </div>\n        {{ /if }}\n\n        <label>E-Mail</label>\n        <input type=\"email\" name=\"email\" />\n\n        <label>Password</label>\n        <input type=\"password\" name=\"password\" />\n\n        <label>Password Confirmation</label>\n        <input type=\"password\" name=\"password_confirmation\" />\n\n        <button>Submit</button>\n\n    {{ /if }}\n\n{{ /user:reset_password_form }}\n```\n::tab blade\n```blade\n<s:user:reset_password_form>\n  @if ($success)\n    <p class=\"alert alert-success\">Password has been reset.</p>\n  @elseif ($url_invalid)\n    <p class=\"alert alert-danger\">This reset URL is invalid.</p>\n  @else\n\n    @if ($errors)\n      <div class=\"alert alert-danger\">\n        @foreach ($errors as $error)\n          {{ $error }}<br>\n        @endforeach\n      </div>\n    @endif\n\n    <label>E-Mail</label>\n    <input type=\"email\" name=\"email\" />\n\n    <label>Password</label>\n    <input type=\"password\" name=\"password\" />\n\n    <label>Password Confirmation</label>\n    <input type=\"password\" name=\"password_confirmation\" />\n\n    <button>Submit</button>\n  @endif\n</s:user:reset_password_form>\n```\n::\n\n## Arriving at this URL\n\nThis URL needs to have the appropriate `token` query parameter (e.g. `/some/url?token=the-generated-token-goes-here`)\n\nVisiting the URL containing this form _directly_ will set a `url_invalid` variable. You can use this variable to check if they've actually arrived here through the password reset mail. This variable just checks whether there is a `token` query parameter available in the URL. This token will then be sent along with the email address and new password they fill out in an attempt to reset their password. If the token is incorrect, a validation error will be shown after submitting the form.\n\nThe reset password mail which contains the right `token` may be sent by using a `user:forgot_password_form` tag.\n"
  },
  {
    "path": "content/collections/tags/user-reset_two_factor_recovery_codes_form.md",
    "content": "---\ntitle: User:Reset_Two_Factor_Recovery_Codes_Form\ndescription: Renders a form to generate new recovery codes\nintro: If a user has used some of their recovery codes or suspects they've been compromised, they can generate a fresh set. This invalidates all existing codes.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: Where the user should be taken after generating new codes.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"reset-form\"`.\nvariables:\n  -\n    name: success\n    type: string\n    description: A success message.\nid: 7e2c3f8a-9b1d-4e6f-2a5c-0d3e4f7a9b1c\n---\n## Overview\n\nThe `user:reset_two_factor_recovery_codes_form` tag renders a form that allows authenticated users to generate a new set of recovery codes. When submitted, all existing recovery codes are invalidated and replaced with new ones.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. No input fields are required—just a submit button.\n\n:::tip\nThis form requires the user to be authenticated with 2FA enabled and an [elevated session](/tags/user-elevated_session_form). If the session isn't elevated, the user will be redirected to confirm their identity first.\n:::\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:reset_two_factor_recovery_codes_form redirect=\"/account/recovery-codes\" }}\n\n    {{ if success }}\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ success }}\n        </div>\n    {{ /if }}\n\n    <p>Generate new recovery codes? Your current codes will be invalidated.</p>\n    <button type=\"submit\">Generate New Codes</button>\n\n{{ /user:reset_two_factor_recovery_codes_form }}\n```\n::tab blade\n```blade\n<s:user:reset_two_factor_recovery_codes_form redirect=\"/account/recovery-codes\">\n    @if ($success)\n        <div class=\"bg-green-300 text-white p-2\">\n            {{ $success }}\n        </div>\n    @endif\n\n    <p>Generate new recovery codes? Your current codes will be invalidated.</p>\n    <button type=\"submit\">Generate New Codes</button>\n</s:user:reset_two_factor_recovery_codes_form>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-roles.md",
    "content": "---\nid: 878f0dd7-2d31-479c-b58d-bc60685fa7e3\nblueprint: tag\ntitle: User_Roles\ndescription: 'Fetch, and iterate over Users Roled and their data.'\nintro: 'The `user_roles` tag is used to return any roles you have added to collate the users on your site.'\nparameters:\n  -\n    name: handle\n    type: string\n    description: 'The handle(s) of the roles you want to return. You may specify multiple roles by pipe separating them: `{{ user_roles handle=\"jocks|geeks\" }}`.'\n    required: false\nvariables:\n  -\n    name: handle\n    type: string\n    description: The role's unique identifier.\n  -\n    name: title\n    type: string\n    description: The role's display title.\n---\n## Overview\n\nThe User Roles tag fetches lists of the user roles on your site so you can do whatever you want with them.\n\nA simple example is to loop through all the roles and list them by handle:\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ user_roles }}\n    <li>{{ handle }}</li>\n{{ /user_roles }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n<s:user_roles>\n  <li>{{ $handle }}</li>\n</s:user_roles>\n</ul>\n\n{{-- Aliasing the roles. --}}\n<s:user_roles\n  as=\"roles\"\n>\n  ...\n\n  @foreach ($roles as $role)\n    ...\n  @endforeach\n\n  ...\n</s:user_roles>\n```\n::\n\n## Filtering\n\nIf you only want a specific role or roles, you can pass their handle(s) using the `handle` parameter.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user_roles handle=\"role_1|role_2\" }}\n  // cool stuff goes here\n{{ /user_roles }}\n```\n::tab blade\n```blade\n<s:user_roles\n  handle=\"role_1|role_2\"\n>\n  // cool stuff goes here\n</s:user_roles>\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-two_factor_challenge_form.md",
    "content": "---\ntitle: User:Two_Factor_Challenge_Form\ndescription: Renders a form for users to enter their 2FA code during login\nintro: When a user with two-factor authentication enabled logs in, they need to verify their identity with a code from their authenticator app. This tag renders that verification form.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: Where the user should be taken after successful verification.\n  -\n    name: error_redirect\n    type: string\n    description: Where the user should be redirected on validation errors.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When set to true, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"2fa-form\"`.\nvariables:\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\n  -\n    name: error\n    type: array\n    description: An array of validation errors indexed by field names. Suitable for targeting fields. eg. `{{ error:code }}`\n  -\n    name: success\n    type: string\n    description: A success message.\nid: 3a8e9b4c-5d7f-4a2b-8c1e-6f9d0a3b5c7e\n---\n## Overview\n\nThe `user:two_factor_challenge_form` tag renders a form for users to complete the second step of two-factor authentication. After submitting valid credentials on the login form, users with 2FA enabled are redirected to this challenge form.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. Users can verify their identity by entering either a `code` from their authenticator app or a `recovery_code`.\n\n:::tip\nThis form will only render content if there's a pending 2FA challenge in the session. If accessed without a pending challenge, the form contents won't be displayed.\n:::\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:two_factor_challenge_form redirect=\"/dashboard\" }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    <p>Enter the 6-digit code from your authenticator app:</p>\n    <input type=\"text\" name=\"code\" inputmode=\"numeric\" autocomplete=\"one-time-code\" maxlength=\"6\" />\n\n    <p>Or use a recovery code:</p>\n    <input type=\"text\" name=\"recovery_code\" />\n\n    <button type=\"submit\">Verify</button>\n\n{{ /user:two_factor_challenge_form }}\n```\n::tab blade\n```blade\n<s:user:two_factor_challenge_form redirect=\"/dashboard\">\n    @if ($errors)\n        <div class=\"bg-red-300 text-white p-2\">\n            @foreach ($errors as $error)\n                {{ $error }}<br>\n            @endforeach\n        </div>\n    @endif\n\n    <p>Enter the 6-digit code from your authenticator app:</p>\n    <input type=\"text\" name=\"code\" inputmode=\"numeric\" autocomplete=\"one-time-code\" maxlength=\"6\" />\n\n    <p>Or use a recovery code:</p>\n    <input type=\"text\" name=\"recovery_code\" />\n\n    <button type=\"submit\">Verify</button>\n</s:user:two_factor_challenge_form>\n```\n::\n\n## Redirect Behavior\n\nIf you don't specify a `redirect` parameter, the form will use the `redirect` value from the original login form. This allows you to specify the redirect destination once on the login form and have it carry through the entire 2FA flow.\n"
  },
  {
    "path": "content/collections/tags/user-two_factor_enable_form.md",
    "content": "---\ntitle: User:Two_Factor_Enable_Form\ndescription: Renders the first step of 2FA setup — generates the user's 2FA secret\nintro: Step one of the two-factor authentication setup flow. Submitting this form generates a 2FA secret for the user and sends them on to the setup page where they confirm the code.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: Where the user should be taken after their 2FA secret has been generated. Typically this is the page that renders `{{ user:two_factor_setup_form }}`.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When set to true, the `redirect` parameter will get overridden by a `redirect` query parameter in the URL.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"2fa-enable-form\"`.\nvariables:\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\n  -\n    name: error\n    type: array\n    description: An array of validation errors indexed by field names.\n  -\n    name: success\n    type: string\n    description: A success message.\nid: 8f4c6868-0aee-4814-a498-a4e118c2f400\n---\n## Overview\n\nThe `user:two_factor_enable_form` tag renders **step one** of the two-factor authentication setup flow. Submitting it generates the user's 2FA secret (and eventually their recovery codes, once they confirm), then redirects them on to the setup page where the QR code and confirmation form are displayed.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. No input fields are required — just a submit button.\n\n:::tip\nThis form only renders for an authenticated user who does **not** already have 2FA enabled. It also requires an [elevated session](/tags/user-elevated_session_form) — if the session isn't elevated, the user will be redirected to confirm their identity first.\n:::\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:two_factor_enable_form redirect=\"/account/2fa/setup\" }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    <p>Click below to start setting up two-factor authentication. You'll be taken to a page where you can scan a QR code with your authenticator app.</p>\n\n    <button type=\"submit\">Set Up Two-Factor Authentication</button>\n\n{{ /user:two_factor_enable_form }}\n```\n::tab blade\n```blade\n<s:user:two_factor_enable_form redirect=\"/account/2fa/setup\">\n    @if ($errors)\n        <div class=\"bg-red-300 text-white p-2\">\n            @foreach ($errors as $error)\n                {{ $error }}<br>\n            @endforeach\n        </div>\n    @endif\n\n    <p>Click below to start setting up two-factor authentication. You'll be taken to a page where you can scan a QR code with your authenticator app.</p>\n\n    <button type=\"submit\">Set Up Two-Factor Authentication</button>\n</s:user:two_factor_enable_form>\n```\n::\n\n## Redirect behavior\n\nAfter the secret is generated, Statamic decides where to send the user in the following order:\n\n1. The form's `redirect` parameter (submitted as `_redirect`).\n2. The `statamic.users.two_factor_setup_url` config key in `config/statamic/users.php`.\n3. The referring page (i.e. back to where the form was rendered).\n\nWhichever destination is used, that page should render [`{{ user:two_factor_setup_form }}`](/tags/user-two_factor_setup_form) so the user can complete setup.\n"
  },
  {
    "path": "content/collections/tags/user-two_factor_enabled.md",
    "content": "---\ntitle: User:Two_Factor_Enabled\ndescription: Returns whether the current user has 2FA enabled\nintro: A boolean tag that returns `true` when the authenticated user has two-factor authentication enabled. Useful for conditionally showing recovery code or disable UI.\nid: fa2eaa81-6a76-48d1-8b37-a0abda40b09e\n---\n## Overview\n\nThe `user:two_factor_enabled` tag returns `true` when the currently authenticated user has confirmed their two-factor authentication setup, and `false` otherwise (including when nobody is logged in).\n\n### Example\n\nBecause this is a tag rather than a variable, use the extra brace syntax inside conditionals:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {user:two_factor_enabled} }}\n    <h2>Your Recovery Codes</h2>\n    {{ user:two_factor_recovery_codes }}\n        <li><code>{{ code }}</code></li>\n    {{ /user:two_factor_recovery_codes }}\n\n    {{ user:disable_two_factor_form }}\n        <button type=\"submit\">Disable 2FA</button>\n    {{ /user:disable_two_factor_form }}\n{{ else }}\n    {{ user:two_factor_enable_form redirect=\"/account/2fa/setup\" }}\n        <button type=\"submit\">Enable 2FA</button>\n    {{ /user:two_factor_enable_form }}\n{{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::tag('user:two_factor_enabled')->fetch())\n    <h2>Your Recovery Codes</h2>\n    <s:user:two_factor_recovery_codes>\n        <li><code>{{ $code }}</code></li>\n    </s:user:two_factor_recovery_codes>\n\n    <s:user:disable_two_factor_form>\n        <button type=\"submit\">Disable 2FA</button>\n    </s:user:disable_two_factor_form>\n@else\n    <s:user:two_factor_enable_form redirect=\"/account/2fa/setup\">\n        <button type=\"submit\">Enable 2FA</button>\n    </s:user:two_factor_enable_form>\n@endif\n```\n::\n"
  },
  {
    "path": "content/collections/tags/user-two_factor_recovery_codes.md",
    "content": "---\ntitle: User:Two_Factor_Recovery_Codes\ndescription: Displays the user's 2FA recovery codes\nintro: Recovery codes provide a backup way for users to access their account if they lose their authenticator device. This tag displays those codes.\nvariables:\n  -\n    name: code\n    type: string\n    description: A single recovery code.\nid: 5c0a1d6e-7f9b-4c4d-0e3a-8b1f2c5d7e9a\n---\n## Overview\n\nThe `user:two_factor_recovery_codes` tag loops through and displays the authenticated user's recovery codes. Each recovery code can only be used once to log in if the user doesn't have access to their authenticator app.\n\n:::tip\nThis tag requires the user to be authenticated with 2FA enabled. If 2FA is not enabled, the tag won't render any content.\n:::\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {user:two_factor_enabled} }}\n    <h2>Your Recovery Codes</h2>\n    <p>Store these codes in a safe place. Each code can only be used once.</p>\n\n    <ul class=\"recovery-codes\">\n        {{ user:two_factor_recovery_codes }}\n            <li><code>{{ code }}</code></li>\n        {{ /user:two_factor_recovery_codes }}\n    </ul>\n{{ else }}\n    <p>You don't have two-factor authentication enabled.</p>\n{{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::tag('user:two_factor_enabled')->fetch())\n    <h2>Your Recovery Codes</h2>\n    <p>Store these codes in a safe place. Each code can only be used once.</p>\n\n    <ul class=\"recovery-codes\">\n        <s:user:two_factor_recovery_codes>\n            <li><code>{{ $code }}</code></li>\n        </s:user:two_factor_recovery_codes>\n    </ul>\n@else\n    <p>You don't have two-factor authentication enabled.</p>\n@endif\n```\n::\n\n:::tip\n[`{{ user:two_factor_enabled }}`](/tags/user-two_factor_enabled) is a tag, not a variable, so conditionals need the extra braces: `{{ if {user:two_factor_enabled} }}`.\n:::\n\n"
  },
  {
    "path": "content/collections/tags/user-two_factor_recovery_codes_download_url.md",
    "content": "---\ntitle: User:Two_Factor_Recovery_Codes_Download_URL\ndescription: Outputs a URL to download recovery codes as a text file\nid: 6d1b2e7f-8a0c-4d5e-1f4b-9c2a3d6e8f0b\n---\n## Overview\n\nThe `user:two_factor_recovery_codes_download_url` tag outputs a URL that allows the authenticated user to download their recovery codes as a plain text file. This provides a convenient way for users to save their codes offline.\n\n:::tip\nThis tag requires the user to be authenticated with 2FA enabled. If 2FA is not enabled, the tag returns an empty string.\n:::\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if {user:two_factor_enabled} }}\n    <a href=\"{{ user:two_factor_recovery_codes_download_url }}\">\n        Download Recovery Codes\n    </a>\n{{ /if }}\n```\n::tab blade\n```blade\n@if (Statamic::tag('user:two_factor_enabled')->fetch())\n    <a href=\"{{ Statamic::tag('user:two_factor_recovery_codes_download_url')->fetch() }}\">\n        Download Recovery Codes\n    </a>\n@endif\n```\n::\n\n:::tip\n[`{{ user:two_factor_enabled }}`](/tags/user-two_factor_enabled) is a tag, not a variable, so conditionals need the extra braces: `{{ if {user:two_factor_enabled} }}`.\n:::\n"
  },
  {
    "path": "content/collections/tags/user-two_factor_setup_form.md",
    "content": "---\ntitle: User:Two_Factor_Setup_Form\ndescription: Renders the second step of 2FA setup — the QR code and confirmation form\nintro: Step two of the two-factor authentication setup flow. This tag displays the QR code and verifies the code the user enters to confirm their authenticator app is working.\nparameters:\n  -\n    name: redirect\n    type: string\n    description: Where the user should be taken after successful setup (e.g., a recovery codes page).\n  -\n    name: error_redirect\n    type: string\n    description: Where the user should be redirected on validation errors.\n  -\n    name: allow_request_redirect\n    type: boolean\n    description: When set to true, the `redirect` and `error_redirect` parameters will get overridden by `redirect` and `error_redirect` query parameters in the URL.\n  -\n    name: HTML Attributes\n    type:\n    description: >\n      Set HTML attributes as if you were in an HTML element. For example, `class=\"required\" id=\"2fa-setup-form\"`.\nvariables:\n  -\n    name: qr_code\n    type: string\n    description: SVG markup for the QR code that can be rendered directly in the template.\n  -\n    name: qr_code_url\n    type: string\n    description: A data URL for the QR code, suitable for use with an `<img>` tag's `src` attribute.\n  -\n    name: secret_key\n    type: string\n    description: The TOTP secret key for users who prefer to enter it manually into their authenticator app.\n  -\n    name: errors\n    type: array\n    description: An array of validation errors.\n  -\n    name: error\n    type: array\n    description: An array of validation errors indexed by field names. Suitable for targeting fields. eg. `{{ error:code }}`\n  -\n    name: success\n    type: string\n    description: A success message.\nid: 4b9f0c5d-6e8a-4b3c-9d2f-7a0e1b4c6d8f\n---\n## Overview\n\nThe `user:two_factor_setup_form` tag renders **step two** of the two-factor authentication setup flow. At this point the user has already generated a 2FA secret (via [`{{ user:two_factor_enable_form }}`](/tags/user-two_factor_enable_form)) and just needs to scan the QR code with their authenticator app and enter a verification code to confirm the setup.\n\nThe tag will render the opening and closing `<form>` HTML elements for you. You'll need to provide a `code` input field for the verification code.\n\n:::tip\nThis form only renders for an authenticated user who has a 2FA secret but hasn't yet confirmed it. If the user doesn't have a secret yet, send them through [`{{ user:two_factor_enable_form }}`](/tags/user-two_factor_enable_form) first. If the user already has 2FA enabled, the form contents won't be rendered.\n:::\n\n### Example\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:two_factor_setup_form redirect=\"/account/recovery-codes\" }}\n\n    {{ if errors }}\n        <div class=\"bg-red-300 text-white p-2\">\n            {{ errors }}\n                {{ value }}<br>\n            {{ /errors }}\n        </div>\n    {{ /if }}\n\n    <h2>Set Up Two-Factor Authentication</h2>\n\n    <p>Scan this QR code with your authenticator app:</p>\n\n    {{# Option 1: Render SVG directly #}}\n    <div class=\"qr-code\">\n        {{ qr_code }}\n    </div>\n\n    {{# Option 2: Use as image source #}}\n    <img src=\"{{ qr_code_url }}\" alt=\"QR Code\" width=\"200\" height=\"200\" />\n\n    <p>Or enter this code manually: <code>{{ secret_key }}</code></p>\n\n    <label>\n        Enter the 6-digit code from your app to confirm:\n        <input type=\"text\" name=\"code\" inputmode=\"numeric\" autocomplete=\"one-time-code\" maxlength=\"6\" />\n    </label>\n\n    <button type=\"submit\">Enable Two-Factor Authentication</button>\n\n{{ /user:two_factor_setup_form }}\n```\n::tab blade\n```blade\n<s:user:two_factor_setup_form redirect=\"/account/recovery-codes\">\n    @if ($errors)\n        <div class=\"bg-red-300 text-white p-2\">\n            @foreach ($errors as $error)\n                {{ $error }}<br>\n            @endforeach\n        </div>\n    @endif\n\n    <h2>Set Up Two-Factor Authentication</h2>\n\n    <p>Scan this QR code with your authenticator app:</p>\n\n    {{-- Option 1: Render SVG directly --}}\n    <div class=\"qr-code\">\n        {!! $qr_code !!}\n    </div>\n\n    {{-- Option 2: Use as image source --}}\n    <img src=\"{{ $qr_code_url }}\" alt=\"QR Code\" width=\"200\" height=\"200\" />\n\n    <p>Or enter this code manually: <code>{{ $secret_key }}</code></p>\n\n    <label>\n        Enter the 6-digit code from your app to confirm:\n        <input type=\"text\" name=\"code\" inputmode=\"numeric\" autocomplete=\"one-time-code\" maxlength=\"6\" />\n    </label>\n\n    <button type=\"submit\">Enable Two-Factor Authentication</button>\n</s:user:two_factor_setup_form>\n```\n::\n\n\n\n## Displaying the QR code\n\nYou have two options for displaying the QR code:\n\n1. **SVG Markup** (`qr_code`): Renders directly in the HTML. This is generally preferred as it scales well and doesn't require an additional request.\n\n2. **Data URL** (`qr_code_url`): Use with an `<img>` tag if you prefer image-based rendering or need more control over sizing.\n\n## Flow\n\nThe frontend 2FA setup flow is split across two pages:\n\n1. Submit [`{{ user:two_factor_enable_form }}`](/tags/user-two_factor_enable_form) — this generates the user's 2FA secret and, by default, redirects to `statamic.users.two_factor_setup_url` (or wherever you specify via `_redirect`).\n2. On that setup page, use `{{ user:two_factor_setup_form }}` (this tag) to display the QR code and confirm the code.\n"
  },
  {
    "path": "content/collections/tags/users.md",
    "content": "---\nid: 878f0dd7-2d31-479c-b58d-bc60685fa7d2\nblueprint: tag\ntitle: Users\ndescription: 'Fetch, filter, and iterate over Users and their data.'\nintro: 'The users tag can be used to fetch, filter, and iterate over Users and their data.'\nparameters:\n  -\n    name: group\n    type: string\n    description: 'The user group or groups to filter by. You may specify multiple groups by pipe separating them: `{{ users group=\"jocks|geeks\" }}`.'\n  -\n    name: role\n    type: string\n    description: 'The role or roles to filter by. You may specify multiple roles by pipe separating them: `{{ users role=\"author|editor\" }}`.'\n  -\n    name: sort\n    type: string\n    description: 'Sort users by field name (or `random`). You may pipe-separate multiple fields for sub-sorting and specify sort direction of each field using a colon. For example, `sort=\"title\"` or `sort=\"date:asc|title:desc\"` to sort by date then by title.'\n    required: false\n  -\n    name: limit\n    type: integer\n    description: 'Limit the total results returned.'\n    required: false\n  -\n    name: filter|query_scope\n    type: string\n    description: 'Apply a custom [query scope](/extending/query-scopes-and-filters)'\n    required: false\n  -\n    name: offset\n    type: integer\n    description: 'Skip a specified number of results.'\n    required: false\n  -\n    name: as\n    type: string\n    description: 'Alias your entries into a new variable loop.'\n    required: false\n  -\n    name: scope\n    type: string\n    description: 'Scope your entries with a variable prefix.'\n    required: false\nvariables:\n  -\n    name: first\n    type: boolean\n    description: 'Is this the first item in the loop?'\n  -\n    name: last\n    type: boolean\n    description: 'Is this the last item in the loop?'\n  -\n    name: count\n    type: integer\n    description: 'The number/index of current iteration in the loop, starting from 1'\n  -\n    name: index\n    type: integer\n    description: 'The number/index of current iteration in the loop, starting from 0'\n  -\n    name: no_results\n    type: boolean\n    description: 'Returns true if there are no results.'\n  -\n    name: total_results\n    type: integer\n    description: 'The total number of results in the loop when there are results. You should use `no_results` to check if any results exist.'\n  -\n    name: 'user data'\n    type: mixed\n    description: 'Each result has access to all the variables inside that entry (`name`, `email`, etc).'\nvariables_content: |\n  The following variables are **Antlers-only**. See [Loop variables](/antlers#loop-variables) for details, or the [Blade equivalents](/blade#loop-variables) if you're writing Blade.\n---\n## Overview\n\nThe Users tag works very much like the [Collection Tag](/tags/collection). It fetches, filters, sorts, groups, and manipulates lists of your users so you can do whatever you want with them.\n\nA simple example is to loop through all the users and list them by name:\n\n\n::tabs\n\n::tab antlers\n```antlers\n<ul>\n{{ users }}\n    <li>{{ name }}</li>\n{{ /users }}\n</ul>\n```\n::tab blade\n```blade\n<ul>\n<s:users>\n  <li>{{ $name }}</li>\n</s:users>\n</ul>\n```\n::\n\n## Filtering\n\nYou can filter you users by group, role, field, or even custom filter class if you need extra control.\n\n### Conditions\n\nWant to avoid listing users who have the words \"hipster\" and \"coffee\" in their bio?\n\n::tabs\n\n::tab antlers\n```antlers\n{{ users bio:doesnt_contain=\"hipster\" bio:doesnt_contain=\"coffee\" }}\n```\n::tab blade\n```blade\n<s:users\n  bio:doesnt_contain=\"hipster\"\n  bio:doesnt_contain=\"coffee\"\n>\n  ...\n</s:users>\n```\n::\n\nThere are a whole pile of conditions available to you, like `:is`, `:isnt`, `:contains`, `:starts_with`, and `:is_before`. Check out this page [dedicated to conditions](/conditions).\n\n### Custom Query Scopes\n\nDoing something custom or complicated? You can create [query scopes](/extending/query-scopes-and-filters) to narrow down those results with the `query_scope` or `filter` parameter:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ users query_scope=\"your_query_scope\" }}\n```\n::tab blade\n```blade\n<s:users\n  query_scope=\"your_query_scope\"\n>\n  ...\n</s:users>\n```\n::\n\n### Examples\n\n#### Only super users\n\n::tabs\n\n::tab antlers\n```antlers\n{{ users super:is=\"true\" }}\n  // these people are powerful\n{{ /users }}\n```\n::tab blade\n```blade\n<s:users\n  super:is=\"true\"\n>\n  // these people are powerful\n</s:users>\n```\n::\n\n#### Exclude users with gmail email address\n\n::tabs\n\n::tab antlers\n```antlers\n{{ users email:doesnt_end_with=\"@gmail.com\" }}\n  // cool stuff goes here\n{{ /users }}\n```\n::tab blade\n```blade\n<s:users\n  email:doesnt_end_with=\"@gmail.com\"\n>\n  // cool stuff goes here\n</s:users>\n```\n::\n\n"
  },
  {
    "path": "content/collections/tags/vite-content.md",
    "content": "---\nid: 6f6bff0a-074e-4706-8427-669ad951e8e0\nblueprint: tag\ntitle: Vite:Content\ndescription: 'Returns the contents of a Vite asset'\nintro: 'This tag is used in tandem with the Vite build tool to return the contents of CSS and JavaScript files.'\nparameters:\n  -\n    name: src\n    type: string\n    description: |\n      The path to the file, relative to your project root. You may pass multiple files and paths.\n  -\n    name: directory\n    type: string\n    description: |\n      The path to the desired build directory, relative to the `public` directory. Defaults to `build`.\n---\nThe `vite:content` tag allows you to output the contents of a Vite asset. This is useful if you need to inline the contents of a CSS or JavaScript file.\n\n::tabs\n\n::tab antlers\n```antlers\n<style>\n    {{ vite:content src=\"resources/css/app.css\" }}\n</style>\n\n<script>\n    {{ vite:content src=\"resources/js/app.js\" }}\n</script>\n```\n::tab blade\n```blade\n// Using Antlers Blade components.\n<style>\n  <s:vite:content src=\"resources/css/app.css\" />\n</style>\n\n<script>\n  <s:vite:content src=\"resources/js/app.js\" />\n</script>\n\n// Using Blade directives.\n@use('Illuminate\\Support\\Facades\\Vite')\n\n<style>\n  {!! Vite::content('resources/css/app.css') !!}\n</style>\n\n<script>\n  {!! Vite::content('resources/js/app.js') !!}\n</script>\n```\n::\n\n:::warning Psst!\nThe `vite:content` tag will only output the contents of \"built\" assets. This means that changes made while running `npm run dev` will not be reflected in the output.\n:::\n\nIf you need to, you can also specify a custom build directory.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ vite src=\"resources/css/tailwind.css|resources/js/site.js\" directory=\"bundle\" }}\n```\n::tab blade\n```blade\n<s:vite\n  src=\"resources/css/tailwind.css|resources/js/site.js\"\n  directory=\"bundle\"\n/>\n```\n::\n\nWhen using these options, please make sure to also adjust your `vite.config.js` file. More about advanced customization can be found in [Laravel's Vite docs](https://laravel.com/docs/13.x/vite#advanced-customization).\n"
  },
  {
    "path": "content/collections/tags/vite.md",
    "content": "---\nid: aeec964c-ab02-48d1-997c-8f1e331204a3\nblueprint: tag\ntitle: Vite\ndescription: 'Returns the path to Vite assets'\nintro: 'This tag is used in tandem with the Vite build tool to return the path to CSS and JavaScript files.'\nparameters:\n  -\n    name: src\n    type: string\n    description: |\n      The path to the file, relative to your project root. You may pass multiple files and paths.\n  -\n    name: directory\n    type: string\n    description: |\n      The path to the desired build directory, relative to the `public` directory. Defaults to `build`.\n  -\n    name: hot\n    type: string\n    description: |\n      The path to the desired location for the hot file, relative to your project root. Defaults to `public/hot`.\n---\nThe `vite` tag is a wrapper around [Laravel's Vite integration](https://laravel.com/docs/vite). It is essentially an Antlers version of the `@vite` Blade directive.\n\nYou should pass in both the CSS and JavaScript paths, and it will output the appropriate html tags.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ vite src=\"resources/js/app.js|resources/css/app.css\" }}\n```\n::tab blade\n```blade\n<s:vite src=\"resources/js/app.js|resources/css/app.css\" />\n```\n::\n\nIf you are running in development mode by running `npm run dev`, it will handle hot-reloading (e.g. save a css file, your page will update automatically without you refreshing).\n\n```html\n<script type=\"module\" src=\"http://127.0.0.1:3000/@vite/client\"></script>\n<script type=\"module\" src=\"http://127.0.0.1:3000/resources/js/site.js\"></script>\n<link rel=\"stylesheet\" href=\"http://127.0.0.1:3000/resources/css/site.css\"/>\n```\n\nOtherwise, it will use the compiled assets, which you would have done by running `npm run build`.\n\n```html\n<link rel=\"stylesheet\" href=\"http://yoursite.com/build/assets/site.3bc13c9b.css\"/>\n<script type=\"module\" src=\"http://yoursite.com/build/assets/site.67066a5d.js\"></script>\n```\n\nAdditionally, you can set custom locations for the build directory and hot file.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ vite src=\"resources/css/tailwind.css|resources/js/site.js\" directory=\"bundle\" hot=\"storage/vite.hot\" }}\n```\n::tab blade\n```blade\n<s:vite\n  src=\"resources/css/tailwind.css|resources/js/site.js\"\n  directory=\"bundle\"\n  hot=\"storage/vite.hot\"\n/>\n```\n::\n\nWhen using these options, please make sure to also adjust your `vite.config.js` file. More about advanced customization can be found in [Laravel's Vite docs](https://laravel.com/docs/13.x/vite#advanced-customization).\n\n## Processing Static Assets With Vite\n\nTo process static assets in your Antlers files with Vite, as described in the [Laravel's Vite Docs](https://laravel.com/docs/master/vite#blade-processing-static-assets), you should use:\n\n::tabs\n\n::tab antlers\n```antlers\n<img src=\"{{ vite:asset src=\"resources/images/logo.png\" }}\">\n```\n::tab blade\n```blade\n<img src=\"{{ Vite::asset('resources/images/logo.png') }}\">\n```\n::\n\n## Arbitrary Attributes\n\nIf you need to include additional attributes in your script and style tags, such as the `data-turbo-track` attribute, you can pass them as parameters using the `attr:` prefix:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ vite\n  src=\"foo.js|bar.css\"\n  attr:data-turbo-track=\"reload\"\n  attr:async=\"true\"\n}}\n```\n::tab blade\n```blade\n<s:vite\n  src=\"foo.js|bar.css\"\n  attr:data-turbo-track=\"reload\"\n  attr:async=\"true\"\n/>\n```\n::\n\nYou can also provide attributes that are specific to script or style tags:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ vite\n  src=\"foo.js|bar.css\"\n\n  attr:script:data-turbo-track=\"reload\"\n  attr:script:async=\"true\"\n\n  attr:style:data-turbo-track=\"reload\"\n  attr:style:integrity=\"false\"\n}}\n```\n::tab blade\n```blade\n<s:vite\n  src=\"foo.js|bar.css\"\n\n  attr:script:data-turbo-track=\"reload\"\n  attr:script:async=\"true\"\n\n  attr:style:data-turbo-track=\"reload\"\n  attr:style:integrity=\"false\"\n/>\n```\n::"
  },
  {
    "path": "content/collections/tags/yield.md",
    "content": "---\ntitle: Yield\ndescription: 'Displays content extracted elsewhere by the section tag'\nintro: 'The yield tag is a useful way to abstract and reuse your views by displaying content or markup extracted in a template by the [section tag](/tags/section).'\nid: f3035f71-347e-4b99-bc27-71956315692a\n---\n## Overview\n\nMost commonly this section/yield approach is used to create a global area in your layout that can be changed by your templates. This eliminates the need for any brittle and messy logic.\n\n**Cheatsheet:**\n\n- <span class=\"text-red-600 font-bold\">No thank you:</span> `{{ if template == \"news\" }} hardcode something {{ /if }}`\n- <span class=\"text-green-700 font-bold\">Yes please:</span> `{{ yield:something }}` + `{{ section:something }}`\n\n## Example\n\nIn the example below, everything within the `section:sidebar` tag will _not_ be rendered in the template, but rather in the layout.\n\n::tabs\n\n::tab antlers\n```antlers\n// The Template\n\n<h1>{{ title }}</h1>\n{{ content }}\n\n{{ section:sidebar }}\n  <h2>About the Author</h2>\n  <div>\n    {{ author:name }}\n  </div>\n  {{ author:bio }}\n{{ /section:sidebar }}\n```\n\n```\n// The Layout\n<html>\n  <head>\n    <title>{{ title }} | {{ site_name }}</title>\n  </head>\n  <body>\n    <article>\n      {{ template_content }}\n    </article>\n    <aside>\n      {{ yield:sidebar }}\n    </aside>\n  </body>\n</html>\n```\n::tab blade\n```blade\n// The template\n@extends('layout')\n\n<h1>{{ $title }}</h1>\n{!! $content !!}\n\n@section('sidebar')\n  <h2>About the Author</h2>\n\n  <div>\n    {{ $author['name'] }}\n  </div>\n  {{ $author['bio'] }}\n@stop\n```\n\n```blade\n// The Layout\n<html>\n  <head>\n    <title>{{ $title }} | {{ $site_name }}</title>\n  </head>\n  <body>\n    <article>\n      {!! $template_content !!}\n    </article>\n    <aside>\n      @yield('sidebar')\n    </aside>\n  </body>\n</html>\n```\n::\n\n\n## Fallback Content\n\nIf no section is being pushed into the yield, you may display fallback content by using the tag as a pair.\n\n::tabs\n\n::tab antlers\n```antlers\n<aside>\n  {{ yield:sidebar }}\n    <img src=\"/img/CLIPPY.GIF\">\n    <p>Hi! It looks like you're building a website. Would you like help?</p>\n  {{ /yield:sidebar }}\n</aside>\n```\n::tab blade\n```blade\n// The Layout\n<aside>\n  @section('sidebar')\n    <img src=\"/img/CLIPPY.GIF\">\n    <p>Hi! It looks like you're building a website. Would you like help?</p>\n  @show\n</aside>\n```\n\n```blade\n// The Template\n@extends('layout')\n\n// The following would override the default content:\n@section('sidebar')\n  I would override the default!\n@endsection\n```\n::\n\n## Related Reading\n\nIf you haven't read up on [templates and layouts](/views), you should. It's relevant.\n\n\n[yield_tag]: /tags/yield\n"
  },
  {
    "path": "content/collections/tags.yaml",
    "content": "title: Tags\nicon: favorite-award-prize\ntemplate: page\nlayout: layout\nmount: 424f5092-be39-449a-b660-f4e077631487\nrevisions: false\nroute: '/tags/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\ninject:\n  view_model: App\\ViewModels\\Tags\n"
  },
  {
    "path": "content/collections/tips/building-your-own-entries-repository.md",
    "content": "---\nid: 853b6690-c1fc-46bc-b865-e61a33d14563\ntitle: 'Building your own Entries Repository'\nintro: 'Statamic stores your content in \"flat files\" by default, but its data layer is completely driver-driven – giving you the ability to store content **anywhere**. In this article we''ll show you how to store entries in a database with [Laravel Eloquent](https://laravel.com/docs/13.x/eloquent).'\ntemplate: page\ncategories:\n  - development\n  - database\n  - laravel\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821277\nrelated_entries:\n    - 4238bce4-a94b-4d07-96fa-ea77c1d8e48d\n---\n## Overview\n\nStatamic uses a [repository pattern](/extending/repositories) to interact with your site or application's data. Statamic's core flat file implementation uses the [Stache](/stache) driver, but you can extend and build your own drivers to work with data stored just about anywhere, from MongoDB and [Firebase](https://firebase.google.com/) to a shoebox with a good REST API.\n\n\n### Why would you want to do this?\n\nThe flat file pattern is amazing for a whole pile of reasons. However, if you're going to be working with a huge amount of data (tens of thousands, millions, gazillions, etc), it has its limitations. This is where another storage layer, like a database comes in.\n\nThe ability to trade flexibility for scalability without changing platforms is a powerful feature.\n\n\n## What we're building\n\nIn this article we'll be creating a package that contains an Eloquent powered repository driver along with all the other required moving parts.\n\nYou can check out the finished product on [GitHub][repo], and even use it as a template to jumpstart your own project.\n\n:::tip\nIf you're looking to store your entries (or anything else for that matter) in a traditional database, look no further than the [official Eloquent Driver](https://github.com/statamic/eloquent-driver).\n:::\n\nFor the sake of brevity, we're going to focus only on **entries** for this article. In most cases, entries are the content type with the most records, making them the most likely candidate for needing a database.\n\nEverything you learn here can be applied to Taxonomies, GlobalSets, and all other content types.\n\n## Database Schema\n\nOne of Statamic's great features is being able to throw data of any type into an entry and without needing to create corresponding columns in a database.  But here we are in database land, and we need a table and some columns.\n\nHere's what our database schema will look like. We'll have a number of columns to hold common fields like `id`, `site`, and so on. Additionally, we'll create a `data` column that will store JSON for all of the blueprint-defined fields.\n\n``` php\npublic function up()\n{\n    Schema::create('entries', function (Blueprint $table) {\n        $table->string('id');\n        $table->string('site');\n        $table->string('origin_id')->nullable();\n        $table->boolean('published')->default(true);\n        $table->string('status');\n        $table->string('slug');\n        $table->string('uri')->nullable();\n        $table->string('date')->nullable();\n        $table->string('collection');\n        $table->json('data');\n        $table->timestamps();\n    });\n}\n```\n\nIf you want separate columns for each blueprint field, go for it. But you'll need to define all those fields in your repository and write migrations to add columns whenever you add a field to your blueprints.\n\nThe \"catch all\" JSON field works well in most cases, and allows you to drop this driver into your site and be off and running with very little fiddling.\n\n**Note:** The `id` and `origin_id` columns are strings to make migrating from files easier. If you want to use incrementing integers and aren't starting on a fresh, empty project, you'll need to update all the IDs in your content to use integers (out of the scope of this article). They might be found in relationship field values, collections' mount values, structures, and so on.\n\nUsing strings as IDs is fairly uncommon in Laravel Land, so we'll need to tweak the [Eloquent model](#the-model) to handle it.\n\n## The Repository\n\nWhen working with Entries in PHP, you use the [`Entry`](https://github.com/statamic/cms/blob/master/src/Contracts/Entries/EntryRepository.php) facade class. It automatically routes the request to proper class depending on what driver you're using. For example, fetching all entries with `Entry::all()`, will call `$repository->all()` behind the scenes, which will offload the work to the Stache driver by default, or in this case – our custom Eloquent driver.\n\nWhen building your own custom repository class (like we're doing right now), you'll need to implement all of the methods on the `EntryRepository` interface. These methods — like `all`, `find`, `whereCollection`, and `query`, handle all of the data I/O. Think of this class like a little data router.\n\nThe default Stache implementation pushes most of the logic into its query builder class. For example, the `find` method looks like this:\n\n``` php\npublic function find($id): ?Entry\n{\n    return $this->query()->where('id', $id)->first();\n}\n```\n\nWe can extend the Stache repository to gain all of these features, point to our own query builder, and customize only a small set of methods that need tweaking to work with Eloquent. The resulting class looks something like this. As you can see, it's quite minimal.\n\n``` php\n<?php\n\nnamespace Statamic\\Eloquent\\Entries;\n\nuse Statamic\\Contracts\\Entries\\Entry as EntryContract;\nuse Statamic\\Contracts\\Entries\\QueryBuilder;\nuse Statamic\\Stache\\Repositories\\EntryRepository as StacheRepository;\n\nclass EntryRepository extends StacheRepository\n{\n    public static function bindings(): array\n    {\n        return [\n            EntryContract::class => Entry::class,\n            QueryBuilder::class => EntryQueryBuilder::class,\n        ];\n    }\n\n    public function save($entry)\n    {\n        //\n    }\n\n    public function delete($entry)\n    {\n        //\n    }\n}\n```\n\nAside from overriding the query builder and entry class bindings, there's also the `save` and `delete` methods that control what happens when you call those methods on an entry. We'll fill those in later.\n\nTo have Statamic actually use our class, we'll bind it using the `Statamic::repository()` method in our package's service provider:\n\n``` php\n// src/ServiceProvider.php\n\npublic function register()\n{\n    Statamic::repository(EntryRepositoryContract::class, EntryRepository::class);\n}\n```\n\n\n## The Query Builder\n\nThe Stache entry repository delegates a lot of logic to a query builder which uses the Stache to get the data. Ours will use Eloquent to read from a database instead.\n\nStatamic has a base Eloquent Query Builder class ready for you. Here's a simplified snippet:\n\n``` php\nabstract class EloquentQueryBuilder implements Builder\n{\n    protected $builder;\n\n    public function __construct(EloquentBuilder $builder)\n    {\n        $this->builder = $builder;\n    }\n\n    public function __call($method, $args)\n    {\n        $this->builder->$method(...$args);\n\n        return $this;\n    }\n\n    public function get($columns = ['*'])\n    {\n        $items = $this->builder->get($columns);\n\n        return $this->transform($items, $columns);\n    }\n\n    public function first()\n    {\n        return $this->get()->first();\n    }\n\n    public function where($column, $operator = null, $value = null)\n    {\n        $this->builder->where($this->column($column), $operator, $value);\n\n        return $this;\n    }\n}\n```\n\nThis class takes an actual Eloquent query builder and proxies most method calls onto it (like `where` or `limit`), and then transforms\nregular Eloquent models into whatever Statamic classes are required.\n\nOur Entry query builder can extend this, helping us keep our class simple:\n\n``` php\n<?php\n\nnamespace Statamic\\Eloquent\\Entries;\n\nuse Statamic\\Contracts\\Entries\\QueryBuilder;\nuse Statamic\\Entries\\EntryCollection;\nuse Statamic\\Query\\EloquentQueryBuilder;\nuse Statamic\\Stache\\Query\\QueriesTaxonomizedEntries;\n\nclass EntryQueryBuilder extends EloquentQueryBuilder implements QueryBuilder\n{\n    protected $columns = [\n        'id', 'site', 'origin_id', 'published', 'status', 'slug', 'uri',\n        'date', 'collection', 'created_at', 'updated_at',\n    ];\n\n    protected function transform($items, $columns = [])\n    {\n        return EntryCollection::make($items)->map(function ($model) {\n            return Entry::fromModel($model);\n        });\n    }\n\n    protected function column($column)\n    {\n        if (! in_array($column, $this->columns)) {\n            $column = 'data->'.$column;\n        }\n\n        return $column;\n    }\n}\n```\n\nThe `transform` method will convert the `Collection` of all the Eloquent models into a `EntryCollection` full of Statamic `Entry` classes.\n\nThe `column` method is used whenever we're performing any kind of column based query (like a `where`). If the column isn't in our list, we'll assume it's in the JSON `data` column and adjust the query accordingly.\n\n:::tip\nIn case you didn't know, you can [run queries](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html) on data inside JSON columns. It's pretty awesome.\n:::\n\n``` php\n$query->where('column->field', 'value')\n```\n\nSince the query builder is expecting an instance of the Eloquent query builder, let's wire that up in our provider:\n\n``` php\n// src/ServiceProvider.php\n\npublic function register()\n{\n    $this->app->bind(EntryQueryBuilder::class, function () {\n        return new EntryQueryBuilder(EntryModel::query());\n    });\n}\n```\n\n## The Model\n\nSince we're using Eloquent, we need a [model](https://laravel.com/docs/13.x/eloquent#generating-model-classes). Let's set one up.\n\n``` php\n<?php\n\nnamespace Statamic\\Eloquent\\Entries;\n\nuse Illuminate\\Database\\Eloquent\\Model as Eloquent;\n\nclass EntryModel extends Eloquent\n{\n    public $incrementing = false;\n    protected $keyType = 'string';\n    protected $guarded = [];\n    protected $table = 'entries';\n\n    protected $casts = [\n        'date' => 'datetime',\n        'data' => 'json',\n        'published' => 'bool',\n    ];\n\n    public function origin()\n    {\n        return $this->belongsTo(self::class);\n    }\n}\n```\n\nThis is pretty basic stuff here – just one relationship (`origin()`) which allows us to handle multi-site entries.\n\nThe `incrementing` and `keyType` properties are necessary because we're using strings for the `id` column. When you disable `incrementing` you also need to pass an ID in when saving a new model. Eloquent doesn't know how to generate a new primary key automatically.\n\n## The Entry\n\nIn our repository, we re-bound the native Statamic Entry class to our own. We'll extend the native one but make a handful of tweaks to keep it Eloquent-y. Is that a word? It is now.\n\n``` php\n<?php\n\nnamespace Statamic\\Eloquent\\Entries;\n\nuse Statamic\\Eloquent\\Entries\\EntryModel as Model;\nuse Statamic\\Entries\\Entry as FileEntry;\nuse Statamic\\Facades;\n\nclass Entry extends FileEntry\n{\n    protected $model;\n```\n\nThe `fromModel` is used frequently by the query builder to convert an Eloquent model into a Statamic entry. It's role is to feed attributes into the appropriate entry methods. There's also a getter/setter for setting the `model` so we can reach into it where necessary.\n\n``` php\n    public static function fromModel(Model $model)\n    {\n        return (new static)\n            ->locale($model->site)\n            ->slug($model->slug)\n            ->date($model->date)\n            ->collection($model->collection)\n            ->data($model->data)\n            ->published($model->published)\n            ->model($model);\n    }\n\n    public function model($model = null)\n    {\n        if (func_num_args() === 0) {\n            return $this->model;\n        }\n\n        $this->model = $model;\n\n        $this->id($model->id);\n\n        return $this;\n    }\n```\n\nThe `toModel` method converts the entry _back_ to an Eloquent model where it's ready to be inserted into the database when an entry is saved. We use the `findOrNew` method – it will grab an existing entry if it exists, otherwise create a new one. A freshie, as we say.\n\n```php\n    public function toModel()\n    {\n        return Model::findOrNew($this->id())->fill([\n            'id' => $this->id() ?? (string) Str::uuid(),\n            'origin_id' => $this->originId(),\n            'site' => $this->locale(),\n            'slug' => $this->slug(),\n            'uri' => $this->uri(),\n            'date' => $this->hasDate() ? $this->date() : null,\n            'collection' => $this->collectionHandle(),\n            'data' => $this->data(),\n            'published' => $this->published(),\n            'status' => $this->status(),\n        ]);\n    }\n```\n\nWhen working with files, the last modified date comes from a property named `updated_at` in the entry, which falls back to the file's last modified timestamp. Because we're not dealing with files anymore, we'll use the model's `updated_at` timestamp instead.\n\n``` php\n    public function lastModified()\n    {\n        return $this->model->updated_at;\n    }\n```\n\nIf you aren't familiar with Statamic's multi-site feature, you should know that entries can be localized based off another entry. The `origin_id` gets saved inside the localized entry and is a reference to where the data originated (e.g. the original translation of some content).\n\nSince we're saving the `origin_id` in a column separate from the rest of the YAML-based data, we'll override a few methods to handle reading through the Eloquent relationship.\n\n```php\n    public function origin($origin = null)\n    {\n        if (func_num_args() > 0) {\n            $this->origin = $origin;\n\n            return $this;\n        }\n\n        if ($this->origin) {\n            return $this->origin;\n        }\n\n        if (! $this->model->origin) {\n            return null;\n        }\n\n        return self::fromModel($this->model->origin);\n    }\n\n    public function originId()\n    {\n        return optional($this->origin)->id() ?? optional($this->model)->origin_id;\n    }\n\n    public function hasOrigin()\n    {\n        return $this->originId() !== null;\n    }\n}\n```\n\n## Saving and Deleting\n\nWhen you call `$entry->save()`, or `delete()`, it will perform essential functions inside the method itself – like emitting events. The actual saving/deleting behavior is handed off to the repository.\n\nFor instance, when using the Stache, we may want to write or delete a _file_, but in here we need to insert or delete a database record.\n\nOkay, let's get back to our repository. When it's time to save an entry we'll make a model (an existing or a fresh one), save it to the database, and plop the fresh model back into the entry.\n\n``` php\nclass EntryRepository extends StacheRepository\n{\n    //\n\n    public function save($entry)\n    {\n        $model = $entry->toModel();\n\n        $model->save();\n\n        $entry->model($model->fresh());\n    }\n```\n\nDeleting is as simple as removing the model:\n\n``` php\n    public function delete($entry)\n    {\n        $entry->model()->delete();\n    }\n}\n```\n\n## Collections\n\nWhile we're keeping the collections themselves (not the entries) stored in the filesystem, we need to tell it how to route urls to the database. Time to make custom collection repository to define that.\n\nWhen a collection is updated, specifically it's `route`, all of it's entries will need to have their `uri`s updated.\n\n``` php\n<?php\n\nnamespace Statamic\\Eloquent\\Entries;\n\nuse Illuminate\\Support\\Facades\\Cache;\nuse Statamic\\Stache\\Repositories\\CollectionRepository as StacheRepository;\n\nclass CollectionRepository extends StacheRepository\n{\n    public function updateEntryUris($collection)\n    {\n        $collection\n            ->queryEntries()\n            ->get()->each(function ($entry) {\n                EntryModel::where('id', $entry->id())->update(['uri' => $entry->uri()]);\n            });\n    }\n}\n```\n\n``` php\npublic function register()\n{\n    Statamic::repository(CollectionRepositoryContract::class, CollectionRepository::class);\n}\n```\n\n## Taxonomies\n\nWe don't want to forget about taxonomies. Unless your project doesn't need them, then you could totally skip them like a bad dessert. Vanilla wafers are a terrible dessert. You should always skip vanilla wafers and save your calories for non-garbage foods.\n\n### Storing associations\n\nAnother method in the entry repository is `taxonomize` which is called when an entry is saved. This is a hook to let you organize your taxonomy term associations however appropriate. By default, the Stache repository will loop through the taxonomy fields in the entry and track them in the \"taxonomy terms\" Stache store.\n\nIf you wanted to store all the term associations in the database, you can, but for the purposes of this example we'll just let them stay in the Stache. Let's move on.\n\n### Querying Taxonomies\n\nThe entry query builder has a few of required methods for performing taxonomy based queries. `whereTaxonomy` filters entries by\na single term, and `whereTaxonomyIn` filters by multiple.\n\nSince we're leaving the associations in the Stache we'll be able to query against the taxonomies the same way as the Stache query builder\nwould. Statamic provides a `QueriesTaxonomizedEntries` trait for us to use that'll add those methods. We just need to make sure to compile\nthem before the query is performed in `get`, `paginate`, and `count`.\n\n``` php\nuse Statamic\\Stache\\Query\\QueriesTaxonomizedEntries;\n\nclass EntryQueryBuilder extends EloquentQueryBuilder implements QueryBuilder\n{\n    use QueriesTaxonomizedEntries;\n\n    public function get($columns = ['*'])\n    {\n        $this->addTaxonomyWheres();\n\n        return parent::get($columns);\n    }\n\n    public function paginate($perPage = null, $columns = ['*'])\n    {\n        $this->addTaxonomyWheres();\n\n        return parent::paginate($perPage, $columns);\n    }\n\n    public function count()\n    {\n        $this->addTaxonomyWheres();\n\n        return parent::count();\n    }\n}\n```\n\n:::tip\nIf you were to store the associations in the database, you'd need to define your own `whereTaxonomy` and `whereTaxonomyIn` methods that would query through a pivot table. In that case you probably wouldn't need to override `get`, `paginate`, and `count`.\n:::\n\n## Conclusion\n\nAnd there you have it. You've built a custom Eloquent repository, re-wired all the data I/O touch-points, and should now be able to handle a butt-ton of entries. Good luck!\n\n\n[repo]: https://github.com/statamic/eloquent-entries\n"
  },
  {
    "path": "content/collections/tips/change-timezone-to-utc.md",
    "content": "---\nid: 7af5ee6c-234a-48eb-b02b-c72b52562618\nblueprint: tips\ntitle: 'How to change your timezone to UTC'\nintro: \"Statamic 6 introduces improved timezone handling. Using UTC as your timezone is a best practice.\"\ntemplate: page\nrelated_entries:\n  - 7dfba904-8a74-40e1-b507-51cd2b5f6123\n---\nIt's best practice to keep your app's timezone set to UTC.\n\nIf you've had to change it in the past (maybe to workaround timezone issues in Statamic), you may now change it back to UTC.\n\n:::warning\nBefore continuing, you will want to make a backup of your content and/or database.\n\nAdditionally, you may consider **not** changing your timezone for existing sites. While it is a best practice, it's not a requirement. \n:::\n\n## Migration command\nYou can use our migration command which will update date fields throughout your content:\n\n```bash\nphp please migrate-dates-to-utc America/New_York\n```\n\nYou should replace `America/New_York` with whatever timezone you want to convert dates _from_ (e.g. your old app timezone).\n\nIf you're storing content in a database, or outside of version control, you will need to run this command after deploying to production/staging.\n\nOnce complete, you may change your app's timezone in `config/app.php`:\n\n```php\n// config/app.php\n\n'timezone' => 'America/New_York', // [tl! remove]\n'timezone' => 'UTC', // [tl! add]\n```\n"
  },
  {
    "path": "content/collections/tips/configuring-update-scripts.md",
    "content": "---\nid: 68dcd2ce-f10c-4363-8f8d-3c97d3264dd0\ntitle: 'Configuring update scripts on older installations'\nintro: 'Update scripts were introduced for Statamic 3.1. Learn about update scripts and how to configure them on older Statamic installations.'\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821009\n---\n## What are update scripts?\n\n[Update scripts](/extending/addons#update-scripts) are used for automatically updating data when breaking changes are introduced by Statamic or by addons. This feature was introduced for Statamic 3.1, and new installations will have all of this preconfigured out-of-the-box. For older installations, you may need to manually configure update scripts in your app:\n\n### Updating your composer.json\n\nAdd this `pre-update-cmd` under the `scripts` section of your `composer.json`:\n\n``` json\n\"scripts\": {\n    \"pre-update-cmd\": [\n        \"Statamic\\\\Console\\\\Composer\\\\Scripts::preUpdateCmd\"\n    ],\n    ...\n}\n```\n\n### Running update scripts for the first time\n\nIf the above script was not already registered in your composer.json, you'll need to manually trigger update scripts in your app by running:\n\n``` shell\nphp please updates:run 3.0\n```\n\n:::tip\nThis is a one-time thing and will be automatically triggered by future Statamic updates.\n:::\n"
  },
  {
    "path": "content/collections/tips/content-security-policy.md",
    "content": "---\nid: a3fab450-3ce1-451c-b51d-cfc79bafd317\nblueprint: tips\ntitle: 'Content Security Policy'\ntemplate: page\nintro: Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to site defacement, to malware distribution.\n---\n\n## What is a Content Security Policy (CSP)?\n\nThat intro is straight out of the [MDN documentation][mdn]. If you don't know about CSP, that's a great resource to check out!\n\nIn a nutshell, it prevents against malicious JavaScript. On a content managed website, especially on one where you may accept input from untrusted users, you may want to consider implementing a CSP to prevent unwanted JavaScript being executed.\n\n## How to use it in Statamic\n\nStatamic doesn't come with any CSP out of the box, but since it's a Laravel application, it's quite simple to add your own.\n\nThe simplest way to do this is to add a `meta` tag to your `head`.\n\n```html\n<meta http-equiv=\"Content-Security-Policy\" content=\"...\" />\n```\n\n(Replace the `...` with the appropriate value - more on that [below](#what-values-to-use))\n\nYou may also opt to use a middleware to add a header. If you do this, you'll want to prevent applying it to Control Panel routes, since Statamic needs to be able to run inline JavaScript. You can add it to the `web` middleware group, which your front-end will use but the Control Panel won't.\n\n```php\n// app/Http/Kernel.php [tl! focus]\n\nprotected $middlewareGroups = [ // [tl! focus]\n    'web' => [ // [tl! focus]\n        \\App\\Http\\Middleware\\EncryptCookies::class,\n        \\Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse::class,\n        \\Illuminate\\Session\\Middleware\\StartSession::class,\n        \\Illuminate\\View\\Middleware\\ShareErrorsFromSession::class,\n        \\App\\Http\\Middleware\\VerifyCsrfToken::class,\n        \\Illuminate\\Routing\\Middleware\\SubstituteBindings::class,\n        \\App\\Http\\Middleware\\ContentSecurityPolicy::class, // [tl! ++ focus]\n    ], // [tl! focus]\n]; // [tl! focus]\n```\n```php\n<?php // app/Http/Middleware/ContentSecurityPolicy.php\n\nnamespace App\\Http\\Middleware;\n\nclass ContentSecurityPolicy\n{\n    public function handle($request, Closure $next)\n    {\n        return $next($request)\n            ->header('Content-Security-Policy', \"...\");\n    }\n}\n```\n\nYou may even use a package such as [Laravel CSP by Spatie](https://github.com/spatie/laravel-csp).\n\n## An example of what it protects against\n\nWe all know that you shouldn't trust user input. That's why we say that you should use the [sanitize modifier](/modifiers/sanitize) whenever outputting something from the user.\n\nSanitizing works well to protect you from user's popping `<script>` tags and other HTML in blocks of text. But let's take a look at a situation where you are populating a link with a user-provided URL:\n\n```yaml\nmy_link: http://example.com\n```\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ my_link | sanitize }}\">My Link</a>\n```\n::tab blade\n```blade\n{{-- Blade sanitizes output by default --}}\n<a href=\"{{ $my_link }}\">My Link</a>\n\n{{-- Using the sanitize modifier with Blade's unescaped syntax --}}\n{!! Statamic::modify($my_link)->sanitize() !!}\n```\n::\n\nThis is fine if they provide a real URL, but what about something sneakier:\n\n```yaml\nmy_link: 'javascript:sneakyStuff()'\n```\n\nNow they've put JavaScript into your template. Even if you sanitize the `my_link` variable, it won't matter since there's no HTML to escape. That's where CSP comes in handy.\n\nIf you click that link without a CSP, it will run `sneakyStuff()`. Oh no!\n\nIf you have it enabled, you'll get an error message like `Refused to run the JavaScript URL because it violates the following Content Security Policy`. You've just protected yourself.\n\n\n## What values to use\n\nIn the examples above, you'll see `...` as a placeholder value, and you'll want to use the appropriate policy for your use case. You can find out exactly what works for you by consulting the [MDN documentation][mdn].\n\nBut, you can start with these:\n\n- Only allow `<script src=\"\">` tags with URLs on your own site:\n  ```\n  script-src 'self'\n  ```\n- Only allow `<script src=\"\">` tags with URLs from your own site, or from `unpkg.com`:\n  ```\n  script-src 'self' unpkg.com\n  ```\n- Only allow `<script src=\"\">` tags with URLs from your own site, or from `unpkg.com`, and `unsafe-eval` [which is required for Alpine.js to work](https://alpinejs.dev/advanced/csp).\n  ```\n  script-src 'self' unpkg.com 'unsafe-eval'\n  ```\n\n\n\n[mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP\n"
  },
  {
    "path": "content/collections/tips/converting-from-single-to-multi-site.md",
    "content": "---\nid: e1da92af-a0d8-40bb-9417-52675fad5e1f\ntitle: 'Converting from Single to Multi-Site'\ntemplate: page\ncategories:\n  - development\n  - cli\n  - localization\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821029\n---\n## Automated Conversion\n\nWe recommend using the following command to convert from a single to [multi-site](/multi-site) installation:\n\n``` shell\nphp please multisite\n```\n\n## Manual Conversion\n\nIf you wish to better understand how to manually convert from a single to multi-site installation, the steps are as follows.\n\n### Enable Multisite Config\n\nFirst, enable `multisite` in your `config/statamic/system.php`:\n\n``` php\n'multisite' => true,\n```\n\n### Adding New Sites\n\nNext, you can add new sites through the control panel at `/cp/sites`, or directly in your `resources/sites.yaml` file:\n\n``` yaml\ndefault:\n  name: First Site\n  url: /\n  locale: en_US\nsecond:\n  name: Second Site\n  url: /second/\n  locale: en_US\n```\n\nBy default, the first site handle is named `default`, but feel free to rename it. This handle will be used below.\n\n### Update Default Site Content\n\nNow you'll need to update your default site content file & folder structure, so that Statamic knows where to find it, now that you've enabled multi-site.\n\n1. For each collection:\n    - Move all entries into a directory named after your default site's handle. (eg. `content/collections/blog/*.md` into `content/collections/blog/default/*.md`)\n    - Add a `sites` array to the collection's yaml file with each site you want the entries to be available in.\n2. For each tree structure:\n    - Take the `root` and `tree` variables, and move them in a file in a subdirectory named after your default site's handle. (eg. `content/trees/navigation/pages.yaml` to `content/trees/navigation/default/pages.yaml`)\n    - Add a `sites` array to the root structure's yaml file with each site you want the structure to be available in.\n3. For each global set:\n    - Add a `sites` array to the root global's yaml file with each site you want the global to be available in.\n\nAt this point, your content will be available in the default site. You will need to localize each piece of content by following the steps in its respective documentation.\n\n_**Note:** If you don't add the `sites` to the respective container files, the site selector will not be visible in the control panel._\n\n### Clear The Cache\n\nFinally, clear your cache!\n\n``` shell\nphp artisan cache:clear\n```\n"
  },
  {
    "path": "content/collections/tips/creating-users-by-hand.md",
    "content": "---\nid: 55993382-c928-48d0-8559-c88b226d4657\ntitle: 'Creating Users by Hand'\nintro: 'Did you know you can create users by hand by making new text files? Well now you do. Here''s how.'\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821071\n---\nUser accounts are represented by YAML files in the `users/` directory, named according to their email address. The YAML contents must have a password and role of some kind inside to be able to log into the control panel. The simplest role is a Super User.\n\n:::tip\nYou can also [create users via the command line](/quick-start-guide#create-your-first-user). It's even easier than this.\n:::\n\n## Walkthrough: Creating a new Super User\n\n1. Create a file called `<your.email@example.com.yaml>`. Make sure to use a real email address otherwise that reset password feature won't do a darn thing.\n2. Inside the YAML file add `super: true` and `password: anything_you_want`, where `anything_you_want` is literally anything you want as a password.\n3. Visit any URL on the site and Statamic will spot that unencrypted password and hash/securify it for you.\n4. Now you can log in with `anything_you_want`, or the thing you really wanted.\n\n**Before being securified:**\n``` yaml\n# your.email@example.com.yaml\nsuper: true\npassword: anything_you_want\n```\n\n**After securification:**\n``` yaml\n# your.email@example.com.yaml\nsuper: true\npassword_hash: $2y$10$Vopn8T7e.EMVEjxdP5p.g.AU5GTTN4RklvgR2l0dTwSPeJal91v/q\n```\n\n## Assigning Non-Super Roles\n\nIf you've created roles with limited permissions, you can assign those to your user's YAML file in an array:\n\n``` yaml\nroles:\n  - editor\n  - publisher\n```\n"
  },
  {
    "path": "content/collections/tips/digital-ocean-spaces-for-asset-container.md",
    "content": "---\nid: 2aa44c22-f626-4f9e-826d-c23ef482cf08\ntitle: 'Using Digital Ocean Spaces for an Asset Container'\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821446\n---\nYou might know it's possible to use Amazon S3 for your Asset Containers, but it's also very simple to use [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/).\n\nSince Digital Ocean Spaces is compatible with the Amazon S3 API, you can use the `s3` flysystem driver, but with Digital Ocean credentials.\n\n## Install\n\nFirst, install the AWS Flysystem adapter:\n\n``` shell\ncomposer require league/flysystem-aws-s3-v3\n```\n\n## Configure a filesystem\n\nAdd a filesystem to `config/filesystems.php`, and make sure to use the `s3` driver.\n\n```php\n'do_spaces' => [\n    'driver' => 's3',\n    'key' => env('DO_SPACES_KEY'),\n    'secret' => env('DO_SPACES_SECRET'),\n    'endpoint' => env('DO_SPACES_ENDPOINT'),\n    'region' => env('DO_SPACES_REGION'),\n    'bucket' => env('DO_SPACES_BUCKET'),\n    'root' => env('DO_SPACES_ROOT'),\n    'url' => env('DO_SPACES_URL'),\n    'visibility' => 'public', // Set this public so the files uploaded are available publically.\n],\n```\n\n```env\nDO_SPACES_KEY=\nDO_SPACES_SECRET=\nDO_SPACES_ENDPOINT=\nDO_SPACES_REGION=\nDO_SPACES_BUCKET=\nDO_SPACES_ROOT=\nDO_SPACES_URL=\n```\n\n- The `key` and `secret` can be generated in the \"API\" section of your dashboard.\n- You can find the `endpoint` in the Space's settings. It'll look something like `https://nyc3.digitaloceanspaces.com`.\n- The `region` will be in the endpoint. e.g. `nyc3`\n- The `bucket` will be the name of your space. e.g. `myspace`\n- You only need to set the `root` if you want this disk to be a subfolder of your space. This is fairly uncommon.\n- The `url` will be in the header of the file browser, or in list of your spaces. It'll look something like `https://myspace.nyc3.digitaloceanspaces.com`\n- If you've enabled the CDN, it's possible you'd have a non-Digital Ocean `url`. Look for the \"edge\" URL.\n\n## Link to the asset container\n\nCreate a new Asset Container using this `do_spaces` as a Disk. You can do this via the CP or add a `handle.yaml` file to `content/assets`:\n\n```yaml\ntitle: \"MySpace\"\ndisk: do_spaces\n```\n\n:::tip\nIf you're having performance issues with S3 containers in the Control Panel, check out these [optimization tips](/tips/optimizing-assets).\n:::\n\n"
  },
  {
    "path": "content/collections/tips/disabling-cp-authentication.md",
    "content": "---\nid: b47a6f6c-37bf-4269-9930-9096ee21891a\nblueprint: tips\ntitle: 'Disabling Control Panel Authentication'\nintro: 'BYO login page.'\ntemplate: page\n---\nIn some cases, you may want to disable the Control Panel's authentication pages (login, forgot password, etc.) This could be handy if you are building that part of your app yourself.\n\n1. In `config/statamic/cp.php`, disable the authentication features.\n   ```php\n   'auth' => [\n     'enabled' => false,\n     'redirect_to' => '/your-login-page'\n   ]\n   ```\n\nThe Control Panel will still require an authorized user. This just allows you to provide your own login flow.\n"
  },
  {
    "path": "content/collections/tips/excluding-the-control-panel-from-maintenance-mode.md",
    "content": "---\nid: 4f480db2-f80b-4b97-905c-b946f94c544d\ntitle: 'Excluding the Control Panel from Maintenance Mode'\nintro: '[Laravel''s maintenance mode](https://laravel.com/docs/configuration#maintenance-mode) is a great way to notify visitors that your site is down but will be back up shortly. But what if you still want to get into the control panel? Here''s how.'\ntemplate: page\ncategories:\n  - development\n  - cli\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821104\n---\n## Overview\n\nWhen your site is in Laravel's [maintenance mode](https://laravel.com/docs/configuration#maintenance-mode), a custom view will be displayed for all requests into your site. This makes it easy to \"disable\" your site while it is updating or when you are performing maintenance. The logic for this mode is handled in the default middleware.\n\nTo enable maintenance mode, run the `down` Artisan command:\n\n``` shell\nphp artisan down\n```\n\nAnd to disable maintenance mode, run the reverse `up` Artisan command:\n\n``` shell\nphp artisan up\n```\n\n## Excluding URLs\n\nIt's possible to specify URLs that should remain \"up\" while your application is in maintenance mode. For most applications, you can define these in your app's `bootstrap/app.php` file:\n\n```php\nreturn Application::configure(basePath: dirname(__DIR__))\n    ->withRouting()\n    ->withMiddleware(function (Middleware $middleware) { // [tl! focus]\n        $middleware->preventRequestsDuringMaintenance(except: [ // [tl! focus]\n            '/cp*' // [tl! focus]\n        ]); // [tl! focus]\n    }) // [tl! focus]\n```\n\nIn this example, the Control Panel will be available while the rest of your application is \"down\".\n\n:::hint\nFor older applications, without the `Application::configure()` API in the `bootstrap/app.php` file, you can instead define exclusions in the `app/Http/Middleware/PreventRequestsDuringMaintenance.php` file:\n\n```php\nprotected $except = [\n    '/cp*'\n];\n```\n:::\n"
  },
  {
    "path": "content/collections/tips/gdpr-considerations.md",
    "content": "---\nid: 3859a6bf-8ece-44d0-9a30-4879c93924bf\ntitle: 'GDPR Considerations'\nintro: 'We aren''t lawyers and this isn''t official advice, but here are some things to consider if you need to comply with [GDPR](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation).'\ntemplate: page\ncategories:\n  - privacy-gdpr\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821116\n---\n## User Accounts and Git\n\nIf you're version controlling everything (which we usually recommend), you may want to consider excluding your users from version control or storing them in a database as references to user data will persist in a git repository's history even after that user is removed from the application.\n\n### Option 1: Gitignore rule\n\n```git\n# .gitignore\n\nusers/*\n```\n\nIf you take this approach, your production server will be the single source of truth. You may want to consider having some sort of backup system for the user records. You know, just in case.\n\n### Option 2: Store users in a database\n\nAnother option would be to store your users in a database so you can remove them without leaving data fragments behind.\n\n[Here's an article](/tips/storing-users-in-a-database) on how to do just that.\n\n\n## Form Submissions\n\nYou may also want to disable form submission storing on any [forms](/forms), and opt for email-only notifications.\n\n<figure>\n    <img src=\"/img/tips/form-disable-store-submissions.png\" alt=\"Form submission storage disabled\" width=\"516\">\n    <figcaption>You can disable storing form submissions on your server.</figcaption>\n</figure>\n"
  },
  {
    "path": "content/collections/tips/git-workflow.md",
    "content": "---\nid: b46adc3b-c4de-4148-a388-c8ff498ae9c9\nblueprint: tips\ntitle: 'Git Workflow'\nintro: 'A good workflow revolving around git to manage your deployments is a key factor in a pain-free and efficient project.'\ntemplate: page\n---\nGiven a properly configured VPS solution (like Laravel Forge or similar), your typical deployment workflow would normally look like this:\n\n## As a solo developer {#solo}\n\n1. Make your changes locally\n2. Git commit changes and push to `main`\n3. Changes are automatically deployed to your server\n\n## As a dev team {#team}\n\n1. Make your changes locally\n2. Git commit changes and push to a feature or bug branch (e.g. `feature/new-contact-form`)\n3. Open a Pull Request to your `main` branch\n4. Have a team member review the Pull Request and either request changes or approve\n5. Merge branch to `main`\n6. Changes are automatically deployed to your server\n\n## Content Editing in Production\n\nIf you or your clients manage your content in production (like one would using WordPress), there are two main approaches you can take.\n\n### Version Controlling Content\n\nIf you want to version control your content (which we usually encourage folks to do), we recommend enabling [Git Automation](/git-automation) to automatically commit and deploy changes to content.\n\nWith that enabled, your _developer_ workflow might look like this:\n\n1. Make your changes locally\n2. Git commit changes.\n3. `git pull --rebase` changes from production\n4. Push changes\n5. Changes automatically deployed to server\n\n### Ignoring Content\n\nSometimes it makes sense to keep the content _outside_ of git. Perhaps you have a lot of writers and content changes happen fast and furiously. Or maybe you have a ton of content and want to keep your git repo small.\n\nWhatever the reason, you can ignore the `content` directory in your `.gitignore` file, and just use git to manage everything else [just like usual](#team).\n\n## Additional Reading\n\n- [Gitflow Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) – When working with a team (and even when by yourself) it's a good practice to follow a standardized workflow for working with git. We recommend Gitflow.\n\n- [Zero Downtime Deployment Tips](/tips/zero-downtime-deployments#understanding-the-folder-structure) - If you plan on using a zero downtime deployment tool like [Envoyer](https://envoyer.io/), [Deployer](https://deployer.org/), etc. be sure to read our tips & tricks guide."
  },
  {
    "path": "content/collections/tips/how-to-enable-statamic-pro.md",
    "content": "---\nid: 142626de-5984-4600-b14b-2a753d6979a8\nblueprint: tips\ntitle: 'How to Enable Statamic Pro'\nintro: 'A fresh Statamic install starts in Solo edition mode. Here''s how to enable Pro mode and unlock every feature Statamic has.'\ntemplate: page\n---\nA fresh Statamic install starts in Solo edition mode. You can enable Pro in your `.env` file at any time by running:\n\n``` shell\nphp please pro:enable\n```\n\nYou can also set `pro` to `true` in `config/statamic/editions.php` for all environments (which you may need to do for older installations of Statamic where the above command doesn't exist).\n\nOnce you've opted in, many additional features become be available.\n\n## Trying Pro Mode {#trial-mode}\n\nYou can use Statamic Pro locally without a license key for as long as you'd like. This is called **Trial Mode**.\n\nWhile in trial mode you are also able to try out any commercial [addons](https://statamic.com/addons).\n\n## When It's Time to Launch\n\nOnce it’s time to launch your site on a public domain, there are a few things you need to do:\n\n- [Create a Site](https://statamic.com/account/sites/create) on statamic.com and enter the appropriate domain(s).\n- Purchase a license of [Statamic Pro](https://statamic.com/pricing) (and any paid addons) and attach them to your Site.\n- Add your Site's license key to your environment file in production.\n\n``` env\nSTATAMIC_LICENSE_KEY=your-site-license-key\n```\n\n"
  },
  {
    "path": "content/collections/tips/importing-content.md",
    "content": "---\nid: c52a611e-2694-4a70-a974-75934d178017\ntitle: 'Importing Existing Content'\nintro: \"After configuring collections and blueprints, you'll likely want to import content from an existing CMS into Statamic. This guide walks you through the various options.\"\ntemplate: page\n---\n## Overview\n\nAfter configuring your collections and blueprints in Statamic, your next step will likely be to import existing content into Statamic from a previous CMS.\n\nThis guide walks through the process of importing content using our first-party Importer addon, as well how you can build your own importer using repositories.\n\n## Importer Addon (recommended)\n\nTo make the process of importing content super easy, we've built a first-party [Importer addon](https://github.com/statamic/importer). The addon allows you to import entries, taxonomies and users. It provides an easy-to-use UI for mapping data to blueprint fields.\n\n1. To get started, install the Importer addon using Composer:\n    ```bash\n    composer require statamic/importer\n    ```\n2. Before importing anything, you should review the [Preperation steps](#preparation) detailed in the addon's documentation.\n3. In the Control Panel, under `Utilities > Importer`, you can create an import. You'll be asked to give it a name, upload the file you wish to import and tell it where the data should be imported.\n4. You can then map fields from your blueprint to fields/columns in your file. You will also need to specify a \"Unique Key\", which the Importer will use to determine if the item already exists in Statamic.\n5. All that's left to do is click \"Import\" and watch the magic happen! ✨\n\nFor more detailed instructions on how the Importer works, please see the [addon's documentation](https://statamic.com/addons/statamic/importer/docs).\n\n## Build your own importer\n\nIf you need more flexibility around how the import happens, or would just prefer to handle it yourself, then you can build your own importer.\n\n1. Create a new command to house the importer:\n    ```bash\n    php artisan make:command ImportCommand\n    ```\n2. In that command, you'll need to get the content you're wanting to import. This could be from a JSON file, a spreadsheet or some kind of external API. It's up to you.\n3. Now, for the exciting bit, importing the content!\n\n    You can loop through the content and create entries for eeach item. Statamic provides an [entry repository](/repositories/entry-repository), which allows you to programatically create entries. Here's an example:\n\n    ```php\n    <?php\n\n    namespace App\\Console\\Commands;\n\n    use Illuminate\\Console\\Command;\n    use Illuminate\\Support\\Str;\n    use Statamic\\Facades\\Entry;\n\n    class ImportCommand extends Command\n    {\n        /**\n        * The name and signature of the console command.\n        *\n        * @var string\n        */\n        protected $signature = 'app:import';\n\n        /**\n        * The console command description.\n        *\n        * @var string\n        */\n        protected $description = 'Imports content into Statamic.';\n\n        /**\n        * Execute the console command.\n        */\n        public function handle() // [tl! focus:start]\n        {\n            // Get your content...\n            $teamMembers = [\n                ['name' => 'Josh Lyman', 'role' => 'Deputy Chief of Staff'],\n                ['name' => 'CJ Cregg', 'role' => 'Press Secretary'],\n                ['name' => 'Toby Ziegler', 'role' => 'Communications Director'],\n                ['name' => 'Sam Seaborn', 'role' => 'Deputy Communications Director'],\n            ];\n\n            // Loop through each item and create a new entry using the Entry facade...\n            foreach ($teamMembers as $member) {\n                $entry = Entry::make()\n                    ->collection('team')\n                    ->slug(Str::slug($member['name']))\n                    ->data([\n                        'name' => $member['name'],\n                        'role' => $member['role'],\n                    ]);\n\n                $entry->save();\n            }\n        }  // [tl! focus:end]\n    }\n    ```\n4. Then, simply run the command to import the content:\n    ```\n    php artisan app:import\n    ```\n\nIf you need to import other content, like taxonomy terms or users, please review the docs on [Repositories](/reference/repositories).\n"
  },
  {
    "path": "content/collections/tips/laravel-nova.md",
    "content": "---\nid: 9af8c58e-7a9f-4f9c-89cd-97dfb3de33e6\ntitle: 'Using Statamic Alongside Laravel Nova'\nintro: 'Nova is an admin panel designed to manage your Eloquent models and other things. It can work hand in hand with Statamic.'\ntemplate: page\ncategories:\n  - development\n  - laravel\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821126\n---\nWe've heard users saying they'd like to manage the front-end with Statamic and the back-end with [Laravel Nova](https://nova.laravel.com/). That's fine! It's possible to run both Statamic and Nova together.\n\n:::tip\nIf you'd like to manage Eloquent models within Statamic, [you're able to do that, too](/tips/storing-entries-in-a-database).\n:::\n\n1. Install Nova as per their documentation\n2. Disable Statamic's front-end routing. It conflicts with Nova.\n   ```php\n   // config/statamic/routes.php\n   'enabled' => false,\n   ```\n3. Add Statamic's routes that you just disabled, to your own routes file.\n   ```php\n   // routes/web.php\n   Statamic::additionalWebRoutes();\n   Route::any('/{segments?}', '\\Statamic\\Http\\Controllers\\FrontendController@index')\n      ->where('segments', '(?!nova|cp).*')\n      ->name('statamic.site');\n   ```\n4. Create a [dedicated auth guard](/tips/using-an-independent-authentication-guard) and provider for Statamic.\n   ```php\n   // config/auth.php\n   'guards' => [\n        'web' => [\n            'driver' => 'session',\n            'provider' => 'users',\n        ],\n        'statamic' => [\n            'driver' => 'session',\n            'provider' => 'statamic',\n        ],\n   ],\n\n   'providers' => [\n        'users' => [\n            'driver' => 'eloquent',\n            'model' => \\App\\User::class,\n        ],\n        'statamic' => [\n            'driver' => 'statamic',\n        ],\n   ],\n   ```\n5. Configure Statamic to use that guard.\n   ```php\n   // config/statamic/users.php\n   'guards' => [\n        'cp' => 'statamic',\n        'web' => 'statamic',\n   ],\n   ```\n"
  },
  {
    "path": "content/collections/tips/load-templates-dynamically-based-on-the-url.md",
    "content": "---\nid: 8eac02d5-1fc1-477c-a571-4aba29f1b60e\ntitle: 'Load templates dynamically based on the URL'\nintro: 'If you''ve ever just wanted to start working on the frontend HTML/CSS without messing around with collections and blueprints yet, here''s a fun little trick. These two route rules will give you a homepage and then dynamically map your URLs to match the folder structure of your views directory.'\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821148\n---\n## The Snippet\nAll you need to do is drop these route rules in your `routes/web.php` file. Everything else will work as usual – tags, modifiers, etc, and use the default `layout.antlers.html` layout.\n\n```php\n# routes/web.php\n\nuse Statamic\\View\\View;\n\nRoute::statamic('/', 'home');\n\nRoute::get('{template}', function ($template) {\n    return View::make($template)->layout('layout');\n})->where('template', '(?!cp).+');\n```\n\n_Note: This route rule assumes the control panel login is `/cp` and you're using the default `layout.antlers.html` layout file. You can edit accordingly if you've customized these things. We trust you can figure out which thing to edit where. We believe in you.\n\n## Examples\nHere's this route rule in action.\n\n| URL | Template |\n|---|---|\n| `/` | `resources/views/home.antlers.html` |\n| `/design` | `resources/views/design.antlers.html` |\n| `/design/stuff` | `resources/views/design/stuff.antlers.html` |\n| `/design/your/popsicle` | `resources/views/design/your/popsicle.antlers.html` |\n"
  },
  {
    "path": "content/collections/tips/localizing-entries.md",
    "content": "---\nid: 4bc357de-4711-4010-8b83-77ca7337e90b\ntitle: 'Localizing Entries'\ntemplate: page\ncategories:\n  - localization\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821157\n---\n:::tip\nYou can use the `php please multisite` to automate converting from a single to a multisite installation.\n:::\n\n## Defining Sites\n\nWhen using [multiple sites](/multi-site), you'll need to specify in the collection's YAML file which sites this collection can be used in.\n\n``` yaml\n# content/collections/blog.yaml\nsites:\n  - en\n  - fr\n```\n\n## Folder Structure\n\nThe folder structure will differ from the single site structure explained in the [entries guide](/collections). Now, entries should be organized into the respective sites.\n\n``` files theme:serendipity-light\ncollections\n  blog.yaml\n  blog/\n    en/\n      2015-01-18.my-first-day.md\n      2015-01-19.paperwork-and-snowshoeing.md\n      2015-03-08.spring-wonderful-spring.md\n      2015-05-16.speeder-bikes-and-wookies.md\n    fr/\n      2015-01-18.my-first-day.md\n      2017-07-14.bastille-day.md\n  news.yaml\n  news/\n    en/\n      2017-04-01.its-happening.md\n```\n\n\n**An entry will only be available in that site if the entry has explicitly been localized.** For example, in the blog above, `my-first-day` would appear in both English and French sites, where `bastille-day` would only appear in the French site.\n\n:::tip\nIf you'd like the entry to be localized into all the sites automatically, you may enable [entry propagation](#propagation) (similar to how it worked in Statamic v2).\n:::\n\n## Localizable fields\n\nIn a [Blueprint](/blueprints), you can define which fields are localizable.\n\nWhile editing a localized entry, only the localizable fields will be editable. The non-localizable fields will be read-only.\n\nTo mark a field as localizable, head to your Blueprint and toggle on the globe icon on the field itself. That's all there is to it.\n\n<figure>\n    <img src=\"/img/tips/localizable-toggle.png\" width=\"463px\" alt=\"The Localizable Field Toggle Setting\">\n    <figcaption>See that little globe icon? Go ahead and click it.</figcaption>\n</figure>\n\n## Entry origins\n\nA localized entry should define where it originated, and will inherit any undefined values from its origin.\n\nFor example, you can create an entry in the English site, then choose to localize it into the French site, and vice versa.\n\n``` yaml\n# en/2015-01-18.my-first-day.md\nid: 123\ntitle: My First Day\nimage: forest.jpg\n```\n\n``` yaml\n# fr/2015-01-18.my-first-day.md\norigin: 123\nid: 456\ntitle: Mon Premier Jour\n```\n\nHere you can see that since the French version does not have `image` defined, it will inherit it from the English version.\n\n:::tip\nNotice that the French version has a different ID from the English version. In Statamic v3, **every entry has its own ID**, which is different from the Statamic v2 behavior.\n:::\n\n## Deleting\n\nAs explained above, when you localize an entry, an `origin` is added to the localization. If you were to delete the original entry, the localization would then\nbe referencing an entry that no longer exists. This could cause some confusion to Statamic!\n\nIf you want to delete an origin, you need to make a decision on how to handle any of its localizations. When deleting entries through the Control Panel, you will be presented with two options:\n\n![](/img/tips/delete-localization-modal.png)\n\n:::note\nWhen a user doesn't have access to the sites an entry is localized into, deleting it will detach the localizations.\n:::\n\n### Option 1: Delete\n\nIf you no longer need the localized version, then you can choose to just delete them.\n\nAn example of this may be when you have an entry and then you've simply translated it. It probably doesn't make sense to keep a translated version if the original no longer exists.\n\nWhen dealing with files, simply make sure that you've also deleted the localization.\n\n### Option 2: Detach\n\nDetaching essentially turns the localizations into their own standalone entries.\n\nAn example of this may be when you have a product being sold in multiple locations. You may have created the product\nin one location, and localized the price in other locations. Then, you discontinue the product in the original location.\nYou probably still want the product to exist in the other location.\n\nWhat will happen with this option is that any data that you haven't overridden on the localization will be copied to it.\nThe `origin` will also be removed.\n\nWhen dealing with files, make sure that you remove the origin, and copy any leftover data across manually.\n\n## Propagation\n\nBy default, when you create an entry, it will only exist in the site you've selected.\n\nYou may choose to automatically create localizations in the rest of the configured sites whenever you create the first entry.\n\n```yaml\n# content/collections/blog.yaml\npropagate: true\n```\n"
  },
  {
    "path": "content/collections/tips/localizing-globals.md",
    "content": "---\nid: 660e700e-0602-49fc-a8fb-ac47b9884e52\ntitle: 'Localizing Globals'\ntemplate: page\ncategories:\n  - localization\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821162\n---\n:::tip\nYou can use the `php please multisite` to automate converting from a single to a multisite installation.\n:::\n\n## Defining Sites\n\nWhen using [multiple sites](/multi-site), you'll need to specify in the Control Panel which sites this global set can be used in.\n\n![/img/globals-site-config.png](/img/globals-site-config.png)\n\n\n## Localizable fields\n\nIn a [Blueprint](/blueprints), you can define which fields are localizable.\n\nWhile editing a localized global set, only the localizable fields will be editable. The non-localizable fields will be read-only.\n\n\n## Origins\n\nA localized global set should reference the origin. In the example above, the french set originates from the english, so the `sport` variable will be inherited.\n"
  },
  {
    "path": "content/collections/tips/localizing-navigation.md",
    "content": "---\nid: 35c9cd07-f377-4fcb-b02c-72c1925e6fdf\ntitle: 'Localizing Navigation'\ntemplate: page\ncategories:\n  - localization\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821176\n---\n:::tip\nYou can use the `php please multisite` to automate converting from a single to a multisite installation.\n:::\n\n## Defining Sites\n\nTo choose which sites the navigation will be available in, just make sure the file exists in the respective sites' directories as described below.\n\n## Folder Structure\n\nThe folder structure will differ from the single site structure explained in the [navigation guide](/navigation). Now, navs should be organized into the respective sites.\n\nThe `tree` array will also be relocated into separate files organized into sites. The meta level information will remain in the existing YAML file.\n\n``` files theme:serendipity-light\ncontent/navigation/\n  nav.yaml\n  site-one/\n    nav.yaml\n  site-two/\n    nav.yaml\n```\n\n:::tip\nA navigation will be considered unavailable for a particular site if a file doesn't exist in its subdirectory.\n:::\n\n## Trees\n\nThe navigation file itself will continue to be responsible for defining things like its name, route, etc.\n\nThe \"tree\" defines the pages and their layout. The tree is what is localized to each site.\n\n``` yaml\n# nav.yaml\ntitle: Nav\nroot: true\ncollections:\n  - pages\n  - articles\n```\n\n``` yaml\n# site-one/nav.yaml\ntree: [...]\n```\n\n``` yaml\n# site-two/nav.yaml\ntree: [...]\n```\n"
  },
  {
    "path": "content/collections/tips/manually-resetting-a-user-password.md",
    "content": "---\nid: f7de14cd-9160-43c1-a017-c3af018ab8ab\ntitle: 'Manually Resetting a User Password'\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821169\n---\nSometimes you're stuck in a perfect storm of an error loop (like if your file permissions are wrong _and_ your didn't configure your email provider correctly) and just need to reset a user's password by any means necessary.\n\nThese are those means.\n\n1. Find the user you're looking for the `users` directory — they're organized by email address.\n2. Open the appropriate YAML file.\n3. Delete the `password_hash` variable.\n4. Add a new `password` variable set to whatever you want your password to be — temporary or otherwise.\n5. Visit any URL on the site and Statamic will spot that unencrypted password and hash it for you.\n6. Now you can log in.\n\n## Example for the sake of clarity\n\n**Before, while you have no idea what the password is:**\n``` yaml\nname: 'Mrs. Buttersworth'\nsuper: true\nid: ca095f7c-8870-4dba-bc97-8f5a43953920\npassword_hash: $2y$10$Sou9pAY.BXgz6xdWwji8wOdCdYo2GppvPOHBp8TEp074aQOtYl8AS\n```\n\n**After Step 4:**\n``` yaml\nname: 'Mrs. Buttersworth'\nsuper: true\nid: ca095f7c-8870-4dba-bc97-8f5a43953920\npassword: moresyruppleaseplzandthankyou!\n```\n\n**After Step 5:**\n``` yaml\nname: 'Mrs. Buttersworth'\nsuper: true\nid: ca095f7c-8870-4dba-bc97-8f5a43953920\npassword_hash: $2y$10$Vopn8T7e.EMVEjxdP5p.g.AU5GTTN4RklvgR2l0dTwSPeJal91v/q\n```\n"
  },
  {
    "path": "content/collections/tips/optimizing-assets.md",
    "content": "---\nid: b50310b0-64ae-4ae4-b219-a637ed89e4d7\ntitle: 'Optimizing Assets'\ntemplate: page\ncategories:\n  - development\n  - performance\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821191\n---\n\n:::tip\n**This only Applies to Statamic 3.1+**\n:::\n:::tip\n**If you are looking at optimisation due to performance issues it may be worth taking a look at the [performance section of the Nav tag](/tags/nav#performance)**\n:::\n\nStatamic's asset system allows you to point at a directory either locally or a remote service like Amazon S3.\n\nDoing things this way allows you to drop assets right onto the disk without needing to specifically use Statamic's Control Panel. Handy!\n\nThe downside to this is that it could slow down because of the number of file operations. Especially on remote services like Amazon S3 or Digital Ocean Spaces, because it would be performing API requests.\n\nDon't worry though, there are ways to keep things zippy!\n\n## 1. Disable the Stache watcher\n\nStatamic stores information about assets in the Stache which is our flat file database. The Stache has a \"watcher\" feature that will keep an eye on your files, and update things whenever it notices they've changed.\n\nIf you turn the watcher off, it won't need to look at the filesystem on every request, and things will become much faster.\n\n```php\n// config/statamic/stache.php\n'watcher' => false,\n```\n\nWith this off, you will need to use the Control Panel to manage your assets (and edit entries, etc) or clear the cache manually if you do make manual changes to the files.\n\n## 2. Enabling Flysystem caching\n\nStatamic's assets use Flysystem under the hood. Flysystem has a feature where it can cache filesystem calls, you just need to install and enable it. Please note that the `flysystem-cached-adapter` package [is not compatible](https://flysystem.thephpleague.com/docs/upgrade-from-1.x/#miscellaneous-changes) with Flysystem v2+ and only works with Laravel <= 8.\n\n``` shell\ncomposer require league/flysystem-cached-adapter\n```\n\n```php\n/// config/filesystems.php\n'disks' => [\n    's3' => [\n        'driver' => 's3',\n        // ...\n        'cache' => true,\n    ]\n]\n```\n\nMore details in the [Laravel Docs](https://laravel.com/docs/8.x/filesystem#caching).\n\n## 3. Disable Filesystem asserts\n\nFlysystem also has a feature where any time you try to read a file, it will first check if it exists. You disable this though, using the `disable_asserts` (that's asserts, not assets) flag on your Asset Container's disk.\n\n```php\n/// config/filesystems.php\n'disks' => [\n    's3' => [\n        'driver' => 's3',\n        // ...\n        'disable_asserts' => true,\n    ]\n]\n```\n\n_It's possible that in the future Statamic will do this automatically for you._\n\n## 4. Check for existence through Assets\n\nThis one is a tip mostly for addon developers.\n\nIf you need to check if a file exists, instead of looking directly at the filesystem, you should do it using the `Asset` object since it has some extra optimizations built in.\n\nYou can call this method a million times and it will only look at the filesystem once. In the case of S3, it would only make a single API call.\n\n```php\n$asset = AssetContainer::find('s3')->makeAsset('foo.jpg');\n$asset->exists();\n```\n\nWhere as these methods would be looking at the filesystem every time. Depending on your situation, this might be what you need, but you should be aware that since it's looking at the filesystem every time, it'll mean an API call to S3 every time.\n\n```php\n$asset->disk()->exists($asset->path());\n\nFile::exists($asset->path());\n```\n"
  },
  {
    "path": "content/collections/tips/overriding-exception-rendering.md",
    "content": "---\nid: 839eac3b-c4d9-4686-9e7e-f90c4515d405\ntitle: 'Overriding Exception Rendering'\nintro: 'Statamic''s HTTP exceptions (404, 403, 401) implement their own `render` method, which means Laravel''s usual `bootstrap/app.php` render callbacks never fire. Each exception exposes a `renderUsing` method so you can provide your own callback instead.'\ntemplate: page\ncategories:\n  - development\n  - laravel\n---\n## The Problem\n\nIf you try to customize how a `NotFoundHttpException` is rendered using Laravel's [standard approach](https://laravel.com/docs/errors#rendering-exceptions)...\n\n```php\n->withExceptions(function (Exceptions $exceptions) {\n    $exceptions->render(function (NotFoundHttpException $e, Request $request) {\n        return response()->redirectTo('/somewhere');\n    });\n})\n```\n\n...nothing happens. That's because Statamic swaps Symfony's exception for its own subclass that implements a `render` method directly on the exception, which Laravel calls before your callback ever gets a chance.\n\n## The Solution\n\nThe following Statamic exceptions expose a static `renderUsing` method you can use to register a callback:\n\n- `Statamic\\Exceptions\\NotFoundHttpException` (404)\n- `Statamic\\Exceptions\\ForbiddenHttpException` (403)\n- `Statamic\\Exceptions\\UnauthorizedHttpException` (401)\n\nRegister your callback from a service provider's `boot` method – `AppServiceProvider` is a fine place.\n\n```php\n// app/Providers/AppServiceProvider.php\n\nuse Illuminate\\Http\\Request;\nuse Statamic\\Exceptions\\NotFoundHttpException;\nuse Statamic\\Facades\\Collection;\nuse Statamic\\Statamic;\nuse Illuminate\\Support\\Str;\n\npublic function boot()\n{\n    NotFoundHttpException::renderUsing(function (Request $request) {\n        if (Statamic::isCpRoute() || Statamic::isApiRoute() || $this->getStatusCode() !== 404) {\n            return;\n        }\n\n        $eventsMount = Collection::findByHandle('events')->mount();\n\n        if (Str::before($request->path(), '/') === $eventsMount->slug()) {\n            return response()->redirectTo($eventsMount->url());\n        }\n    });\n}\n```\n\nA few things to know about the callback:\n\n- `$this` is bound to the exception instance, so you can call things like `$this->getStatusCode()` or `$this->getMessage()`.\n- Return a response to take over rendering entirely. Return nothing (or `null`) to fall through to Statamic's default behavior.\n- Your callback runs **before** the CP and API checks, so if you don't want to hijack those requests you need to bail out yourself using `Statamic::isCpRoute()` and `Statamic::isApiRoute()`.\n\n## When You'd Want This\n\n- Redirecting legacy URLs to a new collection mount.\n- Falling back to a search page when a page isn't found.\n- Returning a custom 403 view for forbidden routes without touching the standard `errors/403.antlers.html` template.\n- Logging 404s to an external service before rendering the default view.\n"
  },
  {
    "path": "content/collections/tips/pushing-related-entries-to-an-algolia-index.md",
    "content": "---\nid: 5fcf5a56-c120-4988-a4c7-0c5e942327b7\ntitle: 'Pushing related entries to an Algolia index'\ntemplate: page\nintro: 'It''s possible to transform the data from Statamic before it gets pushed to an Algolia index, but here''s some help on transforming the data to push related data into the index.'\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821214\n---\nConsider the following index in `config/statamic/search.php`:\n\n```php\n'videos' => [\n    'driver' => 'algolia',\n    'searchables' => 'collection:videos',\n    'fields' => ['title', 'artwork', 'tags', 'description', 'id', 'author'],\n],\n```\n\nHere we have 6 fields that we wish to be in our Algolia index. In this instance `author` is a related entry in another collection. If we were to update the index currently, all we'd see is the entry id from the related collection which isn't very helpful to our Algolia search results.\n\nEnter `transformers` - \"robots in disguise\", I know you just sang that in your head!\n\nIf we update our index to include transformers:\n\n```php\n'videos' => [\n    'driver' => 'algolia',\n    'searchables' => 'collection:videos',\n    'fields' => ['title', 'artwork', 'tags', 'description', 'id', 'author'],\n    'transformers' => [\n        'author' => function ($author) {\n            $entry = Entry::find($author);\n            return [\n                'author_name' => \"{$entry->author_first_name} {$entry->title}\",\n            ];\n        }\n    ]\n],\n```\nWe pass the entry id into a function, lookup the entry by its id, we can then push the author name - where our blueprint has `author_first_name` as a field of course.\n\nHappy transforming.\n\n_Contributed by Steven Grant_\n"
  },
  {
    "path": "content/collections/tips/recursive-nav-examples.md",
    "content": "---\nid: 1e22effb-69e4-46cf-9bad-6500d7347362\ntitle: 'Recursive Nav Examples'\nintro: 'Statamic''s [nav tag](/tags/nav) is capable of some pretty rad stuff, but recursion can be a little bit hard on the old brain (on the old brain).'\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821220\n---\nLet's say we have the following pages:\n\n<figure>\n    <img src=\"/img/tips/recursive-nav-pages.png\" alt=\"Pages hierarchy example\">\n</figure>\n\n## Footer Nav Example\n\nMaybe you would like to render a footer hierarchy, with top level pages as `<h3>`'s, direct sub-items as `<li>` items, while ignoring anything deeper than level 2 in the nav structure:\n\n<figure>\n    <img src=\"/img/tips/recursive-nav-footer-example.png\" alt=\"Footer nav example\">\n</figure>\n\nWe can do this by performing a `depth` check to decide how to render the current item based on it's depth in the nav structure:\n\n::tabs\n\n::tab antlers\n```antlers\n<div class=\"flex\">\n    {{ nav }}\n        {{ if depth == 1 }}\n            <div class=\"mx-10\">\n                <h3 class=\"mb-2\">{{ title }}</h3>\n                {{ if children }}\n                    <ul>{{ *recursive children* }}</ul>\n                {{ /if }}\n            </div>\n        {{ elseif depth == 2 }}\n            <li class=\"my-1\">\n                <a href=\"{{ url }}\">{{ title }}</a>\n            </li>\n        {{ /if }}\n    {{ /nav }}\n</div>\n```\n::tab blade\n```blade\n<div class=\"flex\">\n  <s:nav>\n    @if ($depth == 1)\n      <div class=\"mx-10\">\n        <h3 class=\"mb-2\">{{ $title }}</h3>\n\n        @if (count($children) > 0)\n          <ul>@recursive_children</ul>\n        @endif\n      </div>\n    @elseif ($depth == 2)\n      <li class=\"my-1\">\n        <a href=\"{{ $url }}\">{{ $title }}</a>\n      </li>\n    @endif\n  </s:nav>\n</div>\n```\n::\n\n## Sidebar Nav Example\n\nOr maybe you would like to render a sidebar style nav as a `<ul>`, while applying a different css class based on the page's depth in the nav structure:\n\n<figure>\n    <img src=\"/img/tips/recursive-nav-sidebar-example.png\" alt=\"Sidebar nav example\">\n</figure>\n\nHere we dynamically insert a CSS class based on the current item's `depth` in the nav structure:\n\n::tabs\n\n::tab antlers\n```antlers\n---\nnav_classes:\n  1: 'text-gray-900 font-bold'\n  2: 'text-gray-800 ml-3'\n  3: 'text-gray-500 ml-6 text-sm'\n---\n\n<ul class=\"nav\">\n    {{ nav }}\n        <li>\n            <span class=\"{{ view:nav_classes[depth] }}\">\n                {{ title }}\n            </span>\n            {{ if children }}\n                <ul class=\"{{ depth == 1 ?= 'mb-4' }}\">\n                    {{ *recursive children* }}\n                </ul>\n            {{ /if }}\n        </li>\n    {{ /nav }}\n</ul>\n```\n::tab blade\n```blade\n<ul class=\"nav\">\n  <s:nav>\n    <li>\n      <span @class([\n        'text-gray-900 font-bold'=> $depth == 1,\n        'text-gray-800 ml-3'=> $depth == 2,\n        'text-gray-500 ml-6 text-sm'=> $depth == 3,\n      ])>{{ $title }}</span>\n\n      @if (count($children) > 0)\n        <ul @class([\n          'mb-4' => $depth == 1\n        ])>\n          @recursive_children\n        </ul>\n      @endif\n    </li>\n  </s:nav>\n</ul>\n```\n::"
  },
  {
    "path": "content/collections/tips/reserved-words.md",
    "content": "---\nid: 264048cb-30fd-4529-aa48-7236434d1ec4\ntitle: 'List of Reserved Words'\ntemplate: page\ncategories:\n  - development\n  - troubleshooting\n---\n## Reserved Field Handles\n\nThis is the list of reserved words you shouldn't use as field names, in addition to the names of Statamic's [Tags](/tags) and [contextual variables](/variables).\n\n- `blueprint`\n- `content_type`\n- `count`\n- `elseif`\n- `endif`\n- `endunless`\n- `id`\n- `if`\n- `length`\n- `reference`\n- `resource`\n- `save`\n- `site`\n- `status`\n- `path`\n- `private`\n- `publish`\n- `published`\n- `route`\n- `unless`\n- `uri`\n- `url`\n\n\n:::warning\nSome of these _may_ work as field names in some circumstances, but can have unintended consequences, like overriding global data, behaviors, or creating issues with Vue components inside the Control Panel.\n:::\n\n### Special Cases\n\n- `content` as a field name when _also_ utilizing the \"content area\" of your YAML front-loaded Markdown files (usually when working in the files directly)\n- `global` with any datatype storing data as an array when there is _also_ a global field of the same name.\n- `type` inside a Replicator set. The type is the handle of the set.\n\n### Entries\n\n- `order`\n- `origin`\n- `parent`\n\n### Forms\n\n- `date`\n- `message`\n- `messages`\n\n## Taxonomy Handles\n\n- `register`\n\n## Reserved Wildcards in Statamic Routes\n\n- `entry`\n- `taxonomy`\n"
  },
  {
    "path": "content/collections/tips/setting-default-listing-columns.md",
    "content": "---\nid: 62656a00-d225-4906-8387-de780476497e\ntitle: 'Setting Default Columns on Listing Tables'\nintro: ''\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1700749977\n---\nStatamic allows you to configure \"default preferences\" that apply to all users. This enables you to specify the columns and the default order they are shown in on the Listing Tables.\n\nCurrently, there's no UI for setting default preferences for the listing tables. However, you can easily do this by modifying some friendly YAML files.\n\n1. First, find the relevant listing table and click the \"Customize Columns\" button. It'll open up a modal, enabling you to specify the columns you want to show in the listing table. You can re-order columns from here too.\n\n<figure>\n    <img src=\"/img/tips/customize-columns.png\" alt=\"Columns customizer\">\n</figure>\n\nRepeat this step for any other collections or taxonomies you want to set default columns for.\n\n2. Once you're happy, open your user's YAML file. You'll find a `preferences` key containing the columns you just specified:\n\n```yaml\npreferences:\n  collections:\n    articles:\n      columns:\n        - title\n        - date\n        - status\n```\n\nIf you're [storing users in a database](/tips/storing-users-in-a-database), you'll need to convert the JSON data from the `preferences` column in your `users` table to YAML. You can use an app like [JSON to YAML](https://www.bairesdev.com/tools/json2yaml/) to do this.\n\n3. Next, create a new file called `resources/preferences.yaml`. Copy the contents of the `preferences` array into this new file, resulting in it looking pretty similar to this:\n\n```yaml\ncollections:\n  articles:\n    columns:\n      - title\n      - date\n      - status\n```\n\n4. Finally, if you clear your user's `preferences` array or login as a different user, the default columns will be displayed just as you specified them. 🎉\n"
  },
  {
    "path": "content/collections/tips/storing-content-in-a-database.md",
    "content": "---\nid: 61d9e659-10e4-4eed-94b0-c5e639493dfd\ntitle: 'Storing Content in a Database'\nintro: 'Statamic stores your content in \"flat files\" by default, however, as you scale, you might reach a point where a traditional database might work better. In this short article, we''ll show you how to move your entries (& other content) into a database.'\ntemplate: page\ncategories:\n  - development\n  - database\n  - laravel\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821277\nrelated_entries:\n    - 4238bce4-a94b-4d07-96fa-ea77c1d8e48d\n---\n## Overview\n\nWhen you create a new Statamic site, it will ask if you want to store content in flat files or in a database. If you change your mind, maybe for performance or workflow reasons, it's really easy to switch.\n\n## Moving content to the database\n1. First things first, ensure your database is configured correctly. By default, Statamic sites are configured to use a SQLite database. However, [you're free to change this](https://laravel.com/docs/master/database#configuration) in your `.env`:\n    ```\n    DB_CONNECTION=sqlite\n    # DB_HOST=127.0.0.1\n    # DB_PORT=3306\n    # DB_DATABASE=laravel\n    # DB_USERNAME=root\n    # DB_PASSWORD=\n    ```\n2. Run `php please install:eloquent-driver`. It'll install the [Eloquent Driver](https://github.com/statamic/eloquent-driver) addon, publish it's configuration file and prompt you to select the repositories you wish to move to the database.\n\n    You might find it useful to keep \"configuration\" repositories as flat files, whilst storing the actual content in the database. For example: moving `entries` to the database but leaving `collections` flat-file.\n\n3. And that's you done! Wasn't that easy?\n\n## Change your mind?\nIf you change your mind about moving content to the database, you can always move it back. Just use one of the following commands to export your content back into flat-files:\n\n- Addon Settings: `php please eloquent:export-addon-settings`\n- Assets: `php please eloquent:export-assets`\n- Blueprints and Fieldsets: `php please eloquent:export-blueprints`\n- Collections: `php please eloquent:export-collections`\n- Entries: `php please eloquent:export-entries`\n- Forms: `php please eloquent:export-forms`\n- Globals: `php please eloquent:export-globals`\n- Navs: `php please eloquent:export-navs`\n- Revisions: `php please eloquent:export-revisions`\n- Taxonomies: `php please eloquent:export-taxonomies`\n- Sites: `php please eloquent:export-sites`\n"
  },
  {
    "path": "content/collections/tips/storing-laravel-users-in-files.md",
    "content": "---\nid: 748f88ce-85f6-491b-8e9c-fa2b1895be31\ntitle: 'Storing Laravel Users in Files'\nintro: 'Sometimes the Statamic way overrules the Laravel way.'\ntemplate: page\ncategories:\n  - development\n  - laravel\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821304\n---\nWhen creating new site using the `statamic` command or by cloning `statamic/statamic`, your Laravel application will be preconfigured\nto store users as files. Nothing else is required!\n\nIf you've installed Statamic into an existing Laravel application, it will be expecting users to be stored in the database, but you can switch to the filesystem:\n\n1. In `config/statamic/users.php`, change `repository` to `file`.\n2. In `config/auth.php`, change the users provider driver to `statamic`.\n   ``` php\n    'providers' => [\n        'users' => [\n            'driver' => 'statamic',\n        ],\n    ],\n   ```"
  },
  {
    "path": "content/collections/tips/storing-users-in-a-database.md",
    "content": "---\nid: 4c3f5caa-a861-4ffd-a856-1692cafeb870\ntitle: 'Storing Users in a Database'\nintro: 'If you have a large or unknown number of users, it can be a good idea to store them in a database instead of the filesystem for the sake of performance or scaling.'\ntemplate: page\ncategories:\n  - development\n  - database\n  - laravel\n  - performance\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821321\n---\n\n## From a fresh Statamic project\n\nIf you installed Statamic using the `statamic new` command, or created a project based on the `statamic/statamic` repo, it will be configured to store users in files.\n\nStatamic comes with an Eloquent driver to make the transition as seamless as possible.\n\n1. Ensure you have a [database configured](https://laravel.com/docs/database#configuration).\n1. In your `User` model, add casts for the `preferences` and `two_factor_confirmed_at` columns:\n    ```php\n    class User extends Authenticatable\n    {\n        protected function casts(): array // [tl! focus]\n        { // [tl! focus]\n            return [ // [tl! focus]\n                'preferences' => 'json', // [tl! ++] [tl! focus]\n                'two_factor_confirmed_at' => 'datetime', // [tl! ++] [tl! focus]\n            ]; // [tl! focus]\n        } // [tl! focus]\n\n        // ...\n    }\n    ```\n1. If you plan to import existing file based users, you'll need to use UUIDs for the primary key. You can do this by adding a trait to your user model:\n    ```php\n    class User extends Authenticatable\n    {\n        use \\Illuminate\\Database\\Eloquent\\Concerns\\HasUuids; // [tl! ++] [tl! focus]\n\n        // ...\n    }\n    ```\n1. In `config/statamic/users.php`, use the Eloquent repository.\n    ```php\n    'repository' => 'file', // [tl! --]\n    'repository' => 'eloquent', // [tl! ++]\n    ```\n1. In `config/auth.php`, use the Eloquent provider:\n    ```php\n    'providers' => [\n        'users' => [\n            'driver' => 'statamic', // [tl! --]\n            'driver' => 'eloquent', // [tl! ++]\n            'model' => App\\Models\\User::class, // [tl! ++]\n        ]\n    ]\n    ```\n1. Generate a migration for the role and user group pivot tables.\n    ```cli\n    php please auth:migration\n    ```\n    - If you're planning to import existing file based users, edit the migration to change the `id` & `user_id` columns to the `uuid` type.\n        ```php\n        Schema::table('users', function (Blueprint $table) {\n            $table->uuid('id')->change(); // [tl! ++] [tl! **]\n            $table->boolean('super')->default(false);\n            $table->string('avatar')->nullable();\n            $table->json('preferences')->nullable();\n            $table->timestamp('last_login')->nullable();\n            $table->string('password')->nullable()->change();\n            $table->text('two_factor_secret')->nullable();\n            $table->text('two_factor_recovery_codes')->nullable();\n            $table->timestamp('two_factor_confirmed_at')->nullable();\n        });\n\n        Schema::create('role_user', function (Blueprint $table) {\n            $table->increments('id');\n            $table->foreignId('user_id')->constrained('users')->cascadeOnDelete();  // [tl! ** --]\n            $table->uuid('user_id');  // [tl! ** ++]\n            $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete();  // [tl! ** ++]\n            $table->string('role_id');\n        });\n\n        Schema::create('group_user', function (Blueprint $table) {\n            $table->increments('id');\n            $table->foreignId('user_id')->constrained('users')->cascadeOnDelete();  // [tl! ** --]\n            $table->uuid('user_id');  // [tl! ** ++]\n            $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete();  // [tl! ** ++]\n            $table->string('group_id');\n        });\n        ```\n    - If you're using the `database` session driver and using UUIDs as the primary key for users, make sure to update the `user_id` column in the `sessions` table.\n        ```php\n        Schema::table('sessions', function (Blueprint $table) {  // [tl! ++] [tl! **]\n            $table->foreignUuid('user_id')->nullable()->change();  // [tl! ++] [tl! **]\n        });  // [tl! ++] [tl! **]\n        ```\n    - If you've customized your `user` blueprint, edit the migration so it includes those fields as columns. You can also create a new migration file by running `php artisan make:migration`. You'll have to manually edit the migration file to reflect your changes. Read up on [Laravel database migrations here](https://laravel.com/docs/13.x/migrations).\n        ```php\n        $table->string('some_field');\n        ```\n1. Run the migrations:\n    ```cli\n    php artisan migrate\n    ```\n1. If you have existing file based users, import them:\n    ```cli\n    php please eloquent:import-users\n    ```\n1. If you are using the Statamic forgot password form, add the following method to your User model\n    ```php\n    public function sendPasswordResetNotification($token)\n    {\n        $this->notify(new \\Statamic\\Notifications\\PasswordReset($token));\n    }\n    ```\n\n## In an existing Laravel app\n\nIf you've installed Statamic into an existing Laravel app, it will already be configured to use the Eloquent driver.\n\nYou will need to run migrations to prepare your database for Statamic's user, password reset, and permission setup.\n\n1. Configure the two separate password reset drivers. Unlike a regular Laravel installation, Statamic has a second table to track password _activations_ which are the same as resets, but last a little longer before they expire. This is optional.\n\n   In `config/auth.php` add the following inside the `passwords` array:\n\n    ```php\n    'activations' => [\n        'provider' => 'users',\n        'table' => 'password_activation_tokens',\n        'expire' => 4320,\n        'throttle' => 60,\n    ],\n    ```\n\n    In `config/statamic/users.php` change the `passwords` array to:\n\n    ```php\n    'passwords' => [\n        'resets' => 'users',\n        'activations' => 'activations',\n    ],\n    ```\n\n2. Create and run the migrations.\n\n    This will add some columns to the `users` table (like `super`, and `last_login`), create the `role_user` and `group_user` pivot tables, and create the `password_activations` table.\n\n    ``` shell\n    php please auth:migration\n    php artisan migrate\n    ```\n4. Optional: If you are using the Statamic forgot password form, add the following method to your User model\n    ```php\n    public function sendPasswordResetNotification($token)\n    {\n        $this->notify(new \\Statamic\\Notifications\\PasswordReset($token));\n    }\n    ```\n\n\nThis assumes you are happy to use our opinionated setup. If you need something more custom you can [create your own user driver](/tips/storing-users-somewhere-custom).\n\n## Roles and Groups (optional)\n\nBy default, roles and groups are stored in flat files. If you would like to store them in the database instead, follow these steps:\n\n1. Specify the `roles` and `groups` tables in the `config/statamic/users.php` config file. \n\n    ```php\n    'tables' => [\n        'users' => 'users',\n        'role_user' => 'role_user',\n        'roles' => false, // [tl! --]\n        'roles' => 'roles', // [tl! ++]\n        'group_user' => 'group_user',\n        'groups' => false, // [tl! --]\n        'groups' => 'groups', // [tl! ++]\n    ],\n    ```\n   \n2. Run `php please auth:migration` to generate the required migrations for the `roles` and `groups` tables. This will create two migrations in the `database/migrations` directory. You can delete the duplicate the `statamic_auth_table` migration.\n3. Run the migrations using `php artisan migrate`.\n4. Finally, if you have existing file based roles and groups, you can import them using these commands:\n\n    ```shell\n    php please eloquent:import-roles\n    php please eloquent:import-groups\n    ```\n"
  },
  {
    "path": "content/collections/tips/storing-users-somewhere-custom.md",
    "content": "---\nid: 1ee69ba0-2fa4-4155-9b8d-82536ce95f99\ntitle: 'Storing Users Somewhere Custom'\nintro: 'Sometimes you just gotta be special.'\ntemplate: page\ncategories:\n  - database\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821332\n---\nIf you'd like to store your users somewhere outside the filesystem, and the included Eloquent implementation doesn't quite cut it for you,\nyou're free to write your own.\n\nYou will need to write implementations for all the contracts located in `Statamic\\Contracts\\Auth`. Of course, you may extend the native classes and override where appropriate, instead of writing everything from scratch.\n\nIn a service provider, use the `extend` method on the `UserRepositoryManager` to define a custom repository driver:\n\n``` php\napp(\\Statamic\\Auth\\UserRepositoryManager::class)->extend('custom', function ($app, $config) {\n    return new CustomUserRepository;\n});\n```\n\nAfter you've registered the driver using the `extend` method, you'll want to create a repository in `config/statamic/users.php` that uses the new driver:\n\n``` php\n'repositories' => [\n    'custom' => [\n        'driver' => 'custom',\n    ]\n]\n```\n\nFinally, set that repository as the one you want active:\n\n``` php\n'repository' => 'custom'\n```\n"
  },
  {
    "path": "content/collections/tips/taxonomies-by-hand.md",
    "content": "---\nid: 3f5506d6-03e0-4fcf-b4e8-334c48d51f81\ntitle: 'Working with Taxonomies by Hand'\nintro: 'Sometimes you just don''t feel like using a control panel. Managing content in your Code Editor can be the most productive or fun option.'\ntemplate: page\ncategories:\n  - development\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821454\n---\n## Creating Taxonomies\n\nTaxonomies are kept in `content/taxonomies`, each with their own YAML file, and their entries in a matching subdirectory.\n\n```\ntaxonomies/\n|-- tags.yaml\n|-- tags/\n    |-- good.yaml\n    |-- great.yaml\n    |-- best.yaml\n```\n\nEach Taxonomy's YAML file can contain its title, as well as any data that should be injected into all its terms.\n\n``` yaml\ntitle: Tags\ninject:\n  foo: bar\n```\n\n## Assigning to Collections\n\nA taxonomy (or multiple taxonomies) can be assigned to a collection.\n\n``` yaml\n# content/collections/blog.yaml\n\ntitle: Blog\ntaxonomies:\n  - tags\n```\n\nWhen editing an entry in that collection, taxonomy fields will be automatically added.\n\n\n## Taxonomizing Entries\n\nTo taxonomize an entry, you can add [term values](#term-values-and-slug) to a field named **exactly** the same as the taxonomy's handle.\n\n``` yaml\ntitle: My Entry\ntags:\n  - foo\n  - bar\n  - baz\n```\n\nNow when listing entries that belong to the `foo`, `bar`, or `baz` terms, the entry will appear.\n\n:::tip\nMake sure that the field is named exactly the same as the taxonomy handle, otherwise it will not be considered part of that taxonomy term.\n:::\n\n## Additional Term Data\n\nAdditional data (custom fields) can be added to a term by creating a yaml file matching the term’s [slug](/taxonomies#term-values-and-slugs).\n\n```\nsite/content/taxonomies\n|-- tags.yaml\n`-- tags\n    |-- foo.yaml\n    `-- bar.yaml\n```\n\nIn the YAML file, add data like so:\n\n``` yaml\nfood: bacon\ndrink: whisky\n```\n\nWhenever referencing the Terms in your templates, now `{{ food }}` and `{{ drink }}` would output `bacon` and `whiskey` respectively.\n\nJust like entries, these values will be augmented automatically in your templates depending on the blueprint.\n"
  },
  {
    "path": "content/collections/tips/timezones.md",
    "content": "---\nid: 2080f786-9c04-4916-b77a-c62202ec4f07\ntitle: 'Timezones'\nintro: \"Every developer's worst nightmare.\"\ntemplate: page\nrelated_entries:\n    - 7dfba904-8a74-40e1-b507-51cd2b5f6123\n---\n\n> **This guide is only relevant to sites running Statamic 6 and above.**\n\n## UTC\n\nIt's best practice to store dates in the UTC timezone for consistency, and to convert the timezone just before displaying it.\n\nWe recommend leaving your timezone set to UTC. Statamic will convert dates to the timezone defined in `config/statamic/system.php` when appropriate.\n\n```php\n'display_timezone' => 'America/New_York',\n```\n\nBy default, or when this is not explicitly set, it will also be `UTC`.\n\n\n## Templating\n\nWithin templates, any dates will be [Carbon](https://carbon.nesbot.com) instances in the UTC timezone.\n\nWhen converting a Carbon instance to a string, it will be automatically converted to your configured display timezone. For example:\n\n```antlers\n{{ date }} \"December 25th, 2020\"\n````\n\nSince this automatic conversion only happens when casting a Carbon instance to a string, when a date is passed to a modifier or tag, it will still be a UTC Carbon instance.\n\n### Modifiers\n\nFor example, the `format` modifier will receive a UTC date:\n\n```yaml\ndate: '2020-12-25 03:00' # This is UTC\n```\n\n```antlers\n{{ date | format('Y-m-d H:i') }} \"2020-12-25 03:00\"\n```\n\nSince the format modifier received a UTC date, it applied the formatting for UTC. But, since we want it displayed in New York time as per our configuration, we expect to see it 5 hours earlier.\n\nThere are two options for this:\n1. Apply the `timezone` modifier (or `tz` for short) before passing it along:\n   ```antlers\n   {{ date | tz | format(...) }} \"2020-12-24 20:00\"\n   ```\n2. Opt to convert dates in date modifiers in `config/statamic/system.php`:\n   ```php\n   'localize_dates_in_modifiers' => true,\n   ```\n   ```antlers\n   {{ date | format(...) }} \"2020-12-24 20:00\"   \n   ```\n   _Note that this option will only convert dates when using date-related modifiers like `format`, `days_ago`, etc._\n   \n### Tags\n\nIf a tag _needs_ a Carbon instance in your display timezone, you can modify it before passing it:\n\n::tabs\n::tab antlers\n```antlers\n{{ my_tag :date=\"date|tz\" }}\n```\n::tab blade\n```blade\n{{ Statamic::tag('my_tag')->date(\n    $date->tz(config('statamic.system.display_timezone'))\n) }}\n```\n::\n\nAlthough, a tag shouldn't be expecting this of you.\n\n## Custom Routes\nIf you're passing Carbon instances into templates yourself (eg. from a custom route), you should make sure they're all in UTC.\n\nUnder the hood, Statamic's `Localize` middleware uses Carbon's [`toStringFormat`](https://carbon.nesbot.com/docs/#api-formatting) setting to determine how dates are outputted when PHP calls `__toString()` on a Carbon instance.\n\nYou should ensure you're applying the `Localize` middleware to any custom routes in your application. If you're using `Route::statamic()` or the `statamic.web` middleware group, you'll already be using the `Localize` middleware.\n"
  },
  {
    "path": "content/collections/tips/trailing-slashes.md",
    "content": "---\nid: 66edce16-321a-4e94-8e71-44c1ae7b934e\nblueprint: tips\ntitle: 'Enforce trailing slashes in URLs'\n---\nIf you're moving from another CMS that uses trailing slashes in URLs and you'd like to keep the same format in Statamic for SEO purposes, you can!\n\nSimply add this to the `boot` method of your `AppServiceProvider`:\n\n```php\nuse Statamic\\Facades\\URL;\n\nURL::enforceTrailingSlashes()\n```\n\nNow all URLs built by Statamic will include trailing slashes.\n\n## Redirect\n\nThis only handles how Statamic builds URLs. Statamic will still accept requests to non-trailing-slash URLs. If you want them to redirect to the trailing slash version, add the following to your web server config:\n\n### Apache\n\nAdd this to your `.htaccess` file:\n\n```\nRewriteEngine On\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_URI} !(.*)/$\nRewriteRule ^(.*)$ /$1/ [R=308,L]\n```\n\n### Nginx\n\nAdd this to your `server` block:\n\n```\nlocation / {\n    if ($request_uri ~ \"^[^?\\\\.]*[^/]$\") {\n        return 308 $request_uri/;\n    }\n    try_files $uri $uri/ /index.php?$query_string;\n}\n```\n\n### IIS\n\nAdd this to your `web.config` file:\n\n```\n<system.webServer>\n    <rewrite>\n        <rules>\n            <rule name=\"Add trailing slash\" stopProcessing=\"true\">\n                <match url=\"^([^/]+(?:/[^/]+)*)$\" />\n                <conditions>\n                    <add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" negate=\"true\" />\n                    <add input=\"{REQUEST_FILENAME}\" matchType=\"IsDirectory\" negate=\"true\" />\n                </conditions>\n                <action type=\"Redirect\" url=\"{R:1}/\" redirectType=\"Permanent308\" />\n            </rule>\n        </rules>\n    </rewrite>\n</system.webServer>\n```"
  },
  {
    "path": "content/collections/tips/using-an-independent-authentication-guard.md",
    "content": "---\nid: d42da120-03f9-4eaf-bdfe-420736ca55e7\ntitle: 'Using an Independent Authentication Guard'\ntemplate: page\ncategories:\n  - development\n  - laravel\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821437\n---\nBy default, Statamic comes configured to use the default auth guard. This means that your application's users will also be considered Statamic users.\n\nIf your application's users won't be interacting with Statamic and only a few users will actually be using the Control Panel, it might make sense for you to store your application's users in a database and your Statamic users in YAML files.\n\nTo separate Statamic's users from those of your Laravel application, you'll need to setup two user guards & two user providers in your `config/auth.php` config file.\n\nYou'll need one user guard & user provider for your Eloquent users and another for your Statamic users:\n\n```php\n// config/auth.php\n\n'guards' => [\n    'web' => [\n        'driver' => 'session',\n        'provider' => 'users',\n    ],\n\n    'statamic' => [\n        'driver' => 'session',\n        'provider' => 'statamic',\n    ]\n],\n\n'providers' => [\n    'users' => [\n        'driver' => 'eloquent',\n        'model' => \\App\\Models\\User::class,\n    ],\n\n    'statamic' => [\n        'driver' => 'statamic',\n    ],\n],\n```\n\nIn the above example, the  `statamic`  guard is used to authenticate users using Statamic's flat-file driver and the `web` guard is used to authenticate users using Laravel's built-in Eloquent driver.\n\nWhile you're in the `config/auth.php` file, ensure you have separate brokers for each of the providers. Two for Statamic and one for Eloquent. Statamic separates the concepts of resets and activations where Eloquent doesn't by default.\n\n```php\n'passwords' => [\n\t'resets' => [\n\t\t'provider' => 'users',\n\t\t'table' => 'password_reset_tokens',\n\t\t'expire' => 60,\n\t\t'throttle' => 60,\n\t],\n\n\t'statamic_resets' => [ // [tl! ++:start]\n\t\t'provider' => 'statamic',\n\t\t'table' => 'password_resets',\n\t\t'expire' => 60,\n\t\t'throttle' => 60,\n\t],\n\n\t'statamic_activations' => [\n\t\t'provider' => 'statamic',\n\t\t'table' => 'password_activations',\n\t\t'expire' => 4320,\n\t\t'throttle' => 60,\n\t], // [tl! ++:end]\n],\n```\n\nNext, in Statamic's `users.php` configuration file, you'll want to change the `repository` from `eloquent` to `file` and configure which user guard should be used on the frontend and which should be used for the Control Panel:\n\nIn the example below, we're using the `statamic` driver for Control Panel users and the `web` driver for frontend users.\n\n```php\n// config/statamic/users.php\n'repository' => 'file',\n\n'guards' => [\n    'cp' => 'statamic',\n    'web' => 'web',\n],\n```\n\nYou should also ensure the `passwords` is setup to point to the correct reset & activation configs:\n\n```php\n'passwords' => [\n\t'resets' => 'statamic_resets',\n\t'activations' => 'statamic_activations',\n],\n```\n\n## Non-Statamic routes\n\nIt's worth noting that any non-Statamic routes (eg. any you've manually added in your `routes/web.php` file) will be unaffected by the config changes\n\nThese routes will use whichever guard you have set to as the \"default\" in your `config/auth.php` file:\n\n```php\n'defaults' => [\n  'guard' => 'web',\n  'passwords' => 'resets',\n],\n```\n"
  },
  {
    "path": "content/collections/tips/using-statamic-with-laravel-nightwatch.md",
    "content": "---\nid: 112bf1e2-9616-4d22-ac4e-9fbeba84f32b\ntitle: 'Using Statamic Alongside Laravel Nightwatch'\nintro: 'Laravel Nightwatch is a great way to monitor your application, but a default install on a Statamic site will burn through your event quota faster than a toddler through a bag of fruit snacks. Here''s how to keep it in check.'\ntemplate: page\ncategories:\n  - development\n  - laravel\n  - performance\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1776808338\n---\n## Why this happens\n\nStatamic is a flat file CMS by default. To stay fast, the [Stache](/stache) and several other internals lean heavily on Laravel's cache layer. Every page view can easily trigger hundreds of cache reads and writes.\n\nNightwatch captures every `hit`, `miss`, `write`, `delete`, and `fail` event from the Cache layer. Multiply that by Statamic's cache churn and a modest amount of traffic will empty your monthly event quota in short order.\n\nYou have three good options, in order of bluntness.\n\n## Option 1: Ignore all cache events\n\nThe simplest fix. Add this to your `.env` file and Nightwatch stops capturing cache events entirely:\n\n```env\nNIGHTWATCH_IGNORE_CACHE_EVENTS=true\n```\n\nYou lose visibility into _all_ cache events, including any of your own. If you don't actively monitor cache behavior, this is the right call.\n\n## Option 2: Reject Statamic's cache keys\n\nIf you want to keep monitoring your app's own cache usage, filter Statamic's keys out with `rejectCacheKeys()` in your `AppServiceProvider::boot()` method:\n\n```php\nuse Laravel\\Nightwatch\\Facades\\Nightwatch;\n\npublic function boot(): void\n{\n    Nightwatch::rejectCacheKeys([\n        '/^stache::/',\n        '/^statamic[.:]/',\n        '/^nocache::/',\n        '/^static-cache/',\n        '/^asset-/',\n        '/^responses/',\n    ]);\n}\n```\n\nThese prefixes cover the Stache, the [static cache](/static-caching), [nocache](/tags/nocache) regions, asset metadata, and cached responses. Add-ons may introduce their own keys — check the Nightwatch Cache page in production to see what's still slipping through and tune the list.\n\n### Using a callback instead\n\nIf regex isn't your thing, use `rejectCacheEvents()` with a closure:\n\n```php\nuse Illuminate\\Support\\Str;\nuse Laravel\\Nightwatch\\Facades\\Nightwatch;\nuse Laravel\\Nightwatch\\Records\\CacheEvent;\n\nNightwatch::rejectCacheEvents(function (CacheEvent $cacheEvent) {\n    return Str::startsWith($cacheEvent->key, [\n        'stache::',\n        'statamic.',\n        'statamic::',\n        'nocache::',\n        'static-cache',\n        'asset-',\n        'responses',\n    ]);\n});\n```\n\n## Reject the entry schedule job\n\nStatamic dispatches `HandleEntrySchedule` every minute to publish and unpublish scheduled entries. On a healthy site that's 43,200 queued jobs a month, all of them boring. Drop them before they hit Nightwatch:\n\n```php\nuse Laravel\\Nightwatch\\Facades\\Nightwatch;\nuse Laravel\\Nightwatch\\Records\\QueuedJob;\nuse Statamic\\Jobs\\HandleEntrySchedule;\n\nNightwatch::rejectQueuedJobs(function (QueuedJob $job): bool {\n    return $job->name === HandleEntrySchedule::class;\n});\n```\n\n## Verify the fix\n\nAfter deploying, open Nightwatch's **Cache** and **Queue** pages. You should see your event volume drop significantly and the top keys should now reflect your application, not Statamic's internals.\n\nFor a full list of filtering and sampling options, see the [Nightwatch Cache docs](https://nightwatch.laravel.com/docs/cache) and [Events overview](https://nightwatch.laravel.com/docs/events).\n"
  },
  {
    "path": "content/collections/tips/zero-downtime-deployments.md",
    "content": "---\nid: 5e1dbeb6-b59d-4c6c-a3fa-950c4372acba\nblueprint: tips\ntitle: 'Zero Downtime Deployments'\ntemplate: page\ncategories:\n  - development\n  - troubleshooting\n---\n## Understanding the folder structure\n\nZero downtime deployment services like [Laravel Forge](https://forge.laravel.com/), [Envoyer](https://envoyer.io/) and [Deployer](https://deployer.org/) typically use a multiple-release directory structure and symlinks to handle deployments.\n\nFor example, with Laravel Forge:\n\n``` files theme:serendipity-light\n.env\nstorage\ncurrent -> symlinked to latest release\nreleases\n   20220215112950\n    .env -> symlinked to top level shared .env\n    storage -> symlinked to top level shared storage\n    app\n    routes\n    etc\n   20220322153109\n   20220323180225\n   20220322153109\n```\n\nEvery deployment has its own timestamped release directory, with a fresh clone of the app. The `.env` file is stored at the top level, and shared between releases using symlinks.\n\nAfter a successful deployment, the `current` folder is then symlinked to the latest release. This symlink swap is the secret sauce for zero downtime.\n\n## Cache storage\nStatamic's content management heavily relies on caching, and sometimes it's necessary for the [Stache](/stache) to store absolute file paths in your app's cache. This can lead to deployment errors when users are hitting your frontend, since each release [exists in a separate timestamped folder](#understanding-the-folder-structure).\n\nThe solution is simple. Just as you should never share a cache between different websites, you should never share a cache between your deployed releases.\n\n### How to avoid sharing file cache\n\nThere are two ways to avoid sharing a file cache between your deployment releases:\n\n1. Some services, like Laravel Forge, may allow you to configure the \"shared paths\" between deployments. If your application allows for it, you could remove the `storage` directory from your site's shared paths, ensuring each release has its own `storage` folder.\n2. Another option is to create a `cache` folder at the top level of your app, bypassing the shared `storage` folder. Configure your app to use a custom cache store location by changing `stores.file.path` in `config/cache.php`:\n    ```php\n    'stores' => [\n        'file' => [\n            'driver' => 'file',\n            'path' => storage_path('framework/cache/data'), // [tl! --]\n            'path' => base_path('cache'), // [tl! ++]\n        ],\n    ],\n    ```\n\n### How to avoid sharing Redis cache\n\nTo avoid sharing a Redis cache between your deployment releases, we recommend setting a cache prefix unique to each release on your filesystem. This can be configured by adding a `redis.cache.options.prefix` in `config/database.php`:\n\n```php\n'redis' => [\n    'cache' => [\n        'url' => env('REDIS_URL'),\n        'host' => env('REDIS_HOST', '127.0.0.1'),\n        'password' => env('REDIS_PASSWORD'),\n        'port' => env('REDIS_PORT', '6379'),\n        'database' => env('REDIS_CACHE_DB', '1'),\n        'options' => [ // [tl! ++]\n            'prefix' => basename(base_path()).'_', // [tl! ++]\n        ], // [tl! ++]\n    ],\n],\n```\n\n## Git Automation\n\nIf you plan to use Statamic's [Git Automation](/git-automation) feature alongside zero downtime deployments, you may need to tweak your deployment settings to enable git commits and pushes from each release folder.\n\n### Setting up a Git remote\n\n:::tip\nUnlike other services, Laravel Forge will actually keep the `.git` folder around in each release, meaning you can skip this step.\n:::\n\nMost zero downtime deployment services, like [Envoyer](https://envoyer.io/) and [Deployer](https://deployer.org/) create releases _without_ a `.git` folder, which Statamic needs to commit and push content back to your repository.\n\nYou can work around this by setting up a Git object right after the `Clone New Release` step of your deployment process:\n\n```bash\ngit init\ngit remote add origin git@github.com:your/remote-repository.git\ngit fetch\ngit branch --track main origin/main\ngit reset HEAD\n```\n\nBe sure to modify the above remote to point to your remote repository, along with the branch you wish to track.\n\n### Preventing circular deployments\n\nIf you plan on enabling automatic deployment when commits are pushed to your repository, you may wish to selectively disable deployments when Statamic pushes commits back to your repository.\n\nTo do this, you will first need to append `[BOT]` to Statamic's commit messages [as documented here](/git-automation#customizing-commits). Once this is done, you can add a step to your deployment process to cancel the deployment when the commit message contains `[BOT]`.\n\n```php\nif [[ $FORGE_DEPLOY_MESSAGE =~ \"[BOT]\" ]]; then\n    echo \"AUTO-COMMITTED ON PRODUCTION. NOTHING TO DEPLOY.\"\n    exit 0\nfi\n```\n\n### Ensuring proper deployment hook order\n\nWhen adding these steps to your deployment process, you should be mindful of the order in which they happen. Here's the order we recommend:\n\n* Cancel deployments when commit message contains `[BOT]`\n* Create release\n* Init Git repository & add Git remote (if necessary)\n* The rest of your deployment script...\n\n:::tip\nIf you're using [Static Caching](/static-caching), make sure you warm the cache _after_ updating the current release, otherwise you'll be warming the wrong cache.\n:::\n\n### Committing form submissions\n\nIf you plan on committing form submissions, you will need to store them outside the shared `storage` directory. \n\nTo customize where form submissions are stored, add a `form-submissions` array to your `config/statamic/stache.php` config file:\n\n```php\n'stores' => [\n    'form-submissions' => [ // [tl! ++]\n        'class' => \\Statamic\\Stache\\Stores\\SubmissionsStore::class, // [tl! ++]\n        'directory' => base_path('forms'), // [tl! ++]\n    ], // [tl! ++]\n],\n```\n\nAfter doing this, you will also need to update the tracked path for your submissions in `config/statamic/git.php`:\n\n```php\n'paths' => [\n    base_path('content'),\n    base_path('users'),\n    resource_path('blueprints'),\n    resource_path('fieldsets'),\n    resource_path('forms'),\n    resource_path('users'),\n    resource_path('preferences.yaml'),\n    resource_path('sites.yaml'),\n    storage_path('forms'), // [tl! focus --]\n    base_path('forms'), // [tl! focus ++]\n    public_path('assets'),\n],\n```\n"
  },
  {
    "path": "content/collections/tips.yaml",
    "content": "title: 'Tips & Tricks'\ntemplate: page\nlayout: layout\nmount: b6c40452-8da0-4f03-a53c-714cc3338b9d\nrevisions: false\nroute: '/{mount}/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: public\n  future: private\ninject:\n  section: docs\n"
  },
  {
    "path": "content/collections/troubleshooting/asset-permissions.md",
    "content": "---\nid: ede9cc18-90fe-4775-9e50-83724149abf3\ntitle: 'Troubleshooting Asset Permissions'\nintro: 'You''ve uploaded files to a service like Amazon S3 or Digital Ocean Spaces, but your files are private.'\ntemplate: page\ncategories:\n  - troubleshooting\n  - privacy-gdpr\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821358\n---\nBy default, filesystem disk permissions are private for security.\n\nIf you want your assets to be publicly accessible, you need to set your disk's `visibility` to `public` in `config/filesystems.php`.\n\n``` php\n's3' => [\n    'driver' => 's3',\n    'key' => env('AWS_ACCESS_KEY_ID'),\n    'secret' => env('AWS_SECRET_ACCESS_KEY'),\n    'region' => env('AWS_DEFAULT_REGION'),\n    'bucket' => env('AWS_BUCKET'),\n    'url' => env('AWS_URL'),\n    'visibility' => 'public', // 👈 you're missing this\n],\n```\n\nConversely, if you **want** your files to be private, then you can either remove that line, or set it to `private`.\n\n:::tip\nThis setting only applies to newly uploaded files. You'll need to log into AWS or Spaces and bulk change the permissions on existing files.\n:::\n"
  },
  {
    "path": "content/collections/troubleshooting/assets-missing-urls.md",
    "content": "---\nid: 458b8203-e330-4d78-9bf5-82aaec8d458b\ntitle: 'Assets are Missing URLs'\ntemplate: page\ncategories:\n  - troubleshooting\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821385\n---\nTrying to output an asset's details and `url` is just blank?\n\nPerhaps `alt` text, even `width` and `height` work, but not `url`?\nYou might have something like this:\n\n``` yaml\nmy_asset_field:\n  - path/to/image.jpg\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ my_asset_field }}\n  <img\n    src=\"{{ url }}\" alt=\"{{ alt }}\"\n    width=\"{{ width }}\" height=\"{{ height }}\"\n  />\n{{ /my_asset_field }}\n```\n::tab blade\n```blade\n@foreach ($my_asset_field as $asset)\n  <img\n    src=\"{{ $asset->url }}\" alt=\"{{ $asset->alt }}\"\n    width=\"{{ $asset->width }}\" height=\"{{ $asset->height }}\"\n  />\n@endforeach\n```\n::\n\n```html\n<img src=\"\" alt=\"An image\" width=\"100\" height=\"150\" />\n```\n\nThat'll be because your Asset Container's disk does not have a `url` configured.\n\n``` yaml\n# content/assets/my_container.yaml\ndisk: assets\n```\n\n``` php\n// config/filesystems.php\n'disks' => [\n    'assets' => [\n        'driver' => 'local',\n        'root' => public_path('assets'),\n        'visibility' => 'public',\n        'url' => '/assets', // 👈 you're missing this\n    ],\n]\n```\n\nAsset containers using url-less disks are considered \"private\" and will intentionally not output URLs.\n\n[Read about private asset containers](/assets#private-containers)\n"
  },
  {
    "path": "content/collections/troubleshooting/command-not-found-statamic.md",
    "content": "---\nid: 5d5e0add-3e2b-44c9-8ec2-7d18b9965504\ntitle: 'CLI Command Not Found: Statamic'\nintro: |-\n  In order for you to run globally installed [Composer](https://getcomposer.org) binaries, (like our `statamic` installer) you'll need to tell your computer where it's located.\ntemplate: page\ncategories:\n  - cli\n  - troubleshooting\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622820986\n---\nIf you were to run `statamic` in your terminal, it would have no idea you meant the one you just installed with Composer.\n\n``` shell\n$ statamic new mysite\nCommand not found: statamic\n```\n\nYou _could_ use the full path to the binary instead:\n\n``` shell\n$ ~/.composer/vendor/bin/statamic new mysite\nBuilding a new statamic site.\n```\n\nBut that's silly. Who wants to do that every time?\n\nYou can solve this by adding Composer's `bin` directory to your `PATH` (sometimes seen as `$PATH`).\n\n## MacOS or Linux\n\n1. You'll need to find Composer's global directory. This is usually somewhere in your home directory. This command will output the absolute path:\n    ``` shell\n    composer global config bin-dir --absolute\n    ```\n\n2. Identify which shell you're using. You can determine this by running `echo $SHELL`.\n3. Next, you'll need to add Composer's `bin` directory to your shell `rc` file. Feel free to create the file if it doesn't already exist.\n    - If you're using `bash`, this will be  `~/.bashrc`\n    - If you're using `zsh`, this will be `~/.zshrc`\n\n    ``` shell\n    # Replace the path below with the path identified in step 1\n    export PATH=\"/Users/me/.composer/vendor/bin/\":$PATH # MacOS\n\n    export PATH=\"$HOME/.config/composer/vendor/bin/\":$PATH # Linux\n    ```\n\nTo test it, open a _new_ terminal window and run `echo $PATH`. You should see the composer directory at the end.\n\n## Windows\n\nTo add to your `PATH` on Windows, it requires you to click through some things. Ryan Hoffman has written [an article](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/) with screenshots to walk you through it.\n\nComposer's directory to add is `%USERPROFILE%\\AppData\\Roaming\\Composer\\vendor\\bin`.\n"
  },
  {
    "path": "content/collections/troubleshooting/composer-and-github-authentication.md",
    "content": "---\nid: cf1794da-3dd2-424d-9274-584c561e837b\nblueprint: troubleshooting\ntitle: 'Composer & GitHub Authentication'\nintro: 'If you receive a Composer error similar to `Could not authenticate against github.com`, this usually means you are hitting API rate limits.'\ntemplate: page\ncategories:\n  - development\n  - devops\n---\nYou'll most likely run into this issue when installing a new Statamic site or Starter Kit. It'll look something like this:\n\n```cli\nCould not authenticate against github.com\nError installing starter kit [statamic/multisimplicity].\n```\n\nOr maybe this:\n\n```cli\n- Installing statamic/cms (3.2.1)\nDownloading: connection... Failed to download statamic/cms from dist: Could not authenticate against github.com`\nNow trying to download from source\n```\n\nTo get around this, you can [create a personal access token](https://github.com/settings/tokens/new) within a GitHub's user settings. For installing open source projects via Composer, GitHub doesn't need any scopes or permissions, just an authenticated user. This means you can leave every single box unchecked, unless using private repositories.\n\nOnce the personal access token is created, you can add the following to the project _before_ running the `composer` command again:\n\n```cli\ncomposer config github-oauth.github.com {your-personal-access-token-here}\n```\n"
  },
  {
    "path": "content/collections/troubleshooting/control-panel-page-expired.md",
    "content": "---\nid: 3faef3ae-8673-42ba-901b-a1d7eb3db4b7\nblueprint: troubleshooting\ntitle: '\"419 Page Expired\" error when logging into the Control Panel'\ncategories:\n  - troubleshooting\ntemplate: page\n---\nThere are a few common reasons why you might encounter a \"419 Page Expired\" error when attempting to login to the Control Panel.\n\n## Database session driver\nThe most common reason you'll see this error is if you're using the [`database` session driver](https://laravel.com/docs/session#database).\n\nThe `user_id` column on the `sessions` table expects an integer value. However, because Statamic uses UUIDs for user IDs, the session row is saved incorrectly, causing the 419 error.\n\nYou can workaround this by changing the `user_id` column to a string/varchar:\n\n```php\nSchema::create('sessions', function (Blueprint $table) {\n    $table->string('id')->primary();\n    $table->foreignId('user_id')->nullable()->index(); // [tl! remove]\n    $table->string('user_id')->nullable()->index(); // [tl! add]\n    $table->string('ip_address', 45)->nullable();\n    $table->text('user_agent')->nullable();\n    $table->longText('payload');\n    $table->integer('last_activity')->index();\n});\n```\n\n## Browser Autofill\nAnother potential cause for this issue might be interference by your browser's autocomplete, or an extension which provides similar functionality (like 1Password).\n\nWhen it autocompletes your login details, it might also be changing the value of the hidden `_token` input, which is used for storing your [CSRF token](https://laravel.com/docs/master/csrf#main-content).\n\nTo rule this out, you could try disabling all browser extensions, or logging in to the Control Panel using a different browser.\n"
  },
  {
    "path": "content/collections/troubleshooting/email-not-sending.md",
    "content": "---\nid: 577004ae-5fa4-4535-b34f-cd8e7f721cbb\nblueprint: troubleshooting\ntitle: 'Email not sending'\nintro: Email can be difficult to debug when it seems like you've set everything up correctly.\ntemplate: page\ncategories:\n  - troubleshooting\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821385\n---\n## Email Test Utility\nThe most basic way to test that email is working is to use the Email utility in the control panel.\n\nHead to Utilities > Email. In there you can see all your mail related settings. Maybe you can spot an incorrect setting.\n\nOn this page you can also send a test email. If you do that and see a success message but no email, here's some suggestions:\n\n## Queue\nOne thing you may want to check is that if you've configured a queue, that the queue is actually running.\n\nThe success message could mean that it was successfully queued, but the lack of any email is because the queued job was never executed.\n\nLocally, run the queue worker:\n\n```shell\nphp artisan queue:listen\n```\n\nOr on production, make sure you have a worker configured to run the `queue:work` command.\n\n## Horizon\n\nIf you're using Laravel Horizon, make sure that you didn't just `composer require` it, but that you also installed it using `artisan horizon:install`.  \n"
  },
  {
    "path": "content/collections/troubleshooting/fixing-issues-with-global-composer-packages.md",
    "content": "---\nid: 8e162978-b716-4c8b-a07c-a5ddefc703d5\ntitle: 'Fixing issues with Global Composer packages'\nintro: |\n  When [Composer](https://getcomposer.org) works, it's a fantastic and powerful tool. But what happens when it...doesn't? Here's how to fix one of the most common issues with global dependencies:\n\n  ```\n  Your requirements could not be resolved to an installable set of packages.\n  ```\ntemplate: page\ncategories:\n  - cli\n  - troubleshooting\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1625836991\n---\nIf you've just tried to install Statamic's installer (`composer global require statamic/cli\n`) and encountered an error that includes the phrase \"_Your requirements could not be resolved to an installable set of packages_\", thankfully the fix is probably simple. We wish errors gave you a better idea what to do next, but here we are.\n\n## Update Global Composer Dependencies\n\nThe most common reason for this issue is that your global Composer dependencies are out of date and whatever you're trying to install needs a newer version of package. You can update your global dependencies by running the following command, and then try whatever you were just doing one more time.\n\n``` shell\ncomposer global update\n```\n\n## Popping the Hood\n\nIf that doesn't resolve the issue, you may need to look at your list of global dependencies for clues. You may have a package that requires an older version of PHP or another dependency. This might take a little Googling , trial and error (remove a line and run `composer global update` and try again), or brute force, but will almost definitely result in finding the problem. Eventually. We're sorry it has come to this.\n\nYou can find where the global `composer.json` config file is by running the following command and looking at the `[home]` line.\n\n``` shell\ncomposer config --list --global\n```\n\n:::tip\nIf you're on MacOS, that file will be at `~/.composer/composer.json`\n:::\n"
  },
  {
    "path": "content/collections/troubleshooting/listing-performance.md",
    "content": "---\nid: 711cf0a0-d392-4c7e-b2ff-93a2b82e1b81\ntitle: 'How to Fix Slow Control Panel Entry Listings'\nintro: 'Entry listings in the control panel are feeling a bit sluggish? We have ways of speeding that right up.'\ntemplate: page\ncategories:\n  - performance\n  - troubleshooting\n---\nBy default, Statamic will paginate your entry listing results, as well as limit the visible columns to prevent the loading of extraneous data.\n\n<figure>\n    <img src=\"/img/tips/listing-performance-example.png\" alt=\"Listing performance example\">\n</figure>\n\n## Queries & Augmentation\n\nCertain fieldtypes naturally load slower due to how they are being [augmented](/extending/augmentation) and how much data is being fetched.  Relationship fieldtypes can be particularily slow in the context of a listing, due to additional relationship queries on each displayed entry.  If these fields are [unlisted](/fields#common-settings) or have their visibility disabled in the column selector, Statamic will not fetch or augment this data.  For these reasons, it helps to be mindful about which fields you show by default within your listings.\n\n## Pagination\n\nYou can also improve listing performance by limiting your `pagination_size` within `config/statamic/cp.php`.  Less data shown on the page ultimately means smaller queries and less augmentation.\n\n## Search\n\nIf you find entry search is sluggish, it might be worth looking into creating a custom [search index](/search#indexes) for your collection.  Doing so can drastically improve search query performance.\n"
  },
  {
    "path": "content/collections/troubleshooting/missing-control-panel-assets.md",
    "content": "---\nid: 6056f7d0-f767-496d-a8b0-e1242f69faa2\nblueprint: troubleshooting\ntitle: 'Missing Control Panel Assets (Vite manifest not found)'\ntemplate: page\ncategories:\n  - troubleshooting\n---\nYou've just installed Statamic and are ready to jump into the Control Panel. You head to `/cp` and are met with a `Vite manifest not found` error.\n\n![](/img/vite-manifest-not-found.png)\n\nThe most likely reason for this is that **you have Composer plugins disabled**.\n\nThere may be a number of reasons why they are disabled, such as:\n- You answered no when it asked if you want to trust the plugin\n- You have them disabled in your global composer config\n- Your Docker setup doesn't allow them \n\nRegardless, you'll need to at least enable one. When installing or updating Statamic via Composer, we use the `pixelfear/composer-dist-plugin` plugin to copy the assets into the appropriate location.\n\n1. You may explicitly allow it by adding the following to your `composer.json`.\n\n    ```json\n    {\n        \"name\": \"statamic/statamic\",\n        \"require\": {},\n        \"autoload\": {},\n        \"config\": { // [tl! **]\n            \"optimize-autoloader\": true,\n            \"preferred-install\": \"dist\",\n            \"sort-packages\": true,\n            \"allow-plugins\": { // [tl! **]\n                \"pestphp/pest-plugin\": true,\n                \"php-http/discovery\": true,\n                \"pixelfear/composer-dist-plugin\": true // [tl! ** ++]\n            } // [tl! **]\n        }, // [tl! **]\n        \"minimum-stability\": \"dev\",\n        \"prefer-stable\": true\n    }\n    ```\n\n2. Delete the `vendor/statamic/cms` directory. This will force Composer to re-download Statamic in the next step.\n    ```bash\n    rm -rf vendor/statamic/cms   \n    ```\n3. Update Statamic\n    ```bash\n    composer update statamic/cms\n    ```\n\n:::tip\nFor Docker specifically, you may be able to avoid this in the future by adding this to your `Dockerfile` before any `composer` commands:\n\n```dockerfile\nENV COMPOSER_ALLOW_SUPERUSER=1\n```\n:::\n"
  },
  {
    "path": "content/collections/troubleshooting/required-php-extensions.md",
    "content": "---\nid: 0c30a664-9bc3-4c5e-ad8c-66452b049748\ntitle: 'Required PHP Extensions for Assets'\nintro: 'Is your Control Panel subtly broken?  Check to see if you have these PHP extensions enabled.'\ntemplate: page\ncategories:\n  - development\n  - devops\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622821231\n---\nControl Panel will gently fail, particularly in the **Assets** section and **Glide** on the front end, unless you have the following PHP extensions enabled on your server:\n\n- mbstring\n- exif\n- gd2\n- fileinfo\n\nNot all PHP installations will have these enabled by default.  Consult your php.ini file or **Utilities > PHP Info** to determine whether you have them installed.\n"
  },
  {
    "path": "content/collections/troubleshooting.yaml",
    "content": "title: Troubleshooting\nicon: support\ntemplate: troubleshooting/show\nlayout: layout\nmount: cd0428cf-2c4a-43b9-8e61-dd8123799ed8\ntaxonomies:\n  - categories\nrevisions: false\nroute: '/{mount}/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\n"
  },
  {
    "path": "content/collections/variables/basename.md",
    "content": "---\nid: 4aeaaa17-b92f-4be0-9f46-9e31c2589b8c\nblueprint: variables\ntypes:\n  - asset\ntitle: Basename\ndescription: The basename of the asset, which is the filename with the extension.\n---\nThe basename of the asset, which is the filename _with_ the extension.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ basename }}\n```\n::tab blade\n```blade\n{{ $basename }}\n```\n::\n\n```html\nblack-bear-cubs.jpg\n```\n"
  },
  {
    "path": "content/collections/variables/collection.md",
    "content": "---\nid: 0f6de8a2-b340-4733-a67f-c5f2296d70c6\nblueprint: variables\ntypes:\n  - entry\ntitle: Collection\ndescription: Get the name of the collection the entry belongs to.\n---\nGet the name of the collection the entry belongs to.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ collection }}\n```\n::tab blade\n```blade\n{{ $collection }}\n```\n::\n\n```html\nblog\n```\n"
  },
  {
    "path": "content/collections/variables/config.md",
    "content": "---\nid: 898d2e63-fc2c-4b66-bd26-8a53883a0aad\nblueprint: variables\ntypes:\n  - system\ntitle: Config\ndescription: Access configuration values from Statamic and Laravel config files.\n---\nStatamic (and Laravel) have many config files in the `config` directory. Each file/directory is the 'key' to retrieve its data.\n\nFor example, if you want to check whether the Control Panel is enabled, you could use:\n\n::tabs\n\n::tab antlers\n``` antlers\n{{ config:statamic:cp:enabled }}\n```\n::tab blade\n```blade\n{{ config('statamic.cp.enabled') }}\n```\n::\n\nTo retrieve from the `config/app.php` you would:\n\n::tabs\n\n::tab antlers\n``` antlers\n{{ config:app:variable_you_want }}\n```\n::tab blade\n```blade\n{{ config('statamic.app.variable_you_want') }}\n```\n::\n"
  },
  {
    "path": "content/collections/variables/csrf_field.md",
    "content": "---\nid: 344d0518-9deb-4bfc-ac62-fe48a9304a81\nblueprint: variables\ntypes:\n  - system\ntitle: 'CSRF Field'\ndescription: Outputs the CSRF token inside a hidden field named `_token` from the session.\n---\nA helper to output the CSRF token inside a hidden field named `_token` from the session.\n\nPOST requests in Statamic will be keeping an eye out for a `_token` value.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ csrf_field }}\n```\n::tab blade\n```blade\n{!! $csrf_field !!}\n\n-- or --\n{!! csrf_field() !!}\n```\n::\n\n```\n<input type=\"hidden\" name=\"_token\" value=\"csrftokenhere\" />\n```\n"
  },
  {
    "path": "content/collections/variables/csrf_token.md",
    "content": "---\nid: 1ff95c0b-e62a-46c2-87e7-75d84b10eaf1\nblueprint: variables\ntypes:\n  - system\ntitle: 'CSRF Token'\ndescription: Output the CSRF token from the session.\n---\nOutput the CSRF token from the session.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ csrf_token }}\n```\n::tab blade\n```blade\n{{ $csrf_token }}\n\n-- or --\n\n{{ csrf_token() }}\n```\n::\n"
  },
  {
    "path": "content/collections/variables/current_layout.md",
    "content": "---\nid: 2f00c834-213d-4156-b3a0-5e249a71dc22\nblueprint: variables\ntypes:\n  - system\n  - content\ntitle: Current Layout\ndescription: The name of the layout currently in use.\n---\nThe name of the layout currently in use.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ current_layout }}\n```\n::tab blade\n```blade\n{{ $current_layout }}\n```\n::\n\n```html\nlayouts.blog\n```\n"
  },
  {
    "path": "content/collections/variables/current_template.md",
    "content": "---\nid: ddfaa9a1-eded-4f27-a296-0d8fd3dc6d1b\nblueprint: variables\ntypes:\n  - system\n  - content\ntitle: Current Template\ndescription: The name of the template currently in use.\n---\nThe name of the template currently in use.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ current_template }}\n```\n::tab blade\n```blade\n{{ $current_template }}\n```\n::\n\n```html\nblog.index\n```\n"
  },
  {
    "path": "content/collections/variables/current_uri.md",
    "content": "---\nid: 461b4e7a-2918-40e4-80fc-54b178db4074\nblueprint: variables\ntypes:\n  - system\ntitle: 'Current Uri'\ndescription: The current URI (URL without domain).\n---\nThe current URI (URL without domain).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ current_uri }}\n```\n::tab blade\n```blade\n{{ $current_uri }}\n```\n::\n\n```html\n/variables\n```\n"
  },
  {
    "path": "content/collections/variables/current_url.md",
    "content": "---\nid: 8f32d8f6-b9b6-4126-a517-661e6089ff41\nblueprint: variables\ntypes:\n  - system\ntitle: 'Current Url'\ndescription: The current URL.\n---\nThe current URL.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ current_url }}\n```\n::tab blade\n```blade\n{{ $current_url }}\n```\n::\n\n```html\nhttps://statamic.dev/variables/current_url\n```\n"
  },
  {
    "path": "content/collections/variables/current_user.md",
    "content": "---\nid: ef9c992a-33ed-4b26-84b1-b7e0d9b2b2dd\nblueprint: variables\ntitle: 'Current User'\ndescription: The current user.\n---\nThe current user.\n\n::tabs\n::tab antlers\n```antlers\n{{ current_user }} {{ name }} {{ /current_user }}\n\n{{ current_user:email }}\n```\n::tab blade\n```blade\n{{ $current_user['name'] }}\n\n{{ $current_user['email'] }}\n```\n\n```html\nExample User\n\nuser@example.com\n```"
  },
  {
    "path": "content/collections/variables/date.md",
    "content": "---\nid: 01b2a925-3f4e-473c-a983-ff8c29c8078f\nblueprint: variables\ntypes:\n  - entry\ntitle: Date\ndescription: Get the date of the entry as a Carbon instance, formatted according to system settings.\n---\nGet the date of the entry. This will be a `Carbon` instance.\n\nHowever if you use it in your template as-is, it will get converted to a string using the format defined in your\nsystem settings.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date }}\n```\n::tab blade\n```blade\n{{ $date }}\n```\n::\n\n```html\nFebruary 16, 2016\n```\n"
  },
  {
    "path": "content/collections/variables/datestamp.md",
    "content": "---\nid: 2a5f5263-e61b-47bb-8c45-c02cd49554d6\nblueprint: variables\ntypes:\n  - entry\ntitle: Datestamp\ndescription: Get the timestamp of the entry as an integer. Alias of `timestamp`.\n---\nGet the timestamp of the entry. This will be an integer.\n\nAlias of `timestamp`. Maybe calling it a datestamp makes more sense to you if you aren't using time-based entries.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ datestamp }}\n```\n::tab blade\n```blade\n{{ $datestamp }}\n```\n::\n\n```html\n1425772800\n```\n"
  },
  {
    "path": "content/collections/variables/datestring.md",
    "content": "---\nid: 1a16bf0e-ee75-47b4-ac6e-32c95279c766\nblueprint: variables\ntypes:\n  - entry\ntitle: Datestring\ndescription: Get the pre-formatted date of the entry as a string.\n---\nGet the pre-formatted date of the entry as a string.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ date }}\n```\n::tab blade\n```blade\n{{ $date }}\n```\n::\n\n```html\nFebruary 16, 2016\n```\n"
  },
  {
    "path": "content/collections/variables/edit_url.md",
    "content": "---\nid: 4b4bd7a7-6fc8-4457-b030-56d4474e20b0\nblueprint: variables\ntypes:\n  - content\ntitle: 'Edit Url'\ndescription: Get the URL to edit the current page or entry in the Control Panel.\n---\nGet the URL to edit the current page or entry in the Control Panel, if there is one (for example, there is no `edit_url` for a template route).\n\nThe user will need to login and have permissions, so it's probably best if used in conjunction with [permissions checks](/tags/user-can).\n\n::tabs\n\n::tab antlers\n```antlers\n<a href=\"{{ edit_url }}\">Edit this page</a>\n```\n::tab blade\n```blade\n<a href=\"{{ $edit_url }}\">Edit this page</a>\n```\n::\n\n```html\n<a href=\"/cp/pages/edit/about-ye-old-me\">Edit this page</a>\n```\n"
  },
  {
    "path": "content/collections/variables/entries_count.md",
    "content": "---\nid: b4cd82ee-8a96-4aba-a8fc-dfee171335de\nblueprint: variables\ntypes:\n  - term\ntitle: 'Entries Count'\ndescription: Get the number of entries that use this taxonomy term.\n---\nGet the number of entries that use this taxonomy term.\n\n::tabs\n\n::tab antlers\n```antlers\nThere are {{ entries_count }} 'news' entries.\n```\n::tab blade\n```blade\nThere are {{ $entries_count }} 'news' entries.\n```\n::\n\n```html\nThere are 85 'news' entries.\n```\n"
  },
  {
    "path": "content/collections/variables/environment.md",
    "content": "---\nid: 8426c6e0-6641-11e6-bdf4-0800200c9a66\nblueprint: variables\ntypes:\n  - system\ntitle: Environment\ndescription: Outputs the current environment (the value of `APP_ENV` in your `.env` file).\n---\nOutputs the current environment.\n\nThis will be the value of `APP_ENV` in your `.env` file. If you haven't set that, then it will output the default of `production`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ environment }}\n```\n::tab blade\n```blade\n{{ $environment }}\n```\n::\n\n```html\nproduction\n```\n"
  },
  {
    "path": "content/collections/variables/extension.md",
    "content": "---\nid: c35b0240-645f-44c6-9a9c-0cb1136e7b26\nblueprint: variables\ntypes:\n  - asset\ntitle: Extension\ndescription: The file extension of the asset.\n---\nThe file extension of the asset.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ extension }}\n```\n::tab blade\n```blade\n{{ $extension }}\n```\n::\n\n```html\njpg\n```\n"
  },
  {
    "path": "content/collections/variables/filename.md",
    "content": "---\nid: 0af805cd-0a3e-4b07-afc3-edd1a8a688e0\nblueprint: variables\ntypes:\n  - asset\ntitle: Filename\ndescription: The filename of the asset, without the extension.\n---\nThe filename of the asset, without the extension.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ filename }}\n```\n::tab blade\n```blade\n{{ $filename }}\n```\n::\n\n```html\nblack-bear-cubs\n```\n"
  },
  {
    "path": "content/collections/variables/focus.md",
    "content": "---\nid: d61d8f8c-4e7e-4933-8f34-0cdba2a3ee82\nblueprint: variables\ntypes:\n  - asset\ntitle: Focus\ndescription: The focal point of the asset, defaulting to center (50-50).\n---\nThe focal point of the asset, defaulting to center (`50-50`).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ focus }}\n```\n::tab blade\n```blade\n{{ $focus }}\n```\n::\n\n```html\n50-30\n```\n\nYou may want to use this in CSS with either the [background_position](/modifiers/background_position) modifier, or just by using the [focus_css](/variables/focus_css) variable.\n\n::tabs\n\n::tab antlers\n```antlers\nbackground-position: {{ focus | background_position }};\nbackground-position: {{ focus_css }};\n```\n::tab blade\n```blade\n\nbackground-position: {{ Statamic::modify($focus)->backgroundPosition() }};\nbackground-position: {{ $focus_css }};\n```\n::\n\n```html\nbackground-position: 50% 30%;\nbackground-position: 50% 30%;\n```\n"
  },
  {
    "path": "content/collections/variables/focus_css.md",
    "content": "---\nid: cd50cb3d-28cb-4738-88a2-dac4e4244911\nblueprint: variables\ntypes:\n  - asset\ntitle: 'Focus Css'\ndescription: The focal point of the asset in a format suitable for the background-position CSS property.\n---\nThe focal point of the asset, in a format suitable for the background-position CSS property, if one has been set.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ focus_css }}\n```\n::tab blade\n```blade\n{{ $focus_css }}\n```\n::\n\n```html\n50% 30%\n```\n"
  },
  {
    "path": "content/collections/variables/folder.yaml",
    "content": "order: alphabetical\ntemplate: variable\n"
  },
  {
    "path": "content/collections/variables/get.md",
    "content": "---\nid: 546c4334-df40-4e9a-aff4-a56c43e839d8\nblueprint: variables\ntypes:\n  - system\ntitle: Get\ndescription: An array of GET variables from query strings in the current URL.\n---\nAn array of `GET` variables that come from any query strings present in the current URL. It can be used as a tag pair with access to all your parameters or as a single tag to access parameters directly. A counterpart to `{{ post }}`.\n\n\nExample URL: `/about?show=pants&hide=jeggings`\n\n::tabs\n\n::tab antlers\n```antlers\n{{ get:show }}\n\n{{ get }}\n  {{ show }}\n  {{ hide }}\n{{ /get }}\n\n```\n::tab blade\n```blade\n{{ $get['show'] ?? '' }}\n\n-- or --\n\n{{ request()->get('show') }}\n```\n::\n\n```html\npants\n\npants\njeggings\n```\n\nBe sure to escape these values with the `sanitize` modifier if you plan to use them in output in production.\n\n::tabs\n\n::tab antlers\n```antlers\n<!-- Because let's face it. You really *should* sanitize your jeggings. -->\n{{ get:jeggings | sanitize }}\n```\n::tab blade\n```blade\n{{ request()->get('jeggings') }}\n\n-- or --\n\n{!! Statamic::modify(request()->get('jeggings'))->sanitize() !!}\n```\n::\n"
  },
  {
    "path": "content/collections/variables/get_post.md",
    "content": "---\nid: eadae010-e651-4504-bea4-a9baeea1c0b5\nblueprint: variables\ntypes:\n  - system\ntitle: 'Get Post'\ndescription: Combines both `get` and `post` variables, with POST data taking precedence.\n---\nThis variable combines both `{{ get }}` and `{{ post }}`, with `POST` data taking precedence in the event of identical variables.\n"
  },
  {
    "path": "content/collections/variables/has_timestamp.md",
    "content": "---\nid: 396883d6-a3bc-4d98-a5a0-894e744b0451\nblueprint: variables\ntypes:\n  - entry\ntitle: 'Has Timestamp'\ndescription: A boolean indicating whether an entry is time-based.\n---\nA boolean for whether or not an entry is time-based.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if has_timestamp }}\n    {{ date format=\"F jS, Y g:i a\" }}\n{{ else }}\n    {{ date format=\"F jS, Y\" }}\n{{ /if }}\n```\n::tab blade\n```blade\n@if ($has_timestamp)\n  {{ $date->format('F jS, Y g:i a' )}}\n@else\n  {{ $date->format('F jS, Y') }}\n@endif\n```\n::\n\n```html\nJanuary 1st, 2016 11:30 am\n```\n"
  },
  {
    "path": "content/collections/variables/height.md",
    "content": "---\nid: 299b5fad-d2ac-4bef-9107-100908e2c9d7\nblueprint: variables\ntypes:\n  - asset\ntitle: Height\ndescription: The height of an image asset, in pixels.\n---\nThe height of an image asset, in pixels.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ height }}\n```\n::tab blade\n```blade\n{{ $height }}\n```\n::\n\n```html\n643\n```\n"
  },
  {
    "path": "content/collections/variables/homepage.md",
    "content": "---\nid: 80599fcb-69df-4de8-ad79-c918d9919e24\nblueprint: variables\ntypes:\n  - system\ntitle: Homepage\ndescription: The URL of the homepage. Usually the same as `site:url`.\n---\nThe URL of the homepage. Usually (but not always) the same as `{{ site:url }}`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ homepage }}\n```\n::tab blade\n```blade\n{{ $homepage }}\n```\n::\n\n```html\nhttps://docs.statamic.com/\n```\n"
  },
  {
    "path": "content/collections/variables/id.md",
    "content": "---\nid: a7a7a927-fd46-4b01-887b-99d4ce5f8e1e\nblueprint: variables\ntypes:\n  - content\ntitle: Id\ndescription: The unique identifier of the content.\n---\nThe ID is the identifier of the content. It's usually a long number that you wouldn't want to show anyone.\nHowever it's super handy to pass into other tags.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ id }}\n```\n::tab blade\n```blade\n{{ $page->id }}\n```\n::\n\n```html\n0020c540-d4cd-11e5-a837-0800200c9a66\n```\n"
  },
  {
    "path": "content/collections/variables/is_asset.md",
    "content": "---\nid: 24115b71-ac28-460d-a48a-36ed14950ef8\nblueprint: variables\ntypes:\n  - asset\ntitle: 'Is Asset'\ndescription: A boolean indicating whether the current content is an asset.\n---\nA boolean for whether the current content is an asset. Naturally, this will be `true` for assets.\n"
  },
  {
    "path": "content/collections/variables/is_entry.md",
    "content": "---\nid: 001d8875-64aa-4c23-82d5-f84da62b927e\nblueprint: variables\ntypes:\n  - entry\ntitle: 'Is Entry'\ndescription: A boolean indicating whether the current content is an entry.\n---\nA boolean for whether the current content is an entry. Naturally, this will be `true` for entries.\n"
  },
  {
    "path": "content/collections/variables/is_homepage.md",
    "content": "---\nid: cc7c0df5-b3a6-4f67-8bd8-50d145ae756c\nblueprint: variables\ntypes:\n  - system\ntitle: Is Homepage\ndescription: A boolean indicating whether you're on the homepage.\n---\nWhether you're on the homepage.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if is_homepage }} Home sweet home {{ /if }}\n```\n::tab blade\n```blade\n@if ($is_homepage)\n  Home sweet home\n@endif\n```\n::\n\nIf you're using [multiple sites](/multi-site), this will be true if you're on the homepage for any configured site.\n"
  },
  {
    "path": "content/collections/variables/is_image.md",
    "content": "---\nid: 89e75dec-7f33-4217-b040-dc2a18de5d83\nblueprint: variables\ntypes:\n  - asset\ntitle: 'Is Image'\ndescription: A boolean indicating whether the asset is an image.\n---\nA boolean for whether the asset is an image.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if is_image }}\n    <img src=\"{{ url }}\" />\n{{ else }}\n    <a href=\"{{ url }}\">Download</a>\n{{ /if }}\n```\n::tab blade\n```blade\n@if ($is_image)\n  <img src=\"{{ $url }}\" />\n@else\n  <a href=\"{{ $url }}\">Download</a>\n@endif\n```\n::"
  },
  {
    "path": "content/collections/variables/is_term.md",
    "content": "---\nid: 25dd3a4d-306c-48b3-a204-59eee23231b0\nblueprint: variables\ntypes:\n  - term\ntitle: 'Is Term'\ndescription: A boolean indicating whether the current content is a term.\n---\nA boolean for whether the current content is a term. Naturally, this will be `true` for terms.\n"
  },
  {
    "path": "content/collections/variables/is_video.md",
    "content": "---\ntypes:\n  - asset\nid: 89e75dec-7f33-4217-b040-2c2a18de5d83\ntitle: 'Is Video'\ndescription: A boolean indicating whether the asset is a video.\n---\nA boolean for whether the asset is an video.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if is_video }}\n    <video controls>\n        <source src=\"{{ url }}\" type=\"video/mp4\">\n        Sorry, your browser doesn't support embedded videos.\n    </video>\n{{ else }}\n    <a href=\"{{ url }}\">Download</a>\n{{ /if }}\n```\n::tab blade\n```blade\n@if ($is_video)\n  <video controls>\n    <source src=\"{{ $url }}\" type=\"video/mp4\">\n    Sorry, your browser doesn't support embedded videos.\n  </video>\n@else\n  <a href=\"{{ $url }}\">Download</a>\n@endif\n```\n::\n\n"
  },
  {
    "path": "content/collections/variables/last_modified.md",
    "content": "---\nid: 026f77b4-ab22-4c5f-b3be-ebc452e25c5b\nblueprint: variables\ntypes:\n  - content\ntitle: 'Last Modified'\ndescription: The last modified time for the content file or asset.\n---\nThe last modified time for the content file, or the asset.\n\nNote that this is the timestamp for the file itself, not for when the content was published or updated. For example,\nif you were to use git to deploy this file and it gets written on a server, the last modified time will be different.\n\n::tabs\n\n::tab\n```antlers\n{{ last_modified }}\n```\n::tab blade\n```blade\n{{ $page->last_modified }}\n```\n::"
  },
  {
    "path": "content/collections/variables/last_segment.md",
    "content": "---\nid: d9ae0cd6-a94e-44d7-b748-5506fa2e5ae7\nblueprint: variables\ntypes:\n  - system\ntitle: 'Last Segment'\ndescription: Get the last segment of the current URL.\n---\nSince you don't always know how many segments your URL will have, you can always grab the last segment.\n\nExample URL: `/nestled/safely/under/our/tree`\n\n::tabs\n\n::tab antlers\n```antlers\n{{ last_segment }}\n```\n::tab blade\n```blade\n{{ $last_segment }}\n```\n::\n\n```html\ntree\n```\n"
  },
  {
    "path": "content/collections/variables/live_preview.md",
    "content": "---\nid: e722fd36-1aa5-4e91-8eeb-8b759accf4d2\nblueprint: variables\ntypes:\n  - system\ntitle: 'Live Preview'\ndescription: A boolean indicating whether the current page is being viewed in Live Preview.\nrelated_entries:\n  - cdffd2c9-cf42-495d-a8f1-f416ddfddc29\n---\n\nA boolean for whether the current page is being viewed in [Live Preview](/live-preview).\n\n```antlers\n{{ if live_preview }}\n    <div>If you are seeing this, you're in Live Preview 😎</div>\n{{ /if }}\n```\n"
  },
  {
    "path": "content/collections/variables/logged_in.md",
    "content": "---\nid: 67c2b180-8568-4f92-9571-425898f293d5\nblueprint: variables\ntypes:\n  - system\ntitle: 'Logged In'\ndescription: A boolean indicating whether the visitor is logged in.\n---\nWhether the visitor is logged in.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if logged_in }} Welcome back! {{ /if }}\n```\n::tab blade\n```blade\n@if ($logged_in) Welcome back! @endif\n```\n::\n"
  },
  {
    "path": "content/collections/variables/now.md",
    "content": "---\nid: cbf3a055-9714-4f0f-a33b-508c59b4e1f8\nblueprint: variables\ntypes:\n  - system\ntitle: Now\ndescription: The current date/time, formatted according to your display timezone and default time format.\n---\n\nThe current date/time. If you use it on its own, it will be converted to your [`display_timezone`](/tips/timezones) and formatted using the default time format.\n\nWhen you pass it to a tag parameter, or modifier, it will be treated as a UTC [`Carbon`](https://carbon.nesbot.com/) instance.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ now }}\n```\n::tab blade\n```blade\n{{ $now }}\n\n-- or --\n\n{{ now() }}\n```\n::\n\n```html\nDecember 30th 2015\n```\n\nAlso available as `{{ today }}` and `{{ current_date }}` aliases.\n"
  },
  {
    "path": "content/collections/variables/old.md",
    "content": "---\nid: a1d6bfec-e0dd-45a5-9c9e-200d700de244\nblueprint: variables\ntypes:\n  - system\ntitle: Old\ndescription: An array of sanitized variables POSTed from the previous request, useful for form validation errors.\n---\nAn array of sanitized variables POSTed from the previous request.\n\nIn certain forms – for example, `{{ user:login_form }}` – you may encounter validation errors that will require you to resubmit your the form.\n\nInstead of making your users re-type everything, you may use `{{ old:[field_name] }}` tag to display their previously-entered input values.\n\nWhen using the `{{ user:login_form }}` to login, upon entering incorrect credentials, you will be shown the same page. You can use the `{{ old:[field_name] }}` tags to maintain the values like so:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ user:login_form }}\n\n    {{ if errors }}\n        ...\n    {{ /if }}\n\n\n    <label>Username</label>\n    <input type=\"text\" name=\"username\" value=\"{{ old:username }}\" />\n\n    <label>Password</label>\n    <input type=\"password\" name=\"password\" value=\"{{ old:password }}\" />\n\n    <button>Log in</button>\n\n{{ /user:login_form }}\n```\n::tab blade\n```blade\n<s:user:login_form>\n  @if (count($errors) > 0)\n    ...\n  @endif\n\n  <label>Username</label>\n  <input type=\"text\" name=\"username\" value=\"{{ old('username') }}\" />\n\n  <label>Password</label>\n  <input type=\"password\" name=\"password\" value=\"{{ old('password') }}\" />\n\n  <button>Log in</button>\n\n</s:user:login_form>\n```\n::"
  },
  {
    "path": "content/collections/variables/order.md",
    "content": "---\nid: 4d2aaabf-e845-4d29-bf8e-d2d4d2245945\nblueprint: variables\ntypes:\n  - entry\ntitle: Order\ndescription: Get the order key of the content (the value at the beginning of the filename).\n---\nGet the order key of the content. It's what's at the beginning of the filename.\n\nFor pages this could be a number. For entries it could be a number or a date string (2015-01-02).\n\n::tabs\n\n::tab antlers\n```antlers\n{{ order }}\n```\n::tab blade\n```blade\n{{ $order }}\n```\n::\n\n```html\n1\n```\n"
  },
  {
    "path": "content/collections/variables/order_type.md",
    "content": "---\nid: 4e41a594-cb06-4963-a60e-3031ce568f97\nblueprint: variables\ntypes:\n  - entry\ntitle: 'Order Type'\ndescription: Get the order type of an entry (date, alphabetical, or number).\n---\nGet the order type of an entry. This could be `date`, `alphabetical`, or `number`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ order_type }}\n```\n::tab blade\n```blade\n{{ $order_type }}\n```\n::\n\n```html\n{{ number }}\n```\n"
  },
  {
    "path": "content/collections/variables/path.md",
    "content": "---\nid: dcb4c134-08ad-4b48-a2dd-1f8fcd652c57\nblueprint: variables\ntypes:\n  - asset\ntitle: Path\ndescription: The path to the file, relative to the asset container.\n---\nThe path to the file, relative to the asset container.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ path }}\n```\n::tab blade\n```blade\n{{ $path }}\n```\n::\n\n```html\nimg/black-bear-cubs.jpg\n```\n"
  },
  {
    "path": "content/collections/variables/permalink.md",
    "content": "---\nid: 548cde5a-c65e-42b0-9334-efe18e9e260b\nblueprint: variables\ntypes:\n  - content\ntitle: Permalink\ndescription: Get the absolute URL to the content, including your site URL.\n---\nGet the absolute URL to the content. This will include your site URL.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ permalink }}\n```\n::tab blade\n```blade\n{{ $permalink }}\n```\n::\n\n```html\nhttp://example.com/posts/bacon\n```\n"
  },
  {
    "path": "content/collections/variables/post.md",
    "content": "---\nid: dc9c535d-59ac-475d-af4f-a0204a71f31b\nblueprint: variables\ntypes:\n  - system\ntitle: Post\ndescription: An array of sanitized POST variables from form data submitted to the current URL.\n---\nAn array of sanitized `POST` variables that come from any form data present for a POST to the current URL. It can be used as a tag pair with access to all your data or as a single tag to access variables directly. A counterpart to `{{ get }}`.\n\n```\n<form method=\"post\">\n  <input type=\"hidden\" name=\"_token\" value=\"csrftokenhere\" />\n  <input type=\"text\" name=\"first_name\" value=\"Niles\">\n  <input type=\"text\" name=\"last_name\" value=\"Peppertrout\">\n  <button>Submit</button>\n</form>\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ post }}\n  {{ first_name }} {{ last_name }}\n{{ /post }}\n\nMr. {{ post:last_name }}\n```\n::tab blade\n```blade\n{{ request()->post('first_name') }} {{ request()->post('last_name') }}\n\nMr. {{ request()->post('last_name') }}\n```\n::\n\n```html\nNiles Peppertrout\n\nMr. Peppertrout\n```\n"
  },
  {
    "path": "content/collections/variables/published.md",
    "content": "---\nid: d374ba18-d8cc-4700-a79c-c7da5a26b314\nblueprint: variables\ntypes:\n  - content\ntitle: Published\ndescription: A boolean indicating whether the content is published (not a draft).\n---\nA boolean that specifies whether the content is published. Or \"live\", or \"not a draft\".\n\n::tabs\n\n::tab antlers\n```antlers\n{{ if published }}\n    Published!\n{{ else }}\n    Draft\n{{ /if }}\n```\n::tab blade\n```blade\n@if ($published)\n  Published!\n@else\n  Draft\n@endif\n```\n::"
  },
  {
    "path": "content/collections/variables/response_code.md",
    "content": "---\nid: 3c2eaa93-00ce-4e48-868b-2f89f02c8504\nblueprint: variables\ntypes:\n  - system\ntitle: 'Response Code'\ndescription: The HTTP response code of the request (200 for existing URLs, 404 for non-existent ones).\n---\nThe response code of the request. This will be `200` for URLs that exist and `404` for those that don't.\n"
  },
  {
    "path": "content/collections/variables/segment_x.md",
    "content": "---\nid: edd048d2-5e9a-4f80-ad79-c0c22d711723\nblueprint: variables\ntypes:\n  - system\ntitle: 'Segment X'\ndescription: Get a specific URL segment by number (e.g., `segment_3` returns the third segment).\n---\nGiven \"x\", any segment number, will return the value of that particular URL segment if it's present.\n\nExample URL: `/put/that/cookie/down`\n\n::tabs\n\n::tab antlers\n```antlers\n{{ segment_3 }}\n```\n::tab blade\n```blade\n{{ $segment_3 }}\n```\n::\n\n```html\ncookie\n```\n"
  },
  {
    "path": "content/collections/variables/site.md",
    "content": "---\nid: e6fdbeb6-7808-45b0-b012-89a34993778b\nblueprint: variables\ntypes:\n  - system\ntitle: Site\ndescription: The current site being targeted in the request, available as a single tag or tag pair.\n---\nThe current site being targeted in the request.\n\nIt's a `Statamic\\Sites\\Site` object and can be used as a single tag or tag pair.\n\n``` php\n// config/statamic/sites.php\n\n'sites' => [\n    'default' => [\n        'name' => 'My Statamic Site',\n        'locale' => 'en_US',\n        'url' => '/',\n        'direction' => 'ltr',\n        'attributes' => [\n            'foo' => 'bar',\n        ],\n    ]\n]\n```\n\nAs a single tag, it will output the handle of the site:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ site }}\n```\n::tab blade\n```blade\n{{ $site }}\n```\n::\n\n```html\ndefault\n```\n\nAs a tag pair, you can access additional information:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ site }}\n    {{ handle }}\n    {{ name }}\n    {{ locale }}\n    {{ short_locale }}\n    {{ url }}\n    {{ permalink }}\n    {{ direction }}\n    {{ attributes }}\n        {{ foo }}\n    {{ /attributes }}\n{{ /site }}\n```\n::tab blade\n```blade\n{{ $site->handle }}\n{{ $site->name }}\n{{ $site->locale }}\n{{ $site->short_locale }}\n{{ $site->url }}\n{{ $site->permalink }}\n{{ $site->direction }}\n\n{{ $site->attributes['foo'] }}\n```\n::\n\n```html\ndefault\nMy Statamic Site\nen_US\nen\n/\nhttp://mysite.com/\nltr\nbar\n```\n\nYou can also access those variables directly as single tags:\n\n::tabs\n\n::tab antlers\n```antlers\n{{ site:name }}\n{{ site:attributes:foo }}\n```\n::tab blade\n```blade\n{{ $site->name }}\n{{ $site->attributes['foo'] }}\n```\n::\n\n```html\nMy Statamic Site\nbar\n```\n\n## The `attribute()` Helper\n\nWhen using Blade, accessing an attribute that doesn't exist via `$site->attributes['key']` will throw an \"Undefined array key\" exception. The `attribute()` method handles missing keys gracefully, supports dot notation for nested values, and lets you provide a fallback.\n\n```blade\n{{ $site->attribute('company_name') }}\n\n{{-- Dot notation for nested attributes --}}\n{{ $site->attribute('social.twitter') }}\n\n{{-- Fallback value when the attribute is missing --}}\n{{ $site->attribute('theme', 'standard') }}\n```\n:::tip\nIn Antlers, missing keys are already null-safe, so `{{site:attributes:company_name}}` would work without the helper.\n:::\n"
  },
  {
    "path": "content/collections/variables/sites.md",
    "content": "---\nid: 8e40a5f8-8825-45ed-b0b5-6f7cd3ce946c\nblueprint: variables\ntypes:\n  - system\ntitle: Sites\ndescription: A collection containing all configured sites as Site objects that can be looped over.\n---\nA collection containing all the configured sites as `Statamic\\Sites\\Site` objects which you can loop over using a tag pair.\n\n```yaml\n# resources/sites.yaml\nenglish:\n  name: English Site\n  url: 'http://mysite.com/'\n  locale: en_US\n  direction: ltr\n  attributes:\n    foo: bar\nfrench:\n  name: French Site\n  url: 'http://mysite.com.fr/'\n  locale: en_FR\n  direction: ltr\n  attributes:\n    foo: baz\n```\n\n::tabs\n\n::tab antlers\n```antlers\n{{ sites }}\n    {{ handle }}\n    {{ name }}\n    {{ locale }}\n    {{ short_locale }}\n    {{ url }}\n    {{ direction }}\n    {{ attributes }}\n        {{ foo }}\n    {{ /attributes }}\n\n{{ /sites }}\n```\n::tab blade\n```blade\n@foreach ($sites as $site)\n  {{ $site->handle }}\n  {{ $site->name }}\n  {{ $site->locale }}\n  {{ $site->short_locale }}\n  {{ $site->url }}\n  {{ $site->permalink }}\n  {{ $site->direction }}\n\n  {{ $site->attributes['foo'] }}\n@endforeach\n```\n::\n\n```html\nenglish\nEnglish Site\nen_US\nen\nhttp://mysite.com/\nltr\nbar\n\nfrench\nFrench Site\nfr_FR\nfr\nhttp://mysite.com.fr/\nltr\nbaz\n\n```\n"
  },
  {
    "path": "content/collections/variables/size.md",
    "content": "---\nid: 514642f6-5e7b-4ffc-abad-7a2637615a45\nblueprint: variables\ntypes:\n  - asset\ntitle: Size\ndescription: The file size of the asset in a human-readable format.\n---\nThe file size of the asset, in an appropriate human-readable format.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ assets:files }}\n    {{ size }}\n{{ /assets:files }}\n```\n::tab blade\n```blade\n<s:assets:files>\n  {{ $size }}\n</s:assets:files>\n```\n::\n\n```html\n11 B\n127.69 KB\n1.5 MB\n2 GB\n```\n"
  },
  {
    "path": "content/collections/variables/size_bytes.md",
    "content": "---\nid: d92f6a3d-2e78-4f30-a7ee-3a69bd652eaf\nblueprint: variables\ntypes:\n  - asset\ntitle: 'Size Bytes'\ndescription: The file size of the asset in bytes. Also available as `size_b`.\n---\nThe file size of the asset, in bytes. Also available as `size_b`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ size_bytes }}\n```\n::tab blade\n```blade\n{{ $size_bytes }}\n```\n::\n\n```html\n130754\n```\n"
  },
  {
    "path": "content/collections/variables/size_gigabytes.md",
    "content": "---\nid: 5e1a2049-840d-41b0-9f22-90d368bc7a8d\nblueprint: variables\ntypes:\n  - asset\ntitle: 'Size Gigabytes'\ndescription: The file size of the asset in gigabytes. Also available as `size_gb`.\n---\nThe file size of the asset, in gigabytes. Also available as `size_gb`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ size_gigabytes }}\n```\n::tab blade\n```blade\n{{ $size_gigabytes }}\n```\n::\n\n```html\n0.00\n```\n"
  },
  {
    "path": "content/collections/variables/size_kilobytes.md",
    "content": "---\nid: 15c87908-19aa-4cfe-9da9-cb2f74841fd8\nblueprint: variables\ntypes:\n  - asset\ntitle: 'Size Kilobytes'\ndescription: The file size of the asset in kilobytes. Also available as `size_kb`.\n---\nThe file size of the asset, in kilobytes. Also available as `size_kb`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ size_kilobytes }}\n```\n::tab blade\n```blade\n{{ $size_kilobytes }}\n```\n::\n\n```html\n127.69\n```\n"
  },
  {
    "path": "content/collections/variables/size_megabytes.md",
    "content": "---\nid: 4acb45ef-dea2-483b-97c3-31faeb27b232\nblueprint: variables\ntypes:\n  - asset\ntitle: 'Size Megabytes'\ndescription: The file size of the asset in megabytes. Also available as `size_mb`.\n---\nThe file size of the asset, in megabytes. Also available as `size_mb`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ size_megabytes }}\n```\n::tab blade\n```blade\n{{ $size_megabytes }}\n```\n::\n\n```html\n0.12\n```\n"
  },
  {
    "path": "content/collections/variables/slug.md",
    "content": "---\nid: b391facf-5041-498b-a244-0df2359ec30e\nblueprint: variables\ntypes:\n  - content\ntitle: Slug\ndescription: The string that identifies your content, usually appearing at the end of the URL.\n---\nA slug is the string that identifies your content. It usually sits at the end of the URL.\n\n::tabs\n\n::tab antlers\n```antlers\n<h1>{{ title }}</h1>\n{{ slug }}\n```\n::tab blade\n```blade\n<h1>{{ $title }}</h1>\n{{ $slug }}\n```\n::\n\n```html\n<h1>My Thoughts on Bacon</h1>\nmy-thoughts-on-bacon\n```\n"
  },
  {
    "path": "content/collections/variables/taxonomy.md",
    "content": "---\nid: 43796530-e4af-448f-be6e-2bae1670b2e4\nblueprint: variables\ntypes:\n  - term\ntitle: Taxonomy\ndescription: Get the name of the taxonomy the term belongs to.\n---\nGet the name of the taxonomy the term belongs to.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ taxonomy }}\n```\n::tab blade\n```blade\n{{ $taxonomy }}\n```\n::\n\n```html\ncategories\n```\n"
  },
  {
    "path": "content/collections/variables/timestamp.md",
    "content": "---\nid: aaf843db-96eb-4cfb-ba9c-45a86fcd2d34\nblueprint: variables\ntypes:\n  - entry\ntitle: Timestamp\ndescription: Get the timestamp of the entry as an integer. Alias of `datestamp`.\n---\nGet the timestamp of the entry. This will be an integer.\n\nAlias of `datestamp`.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ timestamp }}\n```\n::tab blade\n```blade\n{{ $timestamp }}\n```\n::\n\n```html\n1425772800\n```\n"
  },
  {
    "path": "content/collections/variables/url.md",
    "content": "---\nid: d511b772-bdbe-4d20-9ed9-3c87d72fb946\nblueprint: variables\ntypes:\n  - content\ntitle: Url\ndescription: Get the relative URL to the content (does not include the site URL).\n---\nGet the URL to the content. This is relative and will _not_ include your site URL.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ url }}\n```\n::tab blade\n```blade\n{{ $url }}\n```\n::\n\n```html\n/posts/bacon\n```\n"
  },
  {
    "path": "content/collections/variables/width.md",
    "content": "---\nid: 16d27ea8-f331-4574-8454-6aa34b5f4eaa\nblueprint: variables\ntypes:\n  - asset\ntitle: Width\ndescription: The width of an image asset, in pixels.\n---\nThe width of an image asset, in pixels.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ width }}\n```\n::tab blade\n```blade\n{{ $width }}\n```\n::\n\n```html\n900\n```\n"
  },
  {
    "path": "content/collections/variables/xml_header.md",
    "content": "---\nid: 81617b1f-000e-4d18-b5ea-592b9d845ccb\nblueprint: variables\ntypes:\n  - system\ntitle: 'Xml Header'\ndescription: Outputs an XML header tag, useful when Statamic's template parser encodes PHP tags.\n---\nSimply outputs an XML Header tag.\n\nStatamic's template parser will encode `<?` and `?>` because they are PHP tags. Silly XML. This global will get you around that.\n\n::tabs\n\n::tab antlers\n```antlers\n{{ xml_header }}\n```\n::tab blade\n```blade\n{{ $xml_header }}\n```\n::\n\n```html\n<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n```\n"
  },
  {
    "path": "content/collections/variables.yaml",
    "content": "title: Variables\nicon: fieldtype-code\ntemplate: page\nlayout: layout\nmount: 0574b585-8ed7-4a51-acc4-6f234f8c42e8\ntaxonomies:\n  - types\nrevisions: false\nroute: '/variables/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\ninject:\n  view_model: App\\ViewModels\\Variables\n"
  },
  {
    "path": "content/collections/widgets/collection.md",
    "content": "---\nid: 65652195-52db-4622-a08a-448785a829db\nblueprint: widgets\ntitle: 'Collection Widget'\nintro: 'Display a list of entries from a collection.'\noptions:\n  -\n    name: collection\n    type: string\n    required: true\n    description: 'The collection''s handle.'\n  -\n    name: width\n    type: int\n    required: false\n    description: 'Width of dashboard area as a percentage. Accepts `25`, `33`, `50`, `66`, `75` and `100`.'\n  -\n    name: sites\n    type: array\n    required: false\n    description: 'Determines the sites in which this widget should be displayed.'\n  -\n    name: limit\n    type: int\n    required: false\n    description: 'Limit number of entries. **Default: 5**.'\n  -\n    name: sort\n    type: string\n    required: false\n    description: 'Sort and order by field name. E.g. `''title:desc''`. Defaults to the collection''s settings.'\n  -\n    name: fields\n    type: array\n    required: false\n    description: 'An array of field handles to be displayed as columns in the widget.'\n  -\n    name: title\n    type: string\n    required: false\n    description: 'The title of the widget. Defaults to the collection name.'\n  -\n    name: show_table_header\n    type: boolean\n    required: false\n    description: 'Show table column headers. Off by default.'\nscreenshot: widgets/collection-v6.webp\nscreenshot_dark: widgets/collection-v6-dark.webp\nnav_title: Collection\n---\n## Configuring\n\nWidgets can be added to the dashboard by modifying the `widgets` array in the `config/statamic/cp.php` file.\n\n``` php\n// config/statamic/cp.php\n\n'widgets' => [\n  [ // [tl! focus:start]\n      'type' => 'collection',\n      'collection' => 'blog',\n      'limit' => 10,\n  ], // [tl! focus:end]\n],\n"
  },
  {
    "path": "content/collections/widgets/form.md",
    "content": "---\nid: 07520b92-0fe2-44ef-ad1b-d9764b99dba7\nblueprint: widgets\ntitle: 'Form Widget'\nintro: 'Display a list of the most recent form submissions.'\nnav_title: Form\noptions:\n  -\n    name: form\n    type: string\n    required: true\n    description: 'The form''s handle.'\n  -\n    name: width\n    type: int\n    required: false\n    description: 'Width of dashboard area as a percentage. Accepts `25`, `33`, `50`, `66`, `75` and `100`.'\n  -\n    name: sites\n    type: array\n    required: false\n    description: 'Determines the sites in which this widget should be displayed.'\n  -\n    name: limit\n    type: int\n    required: false\n    description: 'Limit number of submissions. **Default: 5**.'\n  -\n    name: fields\n    type: array\n    required: false\n    description: 'An array of field handles to include in the list.'\n  -\n    name: title\n    type: string\n    required: false\n    description: 'The title of the widget. Defaults to the form name.'\n  -\n    name: show_table_header\n    type: boolean\n    required: false\n    description: 'Show table column headers. Off by default.'\nscreenshot: widgets/form-v6.webp\nscreenshot_dark: widgets/form-v6-dark.webp\n---\n## Configuring\n\nWidgets can be added to the dashboard by modifying the `widgets` array in the `config/statamic/cp.php` file.\n\n``` php\n// config/statamic/cp.php\n\n'widgets' => [\n  [ // [tl! focus:start]\n      'type' => 'form',\n      'form' => 'contact',\n      'fields' => ['name', 'email'],\n      'limit' => 10,\n  ], // [tl! focus:end]\n],\n"
  },
  {
    "path": "content/collections/widgets/updater.md",
    "content": "---\nid: 62cb634b-06dd-4751-8d4f-3dd141d953e7\nblueprint: widgets\ntitle: 'Updater Widget'\nintro: 'Shows if there are any Statamic core or addon updates available.'\nnav_title: Updater\nscreenshot: widgets/updater-v6.webp\nscreenshot_dark: widgets/updater-v6-dark.webp\noptions:\n  -\n    name: width\n    type: int\n    required: false\n    description: 'Width of dashboard area as a percentage. Accepts `25`, `33`, `50`, `66`, `75` and `100`.'\n  -\n    name: sites\n    type: array\n    required: false\n    description: 'Determines the sites in which this widget should be displayed.'\n---\n## Configuring\n\nWidgets can be added to the dashboard by modifying the `widgets` array in the `config/statamic/cp.php` file.\n\n``` php\n// config/statamic/cp.php\n\n'widgets' => [\n  [ // [tl! focus:start]\n      'type' => 'updater',\n      'width' => 100,\n      'sites' => ['en', 'de', 'fr'],\n  ], // [tl! focus:end]\n],\n"
  },
  {
    "path": "content/collections/widgets.yaml",
    "content": "title: Widgets\nicon: dashboard-layout-3\ntemplate: page\nlayout: layout\nmount: 95da21f5-6011-4eae-973e-dfccc23ddfc3\nrevisions: false\nroute: '/widgets/{slug}'\nsort_dir: asc\ndate_behavior:\n  past: null\n  future: null\npreview_targets:\n  -\n    label: Entry\n    url: '{permalink}'\n    refresh: true\n"
  },
  {
    "path": "content/globals/.gitkeep",
    "content": ""
  },
  {
    "path": "content/globals/default/global.yaml",
    "content": "pro_badges:\n  -\n    id: m8q1g335\n    artwork: pro-badges/artwork/pro-doc-brown.jpg\n    icon: pro-badges/icons/pro-sheriff-badge.png\n    type: pro_badge\n    enabled: true\n  -\n    id: m8q1napf\n    artwork: pro-badges/artwork/pro-marty-mcfly.jpg\n    icon: pro-badges/icons/pro-sheriff-badge.png\n    type: pro_badge\n    enabled: true\nexperimental_badges:\n  - doc\n  - hackerman\n  - rick\npowered_by:\n  - 'the sweat of Rambo'\n  - \"Michael McDonald's beard\"\n  - \"MacGyver's paperclip collection\"\n  - \"Mr. T's mohawk\"\n  - \"Hulk Hogan's triceps\"\n  - \"E.T.'s glowing finger\"\n  - 'the memory of John Candy'\n  - 'the Jamaican bobsled team'\n  - \"Rick Moranis's glasses\"\n  - 'the power of Greyskull'\n  - \"David Hasselhoff's smile\"\n  - \"Weird Al's purple accordion\"\n  - \"Michael Jackson's glove\"\n  - \"Alf's back hair\"\n  - \"Zack Morris's cell phone battery\"\n  - \"Mr. Roger's red cardigan\"\n  - \"Uncle Jesse's hairspray\"\n  - \"the eye of Rocky's tiger\"\n  - \"Blockbuster's 2-for-1 nights\"\n  - \"Kevin Mccallister's Talkboy\"\n  - \"Michelle Tanner's catch phrases\"\n  - \"John McClane's undershirt\"\n  - \"John Hammond's cane\"\n  - 'a Festivus pole'\n  - \"Pheobe's smelly cat\"\n  - 'the Binford 6100'\n  - \"DJ Jazzy Jeff's turntable\"\n  - 'Hi-C Ecto Cooler'\n  - 'the Kool-Aid man'\n  - \"Harry's gold tooth\"\n  - \"Double Dare's physical challenge\"\n  - \"Daniel Larusso's headband\"\n  - 'the presidential fitness test'\n  - \"Marty McFly's orange vest\"\n  - \"Ferris Bueller's Ferrari\"\n  - \"Forrest Gump's box of chocolates\"\n  - \"a Speak N' Spell with fresh batteries\"\n  - 'the Kobayashi porcelain mug'\n  - 'an amp that goes to 11'\n  - 'the Men in Black flashy thing'\n  - 'a new in the box EZ-Bake Oven'\n  - 'an unbroken Super Soaker 200'\n  - 'an old pair of Moon Shoes'\n  - 'a pair of never-worn Reebok pumps'\n  - \"Tommy Pickle's screwdriver\"\n  - 'a bottle of Flintstone vitamins'\n  - 'Fruit by the Foot'\n  - 'two cases of Surge'\n  - 'a fully fed, adult Tamagotchi'\n  - \"a pair of Will Smith's high-tops\"\n  - 'a tube of Pogs'\n  - 'a binder full of Garbage Pail Kids'\n  - 'Peanut, the royal-blue Beanie Baby'\n  - 'your secret Lisa Frank Trapper Keeper'\n  - \"Teddy Ruxpin's voice box\"\n  - 'a bin of mangled G.I. Joes'\n  - 'an ewok snuggling baby Yoda'\n  - 'a mint condition Street Fighter II arcade cabinet'\n  - 'two Nintendo Power Gloves'\n  - 'a rechargable Gameboy magnifier light'\n  - \"a rogue, sentient Speak 'N Spell\"\n  - 'a Michael Jordan rookie card'\n  - 'an unwashed Chicago Bulls jersey'\n  - \"Ross & Rachael's break\"\n  - \"Gunther's extra wide neck tie\"\n  - \"Jerry's puffy shirt\"\n  - \"Al Borland's flannel collection\"\n  - \"Buffy's Dagger of Lex\"\n  - \"Xena's Sword\"\n  - \"Fraiser's Rothko painting\"\n  - \"Chandler's oversized dress shirts\"\n"
  },
  {
    "path": "content/globals/global.yaml",
    "content": "title: Globals\r"
  },
  {
    "path": "content/navigation/.gitkeep",
    "content": ""
  },
  {
    "path": "content/structures/.gitkeep",
    "content": ""
  },
  {
    "path": "content/taxonomies/.gitkeep",
    "content": ""
  },
  {
    "path": "content/taxonomies/categories/cli.yaml",
    "content": "title: CLI\nicon: knowledge-base/cli.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622823769\n"
  },
  {
    "path": "content/taxonomies/categories/database.yaml",
    "content": "title: Database\nicon: knowledge-base/database.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622823356\n"
  },
  {
    "path": "content/taxonomies/categories/development.yaml",
    "content": "title: Development\nicon: knowledge-base/development.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622823878\n"
  },
  {
    "path": "content/taxonomies/categories/devops.yaml",
    "content": "title: DevOps\nicon: knowledge-base/devops.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622823952\n"
  },
  {
    "path": "content/taxonomies/categories/laravel.yaml",
    "content": "title: Laravel\nicon: knowledge-base/laravel.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622824034\n"
  },
  {
    "path": "content/taxonomies/categories/localization.yaml",
    "content": "title: Localization\nicon: knowledge-base/localization.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622824237\n"
  },
  {
    "path": "content/taxonomies/categories/performance.yaml",
    "content": "title: Performance\nicon: knowledge-base/performance.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622824441\n"
  },
  {
    "path": "content/taxonomies/categories/privacy-gdpr.yaml",
    "content": "title: 'Privacy & GDPR'\nicon: knowledge-base/privacy.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622823351\n"
  },
  {
    "path": "content/taxonomies/categories/troubleshooting.yaml",
    "content": "title: Troubleshooting\nicon: knowledge-base/troubleshooting.svg\nupdated_by: 3a60f79d-8381-4def-a970-5df62f0f5d56\nupdated_at: 1622824504\n"
  },
  {
    "path": "content/taxonomies/categories.yaml",
    "content": "title: Categories\n"
  },
  {
    "path": "content/taxonomies/modifier_types.yaml",
    "content": "title: 'Modifier Types'\n"
  },
  {
    "path": "content/taxonomies/tags/beginner.yaml",
    "content": "title: Beginner"
  },
  {
    "path": "content/taxonomies/tags.yaml",
    "content": "title: Tags\nterm_blueprint: page\n"
  },
  {
    "path": "content/taxonomies/types/asset.yaml",
    "content": "---\ntitle: Asset\norder: 5\n---\nAssets have access to all the Content variables, and these:\n"
  },
  {
    "path": "content/taxonomies/types/content.yaml",
    "content": "---\ntitle: Content\norder: 2\n---\n\"Content\" encompasses data types that can be mapped to a URL. These include Entries and Taxonomy Terms. Any time you encounter one of these, the following variables will be available, as well as any fields you've defined in the [blueprint](/blueprints).\n"
  },
  {
    "path": "content/taxonomies/types/entry.yaml",
    "content": "---\ntitle: Entry\norder: 3\n---\nEntries have access to all the Content variables, and these:\n"
  },
  {
    "path": "content/taxonomies/types/system.yaml",
    "content": "---\ntitle: System\norder: 5\n---\nSystem variables are available at any time, regardless of whether the URL you are on is for content (eg. an Entry or Term) or if it has been created using a route.\n"
  },
  {
    "path": "content/taxonomies/types/term.yaml",
    "content": "---\ntitle: Term\norder: 4\n---\nTaxonomy terms have access to all the Content variables, and these:\n"
  },
  {
    "path": "content/taxonomies/types.yaml",
    "content": "title: 'Variable Types'\n"
  },
  {
    "path": "content/trees/collections/pages.yaml",
    "content": "tree:\n  -\n    entry: 5ee53e1b-8933-4b2e-a72d-af09f2b32600\n  -\n    entry: 9157e598-81f9-4669-b25a-d9356d8b1c78\n    children:\n      -\n        entry: 792644d2-8bd2-421d-a080-e0be7fca125c\n      -\n        entry: 56fadb93-b846-4867-ad73-4f721cc940c2\n      -\n        entry: 24a9c9d8-d607-4117-9806-738668c173cd\n      -\n        entry: 1d1920fb-604c-4ac1-8c99-f0de44abc06b\n      -\n        entry: ab08f409-8bbe-4ede-b421-d05777d292f7\n        children:\n          -\n            entry: 61c8db2d-f7bf-4829-bafd-f8a4db5a9a57\n          -\n            entry: 2093f557-8d4a-4baf-bf5c-cbbf584acd3b\n          -\n            entry: e48bde09-8957-401a-a2b4-ba7a4fd26d67\n          -\n            entry: c0009fa6-0f8f-4b45-8d65-0cb784d07031\n          -\n            entry: 18906b4f-be9a-4edb-9bb3-366226863fa2\n          -\n            entry: f8bac6fc-401c-4f0e-b338-386e332c91b8\n          -\n            entry: d4a54957-9863-471a-a188-d06b0e0cd48d\n          -\n            entry: 48c60d99-04e7-47f6-9576-aee1401fcb50\n      -\n        entry: 10d236ff-a80b-4d88-afa8-fe882b0f37a2\n      -\n        entry: f12f8ba3-19ff-48cb-a07b-653b05082d7e\n        children:\n          -\n            entry: 031d1f39-1026-47c7-8d85-18642b33f017\n          -\n            entry: 769f1c97-3fb4-4303-a60b-4096c06b7870\n          -\n            entry: db244b81-13ad-42f1-b593-533c6e165ee6\n          -\n            entry: 4947cda2-d17d-4289-ab37-1f4f64bfa1d4\n          -\n            entry: a1d1a50e-fb42-4a9e-b03f-59dc47f21dc2\n          -\n            entry: e077f513-45c1-4eff-ba87-210340dd6f54\n          -\n            entry: 91e8f239-2f99-47bc-b4dd-3518cd3e36ae\n          -\n            entry: 9a013ab0-bd21-42e1-84ea-fecd052466e9\n          -\n            entry: c3553d05-d1a8-453a-a59b-7e67dd2412a4\n          -\n            entry: ec130472-4f44-4e7e-8dce-71d0c93e8fef\n          -\n            entry: dbad308e-fdae-42ed-9168-75521bc5d5fe\n      -\n        entry: e6f05019-6bdd-488e-ba45-39ae7ea5cee7\n      -\n        entry: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n        children:\n          -\n            entry: 8fd95af9-f635-45bb-a3d1-1fa1db7be4a2\n          -\n            entry: 68d936b0-b1b0-431d-bbe0-a8356decf251\n          -\n            entry: cf38dba4-5cce-4b81-a2f5-e82665e4e11f\n          -\n            entry: 79d022e5-8fb0-4d20-955d-801e0edafa61\n          -\n            entry: 7f809a5e-e555-4ccc-8488-d7310ff8c89c\n          -\n            entry: 01ab4b2b-bee2-4697-b3c6-cb129d783589\n          -\n            entry: 94c521e3-bacb-45e3-b385-00bad3cac401\n  -\n    entry: 884507b0-77ac-469e-a99f-f646065d5954\n    children:\n      -\n        entry: 20f0707b-619d-4a7a-b7dd-aea4122fa1db\n      -\n        entry: 7202c698-942a-4dc0-b006-b982784efb03\n      -\n        entry: 6a18eac8-6139-419c-9d64-a2c960ccc3cd\n      -\n        entry: 1e91dd54-c452-4e3b-8972-dba83c048d3d\n      -\n        entry: 2af9fc45-66d0-4ca5-9761-00017076144f\n      -\n        entry: 54548616-fd6d-44a3-a379-bdf71c492c63\n      -\n        entry: 0327afd5-469b-4119-a75e-2bfe9389eb05\n      -\n        entry: 3d5efc5c-17b1-480b-bb77-53faf3d9552c\n      -\n        entry: 2940c834-7062-47a1-957c-88a69e790cbb\n      -\n        entry: cb21fabb-65ba-4869-9acd-f6aa2fb58a01\n      -\n        entry: 8ed04215-9f46-4000-bd67-c71b21b67d85\n      -\n        entry: 8d9cfb16-36bf-45d0-babb-e501a35ddae6\n      -\n        entry: 6177b316-0eed-4dec-83d1-e5a48a8e00b6\n      -\n        entry: 3c34ef5c-781e-4a22-a09b-25f58bdb58a8\n      -\n        entry: 268d444c-88e3-4b52-bfc6-165749ef3ec1\n  -\n    entry: a9acf949-e23d-42ab-8bc2-21032c98df9a\n    children:\n      -\n        entry: e0e93aba-4abc-4433-9257-3321a4521d60\n      -\n        entry: 3482755d-3d20-42d5-8680-301a1cb95965\n      -\n        entry: dd52c1f6-661b-4408-83c6-691fa341aaa7\n      -\n        entry: 79129d32-3f7c-4215-b6b1-21a2fccafa8d\n      -\n        entry: a92ce050-2c17-4b4d-8a69-c099759c1502\n      -\n        entry: 2ce74b48-d3cc-4b8a-a8d4-f514c0b1d6ff\n      -\n        entry: 249e046f-a9b4-494b-9e4d-084c28e01028\n      -\n        entry: 5eab02e3-c76b-4f44-a304-6a78877d099f\n      -\n        entry: 785ffa10-8b63-44b1-9da3-3837250cacbe\n      -\n        entry: 41e386b3-9df9-4d28-9338-8005399f953c\n      -\n        entry: c095fb87-4c02-462c-9e6f-dfe0b6889248\n      -\n        entry: efcca509-5690-4201-88d7-74c542bb9900\n      -\n        entry: cdffd2c9-cf42-495d-a8f1-f416ddfddc29\n      -\n        entry: fb20f2e0-3881-43e6-8507-3308a18c54b0\n      -\n        entry: a3adf32a-37a5-4e96-beee-f107dc1b81a9\n      -\n        entry: ff397ebf-4b53-4dbd-b81b-0dec839e0e5f\n      -\n        entry: 452c268b-b885-4deb-8e46-1cc3ebc66e4f\n      -\n        entry: b4b46ceb-9feb-4587-8f0d-2080511bf9e3\n      -\n        entry: 421a9f22-bc1c-45e9-81ca-2ba8ad2a5744\n      -\n        entry: 52af4663-ebbd-467c-8659-9c7bb94cb7cc\n      -\n        entry: 6b691e04-8f28-4eb2-8288-b61433883fe4\n      -\n        entry: aa6e0a79-9d3f-493b-92c9-df4d2257bc64\n      -\n        entry: 5bd9426f-23cf-4196-9848-471dff67f5ea\n  -\n    entry: 7e0dd8f1-9988-4173-8453-3ccee12ff976\n    children:\n      -\n        entry: 84100772-18e4-4a22-8759-219b242a320c\n      -\n        entry: d37b2af2-f2bf-493a-9345-7087fb5929ce\n      -\n        entry: c7816387-ebc4-4204-b5f2-8e7073a4db8b\n      -\n        entry: 74c47654-8c47-49b1-a616-ed940ce19977\n      -\n        entry: 9b2f6f55-5355-4334-b90d-d1236fb58887\n      -\n        entry: 7277432d-bb25-458a-a3a2-a72976b44ad5\n      -\n        entry: e8504150-db55-4986-b567-19c046cc03de\n      -\n        entry: e7833062-e05c-42c9-ad35-dc5077f1f0b8\n      -\n        entry: 5e848460-9bbc-449e-8edd-182d918163ff\n      -\n        entry: be292d2b-dc0e-48dc-bce4-0058df27ccc6\n      -\n        entry: fdb45b84-3568-437d-84f7-e3c93b6da3e6\n      -\n        entry: fc564ddf-80c1-4d87-8675-4a41f13c7774\n      -\n        entry: 245068a1-1900-4774-a3ba-29192dc9acff\n      -\n        entry: 131259a5-2072-49d8-9ea4-2099e0338e2f\n      -\n        entry: 75be125b-7d92-496c-ac5d-7098560d3d44\n      -\n        entry: 2e0d2f8f-319d-4cce-bd90-16d6ad32ad37\n      -\n        entry: 420f083d-99be-4d54-9f81-3c09cb1f97b7\n      -\n        entry: fbf59081-ba24-4e82-b011-b687be228c89\n  -\n    entry: a8166db6-860a-44d7-8bb0-8e6baaab995f\n    children:\n      -\n        entry: 83145e6c-45d2-4e9c-a412-48a81f144224\n      -\n        entry: bde2385f-5fee-4cb1-a516-5fe2e2d17e0c\n      -\n        entry: 7fb5f2df-de3e-480f-a613-f38a9109e8d8\n      -\n        entry: fd34ca35-57d1-4d28-97f9-ba6801656b39\n      -\n        entry: 3dbb14fd-a762-4891-bce1-daf13b8c5981\n      -\n        entry: bd1261de-7c4c-4c22-baf5-8a52cdd10c74\n      -\n        entry: 124cb9e8-b5e0-4af6-8271-98ca6b0131b4\n      -\n        entry: 499d808b-18be-42e9-acd0-91bcdff73193\n      -\n        entry: ffa24da8-3fee-4fc9-a81b-fcae8917bd74\n      -\n        entry: 0df63f01-4b97-4c15-89a9-015c02ea3748\n      -\n        entry: 93cf3f23-24c4-4722-a6e2-5b369e952a3b\n  -\n    entry: 79cc18f6-d8c2-4249-97f7-d03febce051a\n    children:\n      -\n        entry: 9a1d8b88-c600-46f2-8727-1deb56f2e87a\n      -\n        entry: 25f01cb5-1ca9-44a1-af1b-885b32b05cc8\n      -\n        entry: 83786f60-def6-11e9-aaef-0800200c9a66\n      -\n        entry: d0668b6e-915b-46da-863e-51fec54b02e2\n      -\n        entry: 06813e5d-158e-4318-aa4a-b29fd87d107f\n  -\n    entry: 75ce3bdc-03d0-4d6a-a2a5-d867d868d763\n    children:\n      -\n        entry: 4e3ca511-fe21-497f-9166-3c7624607a91\n      -\n        entry: 424f5092-be39-449a-b660-f4e077631487\n      -\n        entry: 9751908a-a10c-4c36-abd3-2251e83fbc65\n      -\n        entry: 098cb1c5-94c2-4bc0-add7-9aad39951d67\n  -\n    entry: 662a5918-ac0f-42a1-bf40-5a7a320e87e1\n    children:\n      -\n        entry: 0b4733ea-4ca9-4bb9-9df1-f1ab95553dfe\n      -\n        entry: 0574b585-8ed7-4a51-acc4-6f234f8c42e8\n  -\n    entry: 998ad905-6988-457f-b26a-78bc64de6d3f\n    children:\n      -\n        entry: 9c1efbc5-c6a4-46f1-acce-d38b20122bd6\n      -\n        entry: ccb11ef2-eef3-4c56-9052-e55905cffd1a\n      -\n        entry: e052ecb8-60d9-4afa-980e-ce128c301d70\n  -\n    entry: c2ab2945-4dce-4875-b5fe-48a006dd8193\n    children:\n      -\n        entry: 4b77c19b-129c-4271-a724-eea884eb3e2e\n      -\n        entry: 95da21f5-6011-4eae-973e-dfccc23ddfc3\n      -\n        entry: 5900c99f-89b9-4ee3-834c-cb1b070146e4\n  -\n    entry: a7cd76f6-cef0-43d3-b561-498777673308\n    children:\n      -\n        entry: bc2ad29d-50c3-47fb-9213-8553bcb5a48a\n      -\n        entry: 5bd75435-806e-458b-872e-7528f24df7e6\n      -\n        entry: 15db07e8-6e83-4b6e-89bb-d050b5d2c823\n      -\n        entry: 5f26a634-19ae-4413-8b9e-1ed9c2c76bb0\n  -\n    entry: 3a2e714c-57de-4b16-a916-7e7aba22de03\n    children:\n      -\n        entry: 621873af-a669-42be-8bc3-5546a8c597e6\n      -\n        entry: 9c703f43-30de-4f65-98bb-2b89f80012b7\n      -\n        entry: c51a5de8-4b02-4240-8195-3ff7987c43cf\n      -\n        entry: 512cbb65-0091-47e2-a3b6-c42aae64349e\n  -\n    entry: 9ca16d8c-a5cf-4fdb-8252-898626e2390b\n    children:\n      -\n        entry: 50dfaf8c-8ca4-4b58-ac84-e4c50099e42e\n      -\n        entry: 0267eb5a-f54b-4e3f-bef0-1f16762720b1\n      -\n        entry: ba2e6172-b4dc-443b-8230-b770dec1423c\n      -\n        entry: 73010f4f-bb62-4feb-a6ff-88031f488db8\n      -\n        entry: 6843f6e9-ac62-4128-8d50-887560f201ca\n      -\n        entry: 900414a8-f73a-46f0-82bd-b607767f5d5d\n      -\n        entry: 290e9a74-7c6b-4fd0-a90a-23f7ac38d0c5\n      -\n        entry: c3da9537-5d5f-4b84-a4be-882b89217151\n  -\n    entry: 63e7f70f-3371-4cba-b3fb-9d5a1b519c8b\n    children:\n      -\n        entry: b80820bb-c2e8-475f-98bd-8ea0ef9f5339\n      -\n        entry: f38a3e52-10ba-4bfa-9298-0c95b324c662\n      -\n        entry: bbf752d3-ffdb-4ac9-a5c5-0b32e3db9d6f\n      -\n        entry: b7519137-73b6-46c7-8432-da7725b1d9b4\n      -\n        entry: 4bd202d5-6ef2-4fb3-9e90-aed0d0183071\n      -\n        entry: fc136da3-ba46-46e1-8443-e345d5b548ac\n      -\n        entry: 28068f9a-f269-4646-87e4-881e5477558d\n      -\n        entry: d84613d5-2b2b-465d-8f02-c71b6d2aadf1\n  -\n    entry: 2d6381b8-dc0a-4a5d-b750-1a9dd7c0bb14\n    children:\n      -\n        entry: b6c40452-8da0-4f03-a53c-714cc3338b9d\n      -\n        entry: cd0428cf-2c4a-43b9-8e61-dd8123799ed8\n      -\n        entry: 0efc0286-bfb1-4d54-8a94-8589b35adf88\n      -\n        entry: 55a99a3b-e40d-4033-9a70-823de8e4255f\n      -\n        entry: d0e99506-d01d-484c-884f-46dfc4dcf4c5\n      -\n        entry: e947dc19-9a8a-44c2-911c-171d1f196c91\n      -\n        entry: c9ee871c-002b-48d7-b845-1abac58de337\n      -\n        entry: 550e7bf1-de6e-40ba-9b06-b32d9119e436\n"
  },
  {
    "path": "content/trees/navigation/docs.yaml",
    "content": "tree:\n  -\n    id: c872a03e-cae9-4c2e-81f9-3a2034a21adb\n    title: 'Getting Started'\n    url: /\n    data:\n      navigation_image: navigation/computer.png\n    children:\n      -\n        id: 7cc6dfce-6e71-4bfa-b2fd-609defcf02ad\n        title: 'Learn Statamic'\n        url: /\n      -\n        id: 0bbf1af8-f694-43bf-b8de-6d8c5977d3b7\n        entry: 792644d2-8bd2-421d-a080-e0be7fca125c\n      -\n        id: 67a031f2-f519-4179-b793-0c9d04f44662\n        entry: 1d1920fb-604c-4ac1-8c99-f0de44abc06b\n      -\n        id: c9a67a1a-f321-4925-8085-625c73b7b9e3\n        entry: f12f8ba3-19ff-48cb-a07b-653b05082d7e\n      -\n        id: a3f8cff7-6bf4-42ff-9731-42a2a3aecd55\n        entry: ab08f409-8bbe-4ede-b421-d05777d292f7\n        title: Installing\n      -\n        id: e8ad5c5e-c025-4d15-9ec3-6f8b15b836f5\n        entry: e6f05019-6bdd-488e-ba45-39ae7ea5cee7\n      -\n        id: efa84d9d-48a0-4e61-a745-7c014efeaff7\n        entry: c4f17d05-78bd-41bf-8e06-8dd52f6ec154\n  -\n    id: 561b2475-a98a-498d-8cbe-2de46e8f5d06\n    title: 'Core Concepts'\n    data:\n      navigation_image: navigation/triforce.png\n    children:\n      -\n        id: f03ca036-43d5-4bcb-ba03-260732fdf163\n        entry: 24a9c9d8-d607-4117-9806-738668c173cd\n        title: Overview\n      -\n        id: 5d8ab0c2-6d95-4877-a4ac-985b22ba408b\n        entry: 7277432d-bb25-458a-a3a2-a72976b44ad5\n      -\n        id: 96556b26-af4e-484d-9de7-87e3d5f28d87\n        entry: 10d236ff-a80b-4d88-afa8-fe882b0f37a2\n      -\n        id: 2d35188b-c048-474d-9634-991f1ee840a4\n        entry: 7202c698-942a-4dc0-b006-b982784efb03\n      -\n        id: 02334ddc-88df-4f22-93cd-ce1f26cc7271\n        entry: cb21fabb-65ba-4869-9acd-f6aa2fb58a01\n      -\n        id: 96f3568f-87ec-4c12-9a70-9b7542fefa67\n        entry: 1e91dd54-c452-4e3b-8972-dba83c048d3d\n      -\n        id: e00e0971-1916-462b-8876-d0eb7e29d196\n        entry: 420f083d-99be-4d54-9f81-3c09cb1f97b7\n      -\n        id: 368f9771-f1f0-4bdb-98fc-4a196620e999\n        entry: 2af9fc45-66d0-4ca5-9761-00017076144f\n      -\n        id: ae4b6bf9-31bb-4ec1-9976-cbf8f1d7d9a8\n        entry: 6a18eac8-6139-419c-9d64-a2c960ccc3cd\n      -\n        id: 140277d8-99fa-47eb-8e8f-c9f3cc9d65e3\n        entry: 6b691e04-8f28-4eb2-8288-b61433883fe4\n      -\n        id: 2b4da9ae-fbd2-4477-b59f-2daacd814df4\n        entry: 93cf3f23-24c4-4722-a6e2-5b369e952a3b\n  -\n    id: 3c6081eb-746f-44f0-ae90-fffec9fdef9f\n    title: 'Diving Deeper'\n    data:\n      navigation_image: navigation/sunglasses.png\n      image_width_override: 1.8em\n    children:\n      -\n        id: 50484ca7-40d5-4504-9f8f-adc8d6557a19\n        entry: 9b2f6f55-5355-4334-b90d-d1236fb58887\n      -\n        id: 15abba2b-c463-4a97-bb52-e824be5beddb\n        entry: 83145e6c-45d2-4e9c-a412-48a81f144224\n      -\n        id: 720767c5-43d4-4e8e-9a78-bd9fe45f421f\n        entry: bde2385f-5fee-4cb1-a516-5fe2e2d17e0c\n      -\n        id: 72af747c-32fc-4a13-ac48-27cdaffac194\n        entry: 0327afd5-469b-4119-a75e-2bfe9389eb05\n      -\n        id: 321e7c15-e389-48c7-83bc-ce30eac71c21\n        entry: 3d5efc5c-17b1-480b-bb77-53faf3d9552c\n      -\n        id: b8a3f190-bc8b-4b6e-a9b3-7e519b65a465\n        entry: 7fb5f2df-de3e-480f-a613-f38a9109e8d8\n      -\n        id: 653664b5-2b47-43df-835c-24bd68762ac9\n        entry: fdb45b84-3568-437d-84f7-e3c93b6da3e6\n      -\n        id: 15ecf5ee-df77-48d3-b997-7c7f674e2005\n        entry: 3dbb14fd-a762-4891-bce1-daf13b8c5981\n      -\n        id: 1479865c-f02f-40ee-9520-4968eca62e5a\n        entry: fb20f2e0-3881-43e6-8507-3308a18c54b0\n      -\n        id: 42336640-91e0-11ed-b05e-0800200c9a66\n        entry: 452c268b-b885-4deb-8e46-1cc3ebc66e4f\n      -\n        id: f610e44f-6a75-4da2-8e2b-ebbbfdd8be86\n        entry: 6177b316-0eed-4dec-83d1-e5a48a8e00b6\n      -\n        id: 3bf23373-eb9b-49cf-bbc5-3dd61dd82dec\n        entry: 8ed04215-9f46-4000-bd67-c71b21b67d85\n      -\n        id: 8726b167-918a-4389-a7d6-854e5e771239\n        entry: 8d9cfb16-36bf-45d0-babb-e501a35ddae6\n      -\n        id: a3e00a87-b8ea-4bd9-b617-210f7122f45f\n        entry: 499d808b-18be-42e9-acd0-91bcdff73193\n      -\n        id: 03909ade-aca0-4f00-a941-cc919f5d0765\n        entry: 3c34ef5c-781e-4a22-a09b-25f58bdb58a8\n      -\n        id: 469f0f47-a6b4-4893-b479-71f9bddee672\n        entry: bd1261de-7c4c-4c22-baf5-8a52cdd10c74\n      -\n        id: 716bb7b8-952a-45b7-9c20-24c4e0a24e43\n        entry: ffa24da8-3fee-4fc9-a81b-fcae8917bd74\n      -\n        id: a6408363-0492-4df1-86a1-98cef1205f54\n        entry: 0df63f01-4b97-4c15-89a9-015c02ea3748\n      -\n        id: 29526fd0-a0a1-11ec-b230-0800200c9a66\n        entry: 268d444c-88e3-4b52-bfc6-165749ef3ec1\n  -\n    id: 0cbb901d-581c-433f-b873-1256c70d2f5c\n    title: Frontend\n    data:\n      navigation_image: navigation/nike-air-jordans.png\n      image_width_override: 1.8em\n    children:\n      -\n        id: 6482b878-b8eb-4114-886e-dc8c9eb277b8\n        entry: 84100772-18e4-4a22-8759-219b242a320c\n        title: Overview\n      -\n        id: 9d43191e-652b-48e8-8ec5-5cd6266d793d\n        entry: d37b2af2-f2bf-493a-9345-7087fb5929ce\n        title: 'Antlers Templates'\n      -\n        id: 40ef0b25-0f5e-4523-a527-52a099838d09\n        entry: c7816387-ebc4-4204-b5f2-8e7073a4db8b\n      -\n        id: d7970aa0-e88f-45e7-b313-8f1f49e43cf0\n        entry: e7833062-e05c-42c9-ad35-dc5077f1f0b8\n      -\n        id: 9e8cd49b-55b6-48dc-98c6-308189b89e67\n        entry: 5e848460-9bbc-449e-8edd-182d918163ff\n      -\n        id: 05a7b681-86e7-4ac7-883c-27bc03d79f38\n        entry: fc564ddf-80c1-4d87-8675-4a41f13c7774\n      -\n        id: 08e937a0-bb37-11ec-bf84-0800200c9a66\n        entry: 245068a1-1900-4774-a3ba-29192dc9acff\n        title: 'Image Manipulation'\n      -\n        id: 68c69f7b-3560-40e6-a4ba-abc98d7ce4af\n        entry: 131259a5-2072-49d8-9ea4-2099e0338e2f\n      -\n        id: 9760b14f-ad9b-4999-aa06-25a7ffb48c7d\n        entry: 9c1efbc5-c6a4-46f1-acce-d38b20122bd6\n      -\n        id: 0e3773f2-d81e-48dc-8117-7b2ebf1c90a4\n        entry: 75be125b-7d92-496c-ac5d-7098560d3d44\n      -\n        id: 6784cd38-b385-4401-b159-7b6669c276a3\n        entry: 2e0d2f8f-319d-4cce-bd90-16d6ad32ad37\n      -\n        id: 36b73b3c-c670-4dc3-a490-05f424214666\n        entry: 424f5092-be39-449a-b660-f4e077631487\n      -\n        id: 1ac61fc1-8a38-4693-909b-4e0863d36b7f\n        entry: 9751908a-a10c-4c36-abd3-2251e83fbc65\n      -\n        id: fa7963df-1637-4b80-867b-93219b33d25e\n        entry: 662a5918-ac0f-42a1-bf40-5a7a320e87e1\n      -\n        id: 69f14344-04a5-4938-85b4-dbeb2bddb94b\n        entry: 74c47654-8c47-49b1-a616-ed940ce19977\n      -\n        id: 5de67d86-45f5-4bae-9d41-de5a2a730303\n        entry: fbf59081-ba24-4e82-b011-b687be228c89\n  -\n    id: 1a3486df-4c42-447e-bd1e-0f4d048cf63e\n    title: 'Control Panel'\n    data:\n      navigation_image: navigation/rubiks-cube.png\n    children:\n      -\n        id: 8b7e584d-91ba-46f0-a5f5-feccb02a70ed\n        entry: 249e046f-a9b4-494b-9e4d-084c28e01028\n      -\n        id: 65d7cf90-2df8-4e32-90bc-6c8d787b280a\n        entry: 2ce74b48-d3cc-4b8a-a8d4-f514c0b1d6ff\n        title: Navigation\n      -\n        id: ebb8baad-7ddb-4823-834d-75368a564d96\n        entry: 54548616-fd6d-44a3-a379-bdf71c492c63\n      -\n        id: a086b6cc-9da0-4e79-bd13-c4becb00c2b2\n        entry: 9a1d8b88-c600-46f2-8727-1deb56f2e87a\n      -\n        id: 04e7b53e-af60-4421-8414-cd9b9ea0296e\n        entry: 2940c834-7062-47a1-957c-88a69e790cbb\n      -\n        id: 9919cc65-a3d1-4adb-b450-bb93fed4f6fb\n        entry: dd52c1f6-661b-4408-83c6-691fa341aaa7\n      -\n        id: 5b54ac61-838a-4c5d-8c24-b36a6deb3f89\n        entry: c095fb87-4c02-462c-9e6f-dfe0b6889248\n      -\n        id: 499c5a41-17cf-4d4d-948e-72896c416968\n        entry: cdffd2c9-cf42-495d-a8f1-f416ddfddc29\n      -\n        id: d92527a2-51c3-4f01-a42c-136e06ccf2ec\n        entry: a3adf32a-37a5-4e96-beee-f107dc1b81a9\n      -\n        id: 90a985ba-30b6-4a1f-a7d9-5d9c7dc1d6a4\n        entry: 79129d32-3f7c-4215-b6b1-21a2fccafa8d\n        title: Translations\n      -\n        id: 8fa1da82-bf55-46d2-a858-4ce3debb7a7a\n        entry: 5bd9426f-23cf-4196-9848-471dff67f5ea\n      -\n        id: 4a2e132e-7fd6-4c84-95a1-ab6c732abf12\n        entry: 4b77c19b-129c-4271-a724-eea884eb3e2e\n  -\n    id: 54d885ee-1b58-41ed-9268-baca6e83e9e8\n    title: Addons\n    data:\n      navigation_image: navigation/lego.png\n      image_width_override: 1.8em\n    children:\n      -\n        id: dae0a662-0a66-4a4f-8d2a-b63c2eaf8f65\n        entry: bc2ad29d-50c3-47fb-9213-8553bcb5a48a\n        title: Overview\n  -\n    id: 6e285cb4-23db-4817-abc6-1c76a08def33\n    title: 'Starter Kits'\n    data:\n      navigation_image: navigation/indiana-jones.png\n      image_width_override: 1.8em\n    children:\n      -\n        id: a4591991-324c-4678-9993-a735d09269ed\n        entry: 3a2e714c-57de-4b16-a916-7e7aba22de03\n        title: Overview\n      -\n        id: a6efd593-b837-46c8-8a36-7bd4b509cf0c\n        entry: c51a5de8-4b02-4240-8195-3ff7987c43cf\n      -\n        id: c00f0b6d-2cae-4707-af04-a62cf78a2ca8\n        entry: 512cbb65-0091-47e2-a3b6-c42aae64349e\n      -\n        id: f36f50e5-1935-4203-8c02-4d71965a4d2c\n        entry: 9c703f43-30de-4f65-98bb-2b89f80012b7\n  -\n    id: ae5c17c7-dbde-489f-a1ef-50ac1206a44f\n    title: Resources\n    data:\n      navigation_image: navigation/toucan.png\n      image_width_override: 2.25em\n    children:\n      -\n        id: 02927095-6b41-4832-aaa6-3d2ca40c9eee\n        entry: e947dc19-9a8a-44c2-911c-171d1f196c91\n      -\n        id: 5bc6420e-de7b-4574-822c-0f3a1470b24e\n        entry: 550e7bf1-de6e-40ba-9b06-b32d9119e436\n      -\n        id: 2cfa8593-cb18-4370-aa24-8c2408b59c87\n        entry: 0efc0286-bfb1-4d54-8a94-8589b35adf88\n      -\n        id: 74125138-d6bd-46ce-9bf2-e7f6309a3177\n        entry: 55a99a3b-e40d-4033-9a70-823de8e4255f\n      -\n        id: bfc8f09a-19f7-46af-88bc-a30dc504a71d\n        entry: 56fadb93-b846-4867-ad73-4f721cc940c2\n      -\n        id: 95b57069-5325-4603-8a37-56000633d14f\n        entry: c9ee871c-002b-48d7-b845-1abac58de337\n      -\n        id: 585e9861-1e2b-4dd2-9bef-61109f867660\n        entry: b6c40452-8da0-4f03-a53c-714cc3338b9d\n      -\n        id: d187ac97-9bc6-4354-a3ba-382daf94d657\n        entry: cd0428cf-2c4a-43b9-8e61-dd8123799ed8\n"
  },
  {
    "path": "content/trees/navigation/extending_docs.yaml",
    "content": "tree:\n  -\n    id: c3a7787f-6dad-4eb4-9893-57c1c3dc5bc3\n    entry: 72c7218e-1a9f-49a6-904d-ed8588e57869\n  -\n    id: c21a6b97-f7a1-47cf-9356-f3a9a2954050\n    title: Front-end\n    data:\n      navigation_image: navigation/nike-air-jordans.png\n      image_width_override: 2.1em\n    children:\n      -\n        id: 9d692c0e-df82-463d-af3c-b355a28d91dc\n        entry: af1de577-8f75-4623-a0a3-d4c4c49390aa\n      -\n        id: 814e613e-37fc-44c5-9aa4-083cd74eb66f\n        entry: be292d2b-dc0e-48dc-bce4-0058df27ccc6\n      -\n        id: 860e0c73-bc80-4bc1-9bab-def73cbba957\n        entry: e052ecb8-60d9-4afa-980e-ce128c301d70\n      -\n        id: 8c64ffd1-16be-4de0-9b09-1860973a5219\n        entry: 098cb1c5-94c2-4bc0-add7-9aad39951d67\n  -\n    id: affc2001-e4f5-4e06-9e00-58dd88edbb6e\n    title: 'Control Panel'\n    data:\n      navigation_image: navigation/rubiks-cube.png\n    children:\n      -\n        id: d5214ec3-c89f-4889-8f14-ed2b65a899d6\n        entry: cb8f4d8a-47b6-4567-9510-ed7d9ee9c037\n      -\n        id: 3b8785de-3898-4a94-be81-01c364d02123\n        entry: 83786f60-def6-11e9-aaef-0800200c9a66\n      -\n        id: 7af42e91-a4d7-454f-b9f2-ab19914f4dd3\n        entry: 06813e5d-158e-4318-aa4a-b29fd87d107f\n      -\n        id: e747b182-3ded-42fa-b713-34d6baec9b66\n        entry: 5900c99f-89b9-4ee3-834c-cb1b070146e4\n      -\n        id: 61e2cbcb-dd64-4bfd-af62-33e5a2f18b25\n        entry: 785ffa10-8b63-44b1-9da3-3837250cacbe\n      -\n        id: 0cdd7170-92ad-11ed-b05e-0800200c9a66\n        url: '/preferences#adding-fields'\n        title: Preferences\n      -\n        id: 9e649d17-d0b2-48a9-abf4-534c38c0c7ce\n        entry: ba2e6172-b4dc-443b-8230-b770dec1423c\n      -\n        id: f0e082dc-70e7-413a-8d92-4fab03ea447b\n        entry: 41e386b3-9df9-4d28-9338-8005399f953c\n      -\n        id: 7e4f5154-4499-40a4-929a-92fb81f10bb8\n        entry: aa6e0a79-9d3f-493b-92c9-df4d2257bc64\n      -\n        id: 270cf2ba-ceb6-499d-b1f0-e548a1aed282\n        entry: d0668b6e-915b-46da-863e-51fec54b02e2\n      -\n        id: 044b4ef5-5809-4bde-8988-ca680199926d\n        entry: b4b46ceb-9feb-4587-8f0d-2080511bf9e3\n      -\n        id: 5601026a-9c62-4804-8725-c468c5154c31\n        entry: a96676fe-0ec4-41f5-9205-2fe47988addb\n      -\n        id: 4ca9478d-e066-49e3-ba99-fc005b507609\n        entry: 52af4663-ebbd-467c-8659-9c7bb94cb7cc\n      -\n        id: 65babe8f-192b-449b-9c21-bc0008d531ba\n        entry: 3482755d-3d20-42d5-8680-301a1cb95965\n      -\n        id: f91be384-aa3a-4b15-9d6c-2b10e33f00d8\n        entry: 76abbb51-417c-460c-ad6c-c359329bfbaf\n      -\n        id: 5eba562c-9bdf-4623-9c55-a219956b8ccf\n        entry: 5eab02e3-c76b-4f44-a304-6a78877d099f\n  -\n    id: 73354789-cbf2-4bee-ab86-b289d748a64d\n    title: Javascript\n    data:\n      navigation_image: navigation/joystick.png\n      image_width_override: 1.75em\n    children:\n      -\n        id: b598494b-6e08-4d10-8fb7-7fd6f20a4a20\n        entry: 0782835d-1f44-4b19-99f4-2bb8ad28c7ba\n      -\n        id: 407dca74-15d5-41bb-bd98-a1ea4bd02b63\n        entry: b7519137-73b6-46c7-8432-da7725b1d9b4\n      -\n        id: e24ebb53-28fd-41bc-861c-f08a606092f4\n        entry: bbf752d3-ffdb-4ac9-a5c5-0b32e3db9d6f\n      -\n        id: 2517660b-fdf9-4ba0-8add-9dcb762041a5\n        entry: fc136da3-ba46-46e1-8443-e345d5b548ac\n      -\n        id: 658a74a4-85b6-49b4-81e2-c5fb312c69b1\n        entry: efcca509-5690-4201-88d7-74c542bb9900\n      -\n        id: 1e667ec1-a50d-4fd7-a6ed-cf5750a64dbf\n        entry: 28068f9a-f269-4646-87e4-881e5477558d\n      -\n        id: dbea0d9e-6a6a-42fd-83bf-d0d2eae539b6\n        entry: d84613d5-2b2b-465d-8f02-c71b6d2aadf1\n      -\n        id: 912c7cdd-8c3f-4824-8af5-95ad81f45fb5\n        entry: b80820bb-c2e8-475f-98bd-8ea0ef9f5339\n      -\n        id: 37a82f80-92ad-11ed-b05e-0800200c9a66\n        url: '/preferences#using-javascript'\n        title: Preferences\n  -\n    id: a6b12519-6352-452b-9daf-b7dd76880fb2\n    title: 'Core & Data'\n    data:\n      navigation_image: navigation/triforce.png\n    children:\n      -\n        id: 2a1a9358-2ad6-40d0-9022-78257abc64a9\n        entry: e7833062-e05c-42c9-ad35-dc5077f1f0b8\n      -\n        id: b7d1e399-e739-4575-9121-c37059d6ab4f\n        entry: 0267eb5a-f54b-4e3f-bef0-1f16762720b1\n      -\n        id: 0bc9381c-b65c-4267-8430-1bde81501461\n        entry: 290e9a74-7c6b-4fd0-a90a-23f7ac38d0c5\n      -\n        id: e4373616-8b57-4f3c-9eab-e56f7a7b881e\n        entry: ff397ebf-4b53-4dbd-b81b-0dec839e0e5f\n      -\n        id: 444caa5a-3de4-42ea-aa45-dbcaed5aaf71\n        entry: c3da9537-5d5f-4b84-a4be-882b89217151\n      -\n        id: 48880724-6101-45a8-9286-5ee646645583\n        entry: 6843f6e9-ac62-4128-8d50-887560f201ca\n      -\n        id: 10747789-6efc-427b-bd68-fbf0d5b704db\n        entry: 900414a8-f73a-46f0-82bd-b607767f5d5d\n      -\n        id: ae81c6b6-e582-44a4-a1e8-b85de3e06635\n        entry: fd34ca35-57d1-4d28-97f9-ba6801656b39\n      -\n        id: 6eafca01-ad4b-467f-bef7-3b8f8d187d89\n        entry: 73010f4f-bb62-4feb-a6ff-88031f488db8\n      -\n        id: 8636c400-9838-11ed-87cd-0800200c9a66\n        entry: 0eedd0bb-81a7-4813-b29f-672fe6697021\n  -\n    id: ba853a27-7829-4663-83e0-b762a96d109a\n    title: Addons\n    url: /extending/addons\n    data:\n      navigation_image: navigation/lego.png\n      image_width_override: 2.1em\n    children:\n      -\n        id: 9519cca9-4afd-4143-b949-090f209a57e4\n        entry: 5bd75435-806e-458b-872e-7528f24df7e6\n      -\n        id: a7f60530-f033-11ed-afc5-0800200c9a66\n        entry: 5f26a634-19ae-4413-8b9e-1ed9c2c76bb0\n        title: Vite\n      -\n        id: c08f0634-c324-4320-a698-e5098eaaf6a1\n        entry: 15db07e8-6e83-4b6e-89bb-d050b5d2c823\n"
  },
  {
    "path": "content/trees/navigation/guides.yaml",
    "content": "tree:\n  -\n    id: 3f6da04b-db0d-4520-88e0-3b30811899b9\n    title: 'Getting Started'\n    children:\n      -\n        id: d394b71b-1a7a-438e-b5c4-eccbfff4930d\n        entry: 1d1920fb-604c-4ac1-8c99-f0de44abc06b\n"
  },
  {
    "path": "content/trees/navigation/reference.yaml",
    "content": "tree:\n  -\n    id: 25143313-bcc5-4897-a16f-16b4b4fb42b4\n    entry: 5a8fe626-b073-41fc-8da4-264b54837600\n    data:\n      navigation_image: navigation/fieldtypes.png\n      navigation_image_dark_mode: navigation/fieldtypes-dark-mode.png\n      image_width_override: 2.4em\n      tile_image: tiles/fieldtypes.png\n      tile_image_dark_mode: tiles/fieldtypes-dark-mode.png\n      hue_rotate: hue_rotate_2\n  -\n    id: 6241c501-22d8-420d-8202-46c152c3a4d7\n    entry: a4bc698a-5971-474d-a19c-0c43c0d641b9\n    title: Modifiers\n    data:\n      navigation_image: navigation/eyes.png\n      image_width_override: 2.4em\n      tile_image: tiles/modifiers.png\n      hue_rotate: hue_rotate_1\n  -\n    id: da835fbd-8687-4c91-abaa-d59bc88dbdd2\n    entry: 082e1db7-7b52-4b92-a8bf-4c51ce7d741c\n    data:\n      navigation_image: tiles/repositories.png\n      image_width_override: 2.5em\n      tile_image: tiles/repositories.png\n      hue_rotate: hue_rotate_1\n  -\n    id: 4066b00a-98c2-4bbb-b762-f6f5d0835d78\n    entry: 710810c7-f710-4364-a6e4-cedf8c292d88\n    data:\n      navigation_image: tiles/tags.png\n      image_width_override: 2.5em\n      tile_image: tiles/tags.png\n      hue_rotate: hue_rotate_2\n  -\n    id: f12c6a80-2078-4d59-b3bc-c7970cfe791d\n    entry: 03cf195b-2030-43c1-a6f3-039fa55ffd6a\n    data:\n      navigation_image: tiles/variables.png\n      image_width_override: 2.75em\n      tile_image: tiles/variables.png\n      hue_rotate: hue_rotate_2\n  -\n    id: 43aa419f-3855-42f5-bd74-acf8925989ea\n    entry: 565f0285-43b1-41d2-935c-e52ad00c012c\n    data:\n      navigation_image: tiles/widgets.png\n      image_width_override: 2.5em\n      tile_image: tiles/widgets.png\n      hue_rotate: hue_rotate_2\n"
  },
  {
    "path": "content/trees/navigation/screencasts.yaml",
    "content": "tree:\n  -\n    id: 16b9794b-6309-47d6-a544-1752a523fa30\n    title: 'Getting Started'\n    children:\n      -\n        id: d1b56b30-ae27-4a25-85b2-363e8ae4424a\n        entry: 401a9d31-29f4-4cde-aaed-4ef494dfc611\n      -\n        id: e7ab6f69-2cf3-4fd2-987b-01ba128c4d84\n        entry: 494663ef-8bf5-4d96-a2b5-de89cc627c1f\n      -\n        id: 89293721-878c-4d97-b02a-87786d8bf188\n        entry: dbd8a228-2111-4356-b093-2414f7443215\n      -\n        id: 9ec56b3f-ec0b-4918-a9e1-9ad677da06f8\n        entry: 31d4a021-83da-4349-b9ad-ffcfcf3b5724\n      -\n        id: 6e18ace6-e4df-4c7f-bf20-83257b6ad984\n        entry: a492f0e1-454e-42e6-a9af-cfb2c9a6adde\n  -\n    id: b9599000-371f-41d3-9fb4-9c707c4d8f39\n    title: 'Building a Blog'\n    children:\n      -\n        id: 242b178d-4e84-47f9-821b-21aba7e04b0f\n        entry: a6a956fd-647d-4503-9a4a-3b24198e6e73\n      -\n        id: 89dbe9c6-e2e2-4539-95d3-9e3b11bfc1a2\n        entry: 6946623d-f9d3-40a8-93d1-00721bb3cc32\n      -\n        id: 707cef58-5a23-495f-b7b6-743f1c6e1097\n        entry: 9ea94cd6-9f21-42b8-9019-313ed172cb29\n      -\n        id: b4f506d3-6f38-4be4-86b7-c3f48a2c06ec\n        entry: e15f4700-1c45-460d-8215-4649fbe911f9\n      -\n        id: a1ece39b-5fe3-4df9-860b-2576f7d56128\n        entry: 50758908-d9be-428c-b626-e0a5a5de5d1b\n      -\n        id: 957d7a36-0b78-47a2-963b-803cd144c01a\n        entry: 7044e150-acc1-4a15-bf51-f957371d29ed\n  -\n    id: aa293224-6cea-4a88-be8a-5827aaf8fb9d\n    title: Taxonomies\n    children:\n      -\n        id: 20d8b15a-3118-4a67-ab6f-c4f47f1dded1\n        entry: 1ae2abc9-264a-47b9-b529-bb9f93b74734\n      -\n        id: b95c8b7d-8169-493b-8623-fb88d4ddc295\n        entry: 4253d876-ec1a-4d02-be7a-19ab0bab19fe\n      -\n        id: 0202773b-3b7e-4c8b-8bd8-1c6265fd3589\n        entry: 95cb8f7f-1b9f-440e-9e35-9c9fa6c03687\n  -\n    id: af2d8ca8-4553-4695-835d-cb1ff41997f5\n    title: Search\n    children:\n      -\n        id: 7728dbf5-1695-4f53-a2f7-5f1fc8db0520\n        entry: 2022056a-d901-423a-aaa7-ee04fff40739\n"
  },
  {
    "path": "content/trees/navigation/sections.yaml",
    "content": "{  }"
  },
  {
    "path": "content/trees/navigation/top.yaml",
    "content": "{  }"
  },
  {
    "path": "database/.gitignore",
    "content": "*.sqlite*\n"
  },
  {
    "path": "database/factories/UserFactory.php",
    "content": "<?php\n\nnamespace Database\\Factories;\n\nuse App\\Models\\User;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Support\\Facades\\Hash;\nuse Illuminate\\Support\\Str;\n\n/**\n * @extends Factory<User>\n */\nclass UserFactory extends Factory\n{\n    /**\n     * The current password being used by the factory.\n     */\n    protected static ?string $password;\n\n    /**\n     * Define the model's default state.\n     *\n     * @return array<string, mixed>\n     */\n    public function definition(): array\n    {\n        return [\n            'name' => fake()->name(),\n            'email' => fake()->unique()->safeEmail(),\n            'email_verified_at' => now(),\n            'password' => static::$password ??= Hash::make('password'),\n            'remember_token' => Str::random(10),\n        ];\n    }\n\n    /**\n     * Indicate that the model's email address should be unverified.\n     */\n    public function unverified(): static\n    {\n        return $this->state(fn (array $attributes) => [\n            'email_verified_at' => null,\n        ]);\n    }\n}\n"
  },
  {
    "path": "database/migrations/0001_01_01_000000_create_users_table.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     */\n    public function up(): void\n    {\n        Schema::create('users', function (Blueprint $table) {\n            $table->id();\n            $table->string('name');\n            $table->string('email')->unique();\n            $table->timestamp('email_verified_at')->nullable();\n            $table->string('password');\n            $table->rememberToken();\n            $table->timestamps();\n        });\n\n        Schema::create('password_reset_tokens', function (Blueprint $table) {\n            $table->string('email')->primary();\n            $table->string('token');\n            $table->timestamp('created_at')->nullable();\n        });\n\n        Schema::create('sessions', function (Blueprint $table) {\n            $table->string('id')->primary();\n            $table->foreignId('user_id')->nullable()->index();\n            $table->string('ip_address', 45)->nullable();\n            $table->text('user_agent')->nullable();\n            $table->longText('payload');\n            $table->integer('last_activity')->index();\n        });\n    }\n\n    /**\n     * Reverse the migrations.\n     */\n    public function down(): void\n    {\n        Schema::dropIfExists('users');\n        Schema::dropIfExists('password_reset_tokens');\n        Schema::dropIfExists('sessions');\n    }\n};\n"
  },
  {
    "path": "database/migrations/0001_01_01_000001_create_cache_table.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     */\n    public function up(): void\n    {\n        Schema::create('cache', function (Blueprint $table) {\n            $table->string('key')->primary();\n            $table->mediumText('value');\n            $table->bigInteger('expiration')->index();\n        });\n\n        Schema::create('cache_locks', function (Blueprint $table) {\n            $table->string('key')->primary();\n            $table->string('owner');\n            $table->bigInteger('expiration')->index();\n        });\n    }\n\n    /**\n     * Reverse the migrations.\n     */\n    public function down(): void\n    {\n        Schema::dropIfExists('cache');\n        Schema::dropIfExists('cache_locks');\n    }\n};\n"
  },
  {
    "path": "database/migrations/0001_01_01_000002_create_jobs_table.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     */\n    public function up(): void\n    {\n        Schema::create('jobs', function (Blueprint $table) {\n            $table->id();\n            $table->string('queue')->index();\n            $table->longText('payload');\n            $table->unsignedTinyInteger('attempts');\n            $table->unsignedInteger('reserved_at')->nullable();\n            $table->unsignedInteger('available_at');\n            $table->unsignedInteger('created_at');\n        });\n\n        Schema::create('job_batches', function (Blueprint $table) {\n            $table->string('id')->primary();\n            $table->string('name');\n            $table->integer('total_jobs');\n            $table->integer('pending_jobs');\n            $table->integer('failed_jobs');\n            $table->longText('failed_job_ids');\n            $table->mediumText('options')->nullable();\n            $table->integer('cancelled_at')->nullable();\n            $table->integer('created_at');\n            $table->integer('finished_at')->nullable();\n        });\n\n        Schema::create('failed_jobs', function (Blueprint $table) {\n            $table->id();\n            $table->string('uuid')->unique();\n            $table->text('connection');\n            $table->text('queue');\n            $table->longText('payload');\n            $table->longText('exception');\n            $table->timestamp('failed_at')->useCurrent();\n        });\n    }\n\n    /**\n     * Reverse the migrations.\n     */\n    public function down(): void\n    {\n        Schema::dropIfExists('jobs');\n        Schema::dropIfExists('job_batches');\n        Schema::dropIfExists('failed_jobs');\n    }\n};\n"
  },
  {
    "path": "database/seeders/DatabaseSeeder.php",
    "content": "<?php\n\nnamespace Database\\Seeders;\n\nuse App\\Models\\User;\n use Illuminate\\Database\\Console\\Seeds\\WithoutModelEvents;\nuse Illuminate\\Database\\Seeder;\n\nclass DatabaseSeeder extends Seeder\n{\n    use WithoutModelEvents;\n\n    /**\n     * Seed the application's database.\n     */\n    public function run(): void\n    {\n        // User::factory(10)->create();\n\n        User::factory()->create([\n            'name' => 'Test User',\n            'email' => 'test@example.com',\n        ]);\n    }\n}\n"
  },
  {
    "path": "lang/en/validation.php",
    "content": "<?php\n\nreturn [\n\n    'unique_entry_value' => 'The :attribute has already been taken.',\n    'unique_user_value' => 'The :attribute has already been taken.',\n\n];\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"private\": true,\n    \"type\": \"module\",\n    \"scripts\": {\n        \"dev\": \"vite\",\n        \"build\": \"vite build\"\n    },\n    \"devDependencies\": {\n        \"@alpinejs/intersect\": \"^3.14.1\",\n        \"@vitejs/plugin-vue\": \"^6.0.1\",\n        \"alpinejs\": \"^3.2.4\",\n        \"autoprefixer\": \"^10.4.20\",\n        \"axios\": \"^1.15\",\n        \"laravel-vite-plugin\": \"^2.0.0\",\n        \"lodash\": \"^4.18.1\",\n        \"postcss\": \"^8.5.14\",\n        \"postcss-import\": \"^15.1.0\",\n        \"postcss-nesting\": \"^12.1.5\",\n        \"vite\": \"^7.3.2\",\n        \"vite-plugin-devtools-json\": \"^1.0.0\"\n    },\n    \"dependencies\": {\n        \"@docsearch/js\": \"^3.0.0-alpha.40\",\n        \"@tailwindcss/postcss\": \"^4.1.12\",\n        \"@tailwindcss/vite\": \"^4.1.12\",\n        \"dayjs\": \"^1.10.7\",\n        \"meilisearch-docsearch\": \"^0.7.1\",\n        \"tailwindcss\": \"^4.1.12\"\n    }\n}\n"
  },
  {
    "path": "phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"./vendor/phpunit/phpunit/phpunit.xsd\"\n         bootstrap=\"vendor/autoload.php\"\n         colors=\"true\"\n>\n    <testsuites>\n        <testsuite name=\"Unit\">\n            <directory suffix=\"Test.php\">./tests/Unit</directory>\n        </testsuite>\n        <testsuite name=\"Feature\">\n            <directory suffix=\"Test.php\">./tests/Feature</directory>\n        </testsuite>\n    </testsuites>\n    <source>\n        <include>\n            <directory suffix=\".php\">./app</directory>\n        </include>\n    </source>\n    <php>\n        <env name=\"APP_ENV\" value=\"testing\"/>\n        <env name=\"APP_MAINTENANCE_DRIVER\" value=\"file\"/>\n        <env name=\"BCRYPT_ROUNDS\" value=\"4\"/>\n        <env name=\"BROADCAST_CONNECTION\" value=\"null\"/>\n        <env name=\"CACHE_STORE\" value=\"array\"/>\n        <env name=\"DB_CONNECTION\" value=\"sqlite\"/>\n        <env name=\"DB_DATABASE\" value=\":memory:\"/>\n        <env name=\"DB_URL\" value=\"\"/>\n        <env name=\"MAIL_MAILER\" value=\"array\"/>\n        <env name=\"QUEUE_CONNECTION\" value=\"sync\"/>\n        <env name=\"SESSION_DRIVER\" value=\"array\"/>\n        <env name=\"PULSE_ENABLED\" value=\"false\"/>\n        <env name=\"TELESCOPE_ENABLED\" value=\"false\"/>\n        <env name=\"NIGHTWATCH_ENABLED\" value=\"false\"/>\n    </php>\n</phpunit>\n"
  },
  {
    "path": "please",
    "content": "#!/usr/bin/env php\n<?php\n\nuse Symfony\\Component\\Console\\Input\\ArgvInput;\n\ndefine('LARAVEL_START', microtime(true));\n\n// Register the Composer autoloader...\nrequire __DIR__.'/vendor/autoload.php';\n\n// Bootstrap Laravel...\n$app = require_once __DIR__.'/bootstrap/app.php';\n\n// Rebind the kernel...\nStatamic\\Console\\Please\\Application::rebindKernel();\n\n// Handle the command...\n$status = $app->handleCommand(new ArgvInput);\n\nexit($status);"
  },
  {
    "path": "postcss.config.js",
    "content": "export default {\n    plugins: {\n      'postcss-import': {},\n      'postcss-nesting': {},\n    },\n  }\n"
  },
  {
    "path": "public/.htaccess",
    "content": "<IfModule mod_rewrite.c>\n    <IfModule mod_negotiation.c>\n        Options -MultiViews -Indexes\n    </IfModule>\n\n    RewriteEngine On\n\n    # Handle Authorization Header\n    RewriteCond %{HTTP:Authorization} .\n    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]\n\n    # Redirect Trailing Slashes If Not A Folder...\n    RewriteCond %{REQUEST_FILENAME} !-d\n    RewriteCond %{REQUEST_URI} (.+)/$\n    RewriteRule ^ %1 [L,R=301]\n\n    # Handle Front Controller...\n    RewriteCond %{REQUEST_FILENAME} !-d\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteRule ^ index.php [L]\n</IfModule>\n"
  },
  {
    "path": "public/favicons/site.webmanifest",
    "content": "{\n  \"name\": \"Statamic Docs\",\n  \"short_name\": \"Docs\",\n  \"icons\": [\n    {\n      \"src\": \"/favicons/web-app-manifest-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable\"\n    },\n    {\n      \"src\": \"/favicons/web-app-manifest-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable\"\n    }\n  ],\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#000000\",\n  \"display\": \"standalone\"\n}"
  },
  {
    "path": "public/img/adverts/.gitkeep",
    "content": ""
  },
  {
    "path": "public/img/navigation/.gitkeep",
    "content": ""
  },
  {
    "path": "public/img/pro-badges/.gitkeep",
    "content": ""
  },
  {
    "path": "public/img/pro-badges/artwork/.gitkeep",
    "content": ""
  },
  {
    "path": "public/img/pro-badges/icons/.gitkeep",
    "content": ""
  },
  {
    "path": "public/img/tiles/.gitkeep",
    "content": ""
  },
  {
    "path": "public/index.php",
    "content": "<?php\n\nuse Illuminate\\Http\\Request;\n\ndefine('LARAVEL_START', microtime(true));\n\n// Determine if the application is in maintenance mode...\nif (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {\n    require $maintenance;\n}\n\n// Register the Composer autoloader...\nrequire __DIR__.'/../vendor/autoload.php';\n\n// Bootstrap Laravel and handle the request...\n(require_once __DIR__.'/../bootstrap/app.php')\n    ->handleRequest(Request::capture());\n"
  },
  {
    "path": "public/mix-manifest.json",
    "content": "{\n    \"/js/app.js\": \"/js/app.js\"\n}\n"
  },
  {
    "path": "public/robots.txt",
    "content": "User-agent: *\nDisallow:\n"
  },
  {
    "path": "resources/blueprints/collections/adverts/advert.yaml",
    "content": "tabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              display: Title\n              instructions: \"Isn't used in templating, just use this to identify the advert in the CP\"\n              validate:\n                - required\n          -\n            handle: image\n            field:\n              max_files: 1\n              container: main\n              type: assets\n              display: Image\n              instructions: 'Will be cropped at width x 1.35 height. Recommended dimensions at least 1000px wide.'\n              folder: adverts\n          -\n            handle: heading\n            field:\n              character_limit: 25\n              type: text\n              display: Heading\n              instructions: 'e.g. A Statamic Starter Kit'\n              replicator_preview: false\n          -\n            handle: text\n            field:\n              character_limit: 70\n              type: text\n              display: Text\n              instructions: 'e.g. Optimize your website for search engines with a complete SEO addon.'\n          -\n            handle: link_text\n            field:\n              type: text\n              display: 'Link Text'\n              width: 50\n          -\n            handle: link\n            field:\n              input_type: url\n              type: text\n              display: Link\n              width: 50\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - 'max:200'\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\ntitle: Advert\n"
  },
  {
    "path": "resources/blueprints/collections/extending-docs/page.yaml",
    "content": "title: Page\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            handle: nav_title\n            field:\n              type: text\n          -\n            handle: intro\n            field:\n              display: Intro\n              type: markdown\n              localizable: true\n          -\n            handle: content\n            field:\n              display: Content\n              restrict: false\n              smartypants: true\n              automatic_line_breaks: false\n              automatic_links: false\n              escape_markup: false\n              type: markdown\n              localizable: true\n          -\n            handle: template\n            field:\n              display: Template\n              type: template\n              default: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n"
  },
  {
    "path": "resources/blueprints/collections/fieldtypes/fieldtype.yaml",
    "content": "title: Fieldtype\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            import: common\n            handle: title\n          -\n            import: page\n          -\n            import: common\n            handle: screenshot\n          -\n            import: common\n            handle: screenshot_dark\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            import: common\n            handle: slug\n            config:\n              title: {  }\n              slug: {  }\n"
  },
  {
    "path": "resources/blueprints/collections/guides/guides.yaml",
    "content": "title: Guide\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n"
  },
  {
    "path": "resources/blueprints/collections/knowledge-base/page.yaml",
    "content": "title: Page\nsections:\n  main:\n    display: Main\n    fields:\n      -\n        handle: title\n        field:\n          type: text\n          required: true\n      -\n        handle: intro\n        field:\n          display: Intro\n          type: markdown\n          localizable: true\n      -\n        handle: content\n        field:\n          display: Content\n          restrict: false\n          smartypants: true\n          automatic_line_breaks: false\n          automatic_links: false\n          escape_markup: false\n          type: markdown\n          localizable: true\n      -\n        handle: template\n        field:\n          display: Template\n          type: template\n          default: page\n      -\n        handle: stage\n        field:\n          display: 'Writing Stage'\n          type: select\n          options:\n            - 'Gathering Knowledge'\n            - 'Writing the Draft'\n            - 'Ready for Editing'\n            - 'Needs Polish & Humor'\n            - 'Ready for Feedback'\n            - Complete\n      -\n        handle: nav_title\n        field:\n          type: text\n  sidebar:\n    fields:\n      -\n        handle: slug\n        field:\n          type: slug\n          required: true\n          localizable: true\n          validate:\n            - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n      -\n        handle: categories\n        field:\n          type: terms\n          taxonomies:\n            - categories\n          display: Categories\n          mode: select\n"
  },
  {
    "path": "resources/blueprints/collections/modifiers/modifiers.yaml",
    "content": "title: Modifier\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            import: common\n            handle: title\n          -\n            import: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            import: common\n            handle: slug\n            config:\n              title: {  }\n              slug: {  }\n          -\n            handle: modifier_types\n            field:\n              type: terms\n              taxonomies:\n                - modifier_types\n              display: 'Modifier Types'\n              mode: select\n"
  },
  {
    "path": "resources/blueprints/collections/pages/home.yaml",
    "content": "hide: true\norder: 1\ntitle: Home\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            handle: nav_title\n            field: page.nav_title\n          -\n            handle: breadcrumb_title\n            field: page.breadcrumb_title\n          -\n            handle: intro\n            field: page.intro\n          -\n            handle: tiles\n            field:\n              max_sets: 6\n              type: replicator\n              display: Tiles\n              sets:\n                tiles:\n                  display: Tiles\n                  sets:\n                    tile:\n                      display: Tile\n                      icon: programming-module-box-cube\n                      fields:\n                        -\n                          handle: tile_image\n                          field:\n                            max_files: 1\n                            container: main\n                            type: assets\n                            display: 'Tile Image'\n                            folder: tiles\n                        -\n                          handle: tile_title\n                          field:\n                            character_limit: 38\n                            type: text\n                            display: 'Tile Title'\n                        -\n                          handle: tile_description\n                          field:\n                            character_limit: 60\n                            type: text\n                            display: 'Tile Description'\n                        -\n                          handle: tile_link\n                          field:\n                            type: link\n                            display: 'Tile Link'\n                        -\n                          handle: advanced_options\n                          field:\n                            type: revealer\n                            display: 'Advanced Options'\n                        -\n                          handle: flush_image\n                          field:\n                            type: toggle\n                            display: 'Flush Image'\n                            instructions: 'Makes the image flush, which may be useful, e.g. a screenshot of Antlers code'\n                            width: 50\n                            if:\n                              advanced_options: 'equals true'\n                        -\n                          handle: hue_rotate\n                          field: hue_rotate.hue_rotate\n                          config:\n                            width: 50\n                            if:\n                              advanced_options: 'equals true'\n                        -\n                          handle: dark_mode\n                          field:\n                            type: section\n                            display: 'Dark Mode'\n                            if:\n                              advanced_options: 'equals true'\n                        -\n                          handle: tile_image_dark_mode\n                          field:\n                            max_files: 1\n                            container: main\n                            type: assets\n                            display: 'Tile Image Dark Mode (optional override)'\n                            folder: tiles\n                            width: 50\n                            if:\n                              advanced_options: 'equals true'\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - 'max:200'\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n          -\n            handle: template\n            field:\n              type: template\n              display: Template\n"
  },
  {
    "path": "resources/blueprints/collections/pages/link.yaml",
    "content": "order: 3\ntitle: Link\ntabs:\n  main:\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n          -\n            handle: redirect\n            field:\n              type: group\n              required: true\n              width: '100'\n              fields:\n                -\n                  handle: url\n                  field:\n                    type: link\n                    required: true\n                    width: '100'\n                    display: Location\n                -\n                  handle: status\n                  field:\n                    type: radio\n                    inline: 'true'\n                    required: true\n                    options:\n                      301: '301 (Permanent)'\n                      302: '302 (Temporary)'\n                    width: '100'\n                    display: 'HTTP Status'\n                    default: 302\n  sidebar:\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate: 'max:200'\n"
  },
  {
    "path": "resources/blueprints/collections/pages/page.yaml",
    "content": "order: 2\ntitle: Page\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n          -\n            handle: blueprint\n            field:\n              options:\n                -\n                  key: page\n                  value: Page\n                -\n                  key: link\n                  value: Link\n              type: select\n              display: Blueprint\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate: 'max:200'\n"
  },
  {
    "path": "resources/blueprints/collections/reference/reference.yaml",
    "content": "title: Reference\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n          -\n            handle: parent\n            field:\n              type: entries\n              collections:\n                - reference\n              max_items: 1\n              listable: false\n              localizable: true\n"
  },
  {
    "path": "resources/blueprints/collections/references/references.yaml",
    "content": "title: Reference\nsections:\n  main:\n    display: Main\n    fields:\n      -\n        handle: title\n        field:\n          type: text\n          required: true\n          validate:\n            - required\n      -\n        handle: content\n        field:\n          type: markdown\n          localizable: true\n      -\n        handle: template\n        field:\n          hide_partials: true\n          display: Template\n          type: template\n          icon: template\n          listable: hidden\n          instructions_position: above\n  sidebar:\n    display: Sidebar\n    fields:\n      -\n        handle: slug\n        field:\n          type: slug\n          required: true\n          localizable: true\n          validate:\n            - required\n      -\n        handle: parent\n        field:\n          type: entries\n          collections:\n            - reference\n          max_items: 1\n          listable: false\n          localizable: true\n"
  },
  {
    "path": "resources/blueprints/collections/resource_apis/resource_apis.yaml",
    "content": "title: Resource APIs\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n          -\n            handle: class\n            field:\n              placeholder: \\Statamic\\Facades\\\n              input_type: text\n              antlers: false\n              display: Class\n              type: text\n              icon: text\n              listable: hidden\n              instructions_position: above\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n"
  },
  {
    "path": "resources/blueprints/collections/screencasts/screencasts.yaml",
    "content": "title: Screencast\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            handle: sidebar_title\n            field:\n              input_type: text\n              type: text\n              instructions: 'A shorter title, if necessary'\n              localizable: false\n              listable: hidden\n              display: 'Sidebar Title'\n          -\n            handle: video\n            field:\n              type: video\n              localizable: false\n              listable: hidden\n              display: Video\n          -\n            handle: length\n            field:\n              input_type: text\n              type: text\n              localizable: false\n              listable: hidden\n              display: Length\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n          -\n            handle: date\n            field:\n              type: date\n              required: true\n              default: now\n              validate:\n                - required\n"
  },
  {
    "path": "resources/blueprints/collections/sections/sections.yaml",
    "content": "title: Sections\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            handle: nav_title\n            field: page.nav_title\n          -\n            handle: breadcrumb_title\n            field: page.breadcrumb_title\n          -\n            handle: intro\n            field: page.intro\n          -\n            handle: content\n            field:\n              type: markdown\n              localizable: true\n          -\n            handle: template\n            field:\n              display: Template\n              type: template\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n"
  },
  {
    "path": "resources/blueprints/collections/tags/tag-glide.yaml",
    "content": "title: 'Tag Glide'\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            handle: intro\n            field:\n              display: Intro\n              type: markdown\n              localizable: true\n          -\n            handle: screenshot\n            field:\n              type: assets\n              container: main\n              max_files: 1\n          -\n            handle: content\n            field:\n              display: Content\n              restrict: false\n              smartypants: true\n              automatic_line_breaks: false\n              automatic_links: false\n              escape_markup: false\n              type: markdown\n              localizable: true\n          -\n            handle: parameters\n            field:\n              type: grid\n              mode: stacked\n              fields:\n                -\n                  handle: name\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: type\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: required\n                  field:\n                    type: toggle\n                    width: 33\n                -\n                  handle: description\n                  field:\n                    type: markdown\n          -\n            handle: shape\n            field:\n              type: grid\n              mode: stacked\n              fields:\n                -\n                  handle: name\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: type\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: description\n                  field:\n                    type: markdown\n          -\n            handle: filters\n            field:\n              type: grid\n              mode: stacked\n              fields:\n                -\n                  handle: name\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: type\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: description\n                  field:\n                    type: markdown\n          -\n            handle: other\n            field:\n              type: grid\n              mode: stacked\n              fields:\n                -\n                  handle: name\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: type\n                  field:\n                    type: text\n                    width: 33\n                -\n                  handle: description\n                  field:\n                    type: markdown\n          -\n            handle: template\n            field:\n              display: Template\n              type: template\n              default: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n"
  },
  {
    "path": "resources/blueprints/collections/tags/tag.yaml",
    "content": "title: Tag\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            import: common\n            handle: title\n          -\n            import: page\n          -\n            import: common\n            handle: screenshot\n          -\n            import: common\n            handle: parameters\n          -\n            import: common\n            handle: variables\n          -\n            handle: parent_tag\n            field:\n              display: 'Parent Tag'\n              type: collections\n              collection: tags\n              max_items: 1\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            import: common\n            handle: slug\n            config:\n              title: {  }\n              slug: {  }\n"
  },
  {
    "path": "resources/blueprints/collections/tips/tips.yaml",
    "content": "title: 'Tips & Tricks'\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n"
  },
  {
    "path": "resources/blueprints/collections/troubleshooting/troubleshooting.yaml",
    "content": "title: Troubleshooting\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n          -\n            handle: categories\n            field:\n              type: terms\n              taxonomies:\n                - categories\n              display: Categories\n              mode: select\n"
  },
  {
    "path": "resources/blueprints/collections/ui_components/ui_component.yaml",
    "content": "title: UI Component\ntabs:\n  main:\n    sections:\n      -\n        fields:\n          -\n            handle: content\n            field:\n              type: markdown\n              antlers: true\n"
  },
  {
    "path": "resources/blueprints/collections/variables/variables.yaml",
    "content": "title: Variable\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n          -\n            handle: types\n            field:\n              type: terms\n              taxonomies:\n                - types\n              display: 'Variable Types'\n              mode: select\n"
  },
  {
    "path": "resources/blueprints/collections/widgets/widgets.yaml",
    "content": "title: Widgets\ntabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              required: true\n              validate:\n                - required\n          -\n            import: page\n          -\n            handle: screenshot\n            field:\n              type: assets\n              container: main\n              max_files: 1\n              folder: widgets\n          -\n            handle: screenshot_dark\n            field:\n              type: assets\n              container: main\n              max_files: 1\n              folder: widgets\n  sidebar:\n    display: Sidebar\n    sections:\n      -\n        fields:\n          -\n            handle: slug\n            field:\n              type: slug\n              localizable: true\n              validate:\n                - required\n                - 'new \\Statamic\\Rules\\UniqueEntryValue({collection}, {id}, {site})'\n              required: true\n"
  },
  {
    "path": "resources/blueprints/globals/global.yaml",
    "content": "tabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: pro_badges\n            field:\n              type: replicator\n              display: 'Pro Badges'\n              sets:\n                new_set_group:\n                  display: 'New Set Group'\n                  sets:\n                    pro_badge:\n                      display: 'Pro Badge'\n                      fields:\n                        -\n                          handle: artwork\n                          field:\n                            max_files: 1\n                            container: main\n                            folder: pro-badges/artwork\n                            type: assets\n                            display: Artwork\n                            width: 75\n                        -\n                          handle: icon\n                          field:\n                            max_files: 1\n                            container: main\n                            folder: pro-badges/icons\n                            type: assets\n                            display: Icon\n                            width: 25\n          -\n            handle: experimental_badges\n            field:\n              type: text\n          -\n            handle: powered_by\n            field:\n              type: text\n"
  },
  {
    "path": "resources/blueprints/navigation/docs.yaml",
    "content": "tabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              display: Title\n              type: text\n          -\n            handle: navigation_image\n            field: navigation.navigation_image\n          -\n            handle: image_width_override\n            field: navigation.image_width_override\n"
  },
  {
    "path": "resources/blueprints/navigation/extending_docs.yaml",
    "content": "tabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            handle: title\n            field:\n              type: text\n              display: Title\n          -\n            handle: navigation_image\n            field: navigation.navigation_image\n          -\n            handle: image_width_override\n            field: navigation.image_width_override\n"
  },
  {
    "path": "resources/blueprints/navigation/reference.yaml",
    "content": "tabs:\n  main:\n    display: Main\n    sections:\n      -\n        fields:\n          -\n            import: navigation\n"
  },
  {
    "path": "resources/blueprints/taxonomies/categories/categories.yaml",
    "content": "title: Categories\nsections:\n  main:\n    display: Main\n    fields:\n      -\n        handle: title\n        field:\n          type: text\n          required: true\n          validate:\n            - required\n      -\n        handle: content\n        field:\n          type: markdown\n          localizable: true\n      -\n        handle: icon\n        field:\n          mode: grid\n          container: main\n          folder: knowledge-base\n          restrict: false\n          allow_uploads: true\n          max_files: 1\n          display: Icon\n          type: assets\n          icon: assets\n          listable: hidden\n  sidebar:\n    display: Sidebar\n    fields:\n      -\n        handle: slug\n        field:\n          type: slug\n          required: true\n          validate:\n            - required\n"
  },
  {
    "path": "resources/css/-template.css",
    "content": "* {\n    border: 1px solid red;\n}"
  },
  {
    "path": "resources/css/README.css",
    "content": "/* GROUP NOTES\n===================================================  */\n/* This file contains detailed guide notes about CSS groups without cluttering CSS files */\n\n\n\n/* GROUP NOTES / FORMATTING / COMMENTS / DEFAULT FORMATTING\n===================================================  */\n/* By default comments should sit vertically above code so they're easily seen when scanning the CSS file. It's tempting to add them to the end of the line to save space but this makes them harder to scan / makes CSS harder to debug, and comments are stripped from production CSS anyway. */\n\n/* Here is an example of a comment */\n.something {\n    display: grid;\n    /* Here is a second example of a commment, e.g. Prevent overflow on screens less than em or px value */\n    grid-template-columns: repeat(auto-fit, minmax(min(100%, 24em), 1fr));\n}\n\n\n\n/* GROUP NOTES / FORMATTING / SPACING / SELECTORS\n===================================================  */\n/* Aims of formatting:\n\n    - Readability\n        - It's very important to be able to quickly understand the author's intentions when scanning CSS\n    - Scannability\n        - While spacing things can aid readability, it can also hinder scannability and comprehension\n        - We should try and save space where possible to improve scannability\n        - Because we use 4 spaces (rather than 2) readability is higher and we can generally visually afford to remove spaces between selectors\n        - Only keep a line between selectors if you're grouping themes (for example, layout selectors, positioning, etc.). Here is a good example of grouping themes, adding a line in between:\n\n        ```\n        c-flying-badges {\n            min-height: 100vh;\n            min-height: 100svh;\n\n            /* Needed for overflow: hidden; to work \n            position: relative;\n\n            display: grid;\n            align-content: center;\n            text-align: center;\n            /* Minimum vertical padding of --carousel-product-fake-celing if it exists, with a fallback if it doesn't\n            padding: var(--carousel-product-fake-ceiling, var(--spacing-4xl)) var(--spacing-xl);\n\n            & > * {\n                text-wrap: balance;\n                &:first-child {\n                    padding-block-start: 0;\n                }\n            }\n            & h2 {\n                font-size: var(--font-size-3xl);\n                line-height: var(--font-size-3xl-line-height);\n                max-width: var(--max-width-reading);\n                margin-inline: auto;\n            }\n            & p {\n                font-size: var(--font-size-s);\n                max-width: var(--max-width-reading-long);\n                margin-inline: auto;\n                &:last-of-type {\n                    margin-block-end: 0;\n                }\n            }\n\n            .c-flying-badges__badge {\n                position: absolute;\n                @supports not (animation-timeline: auto) {\n                    rotate: 25deg;\n                    &:nth-of-type(even) {\n                        rotate: -25deg;\n                    }\n                }\n                /* e.g. Pink badge\n                &:nth-of-type(1) {\n                    top: -2rem;\n                    left: -3rem;\n                }\n                /* e.g. Blue badge\n                &:nth-of-type(2) {\n                    bottom: -5%;\n                    left: 8%;\n                }\n                etc.\n        ```\n*/\n/*\n\n    Examples of where we can remove spacing:\n\n    1. Within Objects and Components there should be no line in between _selector_ rules, to keep scannability high. Example:\n        ```\n        .something {\n            \n        }\n        .something-else {\n\n        }\n    ```\n    2. Space in between different media queries can be eliminated without hindering readability\n    3. You can remove space between selectors where code is grouped because it's \"reset\" or \"base\" code. You almost never need to touch this code or comprehend the relationship between different selectors.\n    4. You can remove space between selectors where you'd like to logically group code, to help convey a theme. You might want to do this for \"reset\" CSS, \"utility\" CSS or, for example, where I am targetting headings:\n        ```\n        blockquote\n            /* e.g. >> /blog/intolerance\n            :is(h2) + & {\n                margin-block: 0 var(--spacing-lg);\n            }\n            /* e.g. >> /blog/setting-up-statamic-v3-on-macos-part-4-of-4-publishing-a-statamic-site\n            :is(h3) + & {\n                margin-block-start: var(--spacing-lg);\n            }\n        ```\n    5. There should be NO line between groups for Variable definitions at the top of the stylesheet (in the root)\n\n*/\n\n\n\n/* GROUP NOTES / FORMATTING / SPACING / PROPERTIES AND VALUES\n===================================================  */\n/*\n\n    1. Generally every property/value pair should be on a separte line, _except_ in the following circumstances\n    2. When different values or properties share very similar numbers or letters. Example:\n        ```\n        h2, .h2 {\n            font-size: var(--font-size-l); line-height: var(--font-size-l-line-height);\n        }\n    ```\n\n*/\n\n\n\n/* GROUP NOTES / FORMATTING / SPACING / GROUPING\n===================================================  */\n/*\n\n    There should be THREE LINES to separate each group, but _within_ Objects and Components there should be NO line in between groups, to keep scannability high. Example:\n        ```\n        /* GROUP COMPONENTS / SOMETHING\n        =================================================== \n        .something {\n            \n        }\n        .something-else {\n\n        }\n        /* GROUP COMPONENTS / SOMETHING / MQS\n        =================================================== \n        @media (min-width: 768px) {\n            .something {\n                \n            }\n        }\n    ```\n\n*/\n\n\n\n/* GROUP VARIABLES -- SPACING\n===================================================  */\n/* Cound be useful to use the max value for increasing spacing on hero text elements, e.g.\n\n    padding-inline-start: max(var(--spacing-2xl), 7vw);\n\n*/\n\n\n\n/* GROUP VARIABLES -- DECORATION -- TEXT -- SIZES\n=================================================== */\n/*\n\ne.g. Based on Augmented Fourth - https://type-scale.com/\n\n---\n\nFor thoughts on typography and scale see 'Scaling with vw and clamp units' in my typography.txt file in my wiki\n\n---\n\nCSS min(), max(), and clamp()\n\n- min() — the value used in all situations will be the smallest of the possibilities.\n- max() — the value used in all situations will be the largest of the possibilities.\n- clamp() — accepts three values or calculations: a minimum, preferred, and a maximum. The minimum or maximum will be used if the computed value falls below the minimum or above the maximum. The preferred value will be used if the computed value falls between the two. This allows the property value to adapt to changes in the element or page it is assigned to, while remaining between the minimum and maximum values.\n\nh1 {\n    font-size: clamp(\n    1.1em,  // min - the value is set at this by default. This sholud be a good legible value on smaller screens. Don't worry too much about dramatic type scale.\n    5vw,    // value - when 5vw is bigger than 1.5rem then the font-size becomes 5vw. Don't worry too much about dramatic type scale.\n    1.414em // max - when 5vw hits 1.414em, it stays there as the max. Here is where you should concentrate on aligning to a more dramatic type scale.\n    );\n}\n\nhttps://developer.mozilla.org/en-US/docs/Web/CSS/clamp\nThe <h1> element's font-size is set as clamp(1.8rem, 2.5vw, 2.8rem). This means that the font-size will be set at 1.8rem, until the computed value of 2.5vw becomes greater than that of 1.8rem. At this point, font-size will be set at 2.5vw, until 2.5vw's computed value becomes greater than that of 2.8rem. At this point, the font-size will be set at 2.8rem. clamp() allows you to set a minimum and maximum value.\n\n---\n\nVariable font-size and line-height example...\n(variables are bumped up and down in :root)\n\nfont-size: var(--font-size-xl);\nline-height: var(--font-size-height-l-1);\n\n*/\n\n\n\n/* GROUP ELEMENTS / FRAMEWORK / TEXT / LINK STYLES {/ LINKS} / ACCESSIBILITY / FOCUS\n=================================================== */\n/* Notes...\n\n    Focus = obvious\n    hover = subtle\n\n    - After some thinking I've concluded that...\n        - A _really_ obvious outline maybe combined with a soft hue'd background is the best for outline styles\n        - Use outline on links but box-shadow if you're dealing with buttons or form elements with a border-radius\n        - It's best to make outlines for keyboard users extremely obvious but then remove them totally for non-keyboard users (see 'Remove Focus Styles for Mouse Users' below)\n            - I think extreme both sides is better than going for a middle of the road approach which still offends designers but is very easy to use for keyboard users\n\n    e.g. \n\n    Example 1 - taken from apple.com\n    ---------\n    .el:focus {\n        outline: 4px solid rgba(0,125,250,0.6);\n        outline-offset: 1px;\n    }\n\n    Example 2 - box-shadow example from Statamic cpanel\n    ---------\n    .el:focus {\n        outline: none;\n        box-shadow: 0 0 0 3px rgba(0,125,255,.25);\n    }\n\n    Example 3 - apple.com but more subtle\n    ---------\n    .el:focus {\n        outline: 4px solid white;\n        outline-offset: 4px;\n    }\n\n    Example 4\n    ---------\n    .el:focus {\n        outline: 3px solid hsla(var(--color-turquoise-hsl),0.2);\n        text-decoration: none;\n        background: hsla(var(--color-turquoise-hsl),0.2);\n        border-bottom: 2px solid hsla(var(--color-turquoise-hsl),0.8);\n    }\n\n*/\n/* .u-focus-style-1 a:focus,\n.c-entry-content a:focus {\n    outline: 3px solid hsla(var(--color-turquoise-hsl),0.2);\n    text-decoration: none;\n    background: hsla(var(--color-turquoise-hsl),0.2);\n    border-bottom: 2px solid hsla(var(--color-turquoise-hsl),0.8);\n} */\n\n/* Notes...\n\n    - Use \"u-link-style-custom\" prefix then these are pretty much rare/1 off\n    - Still include them in this group so we can see all link styles for easy maintenance\n\n*/\n\n\n\n/* GROUP ELEMENTS / FRAMEWORK / TEXT / LINK STYLES {/ LINKS} / ACCESSIBILITY / HOVER\n=================================================== */\n/* Notes...\n\n    Focus = obvious\n    hover = subtle\n\n    Subtle effects such as going from light gray to black are best for hover. We want to gentle suggest rather than pop out, like we do with focus.\n\n    - Consider descending from p so we don't affect buttons e.g. reply button for blog comments\n    - Consider darkening the underline/border-bottom color on hover, or removing the border on hover\n    - Consider going from light gray to black e.g. navigation hover events\n\n    e.g. \n\n    .el {\n        color: #eee;\n    }\n    \n    .el:hover {\n        color: black;\n    }\n\n*/\n\n\n\n/* GROUP ELEMENTS / FRAMEWORK / (NON CORE) / TEXT / LINK STYLES {/ LINKS} / ACCESSIBILITY / FOCUS\n=================================================== */\n/* Notes...\n\n    Rian Rietveld, Twitter, on 2018-03-08:\n    - keep a good color contrast for text/borders against the background\n    - not by color alone, so don't only change the color (for color blind users)\n    - in WCAG 2.1 the borders also need to comply to a good contrast\n\n    For focusing on images you could also consider using box-shadow so you can get border radius e.g. box-shadow: var(--box-shadow-focus);\n\n    Other tips:\n    - **https://www.gov.uk/ is a great example of how to handle focus styles**\n    - Do not make colors faint; make them obvious\n    - Try to keep focus colors consistent where possible. You may want to avoid changing colors on buttons though, because it's gross\n    - Use box shadow if you'd like to use border-radius\n\n*/\n/* GROUP ELEMENTS / FRAMEWORK / (NON CORE) / TEXT / LINK STYLES {/ LINKS} / EXTERNAL\n=================================================== */\n/* Source http://css-tricks.com/snippets/jquery/target-only-external-links/ */\n/* a:not([href^=\"http://brewpixel.com\"]):not([href^=\"#\"]):not([href^=\"/\"]) {\n\n} */\n\n\n\n/* GROUP COMPONENTS / FRAMEWORK / BUTTONS\n=================================================== */\n/* Notes...\n\n    Wrap the .c-btn around the a href instead of the other way around because this is the most valid HTML.\n    http://stackoverflow.com/questions/5320404/wrap-link-a-around-div\n\n    <div class=\"btn\">\n        <a href>\n        </a>\n    </div>\n\n*/\n/* GROUP COMPONENTS / FRAMEWORK / BUTTONS / ACCESSIBILITY / HOVER\n=================================================== */\n/* These should be slightly different to focus states. Subtle effects such as changing the background color from blue to darkblue are best for hover. We want to gently suggest rather than pop out (opposite of focus states).\n\n    - Consider darkening the background color slightly e.g. blue to darkblue\n    - Here is a good example...\n    https://material-components.github.io/material-components-web-catalog/#/component/button\n\n*/\n.c-btn a:hover {\n    /* Cancel link hover effect */\n    border-bottom: 0;\n}\n\n/* 1 */\n/* .c-btn--background-transition-example a:hover {\n    background-color: var(--color-brown-dark);\n} */\n\n/* 2 */\n/* .c-btn--2 a:hover {\n    \n} */\n\n\n\n/* GROUP UTILITIES / ANIMATION / IO FRAMEWORK (Authored by me)\n=================================================== */\n/* Here's an example of me using a hidden trigger:\n\n@layer scope {\n    /*\n    [1] Flip the colours on .o-scroll-snap__slide--flip-colours\n    [2] Except when there's a hidden trigger.\n\n    .s-social:has(.o-scroll-snap__slide--flip-colours [data-io-seen]):not(:has([data-io-hidden-trigger])) .c-site-header * {\n        color: black;\n        transition: color 0.5s ease-in 0s;\n    }\n    [3] When the slide contains an explicit trigger then use this to flip the .c-site-header colours\n    .s-social:has(.o-scroll-snap__slide--flip-colours [data-io-hidden-trigger][data-io-seen]) .c-site-header * {\n        color: black;\n    }\n}\n*/"
  },
  {
    "path": "resources/css/base/cp.css",
    "content": ":root {\n    --theme-color-primary: oklch(0.274 0.006 286.033);\n    --theme-color-gray-50: oklch(0.985 0 0);\n    --theme-color-gray-100: oklch(0.967 0.001 286.375);\n    --theme-color-gray-200: oklch(0.92 0.004 286.32);\n    --theme-color-gray-300: oklch(0.871 0.006 286.286);\n    --theme-color-gray-400: oklch(0.705 0.015 286.067);\n    --theme-color-gray-500: oklch(0.552 0.016 285.938);\n    --theme-color-gray-600: oklch(0.442 0.017 285.786);\n    --theme-color-gray-700: oklch(0.37 0.013 285.805);\n    --theme-color-gray-800: oklch(0.274 0.006 286.033);\n    --theme-color-gray-850: oklch(0.236 0.006 286.015);\n    --theme-color-gray-900: oklch(0.21 0.006 285.885);\n    --theme-color-gray-950: oklch(0.141 0.005 285.823);\n    --theme-color-success: oklch(0.792 0.209 151.711);\n    --theme-color-danger: oklch(0.577 0.245 27.325);\n    --theme-color-body-bg: oklch(0.967 0.001 286.375);\n    --theme-color-body-border: transparent;\n    --theme-color-dark-body-bg: oklch(0.21 0.006 285.885);\n    --theme-color-dark-body-border: oklch(0.141 0.005 285.823);\n    --theme-color-content-bg: linear-gradient(to right, hsl(0,0%,99%), #ffffff);\n    --theme-color-content-border: oklch(0.92 0.004 286.32);\n    --theme-color-dark-content-bg: oklch(0.21 0.006 285.885);\n    --theme-color-dark-content-border: oklch(0.141 0.005 285.823);\n    --theme-color-global-header-bg: oklch(0.274 0.006 286.033);\n    --theme-color-dark-global-header-bg: oklch(0.274 0.006 286.033);\n    --theme-color-progress-bar: oklch(93.86% 0.2018 122.24);\n    --theme-color-ui-accent: oklch(0.274 0.006 286.033);\n    --theme-color-dark-ui-accent: oklch(0.141 0.005 285.823);\n    --theme-color-switch-bg: var(--theme-color-ui-accent);\n    --theme-color-dark-switch-bg: var(--theme-color-dark-ui-accent);\n}\n"
  },
  {
    "path": "resources/css/base/elements/elements.css",
    "content": "@layer elements {\n    body {\n        min-height: 100vh;\n\n        font-family: var(--font-family-main);\n        font-weight: var(--font-family-main-weight-normal);\n        /* https://app.typographychecklist.com/ - 23. Use standard ligatures but avoid using discretionary ligatures in body text */\n        font-feature-settings: \"kern\", \"liga\", \"clig\", \"calt\";\n        /* https://css-tricks.com/scrollbar-reflowing/ */\n        scrollbar-gutter: stable both-edges;\n\n        /* --mq-text-bump-1-after to --mq-text-bump-2-before */\n        @media (width >= 1025px) and (width < 1441px) {\n            /* Decrease */\n            font-size: 90%;\n        }\n        /* --mq-text-bump-2-after */\n        @media (width >= 1441px) {\n            /* Decrease */\n            font-size: 96%;\n        }\n    }\n}\n\n/* GROUP ELEMENTS\n=================================================== */\n@layer elements {\n    /* GROUP ELEMENTS / FRAMEWORK\n    =================================================== */\n    ::selection {\n        background: light-dark(hsl(var(--color-purple-hue), 60%, 90%), hsl(var(--color-purple-hue) 60% 30%));\n        color: var(--color-primary-text);\n    }\n    .torchlight ::selection {\n        background: light-dark(hsl(229deg 20% 35%), hsl(229deg 20% 35%));\n        color: white;\n    }\n    select {\n        /* prevent long options making the box longer */\n        max-width: 100%;\n    }\n    mark {\n        background: var(--color-green);\n        /* e.g. >> /static-caching */\n        padding-inline: var(--spacing-3xs);\n    }\n    textarea {\n        line-height: 1.5;\n    }\n    html {\n        /* Consider disabling this if you have pages that require lots of cmd + f https://css-tricks.com/downsides-of-smooth-scrolling/ */\n        scroll-behavior: smooth;\n        /* Opinion on this will differ depending on the font you choose. Do not carpet bomb line height with a * selector though */\n        line-height: 1.5;\n        /* https://web.dev/accent-color/ */\n        accent-color: var(--color-form-accent);\n    }\n    pre, code {\n        /* https://dbushell.com/2024/11/05/webkit-font-smoothing/ */\n        -webkit-font-smoothing: antialiased;\n        font-family: var(--font-family-code);\n        font-weight: var(--font-family-code-weight-normal);\n        + p:not(ol p, ul p) {\n            /* Not on this page e.g. >> /extending/vite-in-addons */\n            padding-block-start: var(--spacing-sm);\n        }\n        /* Put some space between paragraphs and code blocks, except if it's in a tip */\n        p:has(+ &):not(.c-tip p, ol p) {\n            margin-block-end: var(--spacing-md);\n        }\n        li & {\n            /* Make code in bullet points slightly smaller */\n            font-size: 1.025rem;\n        }\n    }\n    code {\n        /* Stop code elements with a long path in causing overflow. Source: https://stackoverflow.com/questions/1165497/how-to-prevent-text-from-overflowing-in-css#3791058 */\n        word-wrap: break-word;\n    }\n    /* Only descending from certain tags, otherwise torchlight is used */\n    :is(h2, h3, h4, p, figure, li) {\n        code {\n            /* e.g. try <code>long text here</code> and you'll see that it overflows on smaller screens */\n            overflow-x: scroll;\n            padding: 0.07rem 0.3rem;\n            margin: 0.15rem;\n            background: var(--color-code-background);\n            border-radius: 5px;\n            font-family: var(--font-family-code);\n            font-weight: var(--font-family-code-weight-medium);\n            letter-spacing: -0.2px;\n            font-size: 90%;\n        }\n    }\n    :is(h2, h3, h4) code {\n        /* Decrease */\n        letter-spacing: -0.7px;\n        font-size: 0.95em;\n        /* Make it stand out more */\n        --color-code-background: var(--color-purple-light-1);\n    }\n\n    figure code {\n        /* Make a little more readable when in a caption because we don't have to worry about surrounding prose */\n        padding: 0.2rem 0.3rem;\n        margin: 0.25rem;\n    }\n\n    /* Prevent images stretching too much */\n    img {\n        /* Maintain aspect ratio */\n        height: auto;\n        .s-main &:not(.c-entry-content *) {\n            /* Some image treatment, particularly in dark mode */\n            filter:\n                contrast(108%)\n                saturate(1.075)\n            ;\n            @container style(--color-scheme: dark) {\n                filter:\n                    hue-rotate(-12deg)\n                    contrast(110%)\n                    opacity(85%)\n                    saturate(1.1)\n                ;\n            }\n        }\n    }\n    /* https://www.youtube.com/watch?v=2lyDv0wOQuQ */\n    img, picture, svg, video {\n        display: block;\n        max-width: 100%;\n    }\n    /* Reset */\n    h1, h2, h3, h4, h5, h6,\n    p,\n    ul, ol,\n    figure, blockquote {\n        margin-block: 0;\n    }\n    blockquote {\n        margin: 0;\n        quotes: \"“\" \"”\" \"‘\" \"’\";\n        /* Only where there are no children */\n        &:not(:has(*)) {\n            &::before {\n                content: open-quote;\n            }\n            &::after {\n                content: close-quote;\n            }\n            /* https://chriscoyier.net/2023/11/27/the-hanging-punctuation-property-in-CSS */\n            text-indent: -0.45em;\n        }\n        @supports (hanging-punctuation: first) {\n            text-indent: 0;\n            hanging-punctuation: first;\n        }\n    }\n    /* Reset */\n    figure {\n        margin-inline: 0;\n        & img {\n            /* Push the caption away from the image */\n            margin-block-end: var(--spacing-2xs);\n        }\n    }\n    /*\n        - https://youtu.be/UWFrl79092w?si=T0cPhMFkQhdkoLiA&t=75\n        - Excluding navigation and pagination\n    */\n    ul:not([class], nav *, [class*=\"pagi\"] *) {\n        padding-inline-start: 0;\n    }\n    ul[class],\n    *[class] > ul {\n        /* Try to target components that have a list, such as .c-paging > ul, and remove default list styling */\n        list-style: none;\n    }\n    /* Don't affect nav */\n    .s-main li:not(:last-child) {\n        padding-block-end: var(--spacing-2xs);\n    }\n    dl,\n    dt,dd,\n    th,\n    td {\n        padding-block-end: var(--spacing-xs);\n    }\n    p {\n        margin-block-end: var(--spacing-md);\n        &:last-child {\n            margin-block-end: 0;\n        }\n        &:has(+ blockquote) {\n            margin-block-end: var(--spacing-xl);\n        }\n    }\n    a {\n        color: inherit;\n    }\n    /* Don't affect Statamic's lists here, which contain p tags like this:\n        <ol>\n            <li>\n                <p>Sponsor games</p>\n            </li>\n            <li>\n                <p>Promote your game</p>\n            </li>\n            <li>\n                <p>Sponsor live events</p>\n            </li>\n        </ol>\n    */\n    .s-main :is(p:not(li p), ol, ul):not(:has(li p)) {\n        line-height: var(--font-size-reading-line-height);\n        list-style-position: inside;\n        padding-inline-start: 0;\n    }\n    /* For Statamic's lists use a different method: */\n    ol:has(li p) {\n        /* e.g. >> /oauth */\n        padding-inline-start: 0;\n    }\n    p + ul {\n        padding-block-start: var(--spacing-xs);\n    }\n    strong {\n        font-weight: var(--font-family-main-weight-heavy);\n    }\n    /* Only target our own name-spaced components so that we don't affect plugins */\n    :is([class^=\"c-\"], [class^=\"o-\"]) svg {\n        fill: currentColor;\n        /* For SVG icons */\n        width: 1em;\n        height: 1em;\n    }\n    :is(h1, h2, h3, h4, h5, h6, p) svg {\n        vertical-align: baseline;\n        margin-inline-end: var(--spacing-2xs);\n    }\n    /* GROUP ELEMENTS / FRAMEWORK / MAIN\n    =================================================== */\n    /* Target the main element with a class rather than the main tag, because the main tag can still be used elsewhere e.g. Meilisearch uses it for its modal */\n    .s-main {\n        &:focus {\n            outline: none;\n        }\n        padding-block-end: var(--spacing-4xl);\n        /* --mq-nav-open-after */\n        @media (width >= 1100px) {\n            padding-block-end: var(--spacing-vh-sm);\n        }\n        /* Custom */\n        @media (width < 1024px) {\n            /* Only when followed by the footer */\n            &:has(+ footer) {\n                margin-block-end: var(--spacing-3xl);\n            }\n        }\n        & > *:last-child {\n            padding-block-end: 0;\n        }\n        /* --mq-grid-after */\n        @media (width >= 1000px) {\n            display: grid;\n            grid-template-columns: var(--grid-template-columns-main);\n            grid-template-areas:\n                \"sidebar-1 breadcrumbs sidebar-2\"\n                \"sidebar-1 content     sidebar-2\"\n            ;\n            gap: var(--spacing-2xl) var(--spacing-lg);\n            max-width: var(--max-width-1);\n            margin-inline: auto;\n        }\n        /* --mq-nav-open-after */\n        @media (width >= 1100px) {\n            /* Decrease */\n            row-gap: var(--spacing-2xl);\n            padding-inline: var(--spacing-lg);\n            grid-template-rows: 2.5rem auto;\n        }\n    }\n    /* GROUP ELEMENTS / FRAMEWORK / HEADINGS\n    =================================================== */\n    /* https://developer.chrome.com/blog/CSS-text-wrap-balance */\n    h1, h2, h3, h4, h5, h6 {\n        /* Not .c-entry-content headings because it feels a bit weird */\n        &:not(.c-entry-content &) {\n            text-wrap: balance;\n        }\n    }\n    blockquote, figcaption, .s-main p {\n        text-wrap: pretty;\n    }\n    figcaption {\n        /* e.g. >> Docs Home */\n        font-family: var(--font-family-main);\n        font-weight: var(--font-family-main-weight-medium);\n    }\n    h1, .h1, h1 a, .h1 a, h2, .h2, h2 a, .h2 a, h3, .h3, h3 a, .h3 a, h4, .h4, h4 a, .h4 a {\n        /* Not when headings are inside li, which they sometimes are, e.g. /tips */\n        &:not(li *) {\n            font-family: var(--font-family-serif);\n            font-weight: var(--font-family-serif-weight-normal);\n            /* https://app.typographychecklist.com/ 91 - Use discretionary ligatures and swashes in headlines */\n            font-feature-settings: \"kern\", \"liga\", \"clig\", \"calt\", \"dlig\", \"swsh\";\n            /* Override iOS Safari's user agent stylesheet */\n            text-decoration: none;\n            color: inherit; /* If you use a link inside a heading, inherit the colour so it's not the default browser-purple */\n        }\n    }\n    h4, h4 a, h5, h5 a, h6, h6 a {\n        /* When a heading is inside a link, escape the link colour. Use the theme-aware primary text var — `initial` resolves to black in Safari/Firefox and breaks dark mode. */\n        color: var(--color-primary-text);\n    }\n    &:is(h4, h4 a, h5, h5 a, h6, h6 a) {\n        font-family: var(--font-family-serif);\n        font-weight: var(--font-family-serif-weight-normal);\n        /* Override iOS Safari's user agent stylesheet */\n        text-decoration: none;\n        color: inherit; /* If you use a link inside a heading, inherit the colour so it's not the default browser-purple */\n    }\n    h1 {\n        font-size: var(--font-size-4xl); line-height: var(--font-size-4xl-line-height);\n    }\n    h2 {\n        font-size: var(--font-size-3xl); line-height: var(--font-size-3xl-line-height);\n    }\n    h3 {\n        font-size: var(--font-size-2xl); line-height: var(--font-size-2xl-line-height);\n    }\n    h4 {\n        font-size: var(--font-size-xl); line-height: var(--font-size-xl-line-height);\n    }\n    h5 {\n        font-size: var(--font-size-lg); line-height: var(--font-size-lg-line-height);\n    }\n    h6 {\n        font-size: var(--font-size-md); line-height: var(--font-size-md-line-height);\n    }\n    /* GROUP ELEMENTS / FRAMEWORK / HEADINGS / HIGH PRIORITY / VERTICAL SPACING\n    =================================================== */\n    h1 {\n        padding-block-end: var(--spacing-md);\n    }\n    h2 {\n        /* E.g. h2 following article intro text */\n        padding-block-end: var(--spacing-md);\n    }\n    * + h2,\n    article + article {\n        /* E.g. h2 following article intro text */\n        padding-block: var(--spacing-sm) var(--spacing-md);\n        &:has(+ h3) {\n            /* e.g. >> /assets */\n            padding-block-end: 0;\n        }\n    }\n    /* GROUP ELEMENTS / FRAMEWORK / HEADINGS / LOW PRIORITY / VERTICAL SPACING\n    =================================================== */\n    h3 {\n        /* e.g. >> /contributing */\n        padding-block: var(--spacing-md) var(--spacing-sm);\n    }\n    h4 {\n        /* e.g. >> /frontend/augmentation */\n        padding-block: var(--spacing-md) var(--spacing-sm);\n        /* e.g. /vue-components/publish-components */\n        h3 + & {\n            padding-block-start: 0;\n        }\n        &:has(code) {\n            /* e.g. >> /forms */\n            padding-block-start: var(--spacing-lg);\n        }\n    }\n    h5 {\n        padding-block: var(--spacing-2xl) var(--spacing-lg);\n    }\n}\n@layer components {\n    /* Put these at the component level e.g. so we make sure we override something like .c-site-footer li { padding-block-end: var(--spacing-2xs); } */\n    /* Nested lists */\n    ol ol,\n    ol ol li:last-child,\n    ul ul,\n    ul ul li:last-child {\n        padding-block-end: 0;\n    }\n\n    button svg,\n    .c-btn :is([src*=\"svg\"], svg) {\n        font-size: 1.1em;\n    }\n}\n/* GROUP ELEMENTS / FRAMEWORK / HEADINGS / SCOPE ADJUSTMENTS\n=================================================== */\n@layer scope {\n    .c-tip,\n    .c-bordered-image {\n        &:has(+ h2) {\n            /* Increase e.g. >> /assets */\n            margin-block-end: calc(var(--spacing-3xl) + var(--spacing-sm));\n        }\n    }\n    /* Decrease bottom padding/margin elements previous to hr */\n    .c-entry-content :has(+ hr) {\n        /* Needed for lists */\n        padding-block-end: 0;\n        margin-block-end: 0;\n    }\n    /* Decrease bottom padding/margin following previous to hr e.g. >> /7 on the HTML prototype */\n    /* Headings */\n    .c-entry-content hr + :is(h2, h3, h4) {\n        padding-block-start: 0;\n    }\n    /* GROUP ELEMENTS / HR\n    =================================================== */\n    /* [1] This first technique is used to fake an hr.\n        - We don't want to use this when we follow a header because it would then appear to high up on the page e.g. /assets\n        - Target direct descendents so that we don't affect components such as .c-icon-grid e.g. /deploying\n    */\n    hr, .markdown h2:not(header + h2), article > h2:not(header + h2) {\n        --border-width: 0.3px;\n    }\n    .markdown h2:not(header + h2), article > h2:not(header + h2, hr + h2, .c-doc-tabs + h2) {\n        position: relative;\n        --distance: var(--spacing-2xl);\n        margin-block-start: var(--distance);\n        p:has(+ &) {\n            padding-block-end: var(--spacing-md);\n        }\n        figure:has(+ &) + h2 {\n            /* e.g. >> /quick-start-guide */\n            margin-block-start: var(--spacing-4xl);\n        }\n        &::before, &::after {\n            content: \"\";\n            position: absolute;\n            top: calc(0% - var(--distance) / 4 - 10px);\n        }\n        &::before {\n            background: linear-gradient(to right, var(--color-purple), var(--color-pink), var(--color-blue));\n            opacity: 0.5;\n            inline-size: 100%;\n            block-size: 1px;\n        }\n        &:after {\n            inline-size: 100%;\n\n            position: absolute;\n            left: 0;\n            content: \"\";\n            inline-size: 100%;\n            border: var(--border-width) dashed var(--color-body-background);\n        }\n    }\n    /* [/2] This second technique is for real hrs */\n    hr {\n        border-image: linear-gradient(to right, var(--color-purple), var(--color-pink), var(--color-blue)) 1;\n        border-width: var(--border-width);\n        position: relative;\n        opacity: 0.5;\n        margin-block: var(--spacing-3xl);\n\n        /* I think this is the only way we can have a dashed border-image */\n        &:after {\n            position: absolute;\n            content: \"\";\n            inset: 0;\n            top: calc(0% - var(--border-width));\n            border: var(--border-width) dashed var(--color-body-background);\n        }\n    }\n}\n\n\n\n/* GROUP ELEMENTS / NAV\n=================================================== */\n@layer elements {\n    nav {\n        ul {\n            list-style: none;\n            margin: 0;\n            padding: 0;\n        }\n        a {\n            color: inherit;\n        }\n        svg:last-child {\n            /* subnav arrow */\n            margin-inline-start: var(--spacing-3xs);\n            width: 0.7rem;\n            color: var(--color-primary-text);\n\n            *:has(> &) {\n                /* The SVG container */\n                display: flex;\n            }\n        }\n    }\n}\n@layer objects {\n    .o-subnav {\n        transition: var(--transition-transform);\n    }\n    .o-subnav--open {\n        transform: scaleY(-1);\n    }\n}\n\n\n\n/* GROUP ELEMENTS / FRAMEWORK / TEXT / LINK STYLES {/ LINKS }\n=================================================== */\n/* Notes...\n\n    - Link styles are stored as group selectors rather than scattered, since we have multiple properties here.\n    - Do not put transitions on all links, it makes keyboard navigation feel slower. Instead, use transition animations on an individual basis e.g. skip-to-content\n\n*/\n\n/* Default */\n@layer elements {\n    a,\n    button {\n        text-decoration-skip-ink: auto;\n        text-underline-offset: 0.2rem;\n        text-decoration-thickness: 1px;\n        text-decoration-color: light-dark(var(--color-primary-accent), var(--color-link));\n        color: var(--color-link);\n    }\n}\n@layer elements {\n    /* Don't target components like .c-icon-grid on /installing */\n    .c-entry-content a:not([class^=\"c-\"] *) {\n        font-weight: var(--font-family-serif-weight-medium);\n        &[target=\"_blank\"] {\n            /* To handle the external link icon e.g. <a href=\"https://aws.amazon.com/s3\" target=\"_blank\">Amazon S3 bucket{{ svg src=\"external-link\" }}</a> */\n            display: inline-flex;\n            align-items: center;\n            gap: 0.2rem;\n            padding-inline-end: 0.2rem;\n            svg {\n                font-size: 0.85em;\n                margin-inline-end: 0;\n            }\n        }\n    }\n}\n/* GROUP ELEMENTS / FRAMEWORK / TEXT / LINK STYLES {/ LINKS} / ACCESSIBILITY / HOVER\n=================================================== */\n@layer components {\n    .c-entry-content a:not(.c-btn):hover {\n        text-decoration-color: var(--color-purple-dark);\n        color: var(--color-purple-dark);\n    }\n}\n/* GROUP ELEMENTS / TEXT / LINK STYLES {/ LINKS} / ACCESSIBILITY / FOCUS\n=================================================== */\n@layer base-docs {\n    :is(a, button, summary):focus-visible,\n    /* focus-visible on input so we don't cause an outline when ticking cookie choices */\n    input:focus-visible,\n    select:focus-visible,\n    textarea:focus-visible {\n        outline: 3px solid var(--color-focus);\n    }\n    /* ensure the focus outline is effective when a link is wrapped around a heading */\n    a:has(> h2, h3, h4, h5, h6) {\n        display: inline-block;\n    }\n    a:focus {\n        p & {\n            /* Offset for text */\n            outline-offset: 5px;\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/base/elements/tables.css",
    "content": "/* GROUP ELEMENTS / FRAMEWORK / (NON CORE) / TABLES\n=================================================== */\n/* Notes...\n\n    - .c-table should be the wrapper\n    - table should be the actual table\n\n*/\n\n/* HTML Example...\n\n    <div class=\"c-table\">\n        <table>\n            <thead>\n                <tr>\n                    <th>Example URL</th>\n                    <th>Route Pattern Rule</th>\n                </tr>\n            </thead>\n            <tbody>\n                <tr>\n                    <td><code>/blog/2021-12-24/merry-christmas</code></td>\n                    <td><code>/blog/{year}-{month}-{day}/{slug}</code></td>\n                </tr>\n                <tr>\n                    <td><code>/blog/2020/still-bored</code></td>\n                    <td><code>/blog/{year}/{slug}</code></td>\n                </tr>\n                <tr>\n                    <td><code>/blog/happy-new-year</code></td>\n                    <td><code>/blog/{slug}</code></td>\n                </tr>\n                <tr>\n                    <td><code>/evergreen-syle</code></td>\n                    <td><code>/{slug}</code></td>\n                </tr>\n            </tbody>\n        </table>\n    </div>\n\n*/\n/* GROUP ELEMENTS / TABLES\n=================================================== */\n@layer elements {\n    .c-table {\n        p:has(+ &) {\n            margin-block-end: 0;\n        }\n        &:not(:last-child) {\n            padding-block-end: var(--spacing-2xs);\n        }\n        h2 + & table {\n            /* e.g. >> /users */\n            margin-block-start: var(--spacing-xs);\n        }\n        :is(h3, h4, h5, h6) + & table {\n            /* e.g. >> /graphql */\n            margin-block-start: 0;\n        }\n    }\n\n    table {\n        width: 100%;\n        max-width: 100%;\n        background: var(--color-gradient-full-light-3);\n        @container style(--color-scheme: dark) {\n            background: var(--color-gradient-full-light-4);\n        }\n        border-collapse: collapse;\n        border-spacing: 0;\n        margin-block: var(--spacing-2xl);\n        /* An heading followed by a table e.g. >> /widgets/all-widgets */\n        header:has(:only-child) + * & {\n            margin-block-start: var(--spacing-md);\n        }\n        font-family: var(--font-family-main);\n\n        td {\n            line-height: var(--font-size-md-line-height);\n            font-weight: var(--font-family-main-weight-normal);\n        }\n\n        .c-entry-content & strong {\n            font-weight: var(--font-family-main-weight-strong);\n        }\n    }\n\n    th {\n        text-align: left;\n    }\n\n    .c-table tbody tr {\n        &:first-child td {\n            padding-block-start: var(--spacing-lg);\n        }\n        &:last-child td {\n            padding-block-end: var(--spacing-xl);\n        }\n    }\n\n    table > thead > tr > th,\n    table > tbody > tr > th,\n    table > tfoot > tr > th,\n    table > thead > tr > td,\n    table > tbody > tr > td,\n    table > tfoot > tr > td {\n        padding: 0.6rem 2rem;\n        padding-inline-end: 20px;\n        line-height: 1.43;\n        vertical-align: top;\n        /* border-top: 1px dashed var(--color-pink-light-1); */\n    }\n\n    table tr td:first-child {\n        &, & * {\n            /* Make the first \"column\" stand out. The * selector is there just in case there's also code, e.g. /content-queries */\n            font-weight: var(--font-family-main-weight-heavy);\n            code {\n                font-weight: var(--font-family-code-weight-strong);\n            }\n        }\n    }\n\n    table > thead > tr > th {\n        padding-block: 1.1rem;\n        vertical-align: bottom;\n        border-block-end: 1px dotted var(--color-pink-light-1);\n        text-transform: uppercase;\n    }\n\n    table > caption + thead > tr:first-child > th,\n    table > colgroup + thead > tr:first-child > th,\n    table > thead:first-child > tr:first-child > th,\n    table > caption + thead > tr:first-child > td,\n    table > colgroup + thead > tr:first-child > td,\n    table > thead:first-child > tr:first-child > td {\n        border-top: 0;\n    }\n\n    table > tbody + tbody {\n        border-top: 2px solid var(--color-pink-light-1);\n    }\n\n    table table {\n        background: white;\n    }\n\n    /* table > thead > tr > th,\n    table > tbody > tr > th,\n    table > tfoot > tr > th,\n    table > thead > tr > td,\n    table > tbody > tr > td,\n    table > tfoot > tr > td {\n        border: 1px solid var(--color-pink-light-1);\n    } */\n\n    table > thead > tr > th,\n    table > thead > tr > td {\n        border-bottom-width: 2px;\n    }\n    /* GROUP ELEMENTS / TABLES / (JAY ADDED)\n    =================================================== */\n    /* Custom */\n    @media (width < 768px) {\n        .c-table {\n            overflow-y: hidden;\n            overflow-x: scroll;\n            /* Make sure we can see the shadow */\n            padding-inline-start: 0.2rem;\n            /* Balance out between the container and table so that the scrollbar appears halfway between the component and the following element */\n            margin-block-end: calc(var(--spacing-2xl) / 2);\n            table {\n                margin-block-end: calc(var(--spacing-2xl) / 2);\n            }\n            -ms-overflow-style: -ms-autohiding-scrollbar;\n            -webkit-overflow-scrolling: touch;\n            width: 100%;\n            box-shadow: var(--box-shadow-s-inset-right);\n        }\n\n        table > thead > tr > th,\n        table > tbody > tr > th,\n        table > tfoot > tr > th,\n        table > thead > tr > td,\n        table > tbody > tr > td,\n        table > tfoot > tr > td {\n            &,\n            /* Statamic adds p tags */\n            & p {\n                white-space: nowrap;\n            }\n        }\n        table > tbody > tr > td:last-child p {\n            /* If the last column contains a paragraph make sure it's a sufficient length e.g. >> /tags/form-create > Variables */\n            min-width: 300px;\n        }\n    }\n    /* GROUP ELEMENTS / TABLES / (MODIFICATIONS) / BORDERED\n    =================================================== */\n    table {\n        border-radius: var(--border-radius-xl);\n        box-shadow: var(--box-shadow-pink-light);\n    }\n    /* GROUP ELEMENTS / TABLES / (JAY ADDED) / (CROSS POLLINATED)\n    =================================================== */\n    /* =JFG. Fix it so tabular info is easier to digest */\n    table {\n        /* e.g. >> /recent-updates */\n        font-size: 0.9rem;\n    }\n}"
  },
  {
    "path": "resources/css/base/reset.css",
    "content": "/* GROUP RESET / FRAMEWORK - VIEW TRANSITIONS\n=================================================== */\n@view-transition {\n    navigation: auto;\n}\n\n@layer base-docs {\n    /* GROUP RESET / FRAMEWORK - Taken from html5boilerplate.com\n    =================================================== */\n    audio,canvas,iframe,img,svg,video {\n        vertical-align: middle;\n    }\n    /* GROUP RESET / FRAMEWORK / NORMALISE\n    =================================================== */\n    /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n    html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}\n    /* GROUP RESET / JAY GEORGE\n    =================================================== */\n    *,\n    *::before,\n    *::after {\n        /* Prevent flickering.\n        When you click a link or a button or a div that has a click function on it,\n        usually a gray box flickers. The below prevents that. Source: http://goo.gl/MZ4Dr5 */\n        -webkit-tap-highlight-color: rgba(0,0,0,0);\n        /* http://www.paulirish.com/2012/box-sizing-border-box-ftw/ */\n        box-sizing: border-box;\n    }\n    iframe {\n        width: 100%;\n        border: none;\n    }\n}"
  },
  {
    "path": "resources/css/base/variables.css",
    "content": ":root {\n    /* GROUP VARIABLES -- LAYOUT -- GRID\n    =================================================== */\n    /* --mq-nav-open-after */\n    @media (width >= 1100px) {\n        --grid-template-columns-main: 19fr 73fr 8fr;\n    }\n    /* --mq-toc-open-after */\n    @media (width >= 1250px) {\n        --grid-template-columns-main: 18fr 64fr 18fr;\n    }\n\n    /* GROUP VARIABLES -- SPACING\n    =================================================== */\n    --spacing-4xs: 0.075rem;\n    --spacing-3xs: 0.15rem;\n    --spacing-2xs: 0.5rem;\n    --spacing-xs: 0.75rem;\n    --spacing-sm: 0.975rem;\n    --spacing-md: 1.35rem;\n\n    --spacing-lg: 1.55rem;\n    --spacing-xl: 1.8rem;\n    --spacing-xl-static: 1.8rem;\n    /* Based on e.g. >> Home on mobile */\n    --spacing-2xl: 1.9rem;\n\n    /* Smaller values for mobile */\n    --spacing-3xl: min(12vw, 2.5rem);\n    /* Increased at higher MQs */\n    --spacing-3xl-horizontal: min(12vw, 3.5rem);\n    --spacing-4xl: min(15vw, 4.7rem);\n    --spacing-5xl: min(18vw, 7.5rem);\n\n    /* e.g. footer padding */\n    --spacing-vh-sm: min(18vw, 10vh);\n\n    --button-spacing-vertical: 1rem;\n    --button-spacing-inline: 1.5rem;\n\n    /* GROUP VARIABLES -- LAYOUT -- CONSTRAINTS -- MAX\n    =================================================== */\n    /* From lowest to highest value */\n    --max-width-reading: 53rem;\n    /* -- */\n    --max-width-1: 105rem;\n\n    /* GROUP VARIABLES -- LAYOUT -- Z-INDEX\n    =================================================== */\n    --z-index-below-body: -1;\n    --z-index-above-body: 1;\n    --z-index-nav: 2;\n    --z-index-above-nav: 3;\n\n    /* GROUP VARIABLES -- DECORATION -- COLORS -- THEME\n    =================================================== */\n    /* Dark to light */\n    --color-black: hsl(210deg 3.85% 5%);\n    /* Stays the same for both light and dark mode */\n    --color-black-static: hsl(210deg 3.85% 0%);\n    /* Make slightly lighter to compensate for a naturally heavier font */\n    --color-black-prose: light-dark(hsl(210deg 3.85% 16%), hsl(0deg 0% 90%));\n    --color-black-light: #334155;\n    /* e.g. .c-site-footer */\n    --color-black-off: hsl(207deg 5% 10%);\n    --color-green: hsl(75deg 100% 65%);\n    --color-blue: hsl(197deg 100% 60%);\n    --color-blue-light-1: hsl(197deg 100% 95%);\n    --color-blue-light-2: hsl(197deg 100% 97%);\n    --color-blue-light-3: hsl(197deg 100% 98%);\n    --color-blue-light-4: hsl(197deg 100% 99%);\n    /* Used for :hover */\n    --color-purple-dark: hsl(256deg 60% 50%);\n    --color-purple-hue: 256deg;\n    --color-purple: hsl(256deg 70% 55%);\n    /* e.g. bullet point decoration */\n    --color-purple-light-1: hsl(256deg 60% 85%);\n    --color-purple-light-2: hsl(256deg 60% 98.5%);\n    --color-purple-light-2-highlight: hsl(256deg 60% 96.25%);\n    --color-pink-hue: 287deg;\n    --color-pink-border: hsl(287deg 80% 90%);\n    --color-pink: hsl(287deg 80% 50%);\n    /* e.g. table border */\n    --color-pink-light-1: hsl(287deg 80% 80%);\n    /* e.g. .c-pill */\n    --color-pink-light-2: hsl(287deg 80% 90%);\n    --color-pink-light-3: hsl(287deg 100% 95%);\n    --color-pink-light-3-static: hsl(287deg 100% 95%);\n    --color-pink-light-4: hsl(287deg 100% 98%);\n    --color-pink-light-5: hsl(287deg 100% 99.5%);\n    --color-red: hsl(0deg 75% 50%);\n    --color-red-bright-hsl: 0deg 95% 50%;\n    --color-red-bright: hsl(0deg 95% 50%);\n    --color-red-light: hsl(0deg 75% 75%);\n    --color-red-burnt-1: hsl(0deg 75% 95%);\n    --color-red-burnt-2: hsl(0deg 75% 97%);\n    --color-red-burnt-3: hsl(0deg 75% 98%);\n    --color-red-burnt-4: hsl(0deg 75% 99%);\n    --color-yellow-hue: 35deg;\n    --color-yellow-hsl: var(--color-yellow-hue) 100% 44.52%;\n    --color-yellow: hsl(var(--color-yellow-hue) 100% 44.52%);\n    /* e.g. ol items */\n    --color-yellow-light-1: hsl(var(--color-yellow-hue) 100% 90%);\n    /* e.g. figcaption on .c-bordered-image */\n    --color-yellow-light-2: hsl(var(--color-yellow-hue) 100% 93.5%);\n    --color-yellow-light-3: hsl(var(--color-yellow-hue) 100% 95%);\n    --color-yellow-light-4: hsl(var(--color-yellow-hue) 100% 98%);\n    --color-yellow-light-5: hsl(var(--color-yellow-hue) 100% 98.5%);\n    --color-yellow-light-6: hsl(var(--color-yellow-hue) 100% 99.5%);\n    --color-gray-background: hsl(217deg 23% 96%);\n    --color-gray-aa: hsl(0deg 0% 40%);\n    /* GROUP VARIABLES -- DECORATION -- COLORS -- SEMANTIC\n    =================================================== */\n    --color-body-background: white;\n    --color-logo-wordmark: var(--color-black);\n    --color-search-form: white;\n    --color-shadow: hsl(0deg 0% 0% / 3%);\n\n    --color-primary-text: var(--color-black);\n    --color-primary-accent: var(--color-purple);\n\n    --color-dropdown-nav-background: white;\n\n    --color-focus: var(--color-primary-accent);\n    --color-link: var(--color-primary-accent);\n    --color-text-underline-light: hsl(0deg 0% 80%);\n    --color-code-background: var(--color-gradient-blue-1);\n    --color-code-background: var(--color-pink-light-2);\n    --color-code-background: hsl(287deg 80% 93.5%);\n    --color-code-block-background: hsl(228.57deg 23% 18%);\n\n    --color-form-accent: var(--color-primary-accent);\n\n    --popover-backdrop: light-dark(hsl(0deg 0% 100% / 80%), hsl(0deg 0% 0% / 80%));\n\n    --color-logo-s-hole: var(--color-logo-wordmark);\n    /* GROUP VARIABLES -- DECORATION -- COLOURS -- THEME -- GRADIENTS\n    =================================================== */\n    --color-gradient-purple: linear-gradient(to var(--gradient-direction, right), var(--color-pink),var(--color-purple));\n    /* e.g. current breadcrumb */\n    --color-gradient-full-light-1: linear-gradient(to bottom, var(--color-blue-light-2), var(--color-pink-light-3), var(--color-yellow-light-3));\n    --color-gradient-full-light-2: linear-gradient(225deg, var(--color-blue-light-1), var(--color-pink-light-3), var(--color-yellow-light-3));\n    --color-gradient-full-light-3: linear-gradient(225deg, var(--color-blue-light-2), var(--color-pink-light-4), var(--color-yellow-light-4));\n    --color-gradient-full-light-4: linear-gradient(225deg, var(--color-blue-light-3), var(--color-pink-light-5), var(--color-yellow-light-5));\n    --color-gradient-full-light-5: linear-gradient(225deg, var(--color-blue-light-4), var(--color-pink-light-5), var(--color-yellow-light-6));\n    --color-gradient-full-light-6: linear-gradient(195deg, var(--color-blue-light-2), var(--color-pink-light-4), var(--color-yellow-light-4));\n    /* e.g. code background. Darkened on higher MQs */\n    --color-gradient-blue-1: linear-gradient(to bottom, var(--color-blue-light-1), hsl(var(--color-pink-hue) 80% 95%));\n    /* e.g. header */\n    --color-gradient-blue-2: linear-gradient(175deg, var(--color-blue-light-1) 0%,var(--color-pink-light-3) 100%);\n    /* -- */\n    --color-gradient-blue-light-1: linear-gradient(to bottom, var(--color-blue-light-2),var(--color-pink-light-3));\n    /* Changed to lighter at higher MQs */\n    --color-gradient-blue-light-2: linear-gradient(to bottom, var(--color-blue-light-3),var(--color-pink-light-4));\n    --color-gradient-burnt: linear-gradient(to right, var(--color-red-burnt-3), var(--color-yellow-light-4));\n    --color-gradient-burnt-dark: linear-gradient(to bottom, var(--color-red-burnt-3), var(--color-red-burnt-4));\n    --color-gradient-fire: linear-gradient(to bottom, hsl(var(--color-red-bright-hsl) / 25%), hsl(var(--color-yellow-hsl) / 25%));\n    /* e.g. figcaption for .c-bordered-image */\n    --color-gradient-burnt-right: linear-gradient(to right, var(--color-yellow-light-2), var(--color-red-burnt-2));\n\n    /* GROUP VARIABLES -- DECORATION -- TEXT -- FONTS\n    =================================================== */\n    --font-family-main: \"Lexend\", sans-serif;\n    --font-family-serif: p22-mackinac-pro, serif;\n    --font-family-code: \"Source Code Pro\", serif;\n\n    /* GROUP VARIABLES -- DECORATION -- TEXT -- WEIGHTS\n    =================================================== */\n    --font-family-main-weight-light: 250;\n    --font-family-main-weight-normal: 350;\n    --font-family-main-weight-medium: 500;\n    --font-family-main-weight-strong: 550;\n    --font-family-main-weight-heavy: 650;\n\n    --font-family-serif-weight-normal: 400;\n    --font-family-serif-weight-medium: 450;\n    --font-family-serif-weight-strong: 600;\n\n    --font-family-code-weight-light: 375;\n    --font-family-code-weight-normal: 500;\n    --font-family-code-weight-medium: 650;\n    --font-family-code-weight-strong: 800;\n\n    /* GROUP VARIABLES -- DECORATION -- TEXT -- SIZES\n    =================================================== */\n    /*\n        -sm sizes are sub 1rem\n        -md is just above 1rem\n        -lg and xl sizes are above 1rem\n    */\n    /* e.g. breadcrumbs */\n    --font-size-2xs: 0.8em;\n    --font-size-xs: 0.83em;\n    /* e.g. .c-tiles-with-description p */\n    --font-size-xs-line-height: 1.4;\n    --font-size-sm: 0.9em;\n    --font-size-sm-fixed: 0.9em;\n    --font-size-sm-line-height: 1.4;\n\n    /* The natural font-size of Lexend feels a touch bigger than p22-mackinac-pro, so you can use this font-size variable to match it. */\n    --font-size-ui: 0.94em;\n    --font-size-ui-rem: 0.95rem;\n    /* A touch lower */\n    --font-size-ui-line-height: 1.5;\n\n    --font-size-reading-line-height: 1.5;\n\n    /* GROUP VARIABLES -- DECORATION -- TEXT -- SIZES -- OUTSIDE SCALE\n    =================================================== */\n    --font-size-md: clamp(0.9em, 4vw, 1em);\n    --font-size-md-line-height: 1.5;\n    --font-size-md-rem: clamp(0.9rem, 4vw, 1rem);\n    /* e.g. .c-tip, with more characters, such as \"Best Practice\" */\n    --font-size-md-uppercase: 1.075em;\n    /* e.g. >> Home > Lead Text */\n    --font-size-lg: clamp(1em, 4vw, 1.2em);\n    --font-size-lg-line-height: 1.5;\n    /* e.g. .c-tip, just a few characters, such as \"Hot Tip!\" */\n    --font-size-lg-uppercase: 1.125em;\n    /* GROUP VARIABLES -- DECORATION -- TEXT -- SIZES -- AUGMENTED FOURTH\n    =================================================== */\n    /* Based on Augmented Fourth - https://type-scale.com/ */\n    --font-size-xl: clamp(1.35em, 6vw, 1.5em);\n    --font-size-xl-line-height: 1.3;\n\n    --font-size-2xl: clamp(1.8em * 0.85, 6vw, 1.8em);\n    --font-size-2xl-line-height: 1.2;\n\n    --font-size-3xl: clamp(2.1em * 0.9, 6vw, 2.1em);\n    --font-size-3xl-line-height: 1.15;\n\n    --font-size-4xl: clamp(2.827em * 0.8, 8.5vw, 2.827em);\n    --font-size-4xl-line-height: 1.15;\n\n    /* e.g. >> Home > Learn Statamic */\n    --font-size-5xl: clamp(3.45em * 0.67, 11vw, 3.45em);\n    /* e.g. deploying/laravel-forge */\n    --font-size-5xl-line-height: 1.05;\n\n    /* GROUP VARIABLES -- DECORATION -- BORDER RADIUS\n    =================================================== */\n    /* e.g. .c-full-width-image */\n    --border-radius-sm: 5px;\n    --border-radius-md: 8px;\n    --border-radius-lg: 20px;\n    /* e.g. >> Home > .c-tiles-with-description */\n    --border-radius-xl: 25px;\n    /* e.g. .c-bordered-image */\n    --border-radius-2xl: 50px;\n\n    /* GROUP VARIABLES -- DECORATION -- SEPARATORS -- BORDERS - ordered from light to dark variants\n    =================================================== */\n    --border-dashed-red: 1px dashed var(--color-red-light);\n    --border-solid: 1px solid var(--color-pink-light-2);\n\n    /* GROUP VARIABLES -- DECORATION -- SEPARATORS -- BOX SHADOWS - ordered from light to dark variants\n    =================================================== */\n    --box-shadow-medium: 0px 0px 15px rgba(0,0,0,0.125);\n    --box-shadow-pink-light: 0px 5px 5px hsl(var(--color-pink-hue) 100% 95%);\n    --box-shadow-pink-light-sm: 0px 5px 5px hsl(var(--color-pink-hue) 100% 97%);\n    --box-shadow-not-t-light: 0 3px 5px var(--color-shadow), 0 4px 4px -2px var(--color-shadow);\n    --box-shadow-not-t-lighter: 0 5px 10px var(--color-shadow), 0 4px 1px -2px var(--color-shadow);\n    --box-shadow-not-t-medium: 0 10px 15px -3px hsl(0deg 0% 0% / 5%), 0 4px 6px -2px hsl(0deg 0% 0% / 5%);\n\n    /* GROUP VARIABLES -- DECORATION -- OTHER\n    =================================================== */\n    --filter-image-boost-content: saturate(1.12);\n    --filter-image-boost-1: contrast(103%) saturate(1.12);\n    --filter-image-boost-1-with-hue-rotate: contrast(103%) saturate(1.12) hue-rotate(-12deg);\n    --filter-image-boost-1-with-hue-rotate-extra: contrast(108%) saturate(1.35) hue-rotate(-12deg);\n    --filter-image-boost-2: contrast(105%) saturate(1.175);\n    --filter-burnt-shadow: drop-shadow(0px 5px 2px var(--color-red-burnt-1));\n\n    /* GROUP VARIABLES -- ANIMATIONS -- TIMING\n    =================================================== */\n    --animation-timing-function-fast-out-slow-in: cubic-bezier(.4,0,.2,1);\n\n    /* GROUP VARIABLES -- ANIMATIONS -- TRANSITIONS\n    =================================================== */\n    --transition-transform: transform 0.3s var(--animation-timing-function-fast-out-slow-in);\n    --animation-timing-function-hipster: cubic-bezier(0.55, 0, 0.1, 1);\n\n    /* --mq-root-variable-adjustments-1 */\n    @media (min-width: 450px) {\n        /* GROUP VARIABLES -- SPACING\n        ===================================================  */\n        /* Based on nav sidebar and toc spacing */\n        --spacing-xl: 2.2rem;\n    }\n    /* --mq-root-variable-adjustments-2 */\n    @media (min-width: 730px) {\n        /* e.g. code background. Darkened on higher MQs */\n        --color-gradient-blue-1: linear-gradient(to bottom, var(--color-blue-light-1), hsl(var(--color-pink-hue) 80% 91%));\n    }\n    /* --mq-root-variable-adjustments-2-portrait */\n    @media (min-width: 730px) and (orientation: portrait) {\n        /* e.g. iPad */\n        --spacing-5xl: 5rem;\n    }\n    /* --mq-text-bump-1-after */\n    @media (width >= 1025px) and (width < 1441px) {\n        /* e.g. >> Home */\n        --button-spacing-vertical: 0.9rem;\n        --button-spacing-inline: 1.25rem;\n        --spacing-md: 1.2rem;\n        --font-size-ui-rem: 0.8rem;\n        --max-width-text: 48rem;\n    }\n    /* --mq-nav-open-after */\n    @media (width >= 1100px) {\n        /* Make colors more subtle because there's more going on. Based on .c-nav-sidebar-with-popover-api-category-heading.o-current-menu-item */\n        --color-pink-light-3: hsl(287deg 100% 97%);\n        --color-blue-light-1: hsl(197deg 100% 96%);\n        --color-yellow-light-3: hsl(var(--color-yellow-hue) 100% 96%);\n    }\n    /* --mq-text-bump-2-after */\n    @media (width >= 1441px) {\n        --font-size-lg: clamp(1em, 4vw, 1.175em);\n        --font-size-ui-rem: 0.85rem;\n        --font-size-md: 1.015em;\n        --font-size-md-rem: 1.015rem;\n    }\n\n    /* GROUP VARIABLES -- LIGHT/DARK MODE\n    =================================================== */\n    /* Inspiration - https://www.smashingmagazine.com/2024/03/setting-persisting-color-scheme-preferences-css-javascript/ */\n    /* Default to light */\n    --color-scheme: light;\n    color-scheme: var(--color-scheme, light);\n\n    /* GROUP VARIABLES -- DARK MODE\n    =================================================== */\n    /* e.g. betterify this page */\n    --color-dark-mode-gray-dark: hsl(260deg 35% 17%);\n    --color-dark-mode-gray: hsl(260deg 15% 20%);\n    /* e.g. .c-related background */\n    --color-dark-mode-gray-background: hsl(240deg 20% 17%);\n    --color-dark-mode-gray-light: hsl(260deg 10% 30%);\n    /* Page preference is \"dark\" */\n    &:has(#color-scheme option[value=\"dark\"]:checked) {\n        /* GROUP VARIABLES -- DECORATION -- TEXT -- WEIGHTS\n        =================================================== */\n        /* Trim heavier weights */\n        --font-family-main-weight-medium: 450;\n        --font-family-main-weight-strong: 500;\n        --font-family-main-weight-heavy: 750;\n        /* GROUP VARIABLES -- DARK MODE -- COLORS\n        =================================================== */\n        /* Thin out fonts */\n        -webkit-font-smoothing: antialiased;\n        --color-scheme: dark;\n        /* any additional dark styles */\n        /* e.g. .c-doc-tabs */\n        --color-black: var(--color-dark-mode-gray);\n        --color-black-off: hsl(var(--color-body-background-hue) 17% 5%);\n        --color-gray-aa: hsl(250deg 3% 53%);\n        /* For dark colours, take the same hs value but dial the lightness right down, making small adjustments as necessary */\n        --color-blue-light-1: hsl(197deg 50% 11%);\n        --color-blue-light-2: hsl(197deg 100% 5%);\n        /* e.g. a folder name in the torchlight file tree view */\n        --color-purple: hsl(256deg 60% 70%);\n        /* e.g. hr-style border before meerkat feedback */\n        --color-purple-light-1: hsl(256deg 40% 32%);\n        /* e.g. torchlight file tree view */\n        --color-purple-light-2: hsl(240deg 20% 10%);\n        --color-purple-light-2-highlight: hsl(256deg 40% 15%);\n        --color-purple-reading: hsl(252deg 70% 70%);\n        /* e.g. hover over a link */\n        --color-purple-dark: hsl(252deg 65% 62%);\n        --color-purple-code-background: hsl(252deg 55% 25%);\n        --color-pink-light-1: hsl(287deg 80% 22%);\n        /* e.g. toc scrollbar */\n        --color-pink-light-2: hsl(287deg 80% 15%);\n        --color-pink-light-3: hsl(287deg 90% 8.5%);\n        --color-pink-light-3-static: hsl(287deg 100% 17%);\n        --color-pink-light-4: hsl(287deg 100% 5%);\n        /* Slightly darker */\n        --color-green: hsl(75deg 100% 50%);\n        @supports (background: oklch(0% 0 0)) {\n            --color-green: oklch(0.9 0.3 128);\n        }\n        --color-green-subtle: hsl(76.34deg 100% 32%);\n        @supports (background: oklch(0% 0 0)) {\n            --color-green-subtle: oklch(0.65 0.25 128);\n        }\n        /* e.g. .c-tip__mascot shadow */\n        --color-red-burnt-1: hsl(305deg 50% 10%);\n        /* e.g. figcaption */\n        --color-red-burnt-2: hsl(305deg 34% 14%);\n        /* e.g. feedback meerkat */\n        --color-red-burnt-3: hsl(236deg 30% 12%);\n        /* e.g. c-tip--warning */\n        --color-red-burnt-4: var(--color-yellow-light-4);\n        /* e.g. .c-docs-header__search */\n        --color-pink-border: unset;\n        --color-yellow-light-1: hsl(44deg 100% 80%);\n        /* e.g. figcaption */\n        --color-yellow-light-2: hsl(235deg 26% 13%);\n        --color-yellow-light-3: hsl(35deg 90% 8%);\n        /* e.g. feedback meerkat */\n        --color-yellow-light-4: hsl(277deg 40% 11.5%);\n        --color-yellow-light-5: var(--color-yellow-light-4);\n        --color-transparent-dark-mode: hsl(0deg 0% 100% / 10%);\n        /* GROUP VARIABLES -- DARK MODE -- COLORS -- GRADIENTS\n        =================================================== */\n        /* e.g. .c-doc-tabs */\n        --color-gradient-blue-1: var(--color-code-background);\n        --color-gradient-blue-2: linear-gradient(to bottom, hsl(197deg 35% 12%) 0%,var(--color-pink-light-3) 100%);\n        --color-gradient-blue-2-radial: radial-gradient(circle at left, hsl(197deg 35% 10%), var(--color-pink-light-3));\n        --color-gradient-blue-light-1: linear-gradient(to bottom, var(--color-blue-light-2),var(--color-pink-light-3));\n        /* e.g. sponsors background */\n        --color-gradient-blue-light-1: var(--color-gradient-full-light-3);\n        /* e.g. .c-related */\n        --color-gradient-blue-light-2: var(--color-gradient-full-light-2);\n\n        /* e.g. .o-current-menu-item in the sidebar */\n        --color-gradient-full-light-2: radial-gradient(circle at left, hsl(217deg 35% 12%), hsl(287deg 40% 10.5%));\n        /* e.g. homepage tiles */\n        --color-gradient-full-light-3: linear-gradient(to bottom, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n        --color-gradient-full-light-6: linear-gradient(to bottom, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n        /* e.g. tables */\n        --color-gradient-full-light-4: linear-gradient(to left, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n        /* e.g. .c-pill-with-description */\n        --color-gradient-full-light-5: linear-gradient(to right, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n        /* e.g. .c-tip--warning */\n        --color-gradient-fire: var(--color-purple-code-background);\n        /* GROUP VARIABLES -- DARK MODE -- COLORS -- SEMANTIC\n        =================================================== */\n        --color-body-background-hue: 230;\n        --color-body-background: hsl(var(--color-body-background-hue) 17% 7%);\n        --color-dropdown-nav-background: var(--color-body-background);\n        --color-primary-text: hsl(0deg 0% 90%);\n        /* e.g. select dropdown for .c-theme-picker on iOS */\n        --color-primary-accent: white;\n        --color-link: var(--color-purple-reading);\n        --color-code-background: var(--color-purple-code-background);\n        --color-logo-wordmark: var(--color-green);\n        --color-search-form: var(--color-transparent-dark-mode);\n        --color-shadow: hsl(var(--color-body-background-hue, 0) 30% 15% / 40%);\n        --color-code-block-background: hsl(228deg 20% 15%);\n        --color-logo-s-hole: var(--color-body-background);\n        /* GROUP VARIABLES -- DECORATION -- SEPARATORS -- BOX SHADOWS - ordered from light to dark variants\n        =================================================== */\n        /* e.g. .c-docs-header__search */\n        --box-shadow-pink-light: unset;\n        --box-shadow-pink-light-sm: 0px 5px 5px hsl(var(--color-shadow) 100% 97%);\n        /* The shadow here stands out a touch more than in light mode */\n        --box-shadow-not-t-light: 0 5px 10px var(--color-shadow), 0 4px 10px -2px var(--color-shadow);\n        /* In dark mode this is the same as the above */\n        --box-shadow-not-t-medium: var(--box-shadow-not-t-light);\n        /* GROUP VARIABLES -- DECORATION -- SEPARATORS -- BORDERS - ordered from light to dark variants\n        =================================================== */\n        --border-dashed-red: 1px dashed var(--color-green-subtle);\n        /* e.g. .c-pill */\n        --border-solid: 1px solid var(--color-black);\n        /* GROUP VARIABLES -- DECORATION -- OTHER\n        =================================================== */\n        --filter-dark-tint:\n            hue-rotate(-12deg)\n            contrast(105%)\n            opacity(95%)\n        ;\n        /* Map the burnt shadow to the dark tint */\n        --filter-burnt-shadow: var(--filter-dark-tint);\n        /* Custom */\n        @media (width <= 505px) {\n            /* For making things stand out a bit more in dark mode on mobile e.g. .c-tiles-with-description */\n            --dark-mode-border: 1px dashed hsl(0deg 0% 100% / 10%);\n        }\n    }\n    /* Page preference is \"system\", and system preference is \"dark\" */\n    /* Note: For now, we need to repeat these values, but in the future, we can use style queries like @container style(--color-scheme: dark) to eliminate the need to repeat the dark mode variables. [Current support](https://caniuse.com/?search=style%20queries). To do this, we must shift everything down so variables are on the `body` instead of the `root`. The root then contains the `colour-scheme` variables, which means we can now query the `colour-scheme` variable since the root is now the container. */\n    @media (prefers-color-scheme: dark) {\n        &:has(#color-scheme option[value=\"system\"]:checked) {\n            /* GROUP VARIABLES -- DECORATION -- TEXT -- WEIGHTS\n            =================================================== */\n            /* Trim heavier weights */\n            --font-family-main-weight-medium: 450;\n            --font-family-main-weight-strong: 500;\n            --font-family-main-weight-heavy: 750;\n            /* GROUP VARIABLES -- DARK MODE -- COLORS\n            =================================================== */\n            /* Thin out fonts */\n            -webkit-font-smoothing: antialiased;\n            --color-scheme: dark;\n            /* any additional dark styles */\n            /* e.g. .c-doc-tabs */\n            --color-black: var(--color-dark-mode-gray);\n            --color-black-off: hsl(var(--color-body-background-hue) 17% 5%);\n            --color-gray-aa: hsl(250deg 3% 53%);\n            /* For dark colours, take the same hs value but dial the lightness right down, making small adjustments as necessary */\n            --color-blue-light-1: hsl(197deg 50% 11%);\n            --color-blue-light-2: hsl(197deg 100% 5%);\n            /* e.g. a folder name in the torchlight file tree view */\n            --color-purple: hsl(256deg 60% 70%);\n            /* e.g. hr-style border before meerkat feedback */\n            --color-purple-light-1: hsl(256deg 40% 32%);\n            /* e.g. torchlight file tree view */\n            --color-purple-light-2: hsl(240deg 20% 10%);\n            --color-purple-light-2-highlight: hsl(256deg 40% 15%);\n            --color-purple-reading: hsl(252deg 70% 70%);\n            /* e.g. hover over a link */\n            --color-purple-dark: hsl(252deg 65% 62%);\n            --color-purple-code-background: hsl(252deg 55% 25%);\n            --color-pink-light-1: hsl(287deg 80% 22%);\n            /* e.g. toc scrollbar */\n            --color-pink-light-2: hsl(287deg 80% 15%);\n            --color-pink-light-3: hsl(287deg 90% 8.5%);\n            --color-pink-light-3-static: hsl(287deg 100% 17%);\n            --color-pink-light-4: hsl(287deg 100% 5%);\n            /* Slightly darker */\n            --color-green: hsl(75deg 100% 50%);\n            @supports (background: oklch(0% 0 0)) {\n                --color-green: oklch(0.9 0.3 128);\n            }\n            --color-green-subtle: hsl(76.34deg 100% 32%);\n            @supports (background: oklch(0% 0 0)) {\n                --color-green-subtle: oklch(0.65 0.25 128);\n            }\n            /* e.g. .c-tip__mascot shadow */\n            --color-red-burnt-1: hsl(305deg 50% 10%);\n            /* e.g. figcaption */\n            --color-red-burnt-2: hsl(305deg 34% 14%);\n            /* e.g. feedback meerkat */\n            --color-red-burnt-3: hsl(236deg 30% 12%);\n            /* e.g. c-tip--warning */\n            --color-red-burnt-4: var(--color-yellow-light-4);\n            /* e.g. .c-docs-header__search */\n            --color-pink-border: unset;\n            --color-yellow-light-1: hsl(44deg 100% 80%);\n            /* e.g. figcaption */\n            --color-yellow-light-2: hsl(235deg 26% 13%);\n            --color-yellow-light-3: hsl(35deg 90% 8%);\n            /* e.g. feedback meerkat */\n            --color-yellow-light-4: hsl(277deg 40% 11.5%);\n            --color-yellow-light-5: var(--color-yellow-light-4);\n            --color-transparent-dark-mode: hsl(0deg 0% 100% / 10%);\n            /* GROUP VARIABLES -- DARK MODE -- COLORS -- GRADIENTS\n            =================================================== */\n            /* e.g. .c-doc-tabs */\n            --color-gradient-blue-1: var(--color-code-background);\n            --color-gradient-blue-2: linear-gradient(to bottom, hsl(197deg 35% 12%) 0%,var(--color-pink-light-3) 100%);\n            --color-gradient-blue-2-radial: radial-gradient(circle at left, hsl(197deg 35% 10%), var(--color-pink-light-3));\n            --color-gradient-blue-light-1: linear-gradient(to bottom, var(--color-blue-light-2),var(--color-pink-light-3));\n            /* e.g. sponsors background */\n            --color-gradient-blue-light-1: var(--color-gradient-full-light-3);\n            /* e.g. .c-related */\n            --color-gradient-blue-light-2: var(--color-gradient-full-light-2);\n\n            /* e.g. .o-current-menu-item in the sidebar */\n            --color-gradient-full-light-2: radial-gradient(circle at left, hsl(217deg 35% 12%), hsl(287deg 40% 10.5%));\n            /* e.g. homepage tiles */\n            --color-gradient-full-light-3: linear-gradient(to bottom, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n            --color-gradient-full-light-6: linear-gradient(to bottom, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n            /* e.g. tables */\n            --color-gradient-full-light-4: linear-gradient(to left, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n            /* e.g. .c-pill-with-description */\n            --color-gradient-full-light-5: linear-gradient(to right, hsl(240deg 25% 10%), hsl(275deg 30% 10%));\n            /* e.g. .c-tip--warning */\n            --color-gradient-fire: var(--color-purple-code-background);\n            /* GROUP VARIABLES -- DARK MODE -- COLORS -- SEMANTIC\n            =================================================== */\n            --color-body-background-hue: 230;\n            --color-body-background: hsl(var(--color-body-background-hue) 17% 7%);\n            --color-dropdown-nav-background: var(--color-body-background);\n            --color-primary-text: hsl(0deg 0% 90%);\n            /* e.g. select dropdown for .c-theme-picker on iOS */\n            --color-primary-accent: white;\n            --color-link: var(--color-purple-reading);\n            --color-code-background: var(--color-purple-code-background);\n            --color-logo-wordmark: var(--color-green);\n            --color-search-form: var(--color-transparent-dark-mode);\n            --color-shadow: hsl(var(--color-body-background-hue, 0) 30% 15% / 40%);\n            --color-code-block-background: hsl(228deg 20% 15%);\n            --color-logo-s-hole: var(--color-body-background);\n            /* GROUP VARIABLES -- DECORATION -- SEPARATORS -- BOX SHADOWS - ordered from light to dark variants\n            =================================================== */\n            /* e.g. .c-docs-header__search */\n            --box-shadow-pink-light: unset;\n            --box-shadow-pink-light-sm: 0px 5px 5px hsl(var(--color-shadow) 100% 97%);\n            /* The shadow here stands out a touch more than in light mode */\n            --box-shadow-not-t-light: 0 5px 10px var(--color-shadow), 0 4px 10px -2px var(--color-shadow);\n            /* In dark mode this is the same as the above */\n            --box-shadow-not-t-medium: var(--box-shadow-not-t-light);\n            /* GROUP VARIABLES -- DECORATION -- SEPARATORS -- BORDERS - ordered from light to dark variants\n            =================================================== */\n            --border-dashed-red: 1px dashed var(--color-green-subtle);\n            /* e.g. .c-pill */\n            --border-solid: 1px solid var(--color-black);\n            /* GROUP VARIABLES -- DECORATION -- OTHER\n            =================================================== */\n            --filter-dark-tint:\n                hue-rotate(-12deg)\n                contrast(105%)\n                opacity(95%)\n            ;\n            /* Map the burnt shadow to the dark tint */\n            --filter-burnt-shadow: var(--filter-dark-tint);\n            /* Custom */\n            @media (width <= 505px) {\n                /* For making things stand out a bit more in dark mode on mobile e.g. .c-tiles-with-description */\n                --dark-mode-border: 1px dashed hsl(0deg 0% 100% / 10%);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/anchors.css",
    "content": "/* GROUP COMPONENTS / ANCHORS\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    Home\n\n    What does it do?\n    ----------------\n    Anchors are added with anchors.js, which b\n\n*/\n/* HTML Example...\n\n    <h1 id=\"learn-statamic\">Learn Statamic<a href=\"#learn-statamic\" class=\"c-anchor\">#</a></h1>\n\n*/\n@layer components {\n    .c-anchor {\n        position: absolute;\n        left: -1em;\n        transition: all 0.35s var(--animation-timing-function-fast-out-slow-in) 0s;\n        padding-block-start: 0.05em;\n        background: linear-gradient(to bottom, hsl(197deg 100% 80%), hsl(var(--color-pink-hue) 100% 65%));\n        -webkit-background-clip: text;\n        -webkit-text-fill-color: transparent;\n        background-clip: text;\n        text-fill-color: transparent;\n        :is(h1, h2, h3, h4, h5, h6):has(&) {\n            position: relative;\n            &:not(:hover) .c-anchor {\n                left: -2rem;\n                opacity: 0;\n                transform: scale(0.9);\n            }\n        }\n    }\n}\n@layer ui {\n    .c-anchor {\n        /* Hide h1 anchors because they're pointless. However, we still need to generate these with JS because they're used to generate the TOC. */\n        h1 &,\n        /* Sometimes headings are in li items e.g. >> /troubleshooting */\n        li & {\n            display: none;\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/buttons.css",
    "content": "/* GROUP COMPONENTS / BUTTONS\n=================================================== */\n/* Notes...\n\n    - You can use a .o-btn-container object to control alignment. For example on the contact form\n\n*/\n/* HTML Example...\n\n    <div class=\"o-btn-container\"> <-- optional object, which you could use to control alignment, e.g. .c-contact-form .o-btn-container { display: flex; justify-content: end; }\n        <div class=\"c-btn c-btn--1\">\n            <a href=\"\">Some button</a>\n        </div>\n    </div>\n\n*/\n\n@layer components {\n    .c-btn,\n    form button,\n    [type=\"submit\"] {\n        display: inline-flex;\n        gap: 1rem;\n        /* I've found it more stable to use pxls rather than ems. */\n        --button-spacing: var(--button-spacing-vertical) var(--button-spacing-inline);\n        padding: var(--button-spacing);\n        font-size: var(--font-size-sm);\n        font-weight: var(--font-family-main-weight-heavy);\n        text-decoration-line: none;\n        /* Hover states with a slight delay, but focus without any because keyboard users want quick feedback. Do not use 'all' because it resizes slowly when resizing the browser window */\n        transition: background-color 0.2s ease 0s;\n    }\n    .c-btn {\n        position: relative;\n        /* e.g. icons in buttons */\n        display: inline-flex;\n        align-items: center;\n        margin-block-end: 1.25rem;\n        text-align: left;\n\n        :is(p) + & {\n            margin-block-start: var(--spacing-sm);\n        }\n    }\n    button {\n        /* Improve usability */\n        cursor: pointer;\n    }\n}\n@layer elements {\n    /*\n        - Don't affect the header\n        - Not .c-btn + .c-btn in case a button is next to a different tag such as <p/>\n    */\n    \n    main &:is(.c-btn, button):not(:last-child) {\n        margin-right: 1rem;\n    }\n    button,\n    [type=\"search\"],\n    [type=\"submit\"] {\n        /* Cancel default button appearance */\n        -webkit-appearance: none!important;\n        border: none;\n    }\n    button {\n        /* Improve usability */\n        cursor: pointer;\n        /* Cancel default button appearance */\n        background: none;\n        /* To combat -apple-system-blueinput on iOS 15 */\n        color: var(--color-primary-text);\n    }\n}\n@layer components {\n    button svg {\n        font-size: 1.2em;\n    }\n}\n/* GROUP COMPONENTS / BUTTONS / TYPES\n=================================================== */\n@layer modifiers {\n    /* 1 */\n    .c-btn--1,\n    form button,\n    [type=\"submit\"] {\n        background: linear-gradient(to top left, var(--color-black) 0%,var(--color-black-light) 100%);\n        color: var(--color-green);\n        border-radius: var(--border-radius-sm);\n        font-weight: var(--font-family-main-weight-medium);\n        @container style(--color-scheme: dark) {\n            background: var(--color-dark-mode-gray-dark);\n        }\n    }\n    /* GROUP COMPONENTS / FRAMEWORK / BUTTONS / ACCESSIBILITY / HOVER\n    =================================================== */\n    /* These should be slightly different to focus states. Subtle effects such as changing the background color from blue to darkblue are best for hover. We want to gently suggest rather than pop out (opposite of focus states).\n\n        - Consider darkening the background color slightly e.g. blue to darkblue\n        - Here is a good example...\n        https://material-components.github.io/material-components-web-catalog/#/component/button\n\n    */\n\n    /* 2 */\n    .c-btn--2 {\n        background: var(--color-gray-background);\n        border-radius: var(--border-radius-xl);\n    }\n\n    /* Inline */\n    .c-btn--inline {\n        padding: 0;\n        text-decoration-line: underline;\n        text-decoration-thickness: 3px;\n        font-weight: var(--font-family-main-weight-heavy);\n    }\n\n    /* Small */\n    .c-btn--s {\n        --button-spacing-vertical: 0.7rem;\n        --button-spacing-inline: 1rem;\n        font-size: 1rem;\n    }\n}"
  },
  {
    "path": "resources/css/components/doc-tabs.css",
    "content": "/* GROUP COMPONENTS / DOC TABS\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    A tabbed interface for displaying different languages e.g. Antlers, Blade, etc.\n\n*/\n/* HTML Example...\n\n*/\n/* Modifiers...\n\n    .c-pill--negative <- will turn the value text to red\n\n*/\n@layer components {\n    .c-doc-tabs {\n        --button-radius: var(--border-radius-lg);\n        margin-block: var(--spacing-3xs) var(--spacing-2xl);\n        /* e.g. p followed by .c-docs-tabs e.g. >> /collections */\n        padding-block-start: var(--spacing-sm);\n        :is(h2, h3, h4, h5, h6) + & {\n            /* e.g. >> /frontend/protecting-content where there's a heading of \"Override the view\" followed by some tabs */\n            padding-block-start: var(--spacing-3xs);\n        }\n        border-radius: var(--button-radius);\n\n        pre {\n            margin-block-end: 0;\n        }\n\n        code {\n            font-size: unset;\n        }\n\n        .tab-content {\n            border-radius: var(--button-radius);\n            border-top-left-radius: 0;\n            border-block-end: 0;\n            /* Sometimes the tab content contains tips e.g. >> /taxonomies and /extending/modifiers */\n            &:not(:has(.c-tip)) {\n                background: var(--color-gradient-full-light-4);\n                @container style(--color-scheme: dark) {\n                    background: var(--color-gradient-full-light-2);\n                }\n                border: 1px solid var(--color-black);\n                p, ol, ul {\n                    padding: var(--spacing-md);\n                }\n                /* Not >> /extending/modifiers */\n                p:has(~ pre) {\n                    /* e.g. >> /fieldtypes/assets */\n                    padding-block-end: 0;\n                }\n                ol, ul {\n                    /* e.g. >> /extending/modifiers */\n                    padding-block-end: var(--spacing-xl);\n                }\n            }\n        }\n        :nth-last-child(1 of .c-tip) {\n            margin-block-end: 0;\n        }\n    }\n    .c-doc-tabs p:not(.c-tip *),\n    .c-doc-tabs__tabs {\n        font-size: unset;\n        font-family: var(--font-family-main);\n        font-weight: var(--font-family-main-weight-strong);\n    }\n    .c-doc-tabs__tabs {\n        display: inline-flex;\n        font-size: var(--font-size-sm);\n        line-height: var(--font-size-sm-line-height);\n        border: 1px solid var(--color-black);\n        border-block-end: 0;\n        border-radius: var(--border-radius-sm);\n        border-bottom-left-radius: 0;\n        border-bottom-right-radius: 0;\n        button {\n            margin: 0;\n            padding: var(--spacing-xs) var(--spacing-md);\n            text-transform: uppercase;\n            font-size: var(--font-size-sm);\n            background: light-dark(var(--color-body-background), transparent);\n            &:first-child {\n                border-top-left-radius: inherit;\n            }\n            &:last-child {\n                border-top-right-radius: inherit;\n            }\n            &:not(:last-child) {\n                border-inline-end: 1px solid var(--color-black);\n            }\n        }\n    }\n    button.c-doc-tabs__active {\n        background: var(--color-gradient-blue-1);\n        font-weight: var(--font-family-main-weight-heavy);\n    }\n}\n@layer plugins {\n    .c-doc-tabs__tabs ~ .tab-content {\n        /* e.g. >> /globals */\n        /* e.g. >> /navigation */\n        /* e.g. >> /data-inheritance */\n        pre:first-of-type:not(:has(p)), code {\n            margin-block-start: 0;\n            border-top-left-radius: 0;\n        }\n    }\n\n    /* Handle tabs with multiple blocks, such as /tags/yield > The Template followed by The Layout */\n    .c-doc-tabs {\n        .tab-content:has(pre + pre) {\n            /* Only when there are multiple pre tags. In this instance it's better to have a solid background because you can't really see the subtle gradient */\n            background: var(--color-yellow-light-2);\n        }\n        pre:has(+ pre) {\n            &, code {\n                border-bottom-left-radius: 0;\n                border-bottom-right-radius: 0;\n            }\n            /* In case there's more than one */\n            + * {\n                &, code {\n                    border-top-left-radius: 0;\n                    border-top-right-radius: 0;\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/docs-header.css",
    "content": "@layer components {\n    /* GROUP COMPONENTS / SITE HEADER\n    =================================================== */\n    /* HTML Example...\n\n        .c-docs-header\n            .c-docs-header__inner\n                .c-docs-header__site-logo\n                .c-docs-header__nav\n                .c-docs-header__cta\n\n    */\n\n    .c-docs-header {\n        position: sticky;\n        top: 0;\n        z-index: var(--z-index-nav);\n        --background-position: 5rem;\n        --gradient: var(--color-gradient-blue-2);\n        --background-image: url('../../../public/img/paper-tear.png');\n        /* Page preference is \"dark\" */\n        html:has(#color-scheme option[value=\"dark\"]:checked) & {\n            --background-image: url('../../../public/img/paper-tear-dark-mode.png');\n        }\n        /* Page preference is \"system\", and system preference is \"dark\" */\n        @media (prefers-color-scheme: dark) {\n            html:has(#color-scheme option[value=\"system\"]:checked) & {\n                --background-image: url('../../../public/img/paper-tear-dark-mode.png');\n            }\n        }\n        background: var(--background-image) no-repeat 0% var(--background-position), var(--gradient);\n        @supports not (animation-timeline: auto) {\n            /* Remove the background image when scroll-driven animations are not supported */\n            background: no-repeat 0% var(--background-position), var(--gradient);\n        }\n    }\n\n    .c-docs-header__inner {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n        gap: var(--spacing-2xs);\n        max-width: var(--max-width-1);\n        margin-inline: auto;\n        padding: var(--spacing-md) var(--spacing-xs) var(--spacing-2xl);\n        margin-block-end: var(--spacing-xs);\n\n        .c-version-selector {\n            text-align: right;\n        }\n\n        /* GROUP COMPONENTS / NAV / CURRENT PAGE\n        =================================================== */\n        .o-current-menu-item > a span {\n            color: var(--color-turquoise);\n        }\n\n        /* GROUP COMPONENTS / SITE HEADER / SITE LOGO\n        =================================================== */\n        .o-logo {\n            display: flex;\n            align-items: center;\n            gap: var(--spacing-sm);\n        }\n        .o-logomark {\n            width: 1em;\n            height: 1em;\n            font-size: 3.75em;\n        }\n        /* svg*/\n        .o-wordmark {\n            width: 11em;\n            block-size: 100%;\n            fill: var(--color-logo-wordmark);\n        }\n    }\n    .c-docs-header__search {\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        gap: var(--spacing-2xs);\n        /* Custom */\n        @media (width >= 700px) {\n            /* Increase */\n            gap: var(--spacing-2xs);\n        }\n        .c-theme-picker {\n            display: flex;\n        }\n    }\n    /* GROUP COMPONENTS / SITE HEADER / SITE LOGO / MQs\n    =================================================== */\n    /* --mq-nav-open-before */\n    @media (width < 1100px) {\n        .c-docs-header {\n            margin-block-end: var(--spacing-lg);\n            @supports (animation-timeline: auto) {\n                /* Less space because the header is not compressed until we scroll further down */\n                margin-block-end: var(--spacing-sm);\n                /* --mq-grid-after */\n                @media (width >= 1000px) {\n                    /* Increase */\n                    margin-block-end: var(--spacing-lg);\n                }\n            }\n            @container style(--color-scheme: dark) {\n                --gradient: linear-gradient(160deg, var(--color-blue-light-1), var(--color-pink-light-3) 75%, var(--color-yellow-light-3));\n            }\n        }\n        .c-docs-header__inner {\n            .o-wordmark {\n                display: none;\n            }\n        }\n    }\n    /* --mq-nav-open-after */\n    @media (width >= 1100px) {\n        .c-docs-header {\n            /* Change to include some yellow */\n            --gradient: linear-gradient(175deg, var(--color-blue-light-1), var(--color-pink-light-3), var(--color-yellow-light-3));\n            @container style(--color-scheme: dark) {\n                /* Change the stop point very slightly for dark mode */\n                --gradient: linear-gradient(140deg, var(--color-blue-light-1), var(--color-pink-light-3) 75%, var(--color-yellow-light-3));\n            }\n\n            /* Custom */\n            @media (width >= 1580px) {\n                /* Increase */\n                background-size: 100% auto, auto auto;\n            }\n\n            .c-nav-collapse-expand,\n            .c-theme-picker {\n                /* Force the flanking icons of the search box to be the same size, so the search box sits centered horizontally. */\n                min-inline-size: 30px;\n            }\n        }\n        .c-docs-header__inner {\n            padding: 1rem var(--spacing-lg) 3rem;\n            margin-block-end: 1rem;\n            .o-logomark {\n                /* Decrease */\n                font-size: 2.75em;\n            }\n\n            &::before {\n                content: '';\n                position: absolute;\n                z-index: var(--z-index-below-body);\n                opacity: 0.6;\n                inline-size: 38.2%;\n                right: 0;\n                height: 85%;\n                transform: scaleX(-1);\n\n                /* https://css-pattern.com/graph-paper/ */\n                --s: 220px; /* control the size*/\n                --c1: white;\n                --c2: transparent;\n                --_g: #0000 90deg,var(--c1) 0;\n                background:\n                  conic-gradient(from 90deg at 1px 1px,var(--_g)),\n                  conic-gradient(from 90deg at 1px 1px,var(--_g)),\n                  var(--c2);\n                background-size: var(--s) var(--s), calc(var(--s)/5) calc(var(--s)/5);\n            }\n        }\n        /* Page preference is \"dark\" */\n        html:has(#color-scheme option[value=\"dark\"]:checked) .c-docs-header__inner::before {\n            opacity: 0.05;\n        }\n        /* Page preference is \"system\", and system preference is \"dark\" */\n        @media (prefers-color-scheme: dark) {\n            html:has(#color-scheme option[value=\"system\"]:checked) .c-docs-header__inner::before {\n                opacity: 0.05;\n            }\n        }\n    }\n    /* Custom */\n    @media (width >= 1200px) {\n        .c-docs-header__inner > * {\n            flex-basis: 100%;\n        }\n    }\n    /* Custom */\n    @media (width >= 2100px) {\n        .c-docs-header {\n            --background-image: url('../../../public/img/paper-tear-large.png');\n            /* Page preference is \"dark\" */\n            html:has(#color-scheme option[value=\"dark\"]:checked) & {\n                --background-image: url('../../../public/img/paper-tear-large-dark-mode.png');\n            }\n            /* Page preference is \"system\", and system preference is \"dark\" */\n            @media (prefers-color-scheme: dark) {\n                html:has(#color-scheme option[value=\"system\"]:checked) & {\n                    --background-image: url('../../../public/img/paper-tear-large-dark-mode.png');\n                }\n            }\n            --background-position: 5.75rem;\n            background-repeat: repeat-x;\n            background-size: 1800px auto, auto auto;\n        }\n        .c-docs-header__inner {\n            /* Increase */\n            padding-block: 1.6rem 3.75rem;\n        }\n    }\n    /* GROUP COMPONENTS / SITE HEADER / SCROLL DRIVEN ANIMATIONS\n    =================================================== */\n    @supports (animation-timeline: auto) {\n        /* --mq-nav-open-after */\n        @media (width >= 1100px) {\n            .c-docs-header {\n                position: fixed;\n                top: unset;\n                inline-size: 100%;\n            }\n            main {\n                padding-block-start: 9rem;\n            }\n        }\n        /* Custom */\n        @media (width >= 2100px) {\n            main {\n                /* Increase */\n                padding-block-start: 11rem;\n            }\n        }\n    }\n    body {\n        view-timeline-name: --body;\n    }\n    @keyframes compress-header {\n       100% {\n            padding-block: var(--spacing-xs);\n       }\n    }\n    .c-docs-header__inner {\n        /* This gets triggered straight away on browsers that don't support scroll-driven animations, so the header will always appear compressed */\n        animation-name: compress-header;\n        animation-timing-function: linear;\n        animation-timeline: --body;\n        animation-range: entry 100% entry 103%;\n        animation-fill-mode: both;\n        @supports (animation-timeline: auto) {\n            margin-block-end: unset;\n        }\n        @supports not (animation-timeline: auto) {\n            /* --mq-nav-open-after */\n            @media (width >= 1100px) {\n                /* [1] I had this option at first to make the \"torn\" header the default */\n                /* animation: unset; */\n                /* [/2] But in the end I made the \"compressed\" header the default for browsers that don't support scroll-driven-animations */\n                padding-block: var(--spacing-xs) var(--spacing-sm);\n                margin-block-end: var(--spacing-3xl);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/entry-content.css",
    "content": "/* GROUP COMPONENTS / ENTRY / ENTRY CONTENT\n=================================================== */\n/*\n\n    - Entry content is the main page content which houses prose and other writing components\n    - Although technically, this is more like a scope, housing different components, it should sit at the component layer so that the components inside can easily override this base layer.\n\n*/\n/* HTML Example...\n\n    <div class=\"c-entry-content-wrapper\"> <-- container to help with placement\n        <article class=\"c-entry-content\">\n            <h1>Title</h1>\n            <p>Entry content</p>\n        </article>\n    </div>\n\n*/\n\n/* Put some rules at the element layer where components should easily override them */\n@layer elements {\n    .c-entry-content {\n        /* Sometimes there are p tags in tables e.g. >> /tags */\n        p:not(table *) {\n            /* Rem instead of em because of p tags inside li tags e.g. e.g. >> /fields */\n            font-size: var(--font-size-md-rem);\n        }\n        strong {\n            font-weight: var(--font-family-serif-weight-strong);\n        }\n    }\n}\n@layer components {\n    .c-entry-content-wrapper {\n        max-width: var(--max-width-1);\n        /* If it's the first entry-content on the page then push it away from the header */\n        :is(main) > section > article &:first-child {\n            padding-block-start: var(--spacing-vh-sm);\n        }\n        /* The last entry-content but not if it's the last main item. */\n        main > *:not(:last-child) &:last-child {\n            padding-block-end: var(--spacing-5xl);\n        }\n        /* If it's the last item set padding to 0 */\n        main > section:last-child &:last-child {\n            padding-block-end: 0;\n        }\n        /* --mq-grid-after */\n        @media (width >= 1000px) {\n            grid-area: content;\n        }\n    }\n    .c-entry-content {\n        --font-size-sm-line-height: 1.4;\n        --font-family-main-weight-normal: 300;\n\n        max-width: var(--max-width-reading);\n        margin-inline: auto;\n        /* Based on e.g. >> Home on mobile */\n        padding-inline: var(--spacing-xl);\n        font-family: var(--font-family-main);\n        font-weight: var(--font-family-main-weight-normal);\n\n        /* e.g. .o-badge-heading */\n        > :has(h1),\n        > h1, > .h1 {\n            padding-block-start: 0;\n        }\n\n        :nth-child(1 of header) {\n            /* Article intro text */\n            p,\n            /* Or if there's no article intro text in the header, instead make the first following paragraph an \"intro\" style text e.g. >> /reference/widgets */\n            &:has(:not(p)) + article > p:first-child {\n                font-size: var(--font-size-lg);\n                line-height: var(--font-size-lg-line-height);\n                max-width: var(--max-width-reading);\n                margin-block-end: var(--spacing-lg);\n            }\n            /* Compress the heading under certain conditions */\n            &:has(h1:only-child) {\n                /* e.g. >> /extending/markdown */\n                &:has(+ h2) h1,\n                /* e.g. >> /ui-components/all-ui-components */\n                &:has(+ .c-table) h1 {\n                    padding-block-end: var(--spacing-2xs);\n                }\n            }\n        }\n\n        & h2 {\n            :is(.c-entry-content) > &:first-child {\n                /* Add top spacing for the beginning of a section */\n                padding-block-start: var(--spacing-md);\n            }\n        }\n\n        ol, ul {\n            /* Rem because sometimes there are lists within lists e.g. >> /taxonomies */\n            font-size: var(--font-size-md-rem);\n            /* e.g. >> /upgrade-guide */\n            /* margin-block: var(--spacing-xl); */\n            &:has(+ :is(h2, h3, h4, h5, h6)) {\n                padding-block-end: var(--spacing-md);\n            }\n            :is(h2, h3, h4, h5, h6) {\n                font-size: inherit;\n                line-height: inherit;\n                padding-block-end: 0;\n            }\n            img {\n                /* e.g. >> /getting-started/deploying/laravel-cloud */\n                border-radius: var(--border-radius-sm);\n                border: 1px solid var(--color-black);\n                /* Screenshots can look a little washed out, let's boost them a smidgen */\n                filter: var(--filter-image-boost-content);\n            }\n        }\n\n        p, li {\n            color: var(--color-black-prose);\n        }\n\n        li :is(ol, ul) {\n            /* e.g.\n\n            <ul>\n                <li>something</li>\n                <li>something else\n                    <ol> <-- we don't want this to have extra padding\n\n            */\n            padding-inline: 0;\n            padding-block-start: 0;\n        }\n\n        li {\n            /* Bard wraps <li> text in a <p> tag */\n            p {\n                padding-block-end: 0!important;\n            }\n        }\n\n        :has(+ .c-tip, + pre) {\n            ol& {\n                /* e.g. >> /forms */\n                padding-block-end: 0;\n            }\n            ul& {\n                /* e.g. >> /graphql */\n                padding-block-end: var(--spacing-xs);\n            }\n        }\n\n        /* Direct descendents only because we don't want to affect components such as .c-related */\n        > ul {\n            --padding-inline-start: min(1.5rem, 5vw);\n            padding-inline-start: var(--padding-inline-start);\n            /* e.g. >> /upgrade-guide */\n            padding-block: var(--spacing-3xs) var(--spacing-2xl);\n            li {\n                padding-inline-start: var(--spacing-3xs);\n                position: relative;\n                &::before {\n                    content: '';\n                    background: var(--color-purple-light-1);\n                    position: absolute;\n                    left: calc(0% - var(--padding-inline-start));\n                    top: 0.5rem;\n                    inline-size: 0.5em;\n                    aspect-ratio: 1;\n                    border-radius: 50%;\n                }\n                /* Lists within lists e.g. >> /taxonomies */\n                ul li {\n                    padding-inline-start: 0;\n                    padding-block-end: var(--spacing-3xs);\n                    &:first-child {\n                        padding-block-start: var(--spacing-2xs);\n                    }\n                    &:last-child {\n                        padding-block-end: var(--spacing-2xs);\n                    }\n                    &::before {\n                        /* Remove the purple dot for lists within lists */\n                        content: unset;\n                    }\n                }\n            }\n        }\n\n        ol {\n            counter-reset: item;\n            li {\n                --size: 1.75rem;\n                list-style: none;\n\n                &:not(:last-child) {\n                    padding-block-end: var(--spacing-xs);\n                }\n                position: relative;\n\n                /* Sometimes we have an ul inside an ol e.g. >> /getting-started/installing/laravel */\n                &:not(ol ul *) {\n                    padding-inline-start: calc(var(--size) + var(--spacing-sm));\n                    &::before {\n                        position: absolute;\n                        left: 0;\n                        display: inline-flex;\n                        justify-content: center;\n                        align-items: center;\n                        inline-size: var(--size);\n                        block-size: var(--size);\n                        content: counter(item);\n                        counter-increment: item;\n                        color: light-dark(var(--color-primary-text), var(--color-body-background));\n                        font-size: var(--font-size-sm);\n                        font-weight: var(--font-family-serif-weight-strong);\n                        margin-inline-end: min(var(--spacing-md), 3vw);\n                        background: var(--color-yellow-light-1);\n                        border-radius: 50%;\n                    }\n                }\n            }\n            ul {\n                /* e.g. >> /getting-started/installing/laravel */\n                padding-block-start: var(--spacing-xs);\n                padding-inline-start: var(--spacing-sm);\n                li {\n                    list-style-type: circle;\n                    padding-inline-start: var(--spacing-4xs);\n                    &:not(:last-child) {\n                        padding-block-end: var(--spacing-2xs);\n                    }\n                }\n            }\n        }\n\n        a {\n            /* Improve legibility */\n            font-weight: var(--font-family-main-weight-strong);\n            &:has(.external):not(.c-icon-grid *) {\n                /* Needed for external link icons Safari 18.3.1 */\n                display: inline-flex;\n            }\n        }\n\n        p {\n            /* e.g. >> /quick-start-guide */\n            + ol {\n                padding-block: var(--spacing-sm) var(--spacing-2xl);\n            }\n            /* e.g. >> /quick-start-guide */\n            + ul {\n                padding-block: var(--spacing-3xs) var(--spacing-2xl);\n            }\n        }\n        /* Followed by some kind of image component (or paragraph like /javascript-frameworks). Not when the h1 is an only child, e.g. /modifiers/add */\n        > :is(header, p):has(+ * img, + p):not:has(h1:only-child) {\n            margin-block-end: calc(var(--spacing-2xl));\n        }\n\n        .external {\n            /* e.g. external links */\n            display: inline-block;\n            margin: 0.15em 0 0 0.1em;\n            font-size: 0.95em;\n        }\n\n        blockquote {\n            font-style: italic;\n            font-weight: var(--font-family-main-weight-medium);\n            padding-inline-start: var(--spacing-lg);\n            margin-block-end: var(--spacing-xl);\n            border-inline-start: 3px solid var(--color-primary-accent);\n        }\n        /* Use this if you want to embed entry content in a component that already handles inline padding */\n        .c-entry-content--flush {\n            padding-inline: 0;\n        }\n\n        /* --mq-wider-entry-content-after */\n        @media (width >= 1500px) {\n            padding-inline: var(--spacing-xl);\n        }\n    }\n}\n\n@layer plugins {\n    .c-entry-content {\n        /* When lists have a pre compress things a little so it's more readable e.g. >> /addons/vite-tooling */\n        li:has(pre) {\n            p {\n                margin-block-end: var(--spacing-sm);\n            }\n            pre {\n                margin-block: var(--spacing-xs);\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/feedback-meerkat.css",
    "content": "/* GROUP COMPONENTS / FEEDBACK MEERKAT\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    Every page\n\n    What does it do?\n    ----------------\n    Feedback panel at the bottom of the page\n\n*/\n/* HTML Example...\n\n    <div class=\"c-feedback-meerkat\">\n        <h2>Docs Feedback</h2>\n        <p>Submit improvements, related content, or suggestions through GitHub.</p>\n        <a href=\"https://github.com/statamic/docs/issues/new\" class=\"c-btn c-btn--1\">Betterify this page</a>\n        <img src=\"/img/meerkat.webp\" alt=\"Meerkat\" width=\"150\" height=\"278\">\n    </div>\n\n*/\n@layer components {\n    .c-feedback-meerkat {\n        margin-block-start: var(--spacing-4xl);\n        text-decoration-line: none;\n        border: 1px solid transparent;\n        color: var(--color-primary-text);\n        /* Custom */\n        @media (width < 500px) {\n            margin-block-start: 6.5rem;\n        }\n        padding: var(--spacing-lg);\n        background: var(--color-gradient-burnt);\n        border-radius: var(--border-radius-lg);\n\n        position: relative;\n        &::before {\n            pointer-events: none;\n            content: '';\n            position: absolute;\n            inset: 0;\n            inline-size: 100%;\n            height: 100%;\n            transform: scaleX(-1);\n            border-radius: 50px;\n\n            /* https://css-pattern.com/graph-paper/ */\n            --s: 220px; /* control the size*/\n            --c1: white;\n            --c2: transparent;\n            --_g: #0000 90deg,var(--c1) 0;\n            background:\n              conic-gradient(from 90deg at 1px 1px,var(--_g)),\n              conic-gradient(from 90deg at 1px 1px,var(--_g)),\n              var(--c2);\n            background-size: var(--s) var(--s), calc(var(--s)/5) calc(var(--s)/5);\n        }\n        /* Page preference is \"dark\" */\n        html:has(#color-scheme option[value=\"dark\"]:checked) & {\n            &::before {\n                opacity: 0.025;\n            }\n        }\n        /* Page preference is \"system\", and system preference is \"dark\" */\n        @media (prefers-color-scheme: dark) {\n            html:has(#color-scheme option[value=\"system\"]:checked) & {\n                &::before {\n                    opacity: 0.025;\n                }\n            }\n        }\n        > * {\n            /* Pull component content above the pseudo content */\n            position: relative;\n        }\n        &::after {\n            content: '';\n            position: absolute;\n            z-index: -2;\n            top: -2rem;\n            right: 0;\n            inline-size: 100%;\n            border-block-start: 1px dashed var(--color-purple-light-1);\n            transition: inline-size 0.25s var(--animation-timing-function-hipster) 0.3s;\n        }\n\n        img {\n            position: absolute;\n            z-index: var(--z-index-below-body);\n            top: 0;\n            inline-size: 60px;\n            transition: all .7s var(--animation-timing-function-hipster) 0.1s;\n            rotate: -5deg;\n        }\n        &:hover {\n            border-color: var(--color-black);\n            transition: border-color 0.35s var(--animation-timing-function-hipster) 0.1s;\n            &::after {\n                inline-size: 87%;\n            }\n            img {\n                top: -80px;\n                rotate: unset;\n            }\n        }\n\n        h2 {\n            font-family: var(--font-family-main);\n            font-size: var(--font-size-xl);\n            line-height: var(--font-size-xl-line-height);\n            font-weight: var(--font-family-main-weight-medium);\n            padding-bottom: 8px;\n        }\n\n        /* Custom */\n        @media (width < 500px) {\n            p {\n                font-size: var(--font-size-sm);\n                line-height: var(--font-size-sm-line-hegiht);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/icon-grid.css",
    "content": "/* GROUP COMPONENTS / ICON GRID\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    A grid of icons with a title and text e.g. /installing\n\n*/\n/* HTML Example...\n\n*/\n@layer components {\n    .c-icon-grid {\n        display: grid;\n        grid-template-columns: repeat(auto-fit, minmax(min(100%, 12em), 1fr));\n        gap: var(--spacing-2xl) var(--spacing-md);\n        &:not(:last-child) {\n            padding-block-end: var(--spacing-4xl);\n        }\n        text-align: center;\n\n        .c-anchor {\n            display: none;\n        }\n\n        img {\n            &:not([src*=\".svg\"]) {\n                max-width: 5.5rem;\n                filter: var(--filter-image-boost-1);\n            }\n            inline-size: 100%;\n            max-height: 5rem;\n            max-width: 9rem;\n        }\n\n        h2, h3 {\n            padding-block: var(--spacing-sm) var(--spacing-3xs);\n            font-family: var(--font-family-main);\n            font-size: var(--font-size-ui);\n            font-weight: var(--font-family-main-weight-heavy);\n            text-transform: uppercase;\n            letter-spacing: 0.3px;\n        }\n\n        p {\n            font-family: var(--font-family-code);\n            font-weight: var(--font-family-code-weight-light);\n            font-size: var(--font-size-xs);\n            line-height: var(--font-size-xs-line-height);\n        }\n\n        header + & {\n            /* e.g. >> /deploying */\n            padding-block-start: var(--spacing-md);\n        }\n        &:has(.c-icon-grid__item__label) {\n            /* A recommended label like e.g. >> /installing */\n            padding-block-start: var(--spacing-lg);\n        }\n    }\n    .c-icon-grid__item {\n        position: relative;\n        p {\n            margin-block-end: 0;\n        }\n\n        > a .external {\n            /* Hide the direct .external links added with javascript because we want to add the icon elsewhere instead */\n            display: none;\n        }\n        .external {\n            background: light-dark(hsl(var(--color-purple-hue) 70% 97%), transparent);\n            border-radius: 50%;\n            * {\n                fill: var(--color-purple);\n            }\n        }\n    }\n    .c-icon-grid__item__label {\n        position: absolute;\n        top: -1rem;\n        left: 50%;\n        transform: translateX(-50%);\n        font-family: var(--font-family-main);\n        font-size: var(--font-size-xs);\n        letter-spacing: 1px;\n        text-transform: uppercase;\n        div {\n            padding: var(--spacing-2xs) var(--spacing-md);\n        }\n    }\n    .c-icon-grid__icon {\n        aspect-ratio: 2 / 1.5;\n        display: grid;\n        justify-content: center;\n        align-content: center;\n        padding: var(--spacing-xl);\n        /* Custom */\n        @media (width < 600px) {\n            max-width: 17rem;\n        }\n        margin-inline: auto;\n        background: var(--color-gradient-full-light-2);\n        border: var(--border-dashed-red);\n        border-radius: var(--border-radius-xl);\n        @supports (corner-shape: squircle) {\n            border-radius: 35px;\n            corner-shape: squircle;\n        }\n    }\n}\n@layer utilities {\n    body .c-icon-grid__item__label div {\n        font-weight: var(--font-family-main-weight-heavy);\n    }\n}"
  },
  {
    "path": "resources/css/components/imagery/bordered-image.css",
    "content": "/* GROUP COMPONENTS / BORDERED IMAGE\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    /assets\n\n    What does it do?\n    ----------------\n    Used on all figured images.\n\n*/\n/* HTML Example...\n\n    <picture class=\"c-bordered-image\">\n        <source> etc.\n        <img />\n    </picture>\n\n    or with a figure\n\n    <figure class=\"c-bordered-image\">\n        <picture>\n            <!-- {{# WebP #}} -->\n            <source\n                srcset=\"/img/2/make-user.png 750w,\n                        /img/2/make-user.png 1000w,\n                        /img/2/make-user.png 1536w\"\n                type=\"image/webp\"\n                sizes=\"(min-width: 1000px) 33.333vw, 100vw\"\n            >\n            <!-- {{# JPEG #}} -->\n            <source\n                srcset=\"/img/2/make-user.png 750w,\n                        /img/2/make-user.png 1000w,\n                        /img/2/make-user.png 1536w\"\n                type=\"image/jpeg\"\n                sizes=\"(min-width: 1000px) 33.333vw, 100vw\"\n            >\n            <!-- {{# Fallback, with dimensions set to minimise layout shift. See Jay George's Wiki under Performance > Images for more information. #}} -->\n            <img\n                src=\"/img/2/make-user.png\"\n                loading=\"lazy\"\n                decoding=\"async\"\n                width=\"906\"\n                height=\"568\"\n                alt=\"Statamic Make:User Command\"\n            >\n        </picture>\n        <figcaption>You can customize user fields later.</figcaption>\n    </figure>\n\n*/\n@layer components {\n    /* Make sure so we don't descend from a further component, so we don't affect things like the homepage where we have the .c-tiles-with-description component */\n    .c-entry-content figure:not(.c-entry-content [class^=\"c-\"]),\n    .c-bordered-image {\n        position: relative;\n        /* e.g. >> /collections and /stache */\n        margin-block-start: var(--spacing-xl);\n        /* e.g. >> /frontend/protecting-content where there's a heading of \"Password form\" followed by a figure */\n        :is(h2, h3, h4, h5, h6) + &,\n        ul + & {\n            /* Decrease, e.g. >> /getting-started/quick-start-guide */\n            margin-block-start: var(--spacing-2xs);\n        }\n        &:not(:last-child) {\n            margin-block-end: calc(var(--spacing-2xl) + var(--spacing-2xs));\n        }\n        img {\n            margin-block-end: 0;\n            margin-inline: auto;\n            /* e.g. >> /getting-started/installing/laravel-forge-1-click */\n            border-radius: var(--border-radius-sm);\n            border: 1px solid var(--color-black);\n            /* Screenshots can look a little washed out, let's boost them a smidgen */\n            filter: var(--filter-image-boost-content);\n        }\n        figcaption {\n            display: inline-block;\n            padding: var(--spacing-xs) var(--spacing-md);\n            background: var(--color-yellow-light-2);\n            /* --mq-nav-open-after */\n            @media (width >= 1100px) {\n                /* Lighter now that we have more things going on */\n                background: var(--color-gradient-burnt-right);\n            }\n            font-weight: var(--font-family-main-weight-medium);\n            font-size: var(--font-size-sm);\n            line-height: var(--font-size-sm-line-height);\n            border-radius: var(--border-radius-sm);\n            border-top-left-radius: 0;\n            border-top-right-radius: 0;\n        }\n        /* Custom */\n        @media (width < 768px) {\n            &:has(figcaption) img {\n                border-bottom-left-radius: 0;\n                border-bottom-right-radius: 0;\n            }\n        }\n        /* Custom */\n        @media (width >= 768px) {\n            padding: var(--spacing-sm);\n            &::before {\n                position: absolute;\n                z-index: var(--z-index-below-body);\n                inset: 0;\n                content: \"\";\n                background: var(--color-gradient-full-light-2);\n                border-radius: var(--border-radius-lg);\n            }\n            &:has(figcaption) {\n                /* Increase. Make a bit more room for the caption */\n                padding-block-end: calc(var(--spacing-3xl) + 1rem);\n            }\n            figcaption {\n                position: absolute;\n                bottom: 0;\n                left: 0;\n                border-radius: 0 var(--border-radius-lg);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/imagery/full-width-image.css",
    "content": "/* GROUP COMPONENTS / FULL WITH IMAGE\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    /assets\n\n    What does it do?\n    ----------------\n    Full width style image as opposed to a bordered image. You may use this as an alternative style.\n\n*/\n/* HTML Example...\n\n    <picture class=\"c-full-width-image\">\n        <source> etc.\n        <img />\n    </picture>\n\n    or with a figure\n\n    <figure class=\"c-full-width-image\">\n        <picture>\n            <source> etc.\n            <img />\n        </picture>\n        <figcaption>Browsing some assets.</figcaption>\n    </figure>\n\n*/\n@layer components {\n    .c-full-width-image {\n        &:not(:last-child) {\n            margin-block-end: calc(var(--spacing-2xl) + var(--spacing-2xs));\n        }\n        img {\n            @container style(--color-scheme: dark) {\n                /* Easier on the eyes */\n                opacity: 0.95;\n            }\n            margin-block-end: 0;\n            border-radius: var(--border-radius-sm);\n            border-bottom-left-radius: 0;\n        }\n        figcaption {\n            display: inline-block;\n            padding: var(--spacing-xs) var(--spacing-md);\n            background: var(--color-yellow-light-3);\n            /* Page preference is \"dark\" */\n            html:has(#color-scheme option[value=\"dark\"]:checked) & {\n                background: var(--color-gradient-full-light-2);\n            }\n            /* Page preference is \"system\", and system preference is \"dark\" */\n            @media (prefers-color-scheme: dark) {\n                html:has(#color-scheme option[value=\"system\"]:checked) & {\n                    background: var(--color-gradient-full-light-2);\n                }\n            }\n            font-weight: var(--font-family-main-weight-normal);\n            border-radius: var(--border-radius-sm);\n            border-top-right-radius: 0;\n            border-top-left-radius: 0;\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/list-turtles.css",
    "content": "/* GROUP COMPONENTS / LIST TURTLES\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    An ordered list styled like the Ninja Turtles.\n\n*/\n/* HTML Example...\n\n    <ol class=\"c-list-turtles\">\n        <li>Forms</li>\n        <li>Indexes</li>\n        <li>Drivers</li>\n    </ol>\n\n*/\n@layer scope {\n    /* Need to override the .c-entry-content ol */\n    .c-entry-content {\n        /* ol*/\n        .c-list-turtles {\n            li {\n                /* Increase */\n                --size: 2.25em;\n                font-family: var(--font-family-main);\n                font-weight: var(--font-family-main-weight-strong);\n                /* Increase */\n                font-size: var(--font-size-md);\n                &::before {\n                    top: -0.2rem;\n                    background: linear-gradient(to bottom, #70D50B 0%,#70B927 100%);\n                }\n                &:not(:last-child) {\n                    padding-block-end: var(--spacing-md);\n                }\n                &::after {\n                    content: \"\";\n                    position: absolute;\n                    top: 0.1rem;\n                    left: 0;\n                    inline-size: calc(var(--size) - 0.2rem);\n                    border: 3px solid var(--color-red-bright);\n                    border-radius: var(--border-radius-sm);\n                }\n                &::marker {\n                    position: relative;\n                }\n                &:nth-child(2)::after {\n                    border-color: oklch(0.8 0.2 224);\n                }\n                &:nth-child(3)::after {\n                    border-color: oklch(0.85 0.2 70);\n                }\n                &:nth-child(4)::after {\n                    border-color: oklch(0.6 0.2 290);\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/logos.css",
    "content": "/* GROUP COMPONENTS / LOGOS\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    /home\n\n    What does it do?\n    ----------------\n    - Logos such as sponsors\n    - Based on https://css-irl.info/aspect-ratio-is-great/\n\n*/\n/* HTML Example...\n\n    <div class=\"c-logo-grid\">\n        <div class=\"c-logo-grid__item\">\n            <img src=\"https://assets.codepen.io/85648/logo-made-up-company.svg\" />\n        </div>\n        <div class=\"c-logo-grid__item\">\n            <img src=\"https://assets.codepen.io/85648/logo-pie-club.svg\" />\n        </div>\n        <div class=\"c-logo-grid__item\">\n            <img src=\"https://assets.codepen.io/85648/logo-birdwatchers.svg\" />\n        </div>\n        <div class=\"c-logo-grid__item\">\n            <img src=\"https://assets.codepen.io/85648/logo-vitamins.svg\" />\n        </div>\n        <div class=\"c-logo-grid__item\">\n            <img src=\"https://assets.codepen.io/85648/logo-angry-jelly.svg\" />\n        </div>\n    </div>\n\n*/\n/* Statamic Example...\n\n    {{ logos }}\n        <div class=\"c-logo-grid__item\">\n            <a href=\"\">\n                <picture>\n                    {{# WebP #}}\n                    <source\n                        srcset=\"{{ glide:url width='185' dpr='2' format='webp' }} 2x\"\n                        type=\"image/webp\"\n                    >\n                    {{# JPEG #}}\n                    <source\n                        srcset=\"{{ glide:url width='185' dpr='2' }} 2x\"\n                        type=\"image/jpeg\"\n                    >\n                    {{# Fallback #}}\n                    <img src=\"{{ glide:url width='185' dpr='2' }}\"\n                        loading=\"lazy\" width=\"{{ width | round }}\" height=\"{{ height | round }}\"\n                        alt=\"{{ if alt }}{{ alt ucfirst='true' ensure_right='.' }}{{ else }}{{ filename ucfirst='true' ensure_right='.' replace='-| ' }}{{ /if }}\"\n                    />\n                </picture>\n            </a>\n        <span>Jeroen Peters</span> <-- an optional image\n        </div>\n    {{ /logos }}\n\n*/\n@layer components {\n    .c-logo-grid {\n        --width: 9rem;\n        display: flex;\n        flex-wrap: wrap;\n        gap: var(--spacing-sm);\n        a {\n            text-decoration: none;\n        }\n    }\n\n    .c-logo-grid__item {\n        align-content: center;\n        padding: var(--spacing-md) var(--spacing-lg);\n        background: var(--color-gradient-blue-light-1);\n        border-radius: var(--border-radius-xl);\n        corner-shape: squircle;\n        font-family: var(--font-family-main);\n        --color-link: var(--color-primary-text);\n        &:has(a):not(:has(img)) {\n            /* e.g. \"Sponsor Statamic\" */\n            padding: 0 var(--spacing-sm);\n            background: none;\n            a {\n                /* Put the padding on the link instead to make it appear smaller */\n                border: 1px dashed var(--color-primary-text);\n                padding: var(--spacing-sm) var(--spacing-lg);\n                border-radius: var(--border-radius-lg);\n                corner-shape: squircle;\n                /* Custom hover effect */\n                &:hover {\n                    color: inherit!important;\n                    text-decoration: underline;\n                    text-decoration-color: inherit!important;\n                    text-underline-position: unset;\n                }\n            }\n        }\n        .external {\n            display: none;\n        }\n        /* [1] For when we have <span> text next to the logo */\n        a {\n            display: flex;\n            flex-wrap: wrap;\n            gap: var(--spacing-sm);\n        }\n        /* [2] */\n        &:has(span) {\n            font-size: var(--font-size-md);\n            line-height: var(--font-size-md-line-height);\n            /* Reduce to compensate for the increased image height */\n            padding-block: var(--spacing-sm);\n            :is(img, svg:not(.external)) {\n                min-inline-size: unset;\n                inline-size: 2.65rem;\n                block-size: 2.65rem;\n                /* e.g. a GitHub avatar */\n                border-radius: var(--border-radius-sm);\n            }\n        }\n        span {\n            align-content: center;\n            text-box: trim-start cap;\n        }\n    }\n\n    .c-logo-grid :is(img, svg:not(.external)) {\n        /* This is needed for rasterised images */\n        object-fit: contain;\n        height: 2rem;\n        /* Where needed, it's best to set sizes for individual logos as an inline style e.g. <img src=\"/img/sponsors/arcustech.svg\" alt=\"Arcustech logo\" class=\"u-hide-in-dark-mode\" /> <img src=\"/img/sponsors/arcustech-dark.svg\" alt=\"Arcustech logo\" class=\"u-hide-in-light-mode\" style=\"min-width: 10rem;\" /> */\n        /* min-inline-size: 8.5rem; */\n    }\n}"
  },
  {
    "path": "resources/css/components/nav/back-nav.css",
    "content": "/* GROUP COMPONENTS / BACK NAV\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    A small navigation component that displays a \"go back\" link.\n\n*/\n/* HTML Example...\n\n    {{ if segment_2 && $collection && ($collection != \"pages\") }}\n        <li>\n            <a href=\"{{ parent:url }}\" class=\"c-back-nav\">&larr; Go back</a>\n            etc.\n        </li>\n    {{ else }}\n        ...\n\n*/\n@layer components {\n    .c-back-nav {\n        display: inline-block;\n        background: var(--color-pink-light-3);\n        @container style(--color-scheme: dark) {\n            /* Make it a bit more obvious */\n            background: var(--color-red-burnt-4);\n        }\n        padding: var(--spacing-3xs) var(--spacing-xs);\n        margin-block-end: var(--spacing-sm);\n        border-radius: 50px;\n\n        &a {\n            font-weight: var(--font-family-main-weight-normal);\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/nav/breadcrumbs.css",
    "content": "/* GROUP COMPONENTS / BREADCRUMBS\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    A navigation component that displays the current page's location. Each link represents a step in the journey to the current page.\n\n*/\n/* HTML Example...\n\n    <nav class=\"c-breadcrumbs\">\n        <ol>\n            <li><a href=\"#\">Home</a></li>\n            <li><a href=\"#\">Pictures</a></li>\n            <li><a href=\"#\">Summer 15</a></li>\n            <li>Italy</li>\n        </ol>\n    </nav>\n\n*/\n@layer components {\n    .c-breadcrumbs {\n        display: inline-block;\n        grid-area: breadcrumbs;\n        & ol {\n            list-style: none;\n            display: flex;\n            align-items: center;\n            gap: var(--spacing-lg);\n            /* For Safari */\n            inline-size: 100%;\n            /* --mq-grid-before */\n            @media (width < 1000px) {\n                position: relative;\n                /* Tweak to align with flanking buttons on mobile */\n                top: -0.1rem;\n                /* 10.5 is an approx width of the two flanking nav buttons, to make sure we stay on the same line for mobile */\n                max-width: calc(100% - 10.5rem);\n                padding-block-end: var(--spacing-3xl);\n                margin-block-start: -0.25rem;\n            }\n            /* --mq-nav-open-before */\n            @media (width < 1100px) {\n                max-width: var(--max-width-reading);\n                margin-inline: auto;\n            }\n            /* --mq-grid-after to --mq-nav-open-before */\n            @media (width >= 1000px) and (width < 1100px) {\n                padding-inline: var(--spacing-xl);\n            }\n        }\n        & li {\n            display: flex;\n            align-items: center;\n            padding-block-end: 0;\n            & svg {\n                inline-size: 0.4rem;\n                margin-inline: 0 1.5rem;\n            }\n        }\n        & a, button {\n            padding: 0;\n            text-decoration: none;\n            color: inherit;\n            text-transform: uppercase;\n            font-size: var(--font-size-2xs);\n            text-box: text;\n            line-height: 1;\n            letter-spacing: 0.5px;\n        }\n        button {\n            font-weight: var(--font-family-main-weight-strong);\n            margin-inline-end: 0;\n        }\n        /* GROUP COMPONENTS / BREADCRUMBS / MQs\n        =================================================== */\n        @media (width < 700px) {\n            li:not(:last-child) {\n                /* Hide the breadcrumb trail when we have limited space */\n                display: none;\n            }\n        }\n        /* --mq-grid-after */\n        @media (width >= 1000px) {\n            /* For Safari */\n            display: flex;\n            ol {\n                gap: 1.7rem;\n                block-size: 100%;\n            }\n            li svg {\n                /* Increase to allow the gradient background to breathe */\n                margin-inline-end: 1.85rem;\n            }\n            button:last-child {\n                position: relative;\n                margin-inline-start: var(--spacing-xs);\n                &::before {\n                    content: '';\n                    position: absolute;\n                    z-index: var(--z-index-below-body);\n                    inset: -0.4rem -1rem;\n                    left: -1.1rem;\n                    background: var(--color-gradient-full-light-2);\n                    border-radius: 30% 40% 25% 60% / 80% 110% 40% 60%;\n                }\n            }\n        }\n        /* --mq-nav-open-after */\n        @media (width >= 1100px) {\n            padding-inline: var(--spacing-xl);\n            width: 100%;\n            max-width: var(--max-width-reading);\n            margin-inline: auto;\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/nav/sidebar-advert.css",
    "content": "/* GROUP COMPONENTS / SIDEBAR ADVERT\n=================================================== */\n@layer ui {\n    .c-sidebar-advert {\n        &:first-child {\n            /* I.e. when the toc is hidden with {{ $hide_toc = 'yes' }} */\n            padding-block-start: var(--spacing-2xl);\n        }\n        padding-inline-start: 3.85rem;\n        padding-inline-start: 2.85rem;\n        a {\n            padding: 0;\n        }\n        img {\n            margin-inline-start: -0.25rem;\n            inline-size: 64%;\n            rotate: -4deg;\n            border-radius: var(--border-radius-sm);\n            box-shadow: 0px 15px 25px -10px hsl(0deg 0% 0% / 55%);\n            /* Make the advert less distracting */\n            opacity: 0.95;\n        }\n        h2 {\n            padding-block: var(--spacing-2xl) var(--spacing-sm);\n            font-size: var(--font-size-xs);\n            font-family: var(--font-family-main);\n            font-weight: var(--font-family-main-weight-normal);\n            letter-spacing: 1px;\n            text-transform: uppercase;\n            color: inherit;\n        }\n        p {\n            margin-block-end: var(--spacing-xs);\n            font-weight: var(--font-family-main-weight-medium);\n            line-height: var(--font-size-sm-line-height);\n            text-wrap: balance;\n            a {\n                text-decoration: underline;\n                font-size: var(--font-size-xs);\n                font-family: var(--font-family-main);\n                font-weight: var(--font-family-main-weight-normal);\n                text-transform: uppercase;\n                text-decoration-color: var(--color-text-underline-light);\n            }\n        }\n        @container style(--color-scheme: dark) {\n            img {\n                /* Tweak the default dark-mode image filter, increasing the hue rotation so there's more of a stylised purple tinge. */\n                filter:\n                    hue-rotate(-18deg)\n                    contrast(110%)\n                    brightness(85%)\n                ;\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/nav/sidebar.css",
    "content": "/* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / API\n=================================================== */\n@layer components {\n    .c-nav-sidebar-with-popover-api {\n        --dropdown-nav-item-padding: 0.25rem;\n        position: sticky;\n        max-width: 15rem;\n\n        nav,\n        nav [popover] {\n            /* Reset */\n            margin: 0;\n            padding: 0;\n            border: 0;\n\n            display: none;\n            opacity: 0;\n            transition: opacity 350ms, margin 350ms allow-discrete, display 350ms allow-discrete, overlay 350ms allow-discrete;\n\n            &:popover-open {\n                display: block;\n                opacity: 1;\n\n                @starting-style {\n                    /* opacity: 0; */\n                    margin-block-start: 0;\n                    margin-inline-start: -50%;\n                    &::backdrop {\n                        /* Needed to get the popover backdrop to fade in when opening */\n                        opacity: 0;\n                    }\n                }\n            }\n        }\n        nav {\n            /* Override the default background color of the popover */\n            background: inherit;\n            [popover] {\n                max-width: 14rem;\n                &:popover-open {\n                    margin-block-start: var(--spacing-3xs);\n                }\n            }\n        }\n        #anchor-nav-mobile {\n            anchor-name: --anchor-nav-mobile;\n        }\n        #popover-nav-sidebar {\n            /* Naturally the user-agent applies fit-content but sometimes this doesn't feel wide enough e.g. /fieldtypes/replicator */\n            min-width: min(60%, 15rem);\n            position-anchor: --anchor-nav-mobile;\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / MOBILE NAV BUTTON\n        =================================================== */\n        .c-nav-sidebar-with-popover-api__mobile-button {\n            padding: 1.2rem;\n            margin: 0;\n            background: var(--color-dropdown-nav-background);\n            border-radius: 40%;\n            box-shadow: var(--box-shadow-not-t-medium);\n            svg {\n                color: var(--color-primary-text);\n                font-size: 1.3em;\n                /* Prevent the mobile nav button from getting squashed on pages with long code overflow areas such as /advanced-topics/oauth */\n                min-width: 1.28rem;\n            }\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / CLOSE BUTTON\n        =================================================== */\n        .c-nav-sidebar-with-popover-api__close-button {\n            position: absolute;\n            z-index: var(--z-index-above-body);\n            right: 0.5rem;\n            top: 0.25rem;\n            margin-inline-end: 0;\n            padding: var(--spacing-xs);\n            & svg {\n                font-size: 1rem;\n                inline-size: unset;\n            }\n        }\n        .c-nav-sidebar-with-popover-api__disclosure {\n            position: absolute;\n            left: 0;\n            font-size: 0.4em;\n            transition: rotate 0.2s ease-in-out;\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / NAV STYLING\n        =================================================== */\n        nav {\n            color: var(--color-primary-text);\n            & * {\n                line-height: 1.3;\n                font-weight: var(--font-family-main-weight-light);\n            }\n            .c-nav-sidebar-with-popover-api-category-heading {\n                position: relative;\n                display: flex;\n                max-width: 14rem;\n                white-space: nowrap;\n                gap: var(--spacing-md);\n                align-items: center;\n                justify-content: space-between;\n                /* Nudge so the nav aligns horizontally with the breadcrumbs */\n                padding: 0.4rem 0 0.4rem var(--spacing-md);\n                font-size: var(--font-size-md);\n                line-height: var(--font-size-md-line-height);\n                font-weight: var(--font-family-main-weight-normal);\n                text-transform: uppercase;\n                text-decoration: none;\n                &:not(:has(input:checked)) .c-nav-sidebar-with-popover-api__disclosure {\n                    rotate: 90deg;\n                }\n                &:focus-visible {\n                    /* Pull the focus outline state in slightly on the category headings so it doesn't disappear into the overflow:hidden; */\n                    outline-offset: -3px;\n                }\n            }\n\n            ul {\n                list-style: none;\n                display: flex;\n                flex-direction: column;\n                font-size: var(--font-size-xs);\n                overscroll-behavior-y: contain;\n                /* Need to cut this short a little to force scrolling when there are a lot of items. */\n                &:not(ul ul) {\n                    height: 100dvh;\n                }\n                /* Increase */\n                outline-offset: 5px;\n\n                li {\n                    &:not(ul ul li) {\n                        padding-block-end: 0;\n                        &:not(:has(input:checked)) {\n                            padding-block-end: var(--spacing-lg);\n                        }\n                    }\n                    position: relative;\n                    padding-block-end: 0;\n                    a {\n                        /* Otherwise the sidebar might grow too big e.g. >> /reference */\n                        max-width: 12rem;\n                        border-radius: var(--border-radius-md);\n                        &:hover {\n                            background: var(--color-pink-light-4);\n                        }\n                    }\n\n                    button {\n                        text-align: left;\n                        display: flex;\n                        flex-wrap: wrap;\n                        align-items: center;\n                        gap: var(--spacing-sm);\n                        /* Reset */\n                        margin-inline-end: 0;\n                        padding: 0;\n                        cursor: pointer;\n                        & svg {\n                            font-size: 0.6em;\n                        }\n                    }\n\n                    ul {\n                        gap: var(--spacing-4xs);\n                        max-height: unset;\n                        padding-inline: calc(var(--spacing-md) - var(--dropdown-nav-item-padding));\n                        font-size: var(--font-size-ui-rem);\n                        border: 1px solid transparent;\n                        /* Switch the border color when the disclosure is open */\n                        label:not(:has(input:checked)) + &,\n                        /* When there's no disclosure add the border by default e.g. >> /fieldtypes/integer */\n                        &:not(label ~ &) {\n                            border-color: var(--color-pink-light-2);\n                            box-shadow: var(--box-shadow-pink-light-sm);\n                        }\n                        border-radius: var(--border-radius-lg);\n                        & a, button, span {\n                            /* Increase the pointer targets a little to improve usability */\n                            padding: var(--dropdown-nav-item-padding);\n                            display: inline-block;\n                            /* Break at word boundaries when possible, for example, break on a long word like User:Forgot_Password_Form: */\n                            word-break: break-word;\n                            text-box: trim-start;\n                        }\n                        .o-current-menu-item {\n                            text-decoration: none;\n                            font-weight: var(--font-family-main-weight-medium);\n                            &::before {\n                                content: '';\n                                border-inline-start: 3.5px solid var(--color-pink-light-1);\n                                position: absolute;\n                                left: calc(0% - (var(--spacing-md) - var(--dropdown-nav-item-padding)));\n                                height: 1.5em;\n                            }\n                        }\n                    }\n                }\n\n                /* Subnav */\n                & ul[popover] {\n                    &:popover-open {\n                        display: flex;\n                    }\n                    padding: var(--spacing-lg);\n                    /* Just clear the outline state of the parent so it looks cleaner when tabbing through with a keyboard */\n                    margin-block-start: var(--spacing-3xs);\n                    letter-spacing: 1px;\n                    & a, button {\n                        outline-offset: unset;\n                    }\n                }\n            }\n            & a:not(.o-current-menu-item) {\n                /* https://app.typographychecklist.com/ - 43. Links that are clearly part of navigation usually don't need a special link treatment. */\n                text-decoration: none;\n            }\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / MQS\n        =================================================== */\n        /* --mq-nav-open-before */\n        @media (width < 1100px) {\n            padding-inline: var(--spacing-xs);\n            top: 0.8rem;\n            @supports not (animation-timeline: auto) {\n                top: 1rem;\n            }\n            z-index: var(--z-index-above-nav);\n            display: inline-block;\n\n            .c-nav-sidebar-with-popover-api__desktop {\n                /* Prevent a lag in seeing the desktop nav collapse if you pull the window in  */\n                transition: unset;\n            }\n            #popover-nav-sidebar {\n                /* position-area: bottom span-right; */\n                box-shadow: var(--box-shadow-medium);\n                body:has(&:popover-open) {\n                    /* Stop the body scrolling if you try and scroll a toc that's shorter than the viewport height */\n                    /* =JFG I've temporarily disabled this because believe there is a bug in iOS 18.5 where :popover-open does not disengage when closing a popover, which means this would cause overflow-clip: clip; to persist even when the popover was dismissed */\n                    /* overflow: clip; */\n                }\n            }\n            nav {\n                background: var(--color-dropdown-nav-background);\n                &::backdrop {\n                    transition: all 0.5s var(--animation-timing-function-hipster) 0s;\n                }\n                &:popover-open::backdrop {\n                    backdrop-filter: blur(5px);\n                    background: var(--popover-backdrop);\n                }\n                & ul {\n                    padding: var(--spacing-2xl) var(--spacing-md);\n                    & ul {\n                        padding: 0 var(--spacing-3xl);\n                    }\n                }\n            }\n            .c-nav-sidebar-with-popover-api-category-heading {\n                inline-size: 91%;\n            }\n        }\n        /* --mq-nav-open-after */\n        @media (width >= 1100px) {\n            grid-area: sidebar-1;\n            --top: 7rem;\n            @media (width >= 2100px) {\n                --top: 8rem;\n            }\n            top: var(--top);\n            block-size: calc(100vh - var(--top) * 2);\n            .c-nav-sidebar-with-popover-api__mobile {\n                /* Prevent a lag in seeing the mobile nav collapse if you pull the window out  */\n                transition: unset;\n            }\n            --color-dropdown-nav-background: unset;\n            nav {\n                /* Override `popover`'s default position:fixed; */\n                position: relative;\n                display: block;\n                opacity: 1;\n                /* Custom */\n                @media (width < 1350px) {\n                    /* Make it less distracting when the sidebars are very close to the main content */\n                    opacity: 0.95;\n                }\n                inline-size: 100%;\n                max-width: var(--max-width-1);\n                margin-inline: auto;\n                padding-block-end: var(--spacing-xl);\n            }\n            .c-nav-sidebar-with-popover-api__mobile-button {\n                display: none;\n            }\n            .c-nav-sidebar-with-popover-api__desktop {\n                ul {\n                    ul {\n                        max-width: min(93%, 12.75rem);\n                    }\n                    &:not(ul ul) {\n                        /* Need to cut this short a little to force scrolling when there are a lot of items. */\n                        height: 86dvh;\n                        padding-block-end: var(--spacing-md);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/nav/toc.css",
    "content": "/* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / API\n=================================================== */\n@layer components {\n    .c-nav-toc-with-popover-api {\n        --dropdown-nav-item-padding: 0.25rem;\n\n        nav,\n        nav [popover] {\n            /* Reset */\n            margin: 0;\n            padding: 0;\n            border: 0;\n\n            display: none;\n            opacity: 0;\n            transition: opacity 350ms, margin 350ms allow-discrete, display 350ms allow-discrete, overlay 350ms allow-discrete;\n\n            &:popover-open {\n                display: block;\n                opacity: 1;\n\n                @starting-style {\n                    opacity: 0;\n                    margin-block-start: 0;\n                    /* margin-inline-start: 50%; */\n                    &::backdrop {\n                        /* Needed to get the popover backdrop to fade in when opening */\n                        opacity: 0;\n                    }\n                }\n            }\n        }\n        nav {\n            /* Override the default background color of the popover */\n            background: inherit;\n            [popover] {\n                max-width: 14rem;\n                &:popover-open {\n                    margin-block-start: var(--spacing-3xs);\n                }\n            }\n        }\n        #anchor-nav-mobile {\n            anchor-name: --anchor-nav-mobile;\n        }\n        #popover-nav-toc {\n            position-anchor: --anchor-nav-mobile;\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / MOBILE NAV BUTTON\n        =================================================== */\n        .c-nav-toc-with-popover-api__mobile-button {\n            padding: 1.2rem;\n            margin: 0;\n            /* Nudge so the nav aligns horizontally with the breadcrumbs */\n            transform: translateY(-0.5rem);\n            background: var(--color-dropdown-nav-background);\n            border-radius: 40%;\n            box-shadow: var(--box-shadow-not-t-medium);\n            svg {\n                color: var(--color-primary-text);\n                font-size: 1.3em;\n            }\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / CLOSE BUTTON\n        =================================================== */\n        .c-nav-toc-with-popover-api__close-button {\n            position: absolute;\n            z-index: var(--z-index-above-body);\n            right: 0.25rem;\n            top: 1.25rem;\n            & svg {\n                font-size: 1rem;\n                inline-size: unset;\n            }\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / NAV STYLING\n        =================================================== */\n        nav {\n            color: var(--color-primary-text);\n            & * {\n                line-height: 1.4;\n                font-weight: var(--font-family-main-weight-light);\n            }\n\n            ul {\n                list-style: none;\n                display: flex;\n                flex-direction: column;\n                gap: var(--spacing-3xl);\n                font-size: var(--font-size-xs);\n                overscroll-behavior-y: contain;\n                /* Need to cut this short a little to force scrolling when there are a lot of items. */\n                &:not(ul ul) {\n                    height: 100dvh;\n                }\n                /* Increase */\n                outline-offset: 5px;\n\n                & li {\n                    padding-block-end: 0;\n                    &:not(ul ul *, .c-sidebar-advert) {\n                        position: relative;\n                        &::before {\n                            content: \"\";\n                            position: absolute;\n                            z-index: var(--z-index-below-body);\n                            top: 0.6rem;\n                            left: 0.6rem;\n                            /* --mq-compressed-toc-after */\n                            @media (width >= 1400px) {\n                                left: var(--spacing-lg);\n                            }\n                            height: calc(100% - 0.6rem);\n                            border-inline-start: 1px solid var(--color-pink-light-3-static);\n                            @container style(--color-scheme: dark) {\n                                border-color: var(--color-pink-light-2);\n                            }\n                        }\n                    }\n\n                    button {\n                        text-align: left;\n                        display: flex;\n                        flex-wrap: wrap;\n                        align-items: center;\n                        gap: var(--spacing-sm);\n                        /* Reset */\n                        margin-inline-end: 0;\n                        padding: 0;\n                        cursor: pointer;\n                        & svg {\n                            font-size: 0.6em;\n                        }\n                    }\n\n                    a, button, span {\n                        /* Increase the pointer targets a little to improve usability */\n                        padding: var(--dropdown-nav-item-padding);\n                        display: inline-block;\n                    }\n\n                    ul {\n                        gap: var(--spacing-3xs);\n                        max-height: unset;\n                        padding: 0 2rem;\n                        /* --mq-compressed-toc-after */\n                        @media (width >= 1400px) {\n                            padding-inline: 3rem;\n                        }\n                        font-size: var(--font-size-ui-rem);\n                        .o-current-menu-item {\n                            text-decoration: none;\n                            font-weight: var(--font-family-main-weight-medium);\n                            &::before {\n                                content: '';\n                                border-inline-start: 4px solid var(--color-pink-light-1);\n                                position: absolute;\n                                left: calc(0% + var(--spacing-xl));\n                                height: 2em;\n                                transform: translateY(-0.25em);\n                            }\n                        }\n                        a {\n                            border-radius: var(--border-radius-md);\n                            &:hover {\n                                background: var(--color-pink-light-4);\n                            }\n                        }\n\n                        ul {\n                            /* Need this to line up so that the scroll spy line also continues to line up */\n                            padding-inline: 0;\n                            a {\n                                /* Break at word boundaries when possible, for example, break on a long word like User:Forgot_Password_Form: */\n                                word-break: break-word;\n                                /* Indent the subnav */\n                                margin-inline-start: 0.3rem;\n                                padding-inline: var(--spacing-2xs);\n                                /* Make it slightly smaller to improve comprehension when the TOC is busy e.g. /tags/form-create */\n                                font-size: 0.83rem;\n                            }\n                        }\n                    }\n                }\n\n                /* Subnav */\n                & ul[popover] {\n                    &:popover-open {\n                        display: flex;\n                    }\n                    padding: var(--spacing-lg);\n                    /* Just clear the outline state of the parent so it looks cleaner when tabbing through with a keyboard */\n                    margin-block-start: var(--spacing-3xs);\n                    letter-spacing: 1px;\n                    & a, button {\n                        outline-offset: unset;\n                    }\n                }\n            }\n            & a:not(.o-current-menu-item) {\n                /* https://app.typographychecklist.com/ - 43. Links that are clearly part of navigation usually don't need a special link treatment. */\n                text-decoration: none;\n            }\n        }\n        .c-nav-toc-with-popover-api-category-heading {\n            display: inline-flex;\n            gap: var(--spacing-md);\n            align-items: center;\n            justify-content: space-between;\n            padding: 0 calc(var(--dropdown-nav-item-padding) + 2rem);\n            /* --mq-compressed-toc-after */\n            @media (width >= 1400px) {\n                padding-inline: calc(var(--dropdown-nav-item-padding) + 3rem);\n            }\n            /* Nudge so the nav aligns horizontally with the breadcrumbs */\n            margin-block: 0.5rem var(--spacing-xs);\n            font-size: var(--font-size-md);\n            line-height: var(--font-size-md-line-height);\n            font-weight: var(--font-family-main-weight-normal);\n            text-transform: uppercase;\n\n            img {\n                /* Override any inline styles added by artwork settings -- {{ image_width_override }} */\n                max-width: 1.45em!important;\n                /* --mq-nav-open-after to custom */\n                @media (width >= 1250px) and (width < 1460px) {\n                    /* Hide the TOC category heading image when we're tight on space */\n                    display: none;\n                }\n            }\n            &.o-current-menu-item {\n                box-shadow: none;\n                background: var(--color-gradient-full-light-2);\n            }\n        }\n        /* GROUP COMPONENTS / NAV DROPDOWN WITH POPOVER API AND ANCHOR POSITIONING / MQS\n        =================================================== */\n        /* --mq-toc-open-before */\n        @media (width < 1250px) {\n            top: 0.8rem;\n            @supports not (animation-timeline: auto) {\n                top: 1rem;\n            }\n            display: inline-block;\n            float: right;\n            padding-inline: var(--spacing-xs);\n            /* Pull the button over to the right when we're on mobile */\n            text-align: right;\n            ul {\n                /* Which means we need to reverse this for the popover */\n                text-align: left;\n            }\n            .c-nav-toc-with-popover-api__desktop {\n                /* Prevent a lag in seeing the desktop nav collapse if you pull the window in  */\n                transition: unset;\n            }\n            #popover-nav-toc {\n                /* position-area: bottom span-right; */\n                box-shadow: var(--box-shadow-medium);\n                &:not(:popover-open) {\n                    /* =JFG I've temporarily disabled this because believe there is a bug in iOS 18.5 where :popover-open does not disengage when closing a popover, which means this would cause pointer-events: none; to persist even when the popover was dismissed */\n                    /* pointer-events: none; */\n                }\n                body:has(&:popover-open) {\n                    /* Stop the body scrolling if you try and scroll a toc that's shorter than the viewport height */\n                    /* =JFG I've temporarily disabled this because believe there is a bug in iOS 18.5 where :popover-open does not disengage when closing a popover, which means this would cause overflow-clip: clip; to persist even when the popover was dismissed */\n                    /* overflow: clip; */\n                }\n            }\n            nav {\n                background: var(--color-dropdown-nav-background);\n                width: min(85%, 24rem);\n                margin-inline-start: auto;\n                &::backdrop {\n                    transition: all 0.5s var(--animation-timing-function-hipster) 0s;\n                }\n                &:popover-open::backdrop {\n                    backdrop-filter: blur(5px);\n                    background: var(--popover-backdrop);\n                }\n                & ul {\n                    padding: var(--spacing-4xl) var(--spacing-xs);\n                    & ul {\n                        padding: 0 var(--spacing-3xl);\n                    }\n                }\n            }\n        }\n        /* --mq-toc-open-after */\n        @media (width >= 1250px) {\n            grid-area: sidebar-2;\n            nav {\n                /* Override `popover`'s default position:fixed; */\n                position: relative;\n                display: block;\n                opacity: 1;\n                ul li ul {\n                    padding-inline-end: 0;\n                }\n            }\n            .c-nav-toc-with-popover-api-category-heading {\n                gap: var(--spacing-xs);\n                &:has(img) {\n                    inline-size: 100%;\n                }\n            }\n            .c-nav-toc-with-popover-api__mobile-button {\n                display: none;\n            }\n            .c-nav-toc-with-popover-api__desktop {\n                position: sticky;\n                --top: 7rem;\n                @media (width >= 2100px) {\n                    --top: 8rem;\n                }\n                top: var(--top);\n                block-size: calc(100vh - var(--top) * 2);\n                ul:not(ul ul) {\n                    /* Need to cut this short a little to force scrolling when there are a lot of items. */\n                    height: 80dvh;\n                    padding-block-end: var(--spacing-xl);\n                }\n            }\n            /* Custom */\n            @media (width < 1350px) {\n                /* Make it less distracting when the sidebars are very close to the main content */\n                opacity: 0.95;\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/panel-list.css",
    "content": "/* GROUP COMPONENTS / PANEL LIST\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    /reference/fieldtypes\n    /reference/modifiers\n\n    What does it do?\n    ----------------\n    Display a list-like panel with a grid of icons e.g. /reference/fieldtypes or a grid of words e.g. /reference/modifiers\n\n*/\n/* HTML Example...\n\n<article class=\"c-panel-list\">\n    <ul>\n        <li>\n            <a href=\"/fieldtypes/array\">\n                <svg>\n            </a>\n        </li>\n    </ul>\n</article>\n\n*/\n@layer components {\n    .c-panel-list {\n        font-family: var(--font-family-code);\n        padding: var(--spacing-2xl) var(--spacing-3xl);\n        border-radius: var(--border-radius-lg);\n        background: var(--color-purple-light-2);\n        &:has(svg) {\n            /* Check if there's any SVGs first because we want to affect e.g. >> /reference/fieldtypes but not /reference/modifiers */\n            a:not(:has(svg)) {\n                /* If there's no icon create a space */\n                margin-inline-start: 29px;\n            }\n        }\n        ul {\n            /* Custom */\n            @media (width >= 500px) {\n                column-count: 2;\n            }\n            /* Word-type list rather than icon-type list */\n            &:not(:has(svg)) {\n                /* More colums when there are no icons e.g. /reference/modifiers */\n                /* Custom */\n                @media (width >= 800px) {\n                    column-count: 3;\n                }\n                gap: var(--spacing-4xl);\n                a {\n                    /* Decrease */\n                    font-size: var(--font-size-xs);\n                    line-height: var(--font-size-xs-line-height);\n                }\n                li {\n                    padding-block-end: var(--spacing-2xs);\n                }\n            }\n        }\n        li {\n            padding-block-end: var(--spacing-xs);\n        }\n        h3 {\n            padding-block-end: var(--spacing-sm);\n            &:first-child {\n                padding-block-start: 0;\n            }\n        }\n        a {\n            display: flex;\n            align-items: center;\n            gap: var(--spacing-xs);\n            /* Where SVGs are used remove text decoration */\n            .c-panel-list:has(svg) &:not(:hover) {\n                text-decoration: none;\n            }\n            font-size: var(--font-size-sm-fixed);\n            line-height: var(--font-size-sm-line-height);\n        }\n   }\n}"
  },
  {
    "path": "resources/css/components/pill-with-description.css",
    "content": "/* GROUP COMPONENTS / PILL WITH DESCRIPTION\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    - Little pill displaying a key and a value, typically demo'ing a parameter, e.g. `alt: string`, along with a paragraph description underneath.\n    - Decoration-wise this is very similar to .c-pill but since the markup structure is different I've separated it out for now.\n\n*/\n/* HTML Example...\n\n    <div class=\"c-pill-with-description\">\n        <header>\n            <h3>Tag</h3>\n            <div>boolean *false*</div>\n        </header>\n        <div>\n            <p>When set to true, this will output an <code>&lt;img&gt;</code> tag with the URL in the <code>src</code> attribute, rather than just the&nbsp;URL.</p>\n        </div>\n    </div>\n\n*/\n@layer components {\n    .c-pill-with-description {\n        /* Use padding instead of margin to avoid collapsing with the next element e.g. a heading like on /tags/collection */\n        padding-block-end: var(--spacing-2xl);\n        /* e.g. >> /tags/form-create – handle|is|in|form|formset */\n        overflow: scroll;\n        &:has(+ &) {\n            /* Slightly Decrease if it's followed by another .c-pill-with-description */\n            padding-block-end: var(--spacing-xl);\n        }\n        header {\n            display: inline-flex;\n            justify-content: center;\n            align-items: center;\n            padding: 0;\n            margin-block-start: var(--spacing-sm);\n            background: var(--color-gradient-full-light-3);\n            @container style(--color-scheme: dark) {\n                background: var(--color-gradient-full-light-2);\n            }\n            border: var(--border-solid);\n            border-block-end: 0;\n            > * {\n                padding: var(--spacing-2xs) var(--spacing-sm);\n                font-size: var(--font-size-sm);\n                line-height: var(--font-size-sm-line-height);\n\n                position: relative;\n                &:not(:last-child)::before {\n                    content: '';\n                    position: absolute;\n                    top: 0;\n                    right: 0;\n                    block-size: 100%;\n                    border-inline-start: var(--border-solid);\n                    rotate: 20deg;\n                }\n                &:first-child {\n                    /* Custom */\n                    font-size: var(--font-size-md);\n                    font-family: var(--font-family-code);\n                    font-weight: var(--font-family-code-weight-medium);\n                    text-transform: lowercase;\n                }\n                &:last-child {\n                    padding-inline: var(--spacing-md);\n                    font-size: var(--font-size-xs);\n                    font-family: var(--font-family-code);\n                    font-weight: var(--font-family-code-weight-medium);\n                    text-transform: uppercase;\n                    letter-spacing: 0.5px;\n                    color: var(--color-purple);\n                }\n            }\n        }\n        > *:last-child {\n            padding: var(--spacing-md) var(--spacing-sm);\n            background: var(--color-gradient-full-light-5);\n            border: var(--border-solid);\n            border-radius: var(--border-radius-lg);\n            border-top-left-radius: 0;\n        }\n        p {\n            font-family: var(--font-family-main);\n            &, & code {\n                /* Custom */\n                font-size: clamp(0.8rem, 3.5vw, 0.9rem);\n            }\n        }\n        ul:last-child {\n            padding-block-end: var(--spacing-sm);\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/pill.css",
    "content": "/* GROUP COMPONENTS / PILL\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    Little pill displaying a key and a value, e.g. Bulk: Yes/No\n\n*/\n/* HTML Example...\n\n    <dl class=\"c-pill c-pill--negative\">\n        <dt>Bulk</dt>\n        <dd>No</dd>\n    </dl>\n\n*/\n/* Modifiers...\n\n    .c-pill--negative <- will turn the value text to red\n\n*/\n@layer components {\n    .c-pill,\n    dl {\n        display: inline-flex;\n        justify-content: center;\n        align-items: center;\n        padding: 0;\n        margin-block: var(--spacing-2xs) var(--spacing-lg);\n        background: var(--color-gradient-full-light-3);\n        border: var(--border-solid);\n        border-radius: var(--border-radius-lg);\n        font-family: var(--font-family-main);\n        font-size: var(--font-size-sm);\n        line-height: var(--font-size-sm-line-height);\n        font-weight: var(--font-family-main-weight-strong);\n        text-transform: uppercase;\n        > * {\n            padding: var(--spacing-xs) var(--spacing-md);\n            margin: 0;\n            font-size: var(--font-size-sm);\n            &:not(:last-child) {\n                border-inline-end: var(--border-solid);\n            }\n        }\n        dd {\n            color: var(--color-purple);\n        }\n    }\n}\n@layer modifiers {\n    .c-pill--negative dd {\n        color: var(--color-red-bright);\n    }\n}"
  },
  {
    "path": "resources/css/components/pro-badge.css",
    "content": "/* GROUP COMPONENTS / PRO BADGE\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    Pro Feature badge used on pages such as Git Automation\n\n*/\n/* HTML Example...\n\n    <header>\n        <div class=\"o-badge-heading\">\n            <h1>{{ title }}</h1>\n            <div class=\"c-pro-badge\">\n                <a href=\"/licensing\">\n                    {{ global:pro_badges sort='random' limit='1' }}\n                        {{ artwork }}\n                            <picture class=\"c-pro-badge__artwork\">\n                                <img/>\n                            </picture>\n                        {{ /artwork }}\n                        <div class=\"c-pro-badge__text\">\n                            {{ icon }}\n                                <picture>\n                                    <img/>\n                                </picture>\n                            {{ /icon }}\n                            Pro Feature\n                        </div>\n                    {{ /global:pro_badges }}\n                </a>\n            </div>\n        </div>\n        {{ intro ?? overview | markdown }}\n    </header>\n\n    Or you can use this in text, with the pro badge on its own e.g.\n\n    <div class=\"c-pro-badge\">\n        <a href=\"/licensing\">\n            <div class=\"c-pro-badge__text\">\n                <span>⭐️</span> Pro Feature <span>⭐️</span>\n            </div>\n        </a>\n    </div>\n\n*/\n@layer components {\n    .c-pro-badge {\n        position: relative;\n\n        img {\n            filter: contrast(102%) saturate(1.05) brightness(105%);\n            @container style(--color-scheme: dark) {\n                filter: var(--filter-dark-tint);\n            }\n        }\n\n        a {\n            &, &:hover {\n                color: var(--color-primary-text)!important;\n            }\n            font-family: var(--font-family-main);\n            font-weight: var(--font-family-main-weight-heavy);\n            font-size: var(--font-size-sm);\n            line-height: var(--font-size-sm-line-height);\n            letter-spacing: 0.25px;\n            text-transform: uppercase;\n            text-align: center;\n            text-decoration: none;\n        }\n\n        + p {\n            margin-block-start: var(--spacing-lg);\n        }\n        h2:has(+ &) {\n            padding-block-end: var(--spacing-xs);\n        }\n    }\n    .c-pro-badge__artwork img {\n        border-radius: 50%;\n    }\n    .c-pro-badge__text {\n        display: inline-flex;\n        justify-content: center;\n        align-items: center;\n        flex-wrap: wrap;\n        gap: var(--spacing-2xs);\n        padding: 0.35rem var(--spacing-2xs);\n        background: linear-gradient(to right, var(--color-yellow-light-3) 0%,var(--color-yellow-light-2) 100%);\n        font-size: var(--font-size-xs);\n        rotate: -3deg;\n        .o-badge-heading & {\n            min-width: 9.5rem;\n            background: linear-gradient(to right, var(--color-yellow-light-4) 0%,var(--color-yellow-light-2) 100%);\n        }\n\n        img {\n            inline-size: 2em;\n        }\n    }\n    .c-pro-badge:has(img) {\n        .c-pro-badge__text {\n            position: absolute;\n            left: -1.75rem;\n            transform: translateY(-1.2rem);\n            rotate: -5deg;\n        }\n        /* --mq-grid-after */\n        @media (width >= 1000px) {\n            bottom: var(--spacing-xl);\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/promo.css",
    "content": ".c-promo {\n    position: fixed;\n    z-index: 50;\n    height: 2.5rem;\n    bottom: .75rem;\n    padding: .5rem;\n    text-align: center;\n    color: white;\n    font-weight: bold;\n    font-size: 0.875rem;\n    width: 100%;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n\n.c-promo__inner {\n    background: var(--color-gradient-purple);\n    padding: .5rem;\n    border-radius: .5rem;\n    margin-inline: auto;\n    position: relative;\n    padding-right: 2.5rem;\n}\n\n.c-promo:hover .c-promo__inner {\n    background: linear-gradient(to right, var(--color-purple), var(--color-pink), var(--color-blue));\n    background-size: 200% 100%;\n    animation: gradient-animate 2s ease infinite;\n}\n\n@keyframes gradient-animate {\n    0% { background-position: 0% 50%; }\n    50% { background-position: 100% 50%; }\n    100% { background-position: 0% 50%; }\n}\n\n.c-promo__close {\n    position: absolute;\n    top: -2px;\n    right: 0.75rem;\n    background: none;\n    border: none;\n    color: white;\n    font-size: 1.25rem;\n    cursor: pointer;\n    padding: 0.25rem;\n    opacity: 0.8;\n    height: 2.5rem;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    transition: opacity 0.2s ease;\n}\n\n.c-promo__close:hover {\n    opacity: 1;\n}\n"
  },
  {
    "path": "resources/css/components/quirky.css",
    "content": "/* GROUP COMPONENTS / QUIRKY\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    /quick-start-guide\n\n    What does it do?\n    ----------------\n    Possibly one-off, quirky components here\n\n*/\n/* HTML Example...\n\n*/\n@layer components {\n    .c-spaced {\n        font-weight: var(--font-family-serif-weight-strong);\n        letter-spacing: 1px;\n        text-transform: uppercase;\n    }\n\n    .clippy {\n        position: fixed;\n        bottom: 0;\n        right: 0;\n        cursor: pointer;\n        margin: 2rem;\n        display: none;\n\n        &.visible {\n            display: block;\n            animation: bounce-in-right 0.8s ease-in-out;\n        }\n    }\n\n    @keyframes bounce-in-right {\n        0% {\n            opacity: 0;\n            transform: translateY(2000px);\n        }\n        60% {\n            opacity: 1;\n            transform: translateY(-30px);\n        }\n        80% { transform: translateY(10px); }\n        100% { transform: translateY(0); }\n    }\n}\n"
  },
  {
    "path": "resources/css/components/related.css",
    "content": "/* GROUP COMPONENTS / RELATED\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    Show related entries to the current article\n\n*/\n/* HTML Example...\n\n    <section class=\"c-related\">\n        <div class=\"u-label\">Learn More!</div>\n        <p>There is more to learn in these related articles:</p>\n        <div class=\"c-related__item\">\n            <ul>\n                <li><a href=\"/\">Reference</a></li>\n                <li><?php include 'img/breadcrumb-chevron.svg'; ?><a href=\"/1.php\">Tags</a></li>\n                <li><?php include 'img/breadcrumb-chevron.svg'; ?><a href=\"/1.php\">Nav</a></li>\n            </ul>\n        </div>\n        <div class=\"c-related__item\">\n            <ul>\n                <li><a href=\"/\">Docs</a></li>\n                <li><?php include 'img/breadcrumb-chevron.svg'; ?><a href=\"/1.php\">Diving deeper</a></li>\n                <li><?php include 'img/breadcrumb-chevron.svg'; ?><a href=\"/1.php\">Structures</a></li>\n            </ul>\n        </div>\n        <div class=\"c-related__item\">\n            <ul>\n                <li><a href=\"/\">Docs</a></li>\n                <li><?php include 'img/breadcrumb-chevron.svg'; ?><a href=\"/1.php\">Resources</a></li>\n                <li><?php include 'img/breadcrumb-chevron.svg'; ?><a href=\"/1.php\">Tips & tricks</a></li>\n                <li><?php include 'img/breadcrumb-chevron.svg'; ?><a href=\"/1.php\">Localizing navigation</a></li>\n            </ul>\n        </div>\n    </section>\n\n*/\n@layer components {\n    .c-related {\n        --background-image-height: 4rem;\n        padding: var(--background-image-height) var(--spacing-xl);\n        margin-block-start: var(--spacing-3xl);\n        background: var(--color-gradient-blue-light-1);\n        /* Custom */\n        @media (width >= 500px) {\n            background: var(--color-gradient-blue-light-2);\n        }\n\n        position: relative;\n        &::before,\n        &::after {\n            content: '';\n            left: 0;\n            inline-size: 100%;\n            block-size: var(--background-image-height);\n            position: absolute;\n            --background-position: 1.5rem;\n            --background-image: url('../../../public/img/paper-tear.png');\n            @container style(--color-scheme: dark) {\n                --background-image: url('../../../public/img/paper-tear-dark-mode.png');\n            }\n            background: var(--background-image) no-repeat 0% var(--background-position), transparent;\n            opacity: 0.8;\n        }\n        &::before {\n            top: 0;\n            transform: scaleY(-1);\n        }\n        &::after {\n            bottom: 0;\n        }\n        & ul {\n            padding: var(--spacing-xs);\n            padding-inline-end: var(--spacing-md);\n            background: light-dark(white, var(--color-dark-mode-gray-background));\n            display: inline-flex;\n            flex-wrap: wrap;\n            align-items: center;\n            gap: var(--spacing-md);\n            /* height: 100%; */\n            font-family: var(--font-family-main);\n            box-shadow: var(--box-shadow-not-t-light);\n            border-radius: var(--border-radius-md);\n        }\n        & li {\n            display: flex;\n            align-items: center;\n            padding-block-end: 0;\n            & svg:not(.external) {\n                margin-inline-end: var(--spacing-md);\n                inline-size: 0.4rem;\n                color: var(--color-gray-aa);\n            }\n            &:last-child a {\n                font-weight: var(--font-family-main-weight-strong);\n                line-height: 1.5;\n                margin-inline-end: 0;\n                text-decoration: underline;\n                text-decoration-color: var(--color-black);\n                color: light-dark(var(--color-black), white);\n            }\n        }\n        a {\n            padding: 0;\n            text-decoration: none;\n            color: var(--color-gray-aa);\n            text-transform: uppercase;\n            font-size: 0.775em;\n            font-weight: var(--font-family-main-weight-normal);\n            text-box: trim-start cap;\n            line-height: 1;\n            letter-spacing: 0.5px;\n        }\n    }\n    .c-related__item {\n        /* First instance of this class */\n        :nth-child(1 of &) {\n            padding-block-start: var(--spacing-sm);\n        }\n        &:not(:last-child) {\n            margin-block-end: var(--spacing-sm);\n        }\n        /* Custom */\n        @media (width < 800px) {\n            li {\n                &:not(:last-child) {\n                    /* Hide breadcrumb trail on smaller vieWordPressorts */\n                    display: none;\n                }\n                svg {\n                    margin-inline-end: var(--spacing-xs);\n                }\n            }\n        }\n    }\n}\n@layer utilities {\n    .c-related .u-label {\n        position: absolute;\n        top: -0.5rem;\n        left: 0;\n        padding: var(--spacing-2xs) var(--spacing-lg);\n        font-size: var(--font-size-md);\n        text-transform: uppercase;\n    }\n}\n@layer scope {\n    @container style(--color-scheme: dark) {\n        /* Change the hover colors for the dark theme */\n        .c-related a {\n            text-decoration-color: white;\n            &:hover {\n                text-decoration-color: white;\n                color: white;\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/search-form.css",
    "content": "/* GROUP COMPONENTS / SEARCH FORM\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    Home\n\n    What does it do?\n    ----------------\n    Search \"box\"\n\n*/\n@layer components {\n    .c-search-form {\n        position: relative;\n        display: flex;\n        margin-block-end: 0;\n        background: var(--color-search-form);\n        border: 1px solid var(--color-pink-border);\n        box-shadow: var(--box-shadow-pink-light);\n        border-radius: 50px;\n\n        & input,\n        #docsearch button {\n            margin: 0;\n            padding-inline: 3.5em var(--spacing-lg);\n            /* Custom */\n            @media (width < 500px) {\n                padding-inline-end: max(2.5rem, 10vw);\n            }\n            border: 0;\n        }\n        & ::placeholder {\n            color: var(--color-black);\n            opacity: 1;\n        }\n    }\n    .c-search-form__icon {\n        left: var(--spacing-md);\n        pointer-events: none;\n        font-size: 1.3em;\n    }\n}\n#docsearch {\n    /* Prevent Safari from nudging the page layout when this is rendered */\n    max-height: 3.6rem;\n    font-size: var(--font-size-sm);\n    button {\n        align-items: center;\n        inline-size: 100%;\n        background: inherit;\n        color: inherit;\n        font-size: inherit;\n        font-weight: inherit;\n    }\n    .docsearch-btn-keys {\n        display: flex;\n        gap: 0.4rem;\n        > * {\n            margin-inline-end: unset;\n        }\n    }\n    .docsearch-btn-placeholder {\n        &::after {\n            content: \"the docs\";\n        }\n        margin-inline-end: 0;\n        padding-inline-end: 0;\n    }\n    .docsearch-btn {\n        padding: var(--button-spacing);\n        /* Custom */\n        @media (width < 450px) {\n            padding-inline: 1.15rem;\n        }\n        height: unset;\n        > * {\n            margin-inline-end: unset;\n        }\n    }\n    .docsearch-btn-key {\n        display: flex;\n        justify-content: center;\n        align-items: center;\n        border: 1px solid hsl(var(--color-pink-hue) 100% 90%);\n        border-radius: 5px;\n        background: var(--docsearch-key-gradient);\n        height: 1.5rem;\n        padding-inline: 0.3rem;\n        font-family: var(--font-family-main);\n        font-size: var(--font-size-sm);\n        /* Page preference is \"dark\" */\n        html:has(#color-scheme option[value=\"dark\"]:checked) & {\n            border: 0;\n        }\n        /* Page preference is \"system\", and system preference is \"dark\" */\n        @media (prefers-color-scheme: dark) {\n            html:has(#color-scheme option[value=\"system\"]:checked) & {\n                border: 0;\n            }\n        }\n    }\n}\n/* --mq-meilisearch-mobile-breakpoint-before */\n@media (width < 768px) {\n    #docsearch .docsearch-btn-placeholder {\n        /* Override Meilsearch hiding the text on lower MQs */\n        display: inline-block!important;\n        /* Custom */\n        @media (width < 520px) {\n            &::after {\n                content: unset;\n            }\n        }\n    }\n    .docsearch-modal {\n        position: relative;\n        top: 1.5%;\n        height: 97%!important;\n        width: 95%!important;\n        left: 50%!important;\n        transform: translateX(-50%);\n    }\n}\n/* --mq-nav-expanded-before */\n@media (width < 680px) {\n    #docsearch {\n        /* Hide when short on space */\n        .docsearch-btn-keys {\n            display: none;\n        }\n    }\n    .c-docs-header .o-logo {\n        min-width: 3em;\n        max-width: 12.5vw;\n    }\n    .c-docs-header__search {\n        .docsearch-btn {\n            min-width: 42vw;\n        }\n    }\n}\n/* GROUP COMPONENTS / SEARCH FORM / MODAL\n=================================================== */\n.docsearch-modal {\n    /* --mq-text-bump-3-before */\n    @media (width < 1800px) {\n        font-size: 1.05em;\n    }\n}\n/* GROUP COMPONENTS / SEARCH FORM / Overrides For Meilisearch CSS\n=================================================== */\n/* Notes...\n\n    - Meilisearch styles are loaded inline so we'll need !important here.\n\n*/\n:root {\n    /* Overrides For Meilisearch CSS */\n    --docsearch-muted-color: inherit!important;\n    --docsearch-primary-color: var(--color-purple)!important;\n    --docsearch-key-shadow: unset!important;\n    --docsearch-key-gradient: linear-gradient(to bottom, hsl(var(--color-yellow-hue) 100% 97%) 0%,white 100%)!important;\n    --docsearch-modal-width: 50rem!important;\n    --docsearch-modal-container-background: hsl(var(--color-purple-hue) 20% 30% / 52%)!important;\n    --docsearch-modal-shadow: 0 0px 100px hsl(var(--color-body-background-hue, 0) 30% 85%)!important;\n    /* Page preference is \"dark\" */\n    &:has(#color-scheme option[value=\"dark\"]:checked) {\n        --docsearch-primary-color: hsl(250deg 50% 50%)!important;\n        --docsearch-key-gradient: var(--color-transparent-dark-mode)!important;\n        --docsearch-modal-container-background: hsl(var(--color-purple-hue) 20% 5% / 70%)!important;\n        --docsearch-modal-background: var(--color-body-background);\n        --docsearch-footer-background: var(--color-body-background);\n        --docsearch-modal-shadow: 0 0px 100px hsl(var(--color-body-background-hue, 0) 30% 20%)!important;\n        --docsearch-hit-shadow: unset;\n    }\n    /* Page preference is \"system\", and system preference is \"dark\" */\n    /* Note: For now, we need to repeat these values, but in the future, we can use style queries to eliminate the need to repeat the dark mode variables. [Current support](https://caniuse.com/?search=style%20queries). To do this, we must shift everything down so variables are on the `body` instead of the `root`. The root then contains the `colour-scheme` variables, which means we can now query the `colour-scheme` variable since the root is now the container. */\n    @media (prefers-color-scheme: dark) {\n        &:has(#color-scheme option[value=\"system\"]:checked) {\n            --docsearch-primary-color: hsl(250deg 50% 50%)!important;\n            --docsearch-key-gradient: var(--color-transparent-dark-mode)!important;\n            --docsearch-modal-container-background: hsl(var(--color-purple-hue) 20% 5% / 50%)!important;\n            --docsearch-modal-background: var(--color-body-background);\n            --docsearch-footer-background: var(--color-body-background);\n            --docsearch-modal-shadow: 0 0px 100px hsl(var(--color-body-background-hue, 0) 30% 20%)!important;\n            --docsearch-hit-shadow: unset;\n        }\n    }\n}\n@container style(--color-scheme: light) {\n    .docsearch-modal-footer-commands-key {\n        border: 1px solid hsl(var(--color-pink-hue) 100% 90%)!important;\n    }\n}\n.docsearch-modal {\n    border-radius: 40px!important;\n}\n.docsearch-modal-search-input-form {\n    border-radius: 100px!important;\n}\n.docsearch-modal-footer {\n    border-radius: 0 0 15px 15px!important;\n}\n.docsearch-modal-search-input {\n    margin-block-end: 0;\n}\n.docsearch-modal-search-hits-item-title {\n    font-weight: var(--font-family-main-weight-strong)!important;\n}\n.docsearch-modal-search-hits-item-text {\n    font-weight: var(--font-family-main-weight-normal)!important;\n}\n@container style(--color-scheme: dark) {\n    .docsearch-modal-search-hits-category {\n        color: var(--color-green)!important;\n    }\n}\n.docsearch-modal-footer-logo-icon {\n    /* The logo colour is distracting otherwise */\n    filter: grayscale(100%);\n    @container style(--color-scheme: dark) {\n        filter: grayscale(100%) invert(1);\n    }\n}\n\n.docsearch-modal,\n.docsearch-modal-search-input-form,\n.docsearch-modal-footer {\n    corner-shape: squircle;\n}\n/* --mq-nav-open-after */\n@media (width >= 1100px) {\n    .docsearch-btn,\n    .c-search-form {\n        corner-shape: squircle;\n    }\n}"
  },
  {
    "path": "resources/css/components/site-footer.css",
    "content": "/* GROUP COMPONENTS / FOOTER\n=================================================== */\n/* HTML Example...\n\n*/\n@layer components {\n    .c-site-footer {\n        background: linear-gradient(to bottom, transparent 0%,var(--color-body-background) 10%);\n        position: relative;\n        z-index: var(--z-index-above-body);\n    }\n    /* GROUP COMPONENTS / FOOTER / NAV\n    =================================================== */\n    .c-site-footer {\n        /* Fix tiny overflow */\n        overflow: clip;\n    }\n    .c-site-footer__nav,\n    .c-site-footer__bottom {\n        max-width: var(--max-width-1);\n        margin-inline: auto;\n        background: var(--color-black-off);\n        padding: 3rem var(--spacing-xl) var(--spacing-4xl);\n        &:has(> :only-child) {\n            /* e.g. Sans Off / Sans On row */\n            padding-block: var(--spacing-lg) var(--spacing-3xl);\n            > :only-child {\n                grid-column: 1/-1;\n            }\n        }\n\n        /* --mq-nav-open-after and --mq-wide-before */\n        @media (width >= 1100px) and (width < 1650px) {\n            /* Vertically align with .c-nav-sidebar-with-popover-api */\n            padding: var(--spacing-3xl) calc(var(--spacing-3xl-horizontal) + var(--spacing-2xs)) var(--spacing-4xl);\n        }\n        /* --mq-wide-after */\n        @media (width >= 1650px) {\n            /* Vertically align with .c-nav-sidebar-with-popover-api */\n            padding-inline: calc(var(--spacing-3xl-horizontal) + var(--spacing-md));\n        }\n\n        position: relative;\n        &::before {\n            content: '';\n            position: absolute;\n            z-index: var(--z-index-below-body);\n            inset: 0;\n            /* Bust out of container */\n            margin-inline: calc(-50vw + 50%);\n            background: inherit;\n        }\n        &:not(:last-child) {\n            padding-block-end: 0;\n        }\n    }\n    .c-site-footer__nav {\n        --color-link: white;\n        display: grid;\n        grid-template-columns: repeat(auto-fit, minmax(min(100%, 11em), 1fr));\n        /* Custom */\n        @media (width >= 370px) and (width < 600px) {\n            /* Force 2 columns at lower MQs e.g. mobile */\n            grid-template-columns: repeat(2, 1fr);\n        }\n        gap: var(--spacing-3xl) var(--spacing-4xl);\n        /* Custom */\n        @media (width < 500px) {\n            /* Bit more room for the .c-footer-arrow svgs */\n            padding-inline: var(--spacing-3xl);\n        }\n        font-family: var(--font-family-code);\n        font-size: var(--font-size-xs);\n        line-height: var(--font-size-xs-line-height);\n\n        img {\n            max-height: 1rem;\n            max-width: 1rem;\n            opacity: 0.35;\n        }\n\n        li {\n            display: inline-flex;\n            flex-wrap: wrap;\n            gap: var(--spacing-3xs);\n            align-items: center;\n            color: var(--color-link);\n            span {\n                color: var(--color-link);\n                /* Custom */\n                @media (width < 1280px) {\n                    display: none;\n                }\n            }\n        }\n\n        p {\n            color: var(--color-link);\n        }\n\n        /* Link Hover Effect */\n        a {\n            display: inline-flex;\n            align-items: center;\n            gap: var(--spacing-2xs);\n            text-decoration: none;\n            position: relative;\n            &::after {\n                width: 0;\n                transition: width .3s ease 0.05s;\n\n                content: '';\n                position: absolute;\n                bottom: -3px;\n                height: 1px;\n                margin-top: 2px;\n                background: white;\n                right: 0;\n            }\n            &:hover::after {\n                width: 100%;\n                left: 0;\n            }\n        }\n    }\n    .c-site-footer-heading {\n        color: white;\n        position: relative;\n        text-transform: uppercase;\n        font-weight: var(--font-family-code-weight-medium);\n        margin-block-end: min(2.85vw, var(--spacing-sm));\n        svg {\n            position: absolute;\n            top: calc(50% + 0.1rem);\n            transform: translate(-1.75rem, -50%);\n        }\n    }\n    .c-site-footer__nav__item ul {\n        display: grid;\n        gap: var(--spacing-2xs);\n    }\n    /* GROUP COMPONENTS / FOOTER / BOTTOM\n    =================================================== */\n    .c-site-footer__bottom {\n        display: grid;\n        grid-template-columns: repeat(auto-fill, minmax(min(100%, 11em), 1fr));\n        gap: var(--spacing-4xl);\n        padding-block-start: 0;\n        /* margin-inline: auto; */\n        img {\n            max-height: 1.5rem;\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/skip-to-content.css",
    "content": "/* GROUP COMPONENTS / SKIP TO CONTENT\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    Accessibilty aid for keyboard users\n\n*/\n/* HTML Example...\n\n    <a href=\"#main\" class=\"c-skip-to-content u-screen-reader-text\" title=\"Skip to content\">\n        Skip to content\n    </a>\n\n    <a href=\"#footer\" class=\"c-skip-to-content u-screen-reader-text\" title=\"Skip to footer navigation\">\n        Skip to footer navigation\n    </a>\n\n*/\n@layer components {\n    .c-skip-to-content {\n        background: var(--color-green);\n        color: var(--color-black-static);\n        padding: var(--spacing-sm) var(--spacing-md);\n        text-decoration-color: var(--color-black-static);\n        font-weight: var(--font-family-main-weight-medium);\n        outline: 0;\n        text-box: text;\n    }\n}"
  },
  {
    "path": "resources/css/components/syntax-explainer.css",
    "content": "/* GROUP COMPONENTS / SYNTAX EXPLAINER\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    /conditions\n\n    What does it do?\n    ----------------\n    - e.g. when explaining on the `/conditions` page, `{field_name}:{condition}=\"{value}\"` might be broken down as per the HTML example below...\n\n\n*/\n/* HTML Example...\n\n<div class=\"c-syntax-explainer\">\n    <span class=\"c-syntax-explainer__1\">{field_name}</span>:<span class=\"c-syntax-explainer__2\">{condition}</span><span>=</span>\"<span class=\"c-syntax-explainer__3\">{value}</span>\"\n</div>\n\n*/\n@layer components {\n    .c-syntax-explainer {\n        margin-block-end: var(--spacing-lg);\n        font-family: var(--font-family-code);\n        font-weight: var(--font-family-code-weight-medium);\n        /* Because of the extra padding */\n        line-height: 2;\n        > * {\n            padding: var(--spacing-3xs) var(--spacing-2xs);\n            border-radius: var(--border-radius-sm);\n        }\n    }\n    .c-syntax-explainer__1 {\n        background: var(--color-pink-light-2);\n    }\n    .c-syntax-explainer__2 {\n        background: var(--color-purple-light-1);\n    }\n    .c-syntax-explainer__3 {\n        background: var(--color-green);\n        color: light-dark(var(--color-primary-text), var(--color-black));\n    }\n}"
  },
  {
    "path": "resources/css/components/theme-picker.css",
    "content": "/* GROUP COMPONENTS / THEME PICKER\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    Home\n\n    What does it do?\n    ----------------\n    A button to switch between light and dark themes\n\n*/\n/* HTML Example...\n\n    <div class=\"c-theme-picker\">\n        <div class=\"c-theme-picker__button\">\n            <img src=\"/img/theme-light.svg\" alt=\"Light theme\" />\n            <img src=\"/img/theme-dark.svg\" alt=\"Dark theme\" />\n        </div>\n        <select id=\"color-scheme\">\n            <option value=\"system\">System</option>\n            <option value=\"dark\">Dark</option>\n            <option value=\"light\">Light</option>\n        </select>\n    </div>\n\n*/\n@layer components {\n    .c-theme-picker {\n        /* Custom */\n        @media (width < 400px) {\n            .c-theme-picker__button {\n                padding: 0;\n                img {\n                    margin: 0;\n                }\n            }\n        }\n        .c-theme-picker__button {\n            img {\n                inline-size: 1.5rem;\n            }\n            .c-theme-picker-dark-icon {\n                opacity: 0.25;\n                /* --mq-grid-after */\n                @media (width >= 1000px) {\n                    opacity: 0.4;\n                }\n            }\n        }\n        /* position: relative; */\n        select {\n            --background: var(--color-body-background);\n            transition: opacity 0.2s ease-in 0s;\n            &:not(:focus){\n                /* Hide the select unless it's focused, to clean up the UI. The select is also focused when the preceeding SVG button is clicked in case the user is using a pointer device like a mouse. */\n                opacity: 0;\n            }\n            &:focus {\n                outline-offset: -1px;\n            }\n            /* Effectively pull the select over the faux button so \"clicking\" it will also open the select. The select remains tababble for keyboard users. */\n            position: absolute;\n            transform: translate(-0.25rem, -0.5rem);\n            /* Custom */\n            @media (width < 640px) {\n                /* Otherwise the theme select overlaps the navigation dots */\n                right: 18%;\n            }\n            /* Select Styling. Inspiration - https://moderncss.dev/custom-select-styles-with-pure-css/ */\n            appearance: none;\n            background: var(--background);\n            background-image: url(../../../public/img/dropdown.svg);\n            background-repeat: no-repeat;\n            background-size: 0.75em;\n            background-position: calc(100%);\n            min-inline-size: 6.5rem;\n            /* Fallback for Safari */\n            block-size: 2.5rem;\n            min-block-size: 2.5rem;\n            border: none;\n            margin: 0;\n            margin-inline-start: var(--spacing-3xs);\n            padding: var(--spacing-2xs);\n            padding-inline-start: var(--spacing-xs);\n            color: var(--color-primary-text);\n            font-size: var(--font-size-sm);\n            border-radius: 50px;\n            box-shadow: 0px 0px 1px var(--color-pink);\n            border-inline: var(--spacing-sm) solid var(--color-body-background);\n            border-inline-start-width: var(--spacing-3xs);\n        }\n    }\n}\n@container style(--color-scheme: dark) {\n    /* This won't work in Safari when nested in a layer for some reason */\n    .c-theme-picker select {\n        background-image: url(../../../public/img/dropdown-dark-mode.svg);\n    }\n}"
  },
  {
    "path": "resources/css/components/tiles-with-description.css",
    "content": "/* GROUP COMPONENTS / TILES WITH DESCRIPTION\n=================================================== */\n/* Notes...\n\n    URL example\n    -----------\n    Home\n\n    What does it do?\n    ----------------\n    Lays out thumbnails with a title and short description\n\n*/\n/* HTML Example...\n\n    <div class=\"c-tiles-with-description\">\n        {{ tiles }}\n            <div class=\"c-tiles-with-description__item{{ flush_image ?= ' c-tiles-with-description__item--flush' }}\">\n                <a href=\"{{ tile_link }}\" class=\"u-link-style-none\">\n                    <figure class=\"c-tiles-with-description__item__thumbnail\">\n                        {{ if tile_image_dark_mode }}\n                            {{ tile_image }}\n                                {{ partial:image_dimensions/tile modifier_classes='u-hide-in-dark-mode' }}\n                            {{ /tile_image }}\n                            {{ tile_image_dark_mode }}\n                                {{ partial:image_dimensions/tile modifier_classes='u-hide-in-light-mode' }}\n                            {{ /tile_image_dark_mode }}\n                        {{ else }}\n                            {{ tile_image }}\n                                {{ partial:image_dimensions/tile }}\n                            {{ /tile_image }}\n                        {{ /if }}\n                        <figcaption>{{ tile_title }}</figcaption>\n                    </figure>\n                </a>\n                <p>{{ tile_description }}</p>\n            </div>\n        {{ /tiles }}\n    </div>\n\n*/\n@layer components {\n    .c-tiles-with-description {\n        display: grid;\n        grid-template-columns: repeat(auto-fit, minmax(min(100%, 13em), 1fr));\n        gap: calc(var(--spacing-2xl) + var(--spacing-3xs)) var(--spacing-2xs);\n        /* Custom */\n        @media (width >= 530px) {\n            column-gap: var(--spacing-sm);\n        }\n        padding-block-end: var(--spacing-3xl);\n    }\n    .c-tiles-with-description__item {\n        max-width: 21rem;\n        margin-inline: auto;\n        font-family: var(--font-family-main);\n        p {\n            padding-inline: var(--spacing-lg);\n            /* padding-inline: var(--spacing-md); */\n            font-size: var(--font-size-ui);\n            line-height: var(--font-size-ui-line-height);\n            max-width: 16rem;\n            text-align: center;\n            margin-inline: auto;\n        }\n\n        position: relative;\n        .external {\n            a > & {\n                /* Hide the direct .external links added with javascript because we want to add the icon elsewhere instead */\n                display: none;\n            }\n            background: light-dark(hsl(var(--color-purple-hue) 70% 97%), transparent);\n            border-radius: 50%;\n            * {\n                fill: var(--color-purple);\n            }\n        }\n\n        figcaption {\n            display: flex;\n            justify-content: center;\n            gap: 0.25rem;\n        }\n    }\n    .c-tiles-with-description__item__thumbnail {\n        padding: var(--spacing-sm);\n        margin-block-end: var(--spacing-xs);\n        text-align: center;\n        background: var(--color-gradient-full-light-6);\n        border: var(--dark-mode-border);\n        border-radius: var(--border-radius-xl);\n        @supports (corner-shape: squircle) {\n            border-radius: 35px;\n            corner-shape: squircle;\n        }\n\n        img {\n            display: inline-block;\n            max-width: 70%;\n            filter: var(--filter-image-boost-1);\n            /* Custom */\n            @media (width < 500px) {\n                margin-block-end: 0;\n            }\n        }\n    }\n    /* Custom */\n    @media (width >= 500px) {\n        .c-tiles-with-description__item p {\n            text-align: left;\n            /* Decrease */\n            font-size: var(--font-size-xs);\n            font-weight: var(--font-family-main-weight-normal);\n            line-height: var(--font-size-xs-line-height);\n        }\n    }\n    /* Custom */\n    @media (width >= 768px) {\n        :is(header, p) + .c-tiles-with-description {\n            padding-block-start: var(--spacing-2xs);\n        }\n    }\n}\n@layer modifiers {\n    .c-tiles-with-description__item--flush {\n        & img {\n            max-width: 100%;\n            /* Custom */\n            @media (width < 500px) {\n                margin-block-end: 1.2rem;\n            }\n        }\n        .c-tiles-with-description__item__thumbnail {\n            padding: var(--spacing-xs);\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/tip.css",
    "content": "/* GROUP COMPONENTS / TIP\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    A positive tip box with a troll. We also have a tip-warning component.\n\n*/\n/* HTML Example...\n\n    <div class=\"c-tip\">\n        <div class=\"c-tip__title\">Hot Tip!</div>\n        <p>If a view file ends with <code>.blade.php</code> it will use Laravel’s <a href=\"https://statamic.dev/blade\" >Blade templates</a>. This same pattern applies for any other template engine you may wish to install in the future, like Twig or something that hasn't been invented yet.</p>\n        <img src=\"/img/tip-troll.webp\" class=\"c-tip__mascot\" alt=\"A troll pointing a teaching stick\" width=\"242\" height=\"293\" />\n    </div>\n\n*/\n/* Modifiers...\n\n    .c-tip--warning <- a warning tip such as leaving the debug bar on for production.\n        - Uses an evil troll and red colours instead.\n\n*/\n@layer components {\n    .c-tip,\n    .best-practice {\n        &:not(.watch) {\n            position: relative;\n            z-index: var(--z-index-above-body);\n            padding: var(--spacing-lg) var(--spacing-2xl) var(--spacing-2xs);\n\n            /* Custom */\n            @media (width < 550px) {\n                padding-inline: var(--spacing-lg);\n            }\n            margin-block: var(--spacing-3xl);\n            font-family: var(--font-family-main);\n            &:hover .c-tip__mascot {\n                animation: wiggle 0.5s linear;\n            }\n            &::before {\n                content: '';\n                position: absolute;\n                inset: 0;\n                z-index: var(--z-index-below-body);\n                background: var(--color-gradient-burnt);\n                rotate: -1deg;\n                border-radius: var(--border-radius-2xl);\n            }\n            p {\n                font-weight: var(--font-family-main-weight-medium);\n                /* Custom */\n                @media (width < 500px) {\n                    font-size: 0.9rem;\n                }\n            }\n            /* GROUP COMPONENTS / TIP / SCOPE TWEAKS\n            =================================================== */\n            &:has(+ h2) {\n                /* e.g. >> /updating */\n                margin-block-end: var(--spacing-lg);\n            }\n            h2 + & {\n                /* e.g. >> /tags/children */\n                margin-block-start: var(--spacing-3xs);\n            }\n            header + & {\n                /* e.g. >> /updating */\n                margin-block-start: var(--spacing-lg);\n            }\n            header:has(h1:only-child) + & {\n                /* e.g. >> ui-components/overview */\n                margin-block-start: var(--spacing-2xs);\n            }\n        }\n    }\n    /* Cross pollinated */\n    .c-tip__title,\n    .hint-title {\n        display: inline-block;\n        padding: var(--spacing-3xs) var(--spacing-md);\n        margin-block-end: var(--spacing-sm);\n        font-size: var(--font-size-lg-uppercase);\n        text-transform: uppercase;\n    }\n    .c-tip__title--long {\n        font-size: var(--font-size-md-uppercase);\n    }\n    /* img*/\n    .c-tip__mascot {\n        position: absolute;\n        z-index: var(--z-index-above-body);\n        top: -1rem;\n        left: -1.75rem;\n        inline-size: 4rem;\n        filter: var(--filter-burnt-shadow);\n    }\n}\n@layer scope {\n    .c-tip--warning {\n        &::before {\n            background: var(--color-gradient-burnt-dark);\n        }\n        .hint-title,\n        .c-tip__title {\n            &, &::before {\n                background: var(--color-red-burnt-1);\n                color: var(--color-red-bright);\n                border-radius: var(--border-radius-sm);\n            }\n            @container style(--color-scheme: dark) {\n                border: 1px solid var(--color-red);\n                &, &::before {\n                    rotate: unset;\n                    border-radius: var(--border-radius-md);\n                }\n            }\n        }\n        img {\n            filter: hue-rotate(-20deg) saturate(1.25);\n        }\n        code {\n            background: var(--color-gradient-fire);\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/version-selector.css",
    "content": "@layer components {\n    .c-version-selector {\n        /* Custom */\n        @media (width < 615px) {\n            /* Restrict the selector width with an ellipisis when space is tight */\n            width: 5.25rem;\n            select {\n                white-space: nowrap;\n                /* 'overflow' value must be different from 'visible' */\n                overflow: hidden;\n                text-overflow: ellipsis;\n            }\n        }\n        select {\n            --background: var(--color-body-background);\n            transition: opacity 0.2s ease-in 0s;\n            &:focus {\n                outline-offset: -1px;\n            }\n            /* Select Styling. Inspiration - https://moderncss.dev/custom-select-styles-with-pure-css/ */\n            appearance: none;\n            background: var(--background);\n            background-image: url(../../../public/img/dropdown.svg);\n            background-repeat: no-repeat;\n            background-size: 0.6em;\n            background-position: 83% 50%;\n            /* --mq-nav-expanded-before */\n            @media (width >= 680px) {\n                background-size: 0.75em;\n                background-position: 87% 50%;\n            }\n            /* Fallback for Safari */\n            block-size: 2.25rem;\n            min-block-size: 2.25rem;\n            padding: var(--spacing-2xs);\n            padding-inline: var(--spacing-xs) 2.15rem;\n            color: var(--color-primary-text);\n            font-size: var(--font-size-sm);\n            border-radius: 50px;\n            border: 1px solid var(--color-pink-border);\n            @container style(--color-scheme: dark) {\n                border: 1px solid var(--color-pink-light-2);\n            }\n            box-shadow: var(--box-shadow-pink-light);\n        }\n    }\n}"
  },
  {
    "path": "resources/css/components/video.css",
    "content": "/* GROUP COMPONENTS / VIDEO\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    A video container for embedding videos like YouTube, Vimeo, etc.\n\n*/\n/* HTML Example...\n\n    <figure class=\"c-video\">\n        <iframe src=\"https://www.youtube.com/embed/POgIsLeWGGQ?si=uG-8hz4u1HRebJsM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n        <figcaption>Watch how to build a simple nav with a Structured Collection</figcaption>\n    </figure>\n\n*/\n@layer components {\n    .c-video {\n        position:relative;\n        margin-inline: auto;\n        &:not(article > :last-child) {\n            margin-block-end: var(--spacing-3xl);\n        }\n        + * {\n            /* Push the next element away instead because of collapsing margins */\n            padding-block-start: var(--spacing-2xl);\n            &:is(h2, h3, h4) {\n                /* Increase the distance if it's a heading */\n                padding-block-start: var(--spacing-3xl);\n            }\n        }\n        &:not(:is(h1, h2, h3, h4, p) + &) {\n            /* A large margin except where it follows a heading or a paragraph */\n            margin-block-start: var(--spacing-3xl);\n        }\n        aspect-ratio: 16 / 9;\n        iframe, video {\n            width: 100%;\n            height: 100%;\n            border: 1px solid var(--color-black);\n            border-radius: var(--border-radius-sm);\n            box-shadow: var(--box-shadow-medium);\n        }\n        &:not(:last-child) {\n            margin-block-end: calc(var(--spacing-2xl) + var(--spacing-2xs));\n        }\n        img {\n            margin-block-end: 0;\n            border-radius: var(--border-radius-sm);\n            border-bottom-left-radius: 0;\n        }\n        figcaption {\n            display: inline-block;\n            padding: var(--spacing-xs) var(--spacing-md);\n            background: var(--color-gradient-full-light-2);\n            font-weight: var(--font-family-main-weight-normal);\n            border-radius: var(--border-radius-lg);\n            border-top-right-radius: 0;\n            border-top-left-radius: 0;\n        }\n    }\n}\n@layer scope {\n    .c-entry-content:has(.c-video:last-child) {\n        /* We need this if the video is the last item in the entry-content because of collapsing margins */\n        padding-block-end: var(--spacing-xl);\n    }\n}"
  },
  {
    "path": "resources/css/objects/badge-heading.css",
    "content": "/* GROUP OBJECTS / BADGE HEADING\n=================================================== */\n/* Notes...\n\n    What does it do\n    ---------------\n    - A container that houses an heading e.g. h1, and a badge such as the Pro Feature badge\n\n*/\n/* HTML Example...\n\n*/\n\n@layer objects {\n    .o-badge-heading {\n        position: relative;\n        display: flex;\n        align-items: center;\n        gap: 2.1rem var(--spacing-2xl);\n\n        > :has(img) {\n            flex-basis: 6rem;\n            flex-shrink: 0;\n        }\n        /* --mq-grid-before */\n        @media (width < 1000px) {\n            flex-wrap: wrap;\n            &:has(h1) {\n                padding-block-end: var(--spacing-xl);\n            }\n            h1 {\n                order: 1;\n                padding-block-end: 0;\n            }\n        }\n        /* --mq-grid-after */\n        @media (width >= 1000px) {\n            justify-content: space-between;\n            align-items: unset;\n        }\n    }\n}\n@layer scope {\n    .o-badge-heading {\n        /* Custom */\n        @media (width >= 1460px) {\n            > :has(img) {\n                /* Pull it out of the flow to let the layout breathe */\n                position: absolute;\n                right: -2rem;\n                top: -4rem;\n                /* Increase */\n                inline-size: 6rem;\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/objects/collapsible-side-menu-with-checkboxes.css",
    "content": "/* GROUP OBJECTS / COLLAPSIBLE SIDE MENU WITH CHECKBOXES\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    - Use a checkbox to hide the following submenu using :has\n\n*/\n/* HTML Example...\n\n    <nav>\n        <ul>\n            <li>\n                <label class=\"o-toggle-subnav\">\n                    Getting Started\n                    <input type=\"checkbox\">\n                </label>\n                <ul>\n                    <li>\n                        <a href=\"/\">Requirements</a>\n                        <a href=\"/1\">Quick start guide</a>\n                        <a href=\"/2\">Upgrade guide</a>\n                        <a href=\"/3\">Installing</a>\n                        <a href=\"/4\">Updating</a>\n                        <a href=\"/5\">Deploying</a>\n                    </li>\n                </ul>\n            </li>\n        </ul>\n    </nav>\n\n*/\n/* :root { <-- I've added this to elements so we can use it for anything in future\n    interpolate-size: allow-keywords;\n} */\n\n@layer objects {\n    .o-toggle-subnav {\n        cursor: pointer;\n        input {\n            /* START SCREEN READER TEXT\n            --------------------------- */\n            clip: rect(1px, 1px, 1px, 1px);\n\n            position: absolute!important;\n            overflow: hidden;\n            width: 1px;\n            height: 1px;\n            /* END SCREEN READER TEXT\n            ------------------------- */\n        }\n\n        outline-offset: -3px;\n        &:has(input:focus-visible) {\n            outline: 3px solid var(--color-focus);\n        }\n\n        + ul {\n            overflow: clip;\n            /* linear-out-slow-in */\n            transition: height 0.25s cubic-bezier(0, 0, 0.2, 1), visibility 0.5s, padding 0.25s cubic-bezier(0, 0, 0.2, 1);\n        }\n        &:has(input:checked) + ul {\n            padding-block: 0!important; \n            height: 0;\n            visibility: hidden;\n        }\n    }\n}\n@keyframes fade-out {\n    from { opacity: 1; }\n    to   { opacity: 0; }\n}\n@layer utilities {\n    .o-toggle-subnav {\n        + ul {\n            padding-block: var(--spacing-xs);\n            /* --mq-text-bump-1-after to --mq-text-bump-2-before */\n            @media (width >= 1025px) and (width < 1441px) {\n                /* Decrease */\n                padding-block: var(--spacing-2xs);\n            }\n        }\n        &:has(input:checked) + ul {\n            .o-current-menu-item::before {\n                animation: 0.2s linear fade-out both;\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/objects/entry-content.css",
    "content": "/* GROUP OBJECTS / ENTRY CONTENT\n=================================================== */\n/* Notes...\n\n    What does it do\n    ---------------\n    - A simple container to just emulate the layout of .c-entry-content\n    - We need this for other content in the central column that should follow the same layout boundaries of the entry content, e.g. Docs Feedback\n\n*/\n/* HTML Example...\n\n    <div class=\"o-entry-content\">\n        <div class=\"c-feedback-meerkat\">\n            <h2>Docs Feedback</h2>\n            <p>Submit improvements, related content, or suggestions through GitHub.</p>\n            <a href=\"https://github.com/statamic/docs/issues/new\" class=\"c-btn c-btn--1\">Betterify this page</a>\n            <img src=\"/img/meerkat.webp\" alt=\"Meerkat\" width=\"150\" height=\"278\">\n        </div>\n    </div>\n\n*/\n\n@layer objects {\n    .o-entry-content {\n        max-width: var(--max-width-reading);\n        margin-inline: auto;\n        padding-inline: var(--spacing-xl);\n    }\n}"
  },
  {
    "path": "resources/css/objects/scroll-shadows.css",
    "content": "/* GROUP OBJECTS / SCROLL SHADOWS using background-attachment: local;\n=================================================== */\n/* Notes...\n\n    What does it do\n    ---------------\n    - Adds a shadow when the container has overflow, using background-attachment: local;\n    - https://css-tricks.com/books/greatest-css-tricks/scroll-shadows/\n\n*/\n/* HTML Example...\n\n    <ul class=\"o-shadow-container-vertical\">\n        <li>Test content</li>\n        <li>Test content</li>\n        <li>Test content</li>\n        <li>etc.</li>\n    </ul>\n\n*/\n\n@layer objects {\n    .o-shadow-container-vertical {\n        overflow: auto;\n\n        --shadow-color: light-dark(hsl(0deg 0% 0% / 5%), hsl(var(--color-body-background-hue, 0) 50% 60% / 15%));\n        --shadow-color-subtle: light-dark(hsl(0deg 0% 0% / 3%), hsl(var(--color-body-background-hue, 0) 50% 60% / 8%));\n\n        background:\n        /* Shadow Cover TOP */\n        linear-gradient(\n            var(--color-body-background) 30%,\n            transparent\n        ) center top,\n\n        /* Shadow Cover BOTTOM */\n        linear-gradient(\n            transparent,\n            var(--color-body-background) 70%\n        ) center bottom,\n\n        /* Shadow TOP */\n        radial-gradient(\n            farthest-side at 50% 0,\n            var(--shadow-color-subtle),\n            transparent\n        ) center top,\n\n        /* Shadow BOTTOM */\n        radial-gradient(\n            farthest-side at 50% 100%,\n            var(--shadow-color),\n            transparent\n        ) center bottom;\n\n        background-repeat: no-repeat;\n        background-size: 100% 40px, 100% 40px, 100% 15px, 100% 15px;\n        background-attachment: local, local, scroll, scroll;\n    }\n}"
  },
  {
    "path": "resources/css/objects/toc-scroll-spy.css",
    "content": "/* GROUP OBJECTS / SCROLL DRIVEN SCROLL SPY TABLE OF CONTENTS\n=================================================== */\n/* Notes...\n\n    - Use JS for a fallback until scroll-target-group are more widely supported\n\n*/\n/* HTML Example...\n\n    <article id=\"content\" class=\"c-entry-content js__scroll-spy-toc__timeline\"> <-- .js__scroll-spy-toc__timeline here watches this section\n        {{ template_content }}\n    </article>\n\n    <section class=\"o-scroll-spy-timeline\">\n        <ul class=\"o-scroll-spy-timeline__toc js__scroll-spy-toc\"> <-- this needs to wrap around the toc\n            <li><a href=\"\">Overview</a></li>\n            <li><a href=\"\">Asset Browser</a></li>\n            <li><a href=\"\">Asset Actions</a></li>\n            <li><a href=\"\">Asset Fields</a></li>\n            <li><a href=\"\">Metadata</a></li>\n            <li><a href=\"\">Containers</a></li>\n            <li><a href=\"\">Blueprints</a></li>\n            <li><a href=\"\">Ordering</a></li>\n            <li><a href=\"\">Drivers</a></li>\n            <li\"><a href=\"\">Frontend Templating</a></li>\n        </ul>\n    </section>\n\n*/\n@layer utilities {\n    .o-scroll-spy-timeline__track {\n        scroll-margin-top: 4em;\n    }\n\n    /* ul*/\n    .o-scroll-spy-timeline__toc {\n        scroll-target-group: auto;\n        --item-padding: var(--spacing-4xs);\n        &, ul {\n            gap: unset;\n        }\n        li {\n            position: relative;\n            &:not(:has(ul)) {\n                padding-block-end: var(--item-padding);\n            }\n            @supports not (scroll-target-group: auto) {\n                ul &:has(> .js--scroll-spy-toc-active)::before {\n                    content: '';\n                    width: 3px;\n                    height: 100%;\n                    background: var(--color-pink-light-1);\n                    position: absolute;\n                    z-index: var(--z-index-below-body);\n                    top: 0;\n                    left: calc(0% - 1.4rem);\n                }\n            }\n            a:target-current {\n                font-weight: var(--font-family-main-weight-medium);\n                &::before {\n                    content: '';\n                    width: 3px;\n                    height: 100%;\n                    background: var(--color-pink-light-1);\n                    position: absolute;\n                    z-index: var(--z-index-below-body);\n                    top: 0;\n                    left: calc(0% - 1.4rem);\n                }\n            }\n            /* An option to hide the timeline position. You may want to do this because many headings are on the page at once, which messes up the TOC position. To use this add {{ push:scope_classes }}u-no-scroll-spy-toc-position{{ /push:scope_classes }} to the template */\n            .u-no-scroll-spy-toc-position & {\n                &::before {\n                    content: unset;\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/style.css",
    "content": "/* GROUP LAYERS\n=================================================== */\n@layer\nbase,\nbase-docs,\nelements,\nobjects,\ncomponents,\nmodifiers,\nutilities,\nplugins,\nui,\nscope;\n\n@import \"./base/cp.css\";\n\n@import \"tailwindcss\";\n\n@custom-variant dark (&:where(.dark, .dark *));\n\n@import './base/variables.css';\n@layer elements {\n    /* This needs to outside the @scope for Safari */\n    html {\n        background: var(--color-body-background);\n        color: var(--color-primary-text);\n    }\n}\n:root {\n    interpolate-size: allow-keywords;\n}\n@import './base/reset.css';\n@import './base/elements/elements.css';\n@import './base/elements/tables.css';\n@import './components/buttons.css';\n@import './components/entry-content.css';\n/* Objects */\n@import './objects/badge-heading.css';\n@import './objects/scroll-shadows.css';\n@import './objects/collapsible-side-menu-with-checkboxes.css';\n@import './objects/toc-scroll-spy.css';\n@import './objects/entry-content.css';\n/* Components > Global */\n@import './components/skip-to-content.css';\n@import './components/docs-header.css';\n@import './components/search-form.css';\n@import './components/site-footer.css';\n@import './components/theme-picker.css';\n@import './components/version-selector.css';\n/* Components > Global > Navigation */\n@import './components/nav/sidebar.css';\n@import './components/nav/sidebar-advert.css';\n@import './components/nav/toc.css';\n@import './components/nav/breadcrumbs.css';\n@import './components/nav/back-nav.css';\n/* Components */\n@import './components/anchors.css';\n@import './components/logos.css';\n@import './components/promo.css';\n@import './components/tiles-with-description.css';\n@import './components/feedback-meerkat.css';\n@import './components/tip.css';\n@import './components/pill.css';\n@import './components/pill-with-description.css';\n@import './components/doc-tabs.css';\n@import './components/pro-badge.css';\n@import './components/icon-grid.css';\n@import './components/panel-list.css';\n@import './components/video.css';\n@import './components/related.css';\n/* Components > Unique/Quriky */\n@import './components/list-turtles.css';\n@import './components/syntax-explainer.css';\n@import './components/quirky.css';\n/* Components > Imagery */\n@import './components/imagery/full-width-image.css';\n@import './components/imagery/bordered-image.css';\n/* Utilites */\n@import './utilities/animation-keyframes.css';\n@import './utilities/utilities.css';\n@import './utilities/hiders.css';\n@import './utilities/label.css';\n/* Other */\n@import './torchlight.css';\n\n@source \"../../resources/views\";\n@source \"../../content\";\n"
  },
  {
    "path": "resources/css/torchlight.css",
    "content": "@layer plugins {\n    pre {\n        &, /* code*/.torchlight {\n            border-radius: calc(var(--border-radius-lg) / 1.125);\n        }\n        line-height: 2;\n        &:not(.c-doc-tabs &) {\n            margin-block-end: 1rem;\n        }\n        &:has(+ h2) {\n            /* e.g. >> /collections and /configuration */\n            margin-block-end: calc(var(--spacing-3xl) + var(--spacing-sm))!important;\n        }\n        &:has(+ h3) {\n            /* e.g. >> /collections and /configuration */\n            margin-block-end: var(--spacing-2xl)!important;\n        }\n        margin-top: 1rem;\n        overflow-x: auto;\n        position: relative;\n        :is(h1,h2,h3,h4,h5,h6) + & {\n            margin-block-start: 0;\n        }\n    }\n\n    /* --mq-grid-after to custom */\n    @media (width >= 1000px) and (width < 1430px) {\n        /* Only affect the width of direct descendants of pre on the entry content, not nested pre tags e.g. >> /ui-components/badge, where it should be 100% of the container */\n        .c-entry-content > pre,\n        .c-doc-tabs pre,\n        .tab-content {\n            /* Prevent the chance of the width of pre affecting the grid layout e.g. >> /quick-start-guide, /fieldtypes/dictionary, /tags/glide */\n            max-width: 53vw;\n        }\n    }\n\n    :not(pre, p, li, figcaption, table *, h2, h3, h4) > code {\n        border-radius: .25rem;\n        font-size: 15px;\n        padding: 1px 2px;\n        word-break: break-word\n    }\n\n    pre code.torchlight {\n        display: block;\n        padding-block: var(--spacing-lg);\n        background: var(--color-code-block-background)!important;\n        font-size: .9rem;\n        /* Custom */\n        @media (width >= 768px) {\n            font-size: 1rem;\n        }\n        line-height: 2;\n        min-width: -moz-max-content;\n        min-width: max-content;\n        &:has(.tl-files-file) {\n            font-size: 0.9em;\n            padding-inline: var(--spacing-xs);\n            background: var(--color-purple-light-2)!important;\n        }\n    }\n\n    pre code.torchlight .line {\n        padding-left: .75rem;\n        padding-right: .75rem\n    }\n\n    @media (min-width: 768px) {\n        pre code.torchlight .line {\n            padding-left:1.5rem;\n            padding-right: 1.5rem\n        }\n    }\n\n    [data-theme=\"olaolu-palenight\"] .line-highlight {\n         /* e.g. >> /content-queries */\n        background: light-dark(hsl(0deg 0% 0% / 4%), hsl(0deg 0% 0% / 90%))!important;\n        border-radius: 0.25rem;\n    }\n\n    pre code.torchlight .line-number,pre code.torchlight .summary-caret {\n        margin-right: 1rem\n    }\n\n    pre .language-badge {\n        /* --tw-text-opacity: 1; */\n        /* color: rgba(255,255,255,var(--tw-text-opacity)); */\n        color: white;\n        display: inline-block;\n        font-size: 10px;\n        letter-spacing: .1em;\n        opacity: .5;\n        padding: .25rem;\n        position: absolute;\n        right: var(--spacing-sm);\n        top: var(--spacing-3xs);\n        text-transform: uppercase;\n        -webkit-user-select: none;\n        -moz-user-select: none;\n        user-select: none\n    }\n\n    .torchlight summary:focus {\n        outline: none\n    }\n\n    .torchlight details>summary::-webkit-details-marker,.torchlight details>summary::marker {\n        display: none\n    }\n\n    .torchlight details .summary-caret:after {\n        pointer-events: none\n    }\n\n    .torchlight .summary-caret-empty:after,.torchlight details .summary-caret-end:after,.torchlight details .summary-caret-middle:after {\n        content: \" \"\n    }\n\n    .torchlight details[open] .summary-caret-start:after {\n        content: \"-\"\n    }\n\n    .torchlight details:not([open]) .summary-caret-start:after {\n        content: \"+\"\n    }\n\n    .torchlight details[open] .summary-hide-when-open {\n        display: none\n    }\n\n    .torchlight details:not([open]) .summary-hide-when-open {\n        display: inline;\n        display: initial\n    }\n\n    .torchlight.has-focus-lines .line:not(.line-focus) {\n        filter: blur(.095rem);\n        opacity: .65;\n        transition: filter .35s,opacity .35s\n    }\n\n    .torchlight.has-focus-lines:hover .line:not(.line-focus) {\n        filter: blur(0);\n        opacity: 1\n    }\n\n    .tl-files-name {\n        padding-left: 1.85em;\n        position: relative;\n        font-size: 1.05em;\n        color: var(--color-primary-text)!important;\n        /* Override inline styles from Torchlight theme */\n        & ~ .token {\n            color: var(--color-primary-text) !important;\n        }\n    }\n\n    .tl-files-name:before {\n        background-position: 50% 50%;\n        background-repeat: no-repeat;\n        background-size: contain;\n        content: \"\";\n        height: 100%;\n        left: 0;\n        position: absolute;\n        top: 0;\n        width: 1.25em\n    }\n\n    .tl-files-folder {\n        position: relative;\n        display: inline-block;\n        color: var(--color-purple)!important;\n        font-weight: var(--font-family-code-weight-medium);\n        /* Custom */\n        @media (width >= 768px) {\n            font-size: 1.15em;\n        }\n        /* Override inline styles from Torchlight theme */\n        & .token {\n            color: var(--color-purple) !important;\n        }\n        /* The last line with a folder */\n        /* Commented out based on e.g. >> /extending/vite-in-addons */\n        /* :nth-last-child(1 of .line:has(&)) {\n            margin-block-end: var(--spacing-2xs);\n        } */\n    }\n    .tl-files-folder:before {\n        background-image: url(\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTc5LjIiIHdpZHRoPSIxNzkuMiI+PHBhdGggZD0iTTE1My42IDEzMS4yVjYwLjhxMC00LTIuOC02Ljh0LTYuOC0yLjhINzMuNnEtNCAwLTYuOC0yLjhUNjQgNDEuNnYtNi40cTAtNC0yLjgtNi44dC02LjgtMi44aC0zMnEtNCAwLTYuOCAyLjh0LTIuOCA2Ljh2OTZxMCA0IDIuOCA2Ljh0Ni44IDIuOEgxNDRxNCAwIDYuOC0yLjh0Mi44LTYuOHptMTIuOC03MC40djcwLjRxMCA5LjItNi42IDE1Ljh0LTE1LjggNi42SDIyLjRxLTkuMiAwLTE1LjgtNi42VDAgMTMxLjJ2LTk2UTAgMjYgNi42IDE5LjR0MTUuOC02LjZoMzJxOS4yIDAgMTUuOCA2LjZ0Ni42IDE1Ljh2My4ySDE0NHE5LjIgMCAxNS44IDYuNnQ2LjYgMTUuOHoiLz48L3N2Zz4=\");\n        width: 1.5em;\n    }\n    .tl-files-folder::after {\n        content: \"\";\n        position: absolute;\n        left: 2px;\n        top: 12px;\n        block-size: 0.6em;\n        inline-size: 1.1em;\n        background: radial-gradient(circle at center, var(--color-yellow-light-1) 60%,transparent 100%);\n    }\n    /* Page preference is \"dark\" */\n    html:has(#color-scheme option[value=\"dark\"]:checked) {\n        .tl-files-folder::before,\n        .tl-files-file:before {\n            filter: invert(1);\n        }\n        .tl-files-folder::after {\n            content: unset;\n        }\n    }\n    /* Page preference is \"system\", and system preference is \"dark\" */\n    @media (prefers-color-scheme: dark) {\n        html:has(#color-scheme option[value=\"system\"]:checked) {\n            .tl-files-folder::before,\n            .tl-files-file:before {\n                filter: invert(1);\n            }\n            .tl-files-folder::after {\n                content: unset;\n            }\n        }\n    }\n\n    .tl-files-file:before {\n        /* background-image: url(\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTc5LjIiIHdpZHRoPSIxNzkuMiI+PHBhdGggZD0iTTE0Ni44IDM4cTIuOCAyLjggNC44IDcuNnQyIDguOHYxMTUuMnEwIDQtMi44IDYuOHQtNi44IDIuOEg5LjZxLTQgMC02LjgtMi44VDAgMTY5LjZWOS42cTAtNCAyLjgtNi44VDkuNiAwaDg5LjZxNCAwIDguOCAydDcuNiA0Ljh6bS00NC40LTI0LjR2MzcuNkgxNDBxLTEtMi45LTIuMi00LjFsLTMxLjMtMzEuM3EtMS4yLTEuMi00LjEtMi4yem0zOC40IDE1Mi44VjY0SDk5LjJxLTQgMC02LjgtMi44dC0yLjgtNi44VjEyLjhIMTIuOHYxNTMuNmgxMjh6Ii8+PC9zdmc+\"); */\n        background-image: url(\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAxNSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEzLjUxODUgMTcuNzYxSDIuMDE4NDVWMS45NDg1NEgxMC42NDM1VjQuODIzNTRIMTMuNTE4NVYxNy43NjFaTTExLjM0MzMgMC41MTE5NjNIMC41ODkwMDFMMC41ODAwMTcgMTkuMTk4NUgxNC45NTVWNC4xMDQ3OUwxMS4zNDI0IDAuNTExOTYzSDExLjM0MzNaIiBmaWxsPSJibGFjayIgc3R5bGU9ImZpbGw6YmxhY2s7ZmlsbC1vcGFjaXR5OjE7Ii8+Cjwvc3ZnPgo=\");\n    }\n\n    .tl-connect-wrap {\n        display: inline-block;\n        position: relative;\n        -webkit-user-select: none;\n        -moz-user-select: none;\n        user-select: none\n    }\n\n    .tl-connect-x-adjust {\n        margin-left: 3px\n    }\n\n    .tl-connect:after,.tl-connect:before {\n        border: 0 solid var(--color-purple-light-1);\n        content: \" \";\n        display: inline-block;\n        height: 55%;\n        position: absolute;\n        width: 50%;\n    }\n\n    .tl-connect-h:after,.tl-connect-h:before {\n        top: 50%\n    }\n\n    .tl-connect-v:after,.tl-connect-v:before {\n        left: 50%\n    }\n\n    .tl-connect-left:before {\n        border-top-width: 1px;\n        left: 0\n    }\n\n    .tl-connect-right:after {\n        border-top-width: 1px;\n        left: 50%\n    }\n\n    .tl-connect-up:before {\n        border-left-width: 1px;\n        top: -5%;\n    }\n\n    .tl-connect-down:after {\n        border-left-width: 1px;\n        top: 50%\n    }\n\n    /* Copy button styles */\n    .copy-button {\n        position: absolute;\n        top: 0.75rem;\n        right: 0.75rem;\n        border: 1px solid hsl(0deg 0% 100% / 20%);\n        border-radius: var(--border-radius-md);\n        color: white;\n        cursor: pointer;\n        display: flex;\n        gap: 0.5rem;\n        padding: 0.5rem 0.75rem;\n        font-size: var(--font-size-sm);\n        font-weight: 500;\n        transition: all 0.2s ease;\n        opacity: 0;\n        backdrop-filter: blur(8px);\n        z-index: var(--z-index-above-body);\n\n        svg {\n            fill: none;\n        }\n\n        &:hover {\n            border-color: hsl(0deg 0% 100% / 30%);\n            transform: translateY(-1px);\n        }\n\n        &:active {\n            transform: translateY(0);\n        }\n\n        &.copied {\n            background: rgba(34, 197, 94, 0.05);\n            border-color: rgba(34, 197, 94, 0.5);\n            color: rgb(34, 197, 94);\n\n            .copy-icon {\n                display: none;\n            }\n\n            .check-icon {\n                display: block;\n            }\n        }\n\n        .check-icon {\n            display: none;\n        }\n    }\n\n    /* Dark mode adjustments for copy button */\n    html:has(#color-scheme option[value=\"dark\"]:checked) .copy-button {\n        background: rgba(0, 0, 0, 0.3);\n        border-color: rgba(255, 255, 255, 0.1);\n        color: rgba(255, 255, 255, 0.8);\n\n        &:hover {\n            background: rgba(0, 0, 0, 0.5);\n            border-color: rgba(255, 255, 255, 0.2);\n            color: rgba(255, 255, 255, 1);\n        }\n    }\n\n    @media (prefers-color-scheme: dark) {\n        html:has(#color-scheme option[value=\"system\"]:checked) .copy-button {\n            background: rgba(0, 0, 0, 0.3);\n            border-color: rgba(255, 255, 255, 0.1);\n            color: rgba(255, 255, 255, 0.8);\n\n            &:hover {\n                background: rgba(0, 0, 0, 0.5);\n                border-color: rgba(255, 255, 255, 0.2);\n                color: rgba(255, 255, 255, 1);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "resources/css/utilities/animation-keyframes.css",
    "content": "/* If they're OK with animation */\n@media (prefers-reduced-motion: no-preference) {\n    @keyframes wiggle {\n        20% {\n            transform: rotate(-7deg);\n        }\n        40% {\n            transform: rotate(7deg);\n        }\n        60% {\n            transform: rotate(-4deg);\n        }\n        80% {\n            transform: rotate(4deg);\n        }\n        100% {\n            transform: rotate(0deg);\n        }\n    }\n}"
  },
  {
    "path": "resources/css/utilities/hiders.css",
    "content": "@layer utilities {\n    /* GROUP UTILITIES / HIDERS\n    =================================================== */\n    /* Notes...\n\n        URL example\n        -----------\n        Home\n\n        What does it do?\n        ----------------\n        Hide things at different breakpoints\n\n    */\n    /* --mq-nav-open-before */\n    @media (width < 1100px) {\n        .u-hide-on-small-screens {\n            display: none!important;\n        }\n    }\n    /* --mq-toc-open-after */\n    @media (width >= 1100px) and (width < 1250px) {\n        .u-hide-on-medium-screens {\n            display: none!important;\n        }\n    }\n    /* --mq-nav-open-after */\n    @media (width >= 1250px) {\n        .u-hide-on-large-screens {\n            display: none!important;\n        }\n    }\n\n    /* GROUP UTILITIES / HIDERS / LIGHT/DARK DISPLAY MODES\n    =================================================== */\n    /* Notes...\n\n        What does it do?\n        ----------------\n        Hide things depending on light/dark display modes\n\n    */\n    /* Page preference is \"light\" */\n    html:has(#color-scheme option[value=\"light\"]:checked) {\n        .u-hide-in-light-mode {\n            display: none;\n        }\n    }\n    /* Page preference is \"system\", and system preference is \"light\" */\n    @media (prefers-color-scheme: light) {\n        html:has(#color-scheme option[value=\"system\"]:checked) {\n            .u-hide-in-light-mode {\n                display: none;\n            }\n        }\n    }\n    /* Page preference is \"dark\" */\n    html:has(#color-scheme option[value=\"dark\"]:checked) {\n        .u-hide-in-dark-mode {\n            display: none;\n        }\n    }\n    /* Page preference is \"system\", and system preference is \"dark\" */\n    @media (prefers-color-scheme: dark) {\n        html:has(#color-scheme option[value=\"system\"]:checked) {\n            .u-hide-in-dark-mode {\n                display: none;\n            }\n        }\n    }\n}"
  },
  {
    "path": "resources/css/utilities/label.css",
    "content": "/* GROUP UTILITIES / LABELS\n=================================================== */\n/* Notes...\n\n    What does it do?\n    ----------------\n    Funky green labels e.g. \"Hot Tip\", \"Recommended\", etc.\n\n*/\n/* HTML Example...\n\n    <div class=\"u-label\">Hot Tip!</div>\n\n*/\n@layer utilities {\n    .u-label,\n    .c-tip__title,\n    .hint-title,\n    .c-icon-grid__item__label div {\n        position: relative;\n        display: inline-block;\n        z-index: var(--z-index-above-body);\n        color: light-dark(var(--color-primary-text), var(--color-body-background));\n        font-family: var(--font-family-main);\n        font-weight: var(--font-family-main-weight-medium);\n        @container style(--color-scheme: dark) {\n            font-weight: var(--font-family-main-weight-heavy);\n        }\n        &::before {\n            content: '';\n            position: absolute;\n            z-index: -1;\n            inset: 0;\n            background: var(--color-green);\n            rotate: 1deg;\n            border-radius: 2% 98% 2% 98% / 98% 2% 98% 2%;\n        }\n        /* Alternate colors for some tip types when there are multiple hints on the page to make things a bit more interesting e.g. /core-concepts */\n        :nth-child(even of .c-tip):is(.c-tip--best-practice, .c-tip--tip) &::before {\n            background: light-dark(hsl(256deg 100% 90%), var(--color-purple));\n        }\n    }\n}"
  },
  {
    "path": "resources/css/utilities/utilities.css",
    "content": "/* GROUP UTILITIES / FRAMEWORK\n=================================================== */\n@layer utilities {\n    .qa-test {\n        border: 3px solid red!important;\n    }\n    /* Images without alt tags. Turn on when needed */\n    /* img:not([alt]) {\n        border: 5px dashed red;\n    } */\n    /* Components > Framework > Nav */\n    .no-js .u-js-only {\n        display: none!important;\n    }\n    /* Text meant only for screen readers. */\n    /* Components > Framework > Nav */\n    .u-screen-reader-text {\n        clip: rect(1px, 1px, 1px, 1px);\n\n        position: absolute!important;\n        overflow: hidden;\n        width: 1px;\n        height: 1px;\n        /* Needed if the text should be visible on keyboard focus */\n        &:focus {\n            clip: auto!important;\n\n            z-index: 100000; /* Above WP toolbar. */\n            display: block;\n            top: 0;\n            left: 0;\n\n            width: auto;\n            height: auto;\n        }\n    }\n    /* Custom */\n    @media (width < 700px) {\n        /* e.g. Sponsors on homepage */\n        .u-center-on-lower-mqs {\n            text-align: center;\n            justify-content: center;\n        }\n    }\n    .u-w-full {\n        width: 100%;\n    }\n    /* This is used to restrict the width of some screenshots, e.g. fieldtypes like /fieldtypes/assets */\n    .s-restrict-width-of-main-imgs main figure img {\n        max-width: 35rem;\n        width: 100%;\n    }\n    .u-image-boost-with-hue-rotate {\n        filter: var(--filter-image-boost-1-with-hue-rotate);\n    }\n    .u-image-boost-with-hue-rotate-extra {\n        filter: var(--filter-image-boost-1-with-hue-rotate-extra);\n    }\n    .u-link-style-none {\n        &, * {\n            text-decoration: none;\n            color: inherit;\n            border-bottom: 0;\n        }\n    }\n    /* GROUP UTILITIES / FRAMEWORK / (NON CORE) / LINE CLAMP\n    =================================================== */\n    /* e.g. <div class=\"u-line-clamp\" style=\"--clamp: 3;\"> */\n    .u-line-clamp {\n        display: -webkit-box;\n        -webkit-box-orient: vertical;\n        overflow: hidden;\n        /* Stop padding ruining the clamp */\n        padding-block-end: 0;\n        -webkit-line-clamp: var(--clamp, 2);\n    }\n    .u-disable-animation * {\n        animation: unset;\n        transition: unset;\n    }\n}"
  },
  {
    "path": "resources/fieldsets/advert_override.yaml",
    "content": "title: 'Advert Override'\nfields:\n  -\n    handle: advert_override\n    field:\n      max_items: 1\n      collections:\n        - adverts\n      type: entries\n      display: 'Advert Override'\n      instructions: 'If you want a specific advert to show on this page rather than a random advert'\n"
  },
  {
    "path": "resources/fieldsets/common.yaml",
    "content": "title: Common\nfields:\n  -\n    handle: screenshot\n    field:\n      type: assets\n      container: main\n      max_files: 1\n  -\n    handle: screenshot_dark\n    field:\n      type: assets\n      container: main\n      max_files: 1\n  -\n    handle: parameters\n    field:\n      type: grid\n      mode: stacked\n      display: 'Add Parameters'\n      fields:\n        -\n          handle: name\n          field:\n            type: text\n            width: 33\n        -\n          handle: type\n          field:\n            type: text\n            width: 33\n        -\n          handle: required\n          field:\n            type: toggle\n            width: 33\n        -\n          handle: description\n          field:\n            type: markdown\n  -\n    handle: variables\n    field:\n      type: grid\n      mode: stacked\n      display: 'Add Variables'\n      fields:\n        -\n          handle: name\n          field:\n            type: text\n            width: 33\n        -\n          handle: type\n          field:\n            type: text\n            width: 33\n        -\n          handle: description\n          field:\n            type: markdown\n"
  },
  {
    "path": "resources/fieldsets/hue_rotate.yaml",
    "content": "title: 'Hue Rotate'\nfields:\n  -\n    handle: hue_rotate\n    field:\n      options:\n        -\n          key: hue_rotate_1\n          value: 'Hue Rotate'\n        -\n          key: hue_rotate_2\n          value: 'Hue Rotate Extra'\n      clearable: true\n      type: button_group\n      display: 'Hue Rotate'\n      instructions: 'Add extra hue rotations to make the image more stylised'\n"
  },
  {
    "path": "resources/fieldsets/navigation.yaml",
    "content": "title: Navigation\nfields:\n  -\n    handle: navigation_image\n    field:\n      max_files: 1\n      container: main\n      folder: navigation\n      type: assets\n      display: 'Navigation Image'\n      instructions: 'Used for the major sections of the sidebar'\n      width: 50\n  -\n    handle: navigation_image_dark_mode\n    field:\n      max_files: 1\n      container: main\n      folder: navigation\n      type: assets\n      display: 'Navigation Image Dark Mode (optional override)'\n      instructions: 'Used for the major sections of the sidebar'\n      width: 50\n  -\n    handle: image_width_override\n    field:\n      type: text\n      display: 'Image Width Override'\n      instructions: 'e.g. `1.9em`. This allows you to override the width for certain navigation images. You may want to do this if the image has an unusual aspect ratio, which might make the image appear smaller, such as sunglasses that are more landscape.'\n  -\n    handle: tile_image\n    field:\n      max_files: 1\n      container: main\n      folder: tiles\n      type: assets\n      display: 'Tile Image'\n      instructions: 'A \"fuller\" version of the image, used for the tile images on the page, e.g. `/reference`'\n      width: 50\n  -\n    handle: tile_image_dark_mode\n    field:\n      max_files: 1\n      container: main\n      folder: tiles\n      type: assets\n      display: 'Tile Image Dark Mode (Optional override)'\n      width: 50\n  -\n    import: hue_rotate\n"
  },
  {
    "path": "resources/fieldsets/page.yaml",
    "content": "title: Page\nfields:\n  -\n    handle: nav_title\n    field:\n      display: 'Alt Nav Title'\n      type: text\n      listable: show\n      width: 50\n  -\n    handle: breadcrumb_title\n    field:\n      display: 'Alt Breadcrumb Title'\n      type: text\n      width: 50\n  -\n    handle: intro\n    field:\n      display: Intro\n      type: markdown\n  -\n    handle: content\n    field:\n      display: Content\n      type: markdown\n  -\n    handle: template\n    field:\n      display: Template\n      type: template\n  -\n    handle: related_entries\n    field:\n      collections:\n        - fieldtypes\n        - modifiers\n        - tags\n        - variables\n        - pages\n        - widgets\n        - troubleshooting\n        - resource_apis\n        - tips\n      display: 'Related Entries'\n      type: entries\n"
  },
  {
    "path": "resources/js/anchors.js",
    "content": "// Add header anchors\nvar elements = document.querySelectorAll('article :is(h2, h3, h4, h5, h6):not(.js__no-anchors)');\n\n// If fewer than 3 headings, also include h1s\nif (elements.length < 3) {\n    elements = document.querySelectorAll('article :is(h1, h2, h3, h4, h5, h6):not(.js__no-anchors)');\n}\n\nArray.prototype.forEach.call(elements, function (el, i) {\n    // Add scroll spy timeline track class and incrementing style\n    el.classList.add('o-scroll-spy-timeline__track');\n});"
  },
  {
    "path": "resources/js/color-scheme-preferences.js",
    "content": "/* DARK MODE - SAVE PREFERENCES\n=================================================== */\n/* Notes...\n    - Inspiration - https://www.smashingmagazine.com/2024/03/setting-persisting-color-scheme-preferences-css-javascript/\n    - If a color scheme preference was previously stored,\n    - select the corresponding option in the color scheme preference UI\n    - unless it is already selected.\n*/\nconst colorSchemeStorageItemName = \"preferredColorScheme\";\nconst colorSchemeSelectorEl = document.querySelector(\"#color-scheme\");\n\nfunction updateDarkClass(colorScheme) {\n    const htmlEl = document.documentElement;\n    \n    if (colorScheme === \"dark\") {\n        htmlEl.classList.add(\"dark\");\n    } else if (colorScheme === \"light\") {\n        htmlEl.classList.remove(\"dark\");\n    } else if (colorScheme === \"system\") {\n        // Check system preference\n        if (window.matchMedia && window.matchMedia(\"(prefers-color-scheme: dark)\").matches) {\n            htmlEl.classList.add(\"dark\");\n        } else {\n            htmlEl.classList.remove(\"dark\");\n        }\n    }\n}\n\nfunction restoreColorSchemePreference() {\n    const colorScheme = localStorage.getItem(colorSchemeStorageItemName);\n\n    if (!colorScheme) {\n        // Default to system preference if no stored preference\n        updateDarkClass(\"system\");\n        return;\n    }\n\n    const option = colorSchemeSelectorEl.querySelector(`[value=${colorScheme}]`);\n\n    if (!option) {\n        localStorage.removeItem(colorSchemeStorageItemName);\n        updateDarkClass(\"system\");\n        return;\n    }\n\n    if (option.selected) {\n        updateDarkClass(colorScheme);\n        return;\n    }\n\n    option.selected = true;\n    updateDarkClass(colorScheme);\n}\n\n/* Store an event target's value in localStorage under colorSchemeStorageItemName */\nfunction storeColorSchemePreference({ target }) {\n    const colorScheme = target.querySelector(\":checked\").value;\n    localStorage.setItem(colorSchemeStorageItemName, colorScheme);\n    updateDarkClass(colorScheme);\n}\n\nif (colorSchemeSelectorEl) {\n    restoreColorSchemePreference();\n\n    colorSchemeSelectorEl.addEventListener(\"input\", storeColorSchemePreference);\n    \n    // Listen for system preference changes when \"system\" is selected\n    if (window.matchMedia) {\n        const darkModeMediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n        darkModeMediaQuery.addEventListener(\"change\", (e) => {\n            const selectedValue = colorSchemeSelectorEl.querySelector(\":checked\")?.value;\n            if (selectedValue === \"system\") {\n                updateDarkClass(\"system\");\n            }\n        });\n    }\n}"
  },
  {
    "path": "resources/js/cookies.js",
    "content": "window.getCookie = function(name) {\n    let matches = document.cookie.match(new RegExp(\n        \"(?:^|; )\" + name.replace(/([\\.$?*|{}\\(\\)\\[\\]\\\\\\/\\+^])/g, '\\\\$1') + \"=([^;]*)\"\n        ));\n\n    return matches ? decodeURIComponent(matches[1]) : undefined;\n}\n\nwindow.setCookie = function(name, value, options = {}) {\n    options = {\n        path: '/',\n        ...options\n    };\n\n    if (options.expires instanceof Date) {\n        options.expires = options.expires.toUTCString();\n    }\n\n    let updatedCookie = encodeURIComponent(name) + \"=\" + encodeURIComponent(value);\n\n    for (let optionKey in options) {\n        updatedCookie += \"; \" + optionKey;\n        let optionValue = options[optionKey];\n        if (optionValue !== true) {\n            updatedCookie += \"=\" + optionValue;\n        }\n    }\n\n    document.cookie = updatedCookie;\n}\n\nwindow.deleteCookie = function(name) {\n    setCookie(name, \"\", {\n        'max-age': -1\n    })\n}\n"
  },
  {
    "path": "resources/js/dayjs.js",
    "content": "import dayjs from 'dayjs';\nimport relativeTime from 'dayjs/plugin/relativeTime';\n\ndayjs.extend(relativeTime);\nwindow.dayjs = dayjs;\n"
  },
  {
    "path": "resources/js/dl.js",
    "content": "document.addEventListener('DOMContentLoaded', function() {\n    const dlElements = document.querySelectorAll('.c-entry-content dl');\n    \n    dlElements.forEach(dl => {\n        const ddElements = dl.getElementsByTagName('dd');\n        for (let dd of ddElements) {\n            if (dd.textContent.toLowerCase().trim() === 'no') {\n                dl.classList.add('c-pill--negative');\n                break;\n            }\n        }\n    });\n});\n"
  },
  {
    "path": "resources/js/docsearch.js",
    "content": "import \"meilisearch-docsearch/css\";\nimport { docsearch } from \"meilisearch-docsearch\";\n\n// This is smart enough to filter out cmd or ctrl based on OS.\n// Precedence also allows `/` to be used, but isn't shown.\nlet hotKeys = [\n    \"cmd+k\",\n    \"ctrl+k\",\n    \"/\",\n];\n\ndocsearch({\n    container: \"#docsearch\",\n    host: \"https://search.statamic.dev\",\n    apiKey: \"a8b8f82076221f9595dceca971be29c36cbccd772de5dbdb7f43dfac41557f95\",\n    indexUid: `docs-${import.meta.env.VITE_STATAMIC_DOCS_VERSION}`,\n    hotKeys: hotKeys,\n});"
  },
  {
    "path": "resources/js/external-links.js",
    "content": "// Add external link icons & set target=\"_blank\"\nvar elements = document.querySelectorAll('article a:not(.custom)');\n\nfunction isExternal(url) {\n    var match = url.match(/^([^:\\/?#]+:)?(?:\\/\\/([^\\/?#]*))?([^?#]+)?(\\?[^#]*)?(#.*)?/);\n    if (typeof match[1] === \"string\" && match[1].length > 0 && match[1].toLowerCase() !== location.protocol) return true;\n    if (typeof match[2] === \"string\" && match[2].length > 0 && match[2].replace(new RegExp(\":(\" + { \"http:\": 80, \"https:\": 443 }[location.protocol] + \")?$\"), \"\") !== location.host) return true;\n    return false;\n}\n\nArray.prototype.forEach.call(elements, function (link, i) {\n    if (isExternal(link.href)) {\n        link.setAttribute('target', '_blank');\n        link.innerHTML += '<svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"external\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg>';\n    }\n});\n"
  },
  {
    "path": "resources/js/language-badges.js",
    "content": "// Add language badges to Torchlight code blocks\nvar elements = document.querySelectorAll('code.torchlight');\nArray.prototype.forEach.call(elements, function (el, i) {\n    if (el.dataset.lang) {\n        let badge = document.createElement('div');\n            badge.className = 'language-badge';\n            badge.innerHTML = el.dataset.lang;\n\n        el.parentElement.appendChild(badge)\n    }\n});\n"
  },
  {
    "path": "resources/js/site.js",
    "content": "import Alpine from 'alpinejs';\nimport './anchors.js';\nimport './cookies.js';\nimport './color-scheme-preferences.js';\nimport './external-links.js';\nimport './dl.js';\nimport './tables.js';\nimport './language-badges.js';\nimport './dayjs.js';\nimport './docsearch.js';\nimport './torchlight.js';\nimport './toc-navigation.js';\n\nAlpine.start();"
  },
  {
    "path": "resources/js/tables.js",
    "content": "document.addEventListener('DOMContentLoaded', function() {\n    // Wrap tables in div with c-table class\n    const tables = document.querySelectorAll('.c-entry-content table');\n    tables.forEach(table => {\n        const wrapper = document.createElement('div');\n        wrapper.className = 'c-table';\n        table.parentNode.insertBefore(wrapper, table);\n        wrapper.appendChild(table);\n    });\n});\n"
  },
  {
    "path": "resources/js/toc-navigation.js",
    "content": "function tocNavigation() {\n    const state = {\n        activeSection: null,\n        activeLink: null\n    };\n\n    function init() {\n        // [1] Find all heading elements (h2-h3)\n        const headingElements = document.querySelectorAll('.js__scroll-spy-toc__timeline h2, .js__scroll-spy-toc__timeline h3');\n        \n        // [2] Find all TOC links\n        const tocLinks = document.querySelectorAll('.js__scroll-spy-toc a');\n\n        // Create intersection observer\n        const observer = new IntersectionObserver((entries) => {\n            entries.forEach(entry => {\n                const id = entry.target.getAttribute('id');\n                \n                if (entry.isIntersecting) {\n                    // [6] Update active section\n                    state.activeSection = id;\n                    \n                    // [4] & [5] Update active classes\n                    tocLinks.forEach(link => {\n                        link.classList.remove('js--scroll-spy-toc-active');\n                        if (link.getAttribute('href') === `#${id}`) {\n                            link.classList.add('js--scroll-spy-toc-active');\n                            // [7] Update active link\n                            state.activeLink = link;\n                        }\n                    });\n                }\n            });\n        }, { \n            threshold: 0.75, // Sets how much of the element needs to be visible before the observer triggers. e.g. 0.1 means 10% of the element must be visible\n            rootMargin: '-10% 0px -50% 0px' // Added rootMargin\n        });\n\n        // [3] Observe each heading element\n        headingElements.forEach(heading => observer.observe(heading));\n    }\n\n    return { init };\n}\n\n// Initialize when DOM is ready, with delay for Vue compatibility\ndocument.addEventListener('DOMContentLoaded', () => {\n    setTimeout(() => tocNavigation().init(), 100);\n});\n"
  },
  {
    "path": "resources/js/torchlight.js",
    "content": "// Add language badges and copy buttons to Torchlight code blocks\nArray.prototype.forEach.call(document.querySelectorAll('code.torchlight'), function (el) {\n    // Add language badge\n    if (el.dataset.lang) {\n        let badge = document.createElement('div');\n        badge.className = 'language-badge';\n        badge.innerHTML = el.dataset.lang === 'md' ? 'markdown' : el.dataset.lang;\n        el.parentElement.appendChild(badge);\n    }\n    \n    // Add copy button\n    let copyButton = document.createElement('button');\n    copyButton.className = 'copy-button';\n    copyButton.innerHTML = `\n        <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"copy-icon\" fill=\"none\" viewBox=\"0 0 14 14\" id=\"Copy-Document--Streamline-Flex\"><desc>Copy Document Streamline Icon: https://streamlinehq.com</desc><g id=\"copy-document\"><path id=\"Intersect\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.3972 11.8956c0.0674 -1.0961 0.1028 -2.23187 0.1028 -3.3956 0 -0.47148 -0.0058 -0.93836 -0.0172 -1.39988 -0.0081 -0.32662 -0.1121 -0.64405 -0.3019 -0.90741 -0.7183 -0.99637 -1.2911 -1.61653 -2.2338 -2.35786 -0.261 -0.20524 -0.5814 -0.31656 -0.911 -0.32388C9.70816 3.5037 9.36573 3.5 9 3.5c-1.10726 0 -2.00102 0.03388 -2.92518 0.09844 -0.79274 0.05538 -1.42235 0.69819 -1.47201 1.50593C4.53542 6.20051 4.5 7.33627 4.5 8.5c0 1.16373 0.03542 2.2995 0.10281 3.3956 0.04966 0.8078 0.67927 1.4506 1.47201 1.506C6.99898 13.4661 7.89274 13.5 9 13.5c1.1073 0 2.001 -0.0339 2.9252 -0.0984 0.7927 -0.0554 1.4223 -0.6982 1.472 -1.506Z\" stroke-width=\"1\"/><path id=\"Intersect_2\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 0.5c0.03142 0 0.06267 0.000027 0.09375 0.000082 0.33071 0.000579 0.64258 0.004239 0.94234 0.010891 0.32965 0.007315 0.64999 0.118641 0.91098 0.323877 0.31142 0.24488 0.58247 0.47654 0.83172 0.71291m-7.175983 0.55661C0.652466 1.29663 1.28208 0.653823 2.07482 0.598441c0.04794 -0.003348 0.09579 -0.006614 0.14359 -0.009798M0.602808 8.89563c0.049659 0.80774 0.679272 1.45057 1.472012 1.50597 0.08403 0.0058 0.16781 0.0114 0.25153 0.0168M0.5 5.5c0 -0.31959 0.002671 -0.63708 0.007942 -0.95221V6.4522C0.502671 6.13707 0.5 5.81959 0.5 5.5Z\" stroke-width=\"1\"/></g></svg>\n\n        <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"check-icon\" fill=\"none\" viewBox=\"0 0 14 14\" id=\"Clipboard-Check--Streamline-Flex\"><desc>Clipboard Check Streamline Icon: https://streamlinehq.com</desc><g id=\"clipboard-check--checkmark-edit-task-edition-checklist-check-success-clipboard-form\"><path id=\"Subtract\" stroke=\"currentColor\" d=\"M4.17349 2.10889c-0.25729 0.02946 -0.60252 0.06018 -0.85608 0.09067 -0.24071 0.02894 -0.47967 0.05767 -0.71681 0.08492 -0.49602 0.05701 -0.88913 0.44564 -0.92587 0.93465 -0.23427 3.11835 -0.23427 6.06189 0 9.18027 0.03674 0.489 0.43009 0.8788 0.92783 0.9188 3.02054 0.2425 5.77459 0.2425 8.79514 0 0.4977 -0.04 0.8911 -0.4298 0.9278 -0.9188 0.2343 -3.11838 0.2343 -6.06192 0 -9.18027 -0.0367 -0.48901 -0.4298 -0.87764 -0.9259 -0.93465 -0.2371 -0.02725 -0.4761 -0.05598 -0.7168 -0.08492 -0.2535 -0.03049 -0.6081 -0.06121 -0.86532 -0.09067\" stroke-width=\"1\"/><path id=\"Union\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.3045 0.54519c0.97879 0.037253 1.52665 0.52425 1.52665 1.36417 0 0.83991 -0.54786 1.3269 -1.52665 1.36416 -1.58314 0.06025 -1.02528 0.06025 -2.60842 0 -0.97879 -0.03726 -1.52665 -0.52425 -1.52665 -1.36416 0 -0.83992 0.54786 -1.326918 1.52665 -1.36417 1.58314 -0.060253 1.02528 -0.060253 2.60842 0Z\" stroke-width=\"1\"/><path id=\"Vector\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m4.75 9.17822 1.63636 1.68748C7.15637 8.65381 7.79765 7.6832 9.25 6.36572\" stroke-width=\"1\"/></g></svg>\n\n        <span class=\"copy-text\">Copy</span>\n    `;\n    \n    // Position the copy button\n    copyButton.style.opacity = '0';\n    \n    // Show button on hover\n    el.parentElement.addEventListener('mouseenter', function() {\n        copyButton.style.opacity = '1';\n    });\n    \n    el.parentElement.addEventListener('mouseleave', function() {\n        copyButton.style.opacity = '0';\n    });\n    \n    // Copy functionality\n    copyButton.addEventListener('click', function() {\n        // Get the text content without line numbers and other elements\n        let textToCopy = '';\n        const lines = el.querySelectorAll('.line');\n        \n        lines.forEach(line => {\n            // Remove line numbers and other non-code elements\n            const lineContent = line.cloneNode(true);\n            const lineNumbers = lineContent.querySelectorAll('.line-number, .summary-caret, .summary-caret-start, .summary-caret-end, .summary-caret-middle, .summary-caret-empty');\n            lineNumbers.forEach(num => num.remove());\n            \n            // Get the text content and add a newline\n            textToCopy += lineContent.textContent + '\\n';\n        });\n        \n        // Remove trailing newline\n        textToCopy = textToCopy.trim();\n        \n        // Copy to clipboard\n        navigator.clipboard.writeText(textToCopy).then(function() {\n            // Show success state\n            copyButton.classList.add('copied');\n            copyButton.querySelector('.copy-text').textContent = 'Copied!';\n            \n            // Reset after 2 seconds\n            setTimeout(function() {\n                copyButton.classList.remove('copied');\n                copyButton.querySelector('.copy-text').textContent = 'Copy';\n            }, 2000);\n        }).catch(function(err) {\n            console.error('Failed to copy: ', err);\n            // Fallback for older browsers\n            const textArea = document.createElement('textarea');\n            textArea.value = textToCopy;\n            document.body.appendChild(textArea);\n            textArea.select();\n            document.execCommand('copy');\n            document.body.removeChild(textArea);\n            \n            // Show success state\n            copyButton.classList.add('copied');\n            copyButton.querySelector('.copy-text').textContent = 'Copied!';\n            \n            setTimeout(function() {\n                copyButton.classList.remove('copied');\n                copyButton.querySelector('.copy-text').textContent = 'Copy';\n            }, 2000);\n        });\n    });\n    \n    // Add the copy button to the code block\n    el.parentElement.appendChild(copyButton);\n});"
  },
  {
    "path": "resources/sites.yaml",
    "content": "default:\n  name: '{{ config:app:name }}'\n  locale: en_US\n  url: /\n"
  },
  {
    "path": "resources/users/groups.yaml",
    "content": "# admin:\n#   title: Administrators\n#   roles:\n#     - admin\n"
  },
  {
    "path": "resources/users/roles.yaml",
    "content": "# admin:\n#   title: Administrator\n#   permissions:\n#     - super\n"
  },
  {
    "path": "resources/views/default.antlers.html",
    "content": "{{ partial:header }}\n\n{{# <div listen-for-intersection-of-titles> #}}\n    {{ if screenshot }}\n        <figure>\n            {{ screenshot }}\n                <img src=\"{{ url }}\" width=\"{{ width | /:2 | +:2 }}\" alt=\"Screenshot of {{ page:title }}\">\n            {{ /screenshot }}\n            {{# e.g. /fieldtypes/assets #}}\n            <figcaption>The {{ title }} in action!</figcaption>\n        </figure>\n    {{ /if }}\n\n    {{ content }}\n\n    {{ if options }}\n        <h2 id=\"options\">Options</h2>\n        {{ options_content | markdown ?? '' }}\n        {{ partial:details :details=\"options\" }}\n    {{ /if }}\n\n    {{ if parameters }}\n        <h2 id=\"parameters\">Parameters</h2>\n        {{ partial:details :details=\"parameters\" }}\n    {{ /if }}\n\n    {{ if variables }}\n        <h2 id=\"variables\">Variables</h2>\n        {{ partial:variables }}\n    {{ /if }}\n{{# </div> #}}\n\n{{ partial:related }}\n"
  },
  {
    "path": "resources/views/deploying.antlers.html",
    "content": "{{ partial:header }}\n\n<div class=\"c-icon-grid\">\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/deploying/laravel-forge\">\n            <div class=\"c-icon-grid__icon\">\n                {{# 2025-07-09 Attributes needed for the SVG to display in Firefox #}}\n                <img src=\"/img/icons/forge.svg\" style=\"width: 300px; height: 58px;\" alt=\"Laravel Forge Logo\" />\n            </div>\n        </a>\n        <h2>Laravel Forge</h2>\n        <p>Server Management Platform</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/deploying/laravel-cloud\">\n            <div class=\"c-icon-grid__icon\">\n                {{# 2025-07-09 Attributes needed for the SVG to display in Firefox #}}\n                <img src=\"/img/laravel-cloud-logo.svg\" style=\"width: 150px; height: 150px;\" class=\"u-hide-in-dark-mode\" alt=\"Laravel Cloud Logo\" />\n                <img src=\"/img/icons/laravel-cloud-dark-mode.svg\" style=\"width: 150px; height: 150px;\" class=\"u-hide-in-light-mode\" alt=\"Laravel Cloud Logo\" />\n            </div>\n        </a>\n        <h2>Laravel Cloud</h2>\n        <p>Managed Infrastructure Provider</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/deploying/ploi\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/icons/ploi.png\" class=\"u-hide-in-dark-mode\" alt=\"Ploi logo\" width=\"483\" height=\"183\" />\n                <img src=\"/img/icons/ploi-dark-mode.png\" class=\"u-hide-in-light-mode\" alt=\"Ploi logo\" width=\"483\" height=\"183\" />\n            </div>\n        </a>\n        <h2>Ploi</h2>\n        <p>Server Management Platform</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/deploying/fortrabbit\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/fortrabbit.svg\" class=\"u-hide-in-dark-mode\" alt=\"Fortrabbit Logo\" />\n                {{# 2025-07-09 Attributes needed for the SVG to display in Firefox #}}\n                <img src=\"/img/icons/fortrabbit-dark-mode.svg\" style=\"width: 723px; height: 125px;\" class=\"u-hide-in-light-mode\" alt=\"Fortrabbit Logo\" />\n            </div>\n        </a>\n        <h2>Fortrabbit</h2>\n        <p>PHP as a Service</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"https://buddy.works/guides/introduction-to-statamic#deployment-with-buddy\" target=\"_blank\">\n            <div class=\"c-icon-grid__icon\">\n                {{# 2025-07-09 Attributes needed for the SVG to display in Firefox #}}\n                <img src=\"/img/buddy-logo.svg\" style=\"width: 131px; height: 150px;\" alt=\"Buddy CI/CD Logo\" />\n            </div>\n        </a>\n        <h2>Buddy {{ svg src='external-link' }}</h2>\n        <p>CI/CD Platform</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"https://kinsta.com/docs/statamic-example-application/\" target=\"_blank\">\n            <div class=\"c-icon-grid__icon\">\n                {{# 2025-07-09 Attributes needed for the SVG to display in Firefox #}}\n                <img src=\"/img/Kinsta_Icon_Black.svg\" style=\"width: 150px; height: 150px;\" alt=\"Kinsta Logo\" class=\"rounded-lg\" />\n            </div>\n        </a>\n        <h2>Kinsta {{ svg src='external-link' }}</h2>\n        <p>Cloud Hosting</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/deploying/netlify\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/icons/netlify.svg\" alt=\"Netlify Logo\" />\n            </div>\n        </a>\n        <h2>Netlify</h2>\n        <p>Netlify Platform</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/deploying/vercel\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/vercel-logo.svg\" class=\"u-hide-in-dark-mode\" alt=\"Vercel Logo\" />\n                <img src=\"/img/icons/vercel-dark-mode.svg\" class=\"u-hide-in-light-mode\" alt=\"Vercel Logo\" />\n            </div>\n        </a>\n        <h2>Vercel</h2>\n        <p>Vercel Platform</p>\n    </div>\n</div>\n\n{{ $suggest = 'yes' }}\n{{ push:scope_classes }}u-no-scroll-spy-toc-position{{ /push:scope_classes }}"
  },
  {
    "path": "resources/views/documentation-search/docs/page.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content | toc:ids }}\n\n{{ if options }}\n    <h2 id=\"options\">Options</h2>\n    {{ if options_content }}\n        {{ options_content | markdown }}\n    {{ /if }}\n    {{ partial:details :details=\"options\" }}\n{{ /if }}"
  },
  {
    "path": "resources/views/documentation-search/extending-docs/page.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content | toc:ids }}\n\n{{ if options }}\n    <h2 id=\"options\">Options</h2>\n    {{ if options_content }}\n        {{ options_content | markdown }}\n    {{ /if }}\n    {{ partial:details :details=\"options\" }}\n{{ /if }}"
  },
  {
    "path": "resources/views/documentation-search/fieldtypes/fieldtype.antlers.html",
    "content": "<article>\n    <figure>\n        {{ screenshot }}\n            <img src=\"{{ url }}\" width=\"{{ width | /:2 | +:2 }}\" alt=\"{{ page:title }} Fieldtype UI\">\n        {{ /screenshot }}\n        <figcaption>The {{ title }} in action!</figcaption>\n    </figure>\n\n    {{ content | toc:ids }}\n\n    {{ if options }}\n        <h2 id=\"options\">Options</h2>\n        {{ partial:details :details=\"options\" }}\n    {{ /if }}\n</article>"
  },
  {
    "path": "resources/views/documentation-search/modifiers/modifiers.antlers.html",
    "content": "<article>\n    {{ content | toc:ids }}\n</article>"
  },
  {
    "path": "resources/views/documentation-search/reference/reference.antlers.html",
    "content": ""
  },
  {
    "path": "resources/views/documentation-search/repositories/repositories.antlers.html",
    "content": "{{ content | toc:ids }}"
  },
  {
    "path": "resources/views/documentation-search/screencasts/screencasts.antlers.html",
    "content": "\n<article class=\"markdown pb-12\">\n    <div class=\"p-2 border shadow-stack rounded\">\n        <div class=\"embed\">\n            <iframe class=\"\" width=\"1120\" height=\"630\" src=\"{{ video | embed_url }}\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"  allowfullscreen></iframe>\n        </div>\n    </div>\n    {{ content }}\n</article>"
  },
  {
    "path": "resources/views/documentation-search/sections/sections.antlers.html",
    "content": ""
  },
  {
    "path": "resources/views/documentation-search/tags/tag-glide.antlers.html",
    "content": "{{ content | toc:ids }}\n\n{{ if parameters }}\n<h2 id=\"parameters\">Parameters</h2>\n{{ /if }}\n\n{{ partial:details details=\"options\" }}\n\n<h3>Size, Crop, and Output</h3>\n{{ partial:details :details=\"shape\" }}\n\n<h3>Filters and Effects</h3>\n{{ partial:details :details=\"filters\" }}\n\n<h3>Other</h3>\n{{ partial:details :details=\"other\" }}\n\n{{ if variables }}\n    <h2 id=\"variables\">Variables</h2>\n    <p>These variables are only available within the <a href=\"#tag-pair\">tag pair</a>.</p>\n    {{ partial:variables }}\n{{ /if }}"
  },
  {
    "path": "resources/views/documentation-search/tags/tag.antlers.html",
    "content": "{{ content | toc:ids }}\n\n{{ if parameters }}\n    <h2 id=\"parameters\">Parameters</h2>\n    {{ partial:details :details=\"parameters\" }}\n{{ /if }}\n\n{{ if variables }}\n    <h2 id=\"variables\">Variables</h2>\n    {{ partial:variables }}\n{{ /if }}"
  },
  {
    "path": "resources/views/documentation-search/tips/tips.antlers.html",
    "content": "<article>\n    {{ content | toc:ids }}\n\n    {{ if options }}\n        <h2 id=\"options\">Options</h2>\n        {{ if options_content }}\n            {{ options_content | markdown }}\n        {{ /if }}\n        {{ partial:details :details=\"options\" }}\n    {{ /if }}\n</article>"
  },
  {
    "path": "resources/views/documentation-search/troubleshooting/troubleshooting.antlers.html",
    "content": "<article>\n    {{ content | toc:ids }}\n\n    {{ if options }}\n        <h2 id=\"options\">Options</h2>\n        {{ if options_content }}\n            {{ options_content }}\n        {{ /if }}\n        {{ partial:details :details=\"options\" }}\n    {{ /if }}\n</article>"
  },
  {
    "path": "resources/views/documentation-search/variables/variables.antlers.html",
    "content": "<article>\n    {{ content | toc:ids }}\n</article>"
  },
  {
    "path": "resources/views/documentation-search/widgets/widgets.antlers.html",
    "content": "<figure>\n    {{ screenshot }}\n    <img src=\"{{ url }}\" width=\"{{ width | /:2 | +:2 }}\" alt=\"{{ title }} Fieldtype UI\">\n    {{ /screenshot }}\n    <figcaption>Behold! The {{ title }}!</figcaption>\n</figure>\n\n{{ content | toc:ids }}\n\n{{ if options }}\n    <h2 id=\"options\">Options</h2>\n    {{ partial:details :details=\"options\" }}\n{{ /if }}"
  },
  {
    "path": "resources/views/errors/404.antlers.html",
    "content": "<h1>Nothing to see here.</h1>\n<img src=\"/img/clippy-404.gif\">\n"
  },
  {
    "path": "resources/views/extending/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content | toc:ids }}\n\n{{ if options }}\n    <h2 id=\"options\">Options</h2>\n    {{ if options_content }}\n        {{ options_content | markdown }}\n    {{ /if }}\n    {{ partial:details :details=\"options\" }}\n{{ /if }}"
  },
  {
    "path": "resources/views/fieldtypes/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ collection:fieldtypes as=\"fieldtypes\" }}\n    <article class=\"c-panel-list\">\n        {{ fieldtypes split=\"1\" }}\n            <ul>\n                {{ items }}\n                    <li>\n                        <a href=\"{{ url }}\">\n                            {{ svg src=\"/img/fieldtypes/icons/{slug}\" }}\n                            {{ title }}\n                        </a>\n                    </li>\n                {{ /items }}\n            </ul>\n        {{ /fieldtypes }}\n    </article>\n{{ /collection:fieldtypes }}\n\n{{ $hide_toc = 'yes' }}\n"
  },
  {
    "path": "resources/views/home.antlers.html",
    "content": "<header>\n    <h1 id=\"learn-statamic\" class=\"o-scroll-spy-timeline__track\">Learn Statamic</h1>\n    {{ intro }}\n</header>\n\n<div class=\"c-tiles-with-description\">\n    {{ tiles }}\n        <div class=\"c-tiles-with-description__item{{ flush_image ?= ' c-tiles-with-description__item--flush' }}\">\n            <a href=\"{{ tile_link }}\" class=\"u-link-style-none\">\n                <figure class=\"c-tiles-with-description__item__thumbnail\">\n                    {{ if tile_image_dark_mode }}\n                        {{ tile_image }}\n                            {{ partial:image_dimensions/tile modifier_classes='u-hide-in-dark-mode' }}\n                        {{ /tile_image }}\n                        {{ tile_image_dark_mode }}\n                            {{ partial:image_dimensions/tile modifier_classes='u-hide-in-light-mode' }}\n                        {{ /tile_image_dark_mode }}\n                    {{ else }}\n                        {{ tile_image }}\n                            {{ partial:image_dimensions/tile }}\n                        {{ /tile_image }}\n                    {{ /if }}\n                    <figcaption>{{ tile_title }} {{ if tile_link | is_external_url }}{{ svg src='external-link' }}{{ /if }}</figcaption>\n                </figure>\n            </a>\n            <p>{{ tile_description | remove_right('.') }}</p>\n        </div>\n    {{ /tiles }}\n</div>\n\n<hr>\n\n<div class=\"mt-8 rounded-2xl px-8 py-10\" style=\"background: var(--color-gradient-full-light-3);\">\n    <div class=\"text-center mb-8\">\n        <h2 id=\"sponsors\" class=\"text-2xl font-semibold tracking-tight text-balance o-scroll-spy-timeline__track\">Loved by the Community</h2>\n        <p class=\"mt-2 opacity-60\">These docs are generously sponsored by these cool people and companies.</p>\n    </div>\n    <div class=\"flex flex-wrap justify-center gap-6\">\n        {{ hero_sponsors }}\n            {{ if error }}\n                <p>We're having trouble loading the sponsors at the moment.</p>\n            {{ else }}\n                <a href=\"{{ url }}\" class=\"group flex items-center gap-3 px-4 py-3 rounded-xl bg-white/60 dark:bg-white/10 hover:bg-white/80 dark:hover:bg-white/20 transition-colors no-underline\">\n                    <img src=\"{{ avatarUrl }}\" class=\"size-10 rounded-lg\">\n                    <span class=\"text-sm font-medium\">{{ name }}</span>\n                </a>\n            {{ /if }}\n        {{ /hero_sponsors }}\n    </div>\n    <div class=\"text-center mt-8\">\n        <a href=\"https://github.com/sponsors/statamic\" class=\"inline-flex items-center gap-2 px-5 py-2.5 rounded-full bg-[var(--color-primary-text)] text-[var(--color-body-background)] text-sm font-medium no-underline hover:opacity-90 transition-opacity\">\n            <svg class=\"size-4\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z\"/></svg>\n            Become a Sponsor\n        </a>\n    </div>\n</div>\n\n{{ $article_footer = 'no' }}\n\n{{# =JFG. Keeping this here for now because it could be useful at some point #}}\n{{# <section class=\"markdown mt-6 md:mt-20\">\n    <h2>Recent Updates</h2>\n    <p>Keep your Statamic superpowers up-to-date with the latest updates to the docs.</p>\n    <table>\n        <thead>\n            <th>Page</th>\n            <th>The Change</th>\n            <th>Section</th>\n            <th>Last Updated</th>\n        </thead>\n        <tbody>\n            {{ collection from=\"docs|tags|modifiers|variables|fieldtypes|screencasts\" sort=\"last_modified:desc\" limit=\"5\" title:not=\"Documentation\" scope=\"entry\" }}\n                <tr>\n                    <td class=\"w-1/2\"><a href=\"{{ url }}\">{{ entry:title ?? entry:slug|title }}</a></td>\n                    <td><a href=\"{{ github_commits_url }}\" target=\"_blank\">View</a></td>\n                    <td>{{ entry:collection | deslugify | title }}</td>\n                    <td x-text=\"dayjs('{{ entry:last_modified format=\"Y-m-d H:i:s\" }}').fromNow(true) + ' ago'\"></td>\n                </tr>\n            {{ /collection }}\n        </tbody>\n    </table>\n    <a href=\"/recent-updates\" class=\"button float-right mt-4\">View all recent updates</a>\n</section> #}}\n"
  },
  {
    "path": "resources/views/image_dimensions/navigation_image.antlers.html",
    "content": "<picture{{ modifier_classes ?= ' class=\"{ modifier_classes }\"'}}>\n    {{# WebP #}}\n    <source\n        srcset=\"{{ glide:url width='65' dpr='2' format='webp' }} 2x,\n                {{ glide:url width='65' dpr='3' format='webp' }} 3x\"\n        type=\"image/webp\"\n    >\n    {{# JPEG #}}\n    <source\n        srcset=\"{{ glide:url width='65' dpr='2' }} 2x,\n                {{ glide:url width='65' dpr='3' }} 3x\"\n        type=\"image/jpeg\"\n    >\n    {{# Fallback, with dimensions set to minimise layout shift. See Jay George's Wiki under Performance > Images for more information. #}}\n    <img \n        src=\"{{ glide:url width='65' dpr='2' }}\"\n        decoding=\"async\"\n        fetchpriority=\"high\"\n        width=\"{{ width }}\" \n        height=\"{{ height }}\"\n        alt=\"{{ if alt }}{{ alt | ucfirst | ensure_right('.') | smartypants }}{{ else }}{{ filename | ucfirst | ensure_right('.') | replace('-| ') | smartypants }}{{ /if }}\"\n        {{ image_width_override ?='style=\"width: {image_width_override};\"' }}{{ if hue_rotate == 'hue_rotate_1' }} class=\"u-image-boost-with-hue-rotate\"{{ elseif hue_rotate == 'hue_rotate_2' }} class=\"u-image-boost-with-hue-rotate-extra\"{{ /if }}\n    />\n</picture>"
  },
  {
    "path": "resources/views/image_dimensions/tile.antlers.html",
    "content": "<picture{{ modifier_classes ?= ' class=\"{ modifier_classes }\"'}}>\n    {{# WebP #}}\n    <source\n        srcset=\"{{ glide:url width='375' dpr='2' format='webp' }} 750w,\n                {{ glide:url width='500' dpr='2' format='webp' }} 1000w,\n                {{ glide:url width='768' dpr='2' format='webp' }} 1536w,\n                {{ glide:url width='1024' dpr='2' format='webp' }} 2048w,\n                {{ glide:url width='1680' dpr='2' format='webp' }} 3360w\"\n        type=\"image/webp\"\n        sizes=\"(min-width: 1000px) 250px, 200px\"\n    >\n    {{# JPEG #}}\n    <source\n        srcset=\"{{ glide:url width='375' dpr='2' }} 750w,\n                {{ glide:url width='500' dpr='2' }} 1000w,\n                {{ glide:url width='768' dpr='2' }} 1536w,\n                {{ glide:url width='1024' dpr='2' }} 2048w,\n                {{ glide:url width='1680' dpr='2' }} 3360w\"\n        type=\"image/jpeg\"\n        sizes=\"(min-width: 1000px) 250px, 200px\"\n    >\n    {{# Fallback, with dimensions set to minimise layout shift. See Jay George's Wiki under Performance > Images for more information. #}}\n    <img \n        src=\"{{ glide:url width='375' dpr='2' }}\"\n        decoding=\"async\"\n        fetchpriority=\"high\"\n        width=\"{{ width }}\" \n        height=\"{{ height }}\"\n        alt=\"{{ if alt }}{{ alt | ucfirst | ensure_right('.') | smartypants }}{{ else }}{{ filename | ucfirst | ensure_right('.') | replace('-| ') | smartypants }}{{ /if }}\"\n        {{ if hue_rotate == 'hue_rotate_1' }} class=\"u-image-boost-with-hue-rotate\"{{ elseif hue_rotate == 'hue_rotate_2' }} class=\"u-image-boost-with-hue-rotate-extra\"{{ /if }}\n    >\n</picture>"
  },
  {
    "path": "resources/views/installing.antlers.html",
    "content": "{{ partial:header }}\n\n<div class=\"c-icon-grid\">\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/installing/laravel-herd\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/icons/laravel-herd.png\" alt=\"Laravel Herd icon\" width=\"256\" height=\"256\" />\n            </div>\n        </a>\n        <h2>Laravel Herd</h2>\n        <p>macOS, Windows</p>\n        <div class=\"c-icon-grid__item__label\">\n            <div>Recommended</div>\n        </div>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/installing/local\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/icons/terminal.svg\" alt=\"an icon that represents the command line\" />\n            </div>\n        </a>\n        <h2>Statamic CLI</h2>\n        <p>macOS, Windows, or Linux</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/installing/laravel\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/icons/laravel.svg\" alt=\"The Laravel logo\" />\n            </div>\n        </a>\n        <h2>Laravel Application</h2>\n        <p>Drop into an existing app</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/installing/docker\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/icons/docker.svg\" alt=\"Docker logo\" />\n            </div>\n        </a>\n        <h2>Docker</h2>\n        <p>Using Laravel Sail</p>\n    </div>\n    <div class=\"c-icon-grid__item\">\n        <a href=\"/installing/ubuntu\">\n            <div class=\"c-icon-grid__icon\">\n                <img src=\"/img/icons/ubuntu.svg\" alt=\"Ubuntu logo\" />\n            </div>\n        </a>\n        <h2>Ubuntu</h2>\n        <p>24.04 LTS</p>\n    </div>\n</div>\n\n<section>\n\n    <h2>Cloud hosting</h2>\n\n    <div class=\"c-icon-grid\" style=\"padding-block-end: 0;\">\n        <div class=\"c-icon-grid__item\">\n            <a href=\"/installing/laravel-forge-1-click\">\n                <div class=\"c-icon-grid__icon\">\n                    {{# 2025-07-09 Attributes needed for the SVG to display in Firefox #}}\n                    <img src=\"/img/icons/forge.svg\" style=\"width: 300px; height: 58px;\" alt=\"Laravel Forge Logo\" />\n                </div>\n            </a>\n            <h3>Forge</h3>\n            <p>One-Click Install</p>\n        </div>\n        <div class=\"c-icon-grid__item\">\n            <a href=\"https://ploi.io/statamic\">\n                <div class=\"c-icon-grid__icon\">\n                    <img src=\"/img/icons/ploi.png\" class=\"u-hide-in-dark-mode\" alt=\"Ploi logo\" width=\"483\" height=\"183\" />\n                    <img src=\"/img/icons/ploi-dark-mode.png\" class=\"u-hide-in-light-mode\" alt=\"Ploi logo\" width=\"483\" height=\"183\" />\n                </div>\n            </a>\n            <h3>Ploi {{ svg src='external-link' }}</h3>\n            <p>One-Click Install</p>\n        </div>\n        <div class=\"c-icon-grid__item\">\n            <a href=\"/installing/digital-ocean\">\n                <div class=\"c-icon-grid__icon\">\n                    {{# 2025-07-09 Attributes needed for the SVG to display in Firefox #}}\n                    <img src=\"/img/icons/digital-ocean.svg\" style=\"width: 201px; height: 150px;\" alt=\"Digital Ocean logo\" />\n                </div>\n            </a>\n            <h3>Digital Ocean</h3>\n            <p>Virtual Private Server</p>\n        </div>\n        <div class=\"c-icon-grid__item\">\n            <a href=\"/installing/linode\">\n                <div class=\"c-icon-grid__icon\">\n                    <img src=\"/img/icons/linode.svg\" alt=\"Linode logo\" />\n                </div>\n            </a>\n            <h3>Linode</h3>\n            <p>Virtual Private Server</p>\n        </div>\n    </div>\n\n</section>\n\n{{ $suggest = 'yes' }}\n{{ push:scope_classes }}u-no-scroll-spy-toc-position{{ /push:scope_classes }}\n"
  },
  {
    "path": "resources/views/layout.antlers.html",
    "content": "<!doctype html>\n<html lang=\"en\" class=\"{{ stack:scope_classes }}{{ value }}{{ !last ?= ' ' }}{{ /stack:scope_classes }}\">\n    {{ partial:head }}\n    {{ partial:site_header }}\n    <body>\n        {{# {{ partial:promo }} #}}\n\n        {{# Need tabindex=\"-1\" to lock the tab focus here when using Skip to Content #}}\n        <main tabindex=\"-1\" id=\"main\" class=\"s-main o-scroll-spy-timeline\">\n            {{ partial:nav_sidebar }}\n            <nav class=\"c-breadcrumbs\">\n                <ol>\n                    {{ nav:breadcrumbs include_home=\"false\" }}\n                        <li>\n                            {{# Output the parent for the homepage #}}\n                            {{ if current_uri == '/' }}\n                                <a href=\"{{ url }}\">Getting Started</a>\n                                </li><li>{{ svg src='breadcrumb-chevron' }}\n                            {{ /if }}\n                            {{ unless first }}{{ svg src='breadcrumb-chevron' }}{{ /unless }}\n                            {{ if is_current }}\n                                <button popovertarget=\"popover-nav-sidebar\">{{ breadcrumb_title ?? nav_title ?? title }}</button>\n                            {{ else }}\n                                <a href=\"{{ url }}\">{{ title }}</a>\n                            {{ /if }}\n                        </li>\n                    {{ /nav:breadcrumbs }}\n                </ol>\n            </nav>\n\n            <div class=\"c-nav-toc-with-popover-api\">\n                {{ unless $hide_toc == 'yes' }}\n                    <button class=\"c-nav-toc-with-popover-api__mobile-button\" popovertarget=\"popover-nav-toc\">\n                        {{ svg src='nav-toc-chevron' }}\n                    </button>\n                    <nav id=\"popover-nav-toc\" class=\"c-nav-toc-with-popover-api__mobile u-hide-on-large-screens\" popover>\n                        <button class=\"c-nav-toc-with-popover-api__close-button\" title=\"Close\" popovertarget=\"popover-nav-toc\">\n                            <svg height=\"100pt\" aria-hidden=\"true\" viewBox=\"0 0 100 100\" width=\"100pt\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m91.668 13.676-5.3398-5.3398-36.328 36.324-36.328-36.324-5.3398 5.3398 36.328 36.324-36.328 36.324 5.3398 5.3398 36.328-36.324 36.328 36.324 5.3398-5.3398-36.328-36.324z\"/></svg>\n                        </button>\n                        <ul>\n                            <li>\n                                <span class=\"c-nav-toc-with-popover-api-category-heading\">On this page</span>\n                                {{ template_content | toc }}\n                            </li>\n                        </ul>\n                    </nav>\n                {{ /unless }}\n                <nav class=\"c-nav-toc-with-popover-api__desktop u-hide-on-small-screens\">\n                    <ul class=\"o-shadow-container-vertical\">\n                        {{ unless $hide_toc == 'yes' }}\n                            <li>\n                                <span class=\"c-nav-toc-with-popover-api-category-heading\">On this page</span>\n                                {{ template_content | toc }}\n                            </li>\n                        {{ /unless }}\n                    </ul>\n                </nav>\n            </div>\n            <div class=\"c-entry-content-wrapper\" name=\"top\" {{ if ! allow_vue }}v-pre{{ /if }}>\n                {{# {{ partial:side-nav }} #}}\n                <article id=\"content\" class=\"c-entry-content js__scroll-spy-toc__timeline\">\n                    {{ template_content }}\n                </article>\n                {{ if $suggest == 'yes' }}\n                    {{ partial:suggest }}\n                {{ else }}\n                    {{# e.g. we output this variable on homepages like / and /reference #}}\n                    {{ unless $article_footer == 'no' }}\n                        {{ partial:edit }}\n                    {{ /unless }}\n                {{ /if }}\n            </div>\n        </main>\n\n        {{ partial:footer }}\n        {{ partial:scripts }}\n    </body>\n</html>\n"
  },
  {
    "path": "resources/views/listing.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content }}\n<table>\n    <thead>\n        <tr>\n            <th>Type</th>\n            <th>Description</th>\n        </tr>\n    </thead>\n    {{ collection :from=\"mount || collection\" }}\n    <tr>\n        <td>\n            <a href=\"{{ url }}\">{{ title }}</a>\n        </td>\n        <td>\n            {{ description | markdown ?? intro | markdown }}\n        </td>\n    </tr>\n    {{ /collection }}\n</table>\n"
  },
  {
    "path": "resources/views/modifiers/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content | toc:ids }}\n\n<article class=\"c-panel-list\">\n    {{ taxonomy:modifier_types collection=\"modifiers\" }}\n        <h3>{{ title | title }}</h3>\n        <ul>\n            {{ entries sort=\"slug\" }}\n                <li><a href=\"{{ url }}\">{{ slug }}</a></li>\n            {{ /entries }}\n        </ul>\n    {{ /taxonomy:modifier_types }}\n</article>\n\n{{ partial:related }}"
  },
  {
    "path": "resources/views/page.antlers.html",
    "content": "{{ partial:header }}\n\n{{# <div listen-for-intersection-of-titles> #}}\n    {{ if screenshot }}\n        {{# If there's a screenshot at the top of the screen we're probably on a fieldtype page like /fieldtypes/assets and want to restrict the img width slightly by default #}}\n        {{ push:scope_classes }}s-restrict-width-of-main-imgs{{ /push:scope_classes }}\n        <figure>\n            {{ screenshot }}\n                <img src=\"{{ url }}\" alt=\"Screenshot of {{ page:title }}\"{{ if screenshot_dark }} class=\"u-hide-in-dark-mode\"{{ /if }}>\n            {{ /screenshot }}\n            {{ if screenshot_dark }}\n                {{ screenshot_dark }}\n                    <img src=\"{{ url }}\" alt=\"Screenshot of {{ page:title }}\" class=\"u-hide-in-light-mode\">\n                {{ /screenshot_dark }}\n            {{ /if }}\n            {{# e.g. /fieldtypes/assets #}}\n            <figcaption>The {{ title }} in action!</figcaption>\n        </figure>\n    {{ /if }}\n\n    {{ content }}\n\n    {{ if options }}\n        <h2 id=\"options\">Options</h2>\n        {{ options_content | markdown ?? '' }}\n        {{ partial:details :details=\"options\" }}\n    {{ /if }}\n\n    {{ if parameters }}\n        <h2 id=\"parameters\">Parameters</h2>\n        {{ partial:details :details=\"parameters\" }}\n    {{ /if }}\n\n    {{ if variables }}\n        <h2 id=\"variables\">Variables</h2>\n        {{ variables_content | markdown ?? '' }}\n        {{ partial:variables }}\n    {{ /if }}\n{{# </div> #}}\n\n{{ partial:related }}\n"
  },
  {
    "path": "resources/views/partials/details.antlers.html",
    "content": "{{ details }}\n<div class=\"c-pill-with-description\">\n    <header>\n        <h3>\n            {{ name }}\n        </h3>\n        <div>\n            {{ type }}\n        </div>\n        {{ if required }}\n            <div>required</div>\n        {{ /if }}\n    </header>\n    <div>\n        {{ description | markdown | widont }}\n    </div>\n</div>\n{{ /details }}\n"
  },
  {
    "path": "resources/views/partials/edit.antlers.html",
    "content": "{{ if {github_edit_url} }}\n<div class=\"o-entry-content\">\n    <a href=\"{{ github_edit_url }}\" class=\"c-feedback-meerkat flex items-center gap-3\">\n        <div>\n            <h2>Got feedback?</h2>\n            <p>Submit improvements, related content, or suggestions with a Pull Request on GitHub.</p>\n        </div>\n        <img src=\"/img/meerkat.webp\" alt=\"Meerkat\" width=\"150\" height=\"278\">\n    </a>\n</div>\n{{ /if }}\n"
  },
  {
    "path": "resources/views/partials/favicons.antlers.html",
    "content": "<link rel=\"icon\" type=\"image/png\" href=\"/favicons/favicon-96x96.png\" sizes=\"96x96\" />\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicons/favicon.svg\" />\n<link rel=\"shortcut icon\" href=\"/favicons/favicon.ico\" />\n<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/favicons/apple-touch-icon.png\" />\n<meta name=\"apple-mobile-web-app-title\" content=\"Docs\" />\n<link rel=\"manifest\" href=\"/favicons/site.webmanifest\" />"
  },
  {
    "path": "resources/views/partials/footer.antlers.html",
    "content": "<footer id=\"footer\" class=\"c-site-footer\">\n    <div class=\"c-site-footer__nav\">\n        <div class=\"c-site-footer__nav__item\">\n            <div class=\"c-site-footer-heading\">\n                {{ svg src='footer-arrow' }}\n                Learn\n            </div>\n            <ul>\n                <li><a href=\"/recent-updates\">What's New</a></li>\n                {{#  @TODO Put Something Here #}}\n            </ul>\n        </div>\n        <div class=\"c-site-footer__nav__item\">\n            <div class=\"c-site-footer-heading\">\n                {{ svg src='footer-arrow' }}\n                Product\n            </div>\n            <ul>\n                <li><a href=\"https://statamic.com\">Statamic.com</a></li>\n                <li><a href=\"https://statamic.com/blog\">Blog</a></li>\n                <li><a href=\"https://statamic.com/pricing\">Pricing</a></li>\n                <li><a href=\"https://statamic.com/license\">License</a></li>\n                <li><a href=\"https://statamic.com/release-notes\">Release Notes</a></li>\n                <li><a href=\"https://statamic.com/roadmap\">Roadmap</a></li>\n                <li><a href=\"https://v1.statamic.com\">V1 Docs</a> / <a href=\"https://v2.statamic.com\">V2 Docs</a></li>\n            </ul>\n        </div>\n        <div class=\"c-site-footer__nav__item\">\n            <div class=\"c-site-footer-heading\">\n                {{ svg src='footer-arrow' }}\n                Community\n            </div>\n            <ul>\n                <li><a href=\"https://statamic.com/discord\"><img src=\"/img/icons/social/discord.svg\" alt=\"Discord\" /> Discord</a><span>(5.8k+ members)</span></li>\n                <li><a href=\"https://github.com/statamic/cms\"><img src=\"/img/icons/social/github.svg\" alt=\"GitHub\" /> GitHub</a><span>(4.3k stars)</span></li>\n                <li><a href=\"https://github.com/statamic/cms/discussions\"><img src=\"/img/icons/social/statamic.svg\" alt=\"Discussions\" /> Discussions</a></li>\n                <li><a href=\"https://twitter.com/statamic\"><img src=\"/img/icons/social/x.svg\" alt=\"X / Twitter\" />Social</a></li>\n                <li><a rel=\"me\" href=\"https://bsky.app/profile/statamic.com\"><img src=\"/img/icons/social/bluesky.svg\" alt=\"Bluesky\" /> Bluesky</a></li>\n                <li><a rel=\"me\" href=\"https://mastodon.social/@statamic\"><img src=\"/img/icons/social/mastodon.svg\" alt=\"Mastodon\" /> Mastodon</a></li>\n            </ul>\n        </div>\n        <div class=\"c-site-footer__nav__item\">\n            <div class=\"c-site-footer-heading\">\n                {{ svg src='footer-arrow' }}\n                Marketplace\n            </div>\n            <ul>\n                <li><a href=\"https://statamic.com/addons\">Addons</a></li>\n                <li><a href=\"https://statamic.com/starter-kits\">Starter Kits</a></li>\n                <li><a href=\"https://statamic.com/sell\">Become a Seller</a></li>\n            </ul>\n        </div>\n        <div class=\"c-site-footer__nav__item\">\n            <div class=\"c-site-footer-heading\">\n                {{ svg src='footer-arrow' }}\n                Support\n            </div>\n            <ul>\n                <li><a href=\"https://statamic.com/support\">Support Center</a></li>\n                <li><a href=\"https://statamic.com/partners\">Partners</a></li>\n                <li><a href=\"https://statamic.com/partners/join\">Become a Partner</a></li>\n                <li><br><small>&copy; Copyright 2012–{{ now | format:Y }} Statamic, LLC. All rights reserved. 🇺🇸</small></li>\n            </ul>\n        </div>\n    </div>\n</footer>\n"
  },
  {
    "path": "resources/views/partials/head.antlers.html",
    "content": "<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n    <title>{{ meta_title ?? title }} // Statamic {{ config:docs:version }} Docs</title>\n    {{ partial:meta }}\n    {{# GROUP WEB FONTS\n    =================================================== #}}\n    {{# Highest priority because render blocking. An additional preload improves the Lighthouse score. #}}\n    {{# Typekit #}}\n    {{# Make Lighthouse happy. Source: https://stackoverflow.com/questions/60411231/preload-typekit-font-css#60812240 #}}\n    {{# https://use.typekit.net & https://p.typekit.net is the font file origin (Lighthouse required both links from Adobe) #}}\n    {{# It may not have the same origin as the CSS file (https://use.typekit.net/pgd3inh.css) #}}\n    <link rel=\"preconnect\" href=\"https://use.typekit.net\" crossorigin>\n    <link rel=\"preconnect\" href=\"https://p.typekit.net\" crossorigin>\n    <link rel=\"stylesheet\" href=\"https://use.typekit.net/wyy0pka.css\"/>\n\n    <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n    <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n    <link href=\"https://fonts.googleapis.com/css2?family=Lexend:wght@250..800&family=Source+Code+Pro:ital,wght@0,200..800;1,200..900&display=swap\" rel=\"stylesheet\">\n    {{# GROUP CSS\n    =================================================== #}}\n    <link rel=\"stylesheet\" href=\"{{ vite:asset src='resources/css/style.css' }}\">\n    {{ if environment == 'local' }}\n        <link rel=\"stylesheet\" href=\"/css/scratch.css\">\n    {{ /if }}\n\n    {{# GROUP JS\n    =================================================== #}}\n    <script>\n        // Test for \"system\" color scheme first. The browser will ignore the subsequent theme-colors.\n        if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n            document.head.insertAdjacentHTML('beforeend', '<meta name=\"theme-color\" content=\"hsl(230deg 17% 7%)\">');\n        }\n        document.addEventListener('DOMContentLoaded', (event) => {\n            // Get preferred color scheme from localStorage, default to 'light'\n            const preferredScheme = localStorage.getItem('preferredColorScheme') || 'light';\n\n            // Check if screen width is greater than 1200px\n            const isWideScreen = window.innerWidth > 1200;\n\n            if (isWideScreen) {\n                // Set theme color based on preferred scheme\n                const themeColor = preferredScheme === 'dark'\n                    ? 'hsl(230deg 17% 7%)'\n                    : 'white';\n                document.head.insertAdjacentHTML('beforeend', `<meta name=\"theme-color\" content=\"${themeColor}\">`);\n            } else {\n                // Set theme color based on preferred scheme\n                const themeColor = preferredScheme === 'dark'\n                    ? 'hsl(230deg 17% 7%)'\n                    : 'hsl(197deg 100% 95%)';\n                document.head.insertAdjacentHTML('beforeend', `<meta name=\"theme-color\" content=\"${themeColor}\">`);\n            }\n        });\n    </script>\n    {{ vite src=\"resources/js/site.js\" }}\n</head>\n"
  },
  {
    "path": "resources/views/partials/header.antlers.html",
    "content": "{{ if pro }}\n    <header>\n        <div class=\"o-badge-heading\">\n            <h1>{{ title }}</h1>\n            <div class=\"c-pro-badge\">\n                <a href=\"/licensing\">\n                    {{ global:pro_badges sort='random' limit='1' }}\n                        {{ artwork }}\n                            <picture class=\"c-pro-badge__artwork\">\n                                {{# WebP #}}\n                                <source\n                                    srcset=\"{{ glide:url width='95' dpr='2' format='webp' }} 190w,\n                                            {{ glide:url width='120' dpr='2' format='webp' }} 240w\"\n                                    type=\"image/webp\"\n                                    sizes=\"(min-width: 1440px) 120px, 95px\"\n                                >\n                                {{# JPEG #}}\n                                <source\n                                    srcset=\"{{ glide:url width='95' dpr='2' }} 190w,\n                                            {{ glide:url width='120' dpr='2' }} 240w\"\n                                    type=\"image/jpeg\"\n                                    sizes=\"(min-width: 1440px) 120px, 95px\"\n                                >\n                                {{# Fallback, with dimensions set to minimise layout shift. See Jay George's Wiki under Performance > Images for more information. #}}\n                                <img\n                                    src=\"{{ glide:url width='95' dpr='2' }}\"\n                                    decoding=\"async\"\n                                    fetchpriority=\"high\"\n                                    width=\"{{ width }}\"\n                                    height=\"{{ height }}\"\n                                    alt=\"{{ if alt }}{{ alt | ucfirst | ensure_right('.') | smartypants }}{{ else }}{{ filename | ucfirst | ensure_right('.') | replace('-| ') | smartypants }}{{ /if }}\"\n                                >\n                            </picture>\n                        {{ /artwork }}\n                        <div class=\"c-pro-badge__text\">\n                            {{ icon }}\n                                <picture>\n                                    {{# WebP #}}\n                                    <source\n                                        srcset=\"{{ glide:url width='80' dpr='2' format='webp' }} 160w\"\n                                        type=\"image/webp\"\n                                        sizes=\"100vw\"\n                                    >\n                                    {{# JPEG #}}\n                                    <source\n                                        srcset=\"{{ glide:url width='80' dpr='2' }} 160w\"\n                                        type=\"image/jpeg\"\n                                        sizes=\"100vw\"\n                                    >\n                                    {{# Fallback, with dimensions set to minimise layout shift. See Jay George's Wiki under Performance > Images for more information. #}}\n                                    <img\n                                        src=\"{{ glide:url width='80' dpr='2' }}\"\n                                        decoding=\"async\"\n                                        fetchpriority=\"high\"\n                                        width=\"{{ width }}\"\n                                        height=\"{{ height }}\"\n                                        alt=\"{{ if alt }}{{ alt | ucfirst | ensure_right('.') | smartypants }}{{ else }}{{ filename | ucfirst | ensure_right('.') | replace('-| ') | smartypants }}{{ /if }}\"\n                                    >\n                                </picture>\n                            {{ /icon }}\n                            Pro Feature\n                        </div>\n                    {{ /global:pro_badges }}\n                </a>\n            </div>\n        </div>\n        {{ intro | markdown ?? overview | markdown }}\n    </header>\n{{ else }}\n    <header>\n        <h1>{{ title }}</h1>\n        {{ intro | markdown ?? overview | markdown }}\n    </header>\n{{ /if }}\n"
  },
  {
    "path": "resources/views/partials/lorem.antlers.html",
    "content": "{{ markdown }}\n## Opinionated but Configurable\n\nStatamic is an opinionated platform. We strive for smart defaults and patterns that help speed up workflow, enforce\nconsistency, and make it easy to share code between projects.\n\nFollowing these conventions will make switching between multiple sites trivial, eliminating the learning curve. You'll\nknow right where everything is. This [is a link](#).\n\nSometimes these conventions don't fit, or you have your own way of doing things you're already perfectly happy with.\nThat's fine. Our conventions can be configured, overridden, or often ignored. Our recommendation is simply this: **if\nyou\ndon't have a good reason, leave it alone.**\n\n\n## Flat First\n\nStatamic has the ability to adapt to any data storage mechanism, from relational databases like MySQL and Postgres, to\nNoSQL solutions like MongoDB and Redis, and more.\n\nHowever, these solutions all add <code>md:words</code> complexity and should only be used when necessary for scale.\n\nStatamic's \"default state\" is to operate in flat file mode, which not only reduces complexity, but opens up a world of\npossibilities, like:\n\n- **complete** end-to-end version control\n- the ability to write and manage content right in your code editor\n- the ability to copy and paste or share configurations between sites\n- dead simple deployment and load balancing scenarios\n- lots, lots more\n\nAs your site scales, you can choose to move from the flat file driver to the one that best suits your needs. Deferring\nthis decision making process is a great way to prevent technical debt.\n{{ /markdown }}\n\n<div class=\"bg-blue-lightest font-display px-8 py-3 my-16 border text-sm shadow-md\">\n    <p><b><i>Pro Tip:</i></b> Always pay for your license before you start building your site so the Statamic team gets\n        paid sooner rather than later.</p>\n</div>\n\n{{ markdown }}\n## Your Content, Your Schema\n\nIt's completely up to you how to organize your content. With nearly 40 different fieldtypes included, there are a lot of\nways to break everything up.\n\nIf you like the \"one big field\" approach with all your content and markup in one chunk, we've got you covered. Or if you\nlike to break everything up into small, discrete, optional fields, showing and hiding things as needed, you can do that\ntoo.\n\nWhat fields are named, how they're organized, grouped, and arranged is all up to you. Your control panel can be as\nsimple or robust as is needed to manage your site intuitively.\n\n## Build Up, Not Rip Out\n\nWe ship with most areas of the site in a \"blank slate\" state. We find it's much easier to turn on the things you need,\nenable features you plan to use, and name things the way you want, than to have to spend precious time clicking about\nthe control panel disabling stuff you'll never end up needing.\n\nIf many of the sites you build share a common set of features, collections, taxonomies, and/or templates, save a copy of\nthat state and use it as a site kickstarter. You'll be able to jump into new projects faster than anyone.\n\n## A LEGO Brick Approach to Features\n\nYou **may** be used to content management systems and platforms that have a long list of explicit pre-built features, or\nplugins that provide these features, like photo galleries, hero images, and so on.\n\nStatamic takes a slightly different angle when combined with our \"Bring Your Own HTML\" core approach, enables you to\nbuild almost anything, like a toolbox full of LEGO bricks.\n\nWant to build a photo gallery? Add an Assets field that lets you select multiple images, and then loop through the\nselected images and render thumbnails on the fly with the Glide tag, and link to the full resolution image.\n\nImage slider? Add an Assets field, select multiple image, and pass the list of images into any number of open source\nimage slider components available on GitHub.\n\nHero Image? Add an Assets field and a text field, and render the text on top of the `background-image` of your choosing.\n\nHopefully you get the idea and see how you can solve almost any challenge with core fieldtypes and some HTML.\n\n## Possible Without the Control Panel\n\nYou can do everything (and more) without ever logging into the Control Panel. Granted, the CP does tend to make some of\nthe more complicated things easier (like creating relationships, discovering all possible options for a given setting,\nand so on), but we love efficiency and your editor is a great place to find it.\n{{ /markdown }}\n"
  },
  {
    "path": "resources/views/partials/meta.antlers.html",
    "content": "<meta name=\"description\" content=\"{{ intro | striptags }}\">\n<meta property=\"og:type\" content=\"website\" />\n<meta property=\"og:title\" content=\"{{ title }}\" />\n<meta property=\"og:description\" content=\"{{ intro | striptags }}\" />\n<meta property=\"og:url\" content=\"{{ permalink }}\" />\n<meta property=\"og:site_name\" content=\"Statamic {{ config:docs:version }} Docs\" />\n<meta property=\"og:locale\" content=\"en_US\" />\n<meta property=\"og:image\" content=\"{{ config:app:url }}/img/social-placeholder.jpg\" />\n<meta property=\"twitter:title\" content=\"{{ title }}\" />\n<meta property=\"twitter:card\" content=\"summary\" />\n<meta property=\"twitter:site\" content=\"@statamic\" />\n<meta property=\"twitter:image\" content=\"{{ config:app:url }}/img/social-placeholder.jpg\" />\n<meta property=\"twitter:description\" content=\"{{ intro | striptags }}\" />\n{{ partial:favicons }}"
  },
  {
    "path": "resources/views/partials/nav_contents.antlers.html",
    "content": "{{# Notes...\n\n    Little hack to stop the scrollbar flickering here...\n        - First we hide the overflow so the view transition captures a \"screenshot\" of the ul without the scroll bar\n        - Then we remove the overflow pretty much as soon as the page has loaded\n        - The overflow is both hidden and removed with JS so it's a cool progressive enhancement\n        - Be careful with the delay between switching the overflow back to auto. If the delay is set to more than 50ms, the scroll bars will flicker in when loading when \"System Settings > Scroll bar behaviour > Show scroll bars\" is set to \"Automatically based on mouse or trackpad\" on macOS.\n\n    We also add a few conditions to check that we're the scroll position is only saved if we're in the same section of the site, e.g. if we're going from Docs to Reference we now have a different sidebar nav so we don't want to apply a previous position.\n#}}\n<ul\n    class=\"o-shadow-container-vertical o-toggle-subnav-container\"\n>\n    {{ if segment_2 && $collection && ($collection != \"pages\") }}\n        <li>\n            <a href=\"{{ parent:url }}\" class=\"c-back-nav\">&larr; Go back</a>\n            <div class=\"o-toggle-subnav c-nav-sidebar-with-popover-api-category-heading o-current-menu-item\">\n                {{ parent:title  }}\n            </div>\n            <ul>\n                {{ collection :from=\"collection\" }}\n                <li>\n                        <a\n                            href=\"{{ url }}\"\n                            {{ if last_segment === slug }}class=\"o-current-menu-item\"{{ /if }}\n                        >\n                            {{ nav_title ?? title }}\n                        </a>\n                    </li>\n                {{ /collection}}\n            </ul>\n        </li>\n\n    {{ else }}\n\n    {{ nav:collection:pages }}\n        <li>\n            <label class=\"o-toggle-subnav c-nav-sidebar-with-popover-api-category-heading{{ if is_current || is_parent }} o-current-menu-item{{ /if }}\">\n                <div class=\"c-nav-sidebar-with-popover-api__disclosure\">{{ svg src='chevron' }}</div>\n                {{ title }}\n                <input id=\"nav-{{ title | slugify }}\" type=\"checkbox\" {{ unless is_current || is_parent || (current_uri == '/' && first) }}checked{{ /unless }}>\n            </label>\n            <ul>\n                {{# Make sure we output the homepage for the 'Getting Started' disclosure #}}\n                {{ if first }}\n                    <li>\n                        <a\n                            href=\"{{ homepage }}\"\n                            {{ if current_uri == '/' }}class=\"o-current-menu-item\"{{ /if }}\n                        >\n                            Learn Statamic\n                        </a>\n                    </li>\n                {{ /if }}\n                {{ if children }}\n                    {{ children scope=\"child\" }}\n                        <li>\n                            <a\n                                href=\"{{ url }}\"\n                                {{ if is_current || is_parent }}class=\"o-current-menu-item\"{{ /if }}\n                                {{ if url | is_external_url }} target=\"_blank\" {{ /if }}\n                            >\n                                {{ nav_title ?? title }}\n                            </a>\n                            {{ if mount }}<span>&rarr;</span>{{ /if }}\n                        </li>\n                    {{ /children }}\n                {{ /if }}\n            </ul>\n        </li>\n    {{ /nav:collection:pages }}\n\n    {{ /if }}\n</ul>\n"
  },
  {
    "path": "resources/views/partials/nav_sidebar.antlers.html",
    "content": "<div class=\"c-nav-sidebar-with-popover-api\">\n    <button class=\"c-nav-sidebar-with-popover-api__mobile-button\" id=\"anchor-nav-mobile\" popovertarget=\"popover-nav-sidebar\">\n        {{ svg src='nav-lines' }}\n    </button>\n    <nav id=\"popover-nav-sidebar\" class=\"c-nav-sidebar-with-popover-api__mobile u-hide-on-medium-screens u-hide-on-large-screens\" popover>\n        <button class=\"c-nav-sidebar-with-popover-api__close-button\" title=\"Close\" popovertarget=\"popover-nav-sidebar\">\n            <svg height=\"100pt\" aria-hidden=\"true\" viewBox=\"0 0 100 100\" width=\"100pt\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m91.668 13.676-5.3398-5.3398-36.328 36.324-36.328-36.324-5.3398 5.3398 36.328 36.324-36.328 36.324 5.3398 5.3398 36.328-36.324 36.328 36.324 5.3398-5.3398-36.328-36.324z\"/></svg>\n        </button>\n        {{ partial:nav_contents }}\n    </nav>\n    <nav class=\"c-nav-sidebar-with-popover-api__desktop u-hide-on-small-screens\">\n        {{ partial:nav_contents }}\n    </nav>\n</div>"
  },
  {
    "path": "resources/views/partials/promo.antlers.html",
    "content": "<div class=\"c-promo\"\n    x-cloak\n    x-data=\"{ dismissed: localStorage.getItem('vote-dismissed') === 'true' }\"\n    x-show=\"!dismissed\">\n    <div class=\"c-promo__inner\">\n        <a href=\"https://cmscritic.com/vote\" target=\"_blank\" class=\"block text-white\">\n            🏆 Vote for your favorite CMS at the CMS Critic Awards! Voting ends Feb 28th.\n        </a>\n        <button\n            @click=\"dismissed = true; localStorage.setItem('vote-dismissed', 'true')\"\n            class=\"c-promo__close relative z-10\"\n            aria-label=\"Dismiss promo\">\n            &times;\n        </button>\n    </div>\n</div>\n\n{{# <a href=\"https://laracasts.com/series/learn-statamic-with-jack?ref=statamic.dev\" class=\"hidden md:block sticky z-50 -top-8 bg-gradient-to-r from-purple to-blue via-pink hover:bg-gradient-animate py-2 text-center text-white font-bold text-sm antialiased\">\n    📼 Learn Statamic with Jack &mdash; a 22 episode series — is now live on <span class=\"bg-yellow text-black px-1 rounded-sm\">Laracasts!</span> 📼\n</a> #}}\n{{# <a href=\"https://statamic.com/blog/statamic-4-unleashed\" class=\"hidden md:block sticky z-50 -top-8 bg-gradient-to-r from-purple to-blue via-pink hover:bg-gradient-animate py-2 text-center text-white font-bold text-sm antialiased\">\n    🔥 <b>Statamic 4 has been unleashed!</b> Explore what's new and improved &rarr;\n</a> #}}\n{{# <a href=\"https://flatcamp.com\" class=\"hidden md:block sticky z-50 -top-8 bg-gradient-to-r from-purple to-blue via-pink hover:bg-gradient-animate py-2 text-center text-white font-bold text-sm antialiased\">\n    🤌 Flat Camp EU 2024 tickets are now available! Spend 3 dedicated days with the core Statamic team.\n</a> #}}\n"
  },
  {
    "path": "resources/views/partials/related.antlers.html",
    "content": "{{ if related_entries }}\n    <section class=\"c-related\">\n        <div class=\"u-label\">{{ badge ?? \"Learn More!\" }}</div>\n        <p>{{ description ?? \"There is more to learn more in these related articles:\" }}</p>\n\n        {{ related_entries as=\"entries\" }}\n            {{ entries group_by=\"collection:title\" }}\n                {{ groups }}\n                    {{ items }}\n                        <div class=\"c-related__item\">\n                            <ul>\n                                <li><a href=\"{{ {mount_url :handle=\"collection:handle\"} ?? '/' }}\">{{ group }}</a></li>\n                                <li>{{ svg src='breadcrumb-chevron' }}<a href=\"{{ url }}\">{{ title }}</a></li>\n                            </ul>\n                        </div>\n                    {{ /items }}\n                {{ /groups }}\n            {{ /entries }}\n        {{ /related_entries }}\n    </section>\n{{ /if }}\n"
  },
  {
    "path": "resources/views/partials/scripts.antlers.html",
    "content": "{{ if environment === \"production\" }}\n    <script src=\"https://cdn.usefathom.com/script.js\" data-site=\"LGYIPVOA\" defer></script>\n{{ /if }}"
  },
  {
    "path": "resources/views/partials/sidebar-promo.antlers.html",
    "content": "---\nquotes:\n  -\n    text: \"Bought Jack McDade's course on design. Going through it now...and it is SO well done!\"\n    cite: \"Justin Jackson, Transistor.fm\"\n  -\n    text: \"For a software dev like me who has no idea how to create a cute hand-drawn dashed line, this course just 100% works.\"\n    cite: \"Ira Zayats, Developer\"\n  -\n    text: \"Just exceptional. Thank you so much, Jack, you smashed it.\"\n    cite: \"Hugo, Developer\"\n  -\n    text: \"Taking your approach on designing things actually makes it fun, more natural, and overall easier.\"\n    cite: \"Dominik, Developer\"\n  -\n    text: \"This course is the most refreshing take on teaching design that I've come across.\"\n    cite: \"Mikaël Sévigny, Developer\"\n---\n\n<a class=\"mt-16 block group\" href=\"https://radicaldesigncourse.com/?ref=statamic.dev\">\n    <img width=\"160\" src=\"https://radicaldesigncourse.com/assets/ads/radical-design-statamic-docs.jpg\" alt=\"Radical Design Course by Jack McDade\" class=\"rounded shadow-soft relative -rotate-3 group-hover:scale-[1.05] block ml-4\">\n    <div class=\"mt-8\">\n        <p class=\"font-mono-alt uppercase text-[11px] text-gray-darker dark:text-gray tracking-widest\">From the creator of Statamic</p>\n        <p class=\"font-bold text-base leading-tight mt-2 font-display dark:text-white\">Learn how to make your websites standout and be remembered.</p>\n        <blockquote class=\"border-l border-gray-dark/50 text-sm text-[#475569] dark:text-gray pl-3 mt-2\">\n            {{ view:quotes | random }}\n                <p>{{ text }}</p>\n                <cite class=\"mt-3 block not-italic text-[12px]\">— {{ cite }}</cite>\n            {{ /view:quotes }}\n        </blockquote>\n    </div>\n</a>\n"
  },
  {
    "path": "resources/views/partials/site_header.antlers.html",
    "content": "<a href=\"#main\" class=\"c-skip-to-content u-screen-reader-text\" title=\"Skip to content\">\n    Skip to content\n</a>\n\n<a href=\"#footer\" class=\"c-skip-to-content u-screen-reader-text\" title=\"Skip to footer navigation\">\n    Skip to footer navigation\n</a>\n\n<header class=\"c-docs-header\">\n    <div class=\"c-docs-header__inner\">\n        <a class=\"o-logo\" href=\"/\">\n            {{ svg src='logomark' }}\n            {{ svg src='wordmark' }}\n        </a>\n        <div class=\"c-docs-header__search\">\n            <form class=\"c-search-form\" action=\"/search-results\">\n                <div id=\"docsearch\" ref=\"docSearch\"></div>\n            </form>\n            <div class=\"c-theme-picker\">\n                <div class=\"c-theme-picker__button\">\n                    <img class=\"c-theme-picker-light-icon u-hide-in-dark-mode\" src=\"/img/theme-light.svg\" alt=\"Light theme\" />\n                    <img class=\"c-theme-picker-dark-icon u-hide-in-light-mode\" src=\"/img/theme-dark.svg\" alt=\"Dark theme\" />\n                </div>\n                <select id=\"color-scheme\">\n                    <option value=\"system\">System</option>\n                    <option value=\"dark\">Dark</option>\n                    <option value=\"light\">Light</option>\n                </select>\n            </div>\n        </div>\n        <div class=\"c-version-selector\">\n            <select\n                x-data=\"{\n                    selectedVersion: '{{ config:docs:version }}',\n                    init() {\n                        this.$watch('selectedVersion', (version) => {\n                            const selectedOption = document.querySelector(`option[value='${version}']`);\n                            const url = selectedOption.getAttribute('data-url');\n\n                            window.location = `${url}/from/{{ page:id }}`;\n                        });\n                    },\n                }\"\n                x-model=\"selectedVersion\"\n            >\n                {{ config:docs:versions }}\n                    <option value=\"{{ version }}\" data-url=\"{{ url }}\">\n                        v&hairsp;{{ branch }}{{ if alpha }}-alpha{{ /if }}{{ if beta }}-beta{{ /if }}\n                    </option>\n                {{ /config:docs:versions }}\n            </select>\n        </div>\n    </div>\n</header>\n"
  },
  {
    "path": "resources/views/partials/suggest.antlers.html",
    "content": "{{ if {github_edit_url} }}\n<div class=\"o-entry-content\">\n    <a href=\"{{ github_edit_url }}\" class=\"c-feedback-meerkat flex items-center gap-3\">\n        <div>\n            <h2>Suggest content</h2>\n            <p>Help us improve the docs by suggesting new content with a Pull Request on GitHub.</p>\n        </div>\n        <img src=\"/img/meerkat.webp\" alt=\"Meerkat\" width=\"150\" height=\"278\">\n    </a>\n</div>\n{{ /if }}\n"
  },
  {
    "path": "resources/views/partials/variables.antlers.html",
    "content": "<table>\n    <thead>\n        <tr>\n            <th>Variable</th>\n            <th>Type</th>\n            <th>Description</th>\n        </tr>\n    </thead>\n    <tbody>\n        {{ variables }}\n        <tr>\n            <td class='font-display font-bold'>{{ name | markdown }}</td>\n            <td><code class=\"whitespace-nowrap\">{{ type }}</code></td>\n            <td>{{ description | markdown  }}</td>\n        </tr>\n        {{ /variables }}\n    </tbody>\n</table>\n"
  },
  {
    "path": "resources/views/reference/index.antlers.html",
    "content": "{{ partial:header }}\n\n<section class=\"c-tiles-with-description\">\n    {{ nav handle=\"reference\" }}\n        <div class=\"c-tiles-with-description__item{{ flush_image ?= ' c-tiles-with-description__item--flush' }}\">\n            <a href=\"{{ url }}\" class=\"u-link-style-none\">\n                <figure class=\"c-tiles-with-description__item__thumbnail\">\n                    {{ if tile_image_dark_mode }}\n                        {{ tile_image }}\n                            {{ partial:image_dimensions/tile modifier_classes='u-hide-in-dark-mode' }}\n                        {{ /tile_image }}\n                        {{ tile_image_dark_mode }}\n                            {{ partial:image_dimensions/tile modifier_classes='u-hide-in-light-mode' }}\n                        {{ /tile_image_dark_mode }}\n                    {{ else }}\n                        {{ tile_image }}\n                            {{ partial:image_dimensions/tile }}\n                        {{ /tile_image }}\n                    {{ /if }}\n                    <figcaption>{{ collection :in=\"slug\" as=\"entries\" }}{{ entries | count }} {{ title }}{{ /collection }}</figcaption>\n                </figure>\n            </a>\n            {{ content }}\n        </div>\n    {{ /nav }}\n</section>\n\n<hr>\n\n<h2 id=\"sponsors\" class=\"u-center-on-lower-mqs o-scroll-spy-timeline__track\">Sponsors</h2>\n<p class=\"u-center-on-lower-mqs\">We've reserved this space to thank our top open source sponsors.</p>\n<div class=\"c-logo-grid u-center-on-lower-mqs\">\n    {{ hero_sponsors }}\n        <div class=\"c-logo-grid__item\">\n            {{ if error }}\n                <p>We're having trouble loading the sponsors at the moment.</p>\n            {{ else }}\n                <a href=\"{{ url }}\">\n                    <img src=\"{{ avatarUrl }}\">\n                    <span>{{ name }}</span>\n                </a>\n            {{ /if }}\n        </div>\n    {{ /hero_sponsors }}\n    <div class=\"c-logo-grid__item\">\n        <a href=\"https://github.com/sponsors/statamic\">Sponsor Statamic</a>\n    </div>\n</div>\n\n{{ $article_footer = 'no' }}"
  },
  {
    "path": "resources/views/repositories/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ collection:resource_apis as=\"repositories\" }}\n    <article class=\"c-panel-list\">\n        {{ repositories split=\"1\" }}\n            <ul>\n                {{ items }}\n                    <li>\n                        <a href=\"{{ url }}\">\n                            {{ svg src=\"/img/repositories/{nav_title | slugify}\" }}\n                            {{ nav_title }}\n                        </a>\n                    </li>\n                {{ /items }}\n            </ul>\n        {{ /repositories }}\n    </article>\n{{ /collection:resource_apis }}"
  },
  {
    "path": "resources/views/search.antlers.html",
    "content": "<article class=\"markdown pb-8\">\n    <h1>Search Results</h1>\n\n    {{ search:results as=\"results\" }}\n\n        {{ if no_results }}\n\n            {{ if get:q|length < 3 }}\n                <p>Please search for at least 3 characters.</p>\n            {{ else }}\n                <p>No results for <b>{{ get:q }}</b></p>\n            {{ /if }}\n\n        {{ else }}\n\n            <p>Showing results for <b>{{ get:q }}</b></p>\n            <ul>\n                {{ results }}\n                    <li>\n                        <div class=\"flex items-center\">\n                            <a href=\"{{ url }}\">{{ title }}</a>\n                            {{ unless collection == \"docs\" }}\n                            <span class=\"text-2xs text-grey-700 antialiased border-sm bg-grey-400 px-2 p-sm rounded ml-2\">\n                                {{ if collection === \"tags\" }}Tag\n                                {{ elseif collection === \"fieldtypes\" }}Fieldtype\n                                {{ elseif collection === \"modifiers\" }}Modifier\n                                {{ elseif collection === \"variables\" }}Variable\n                                {{ /if }}\n                            </span>\n                            {{ /unless }}\n                        </div>\n                        {{ if intro }}\n                            <div class=\"text-grey-600 text-sm\">{{ intro }}</div>\n                        {{ /if }}\n                    </li>\n                {{ /results }}\n            </ul>\n\n        {{ /if }}\n\n    {{ /search:results }}\n\n</div>\n"
  },
  {
    "path": "resources/views/sitemap.antlers.html",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n{{ get_content from=\"6aa5449b-5d90-47de-97e7-82ba5f665250\" }}\n  <url>\n    <loc>{{ permalink remove_right=\"/documentation\" }}</loc>\n    <lastmod>{{ last_modified format=\"Y-m-d\" }}</lastmod>\n  </url>\n{{ /get_content }}\n{{ collection from=\"*\" }}\n  <url>\n    <loc>{{ permalink }}</loc>\n    <lastmod>{{ last_modified format=\"Y-m-d\" }}</lastmod>\n  </url>\n{{ /collection }}\n</urlset>\n"
  },
  {
    "path": "resources/views/social.antlers.html",
    "content": "<html lang=\"en\" class=\"bg-white\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n    <link rel=\"stylesheet\" href=\"https://use.typekit.net/huy6nsb.css\" defer>\n</head>\n{{# 1280x640 #}}\n<body class=\"font-sans leading-normal markdown flex flex-col\">\n    <div class=\"h-4 bg-gradient-to-r from-purple to-blue via-pink\"></div>\n    <div class=\"flex-1 pl-12 pr-8 pt-2 flex flex-col justify-center relative z-10\">\n        {{ svg src=\"docs-logo\" class=\"block text-pink w-[300px] h-[46px]\" }}\n\n        <div class=\"flex items-center\">\n            {{# <h1 class=\"{{ title | length > 36 ?= 'text-5xl'}}\">{{ title | widont }}</h1> #}}\n            <h1 class=\"text-9xl mt-4\">{{ title }}</h1>\n            {{ partial:pro-badge }}\n        </div>\n\n        <p class=\"font-mono-alt ml-2 flex text-2xl text-gray-darker items-center\">\n            {{ svg src=\"link\" class=\"h-12 w-12 -ml-2 mr-5\" }}\n            {{ url }}\n        </p>\n    </div>\n    <div class=\"bg-black relative h-2 mt-4\">\n        <img src=\"/img/splatter-palm.png\" alt=\"\" class=\"absolute bottom-0 left-1\" width=\"300\">\n    </div>\n    <img src=\"/img/splatter-feedback.png\" alt=\"\" class=\"absolute max-w-none w-[1600px] flip-y left-0 bottom-[-460px] z-0\">\n</body>\n</html>\n"
  },
  {
    "path": "resources/views/tabs.antlers.html",
    "content": " <div x-data=\"{ tab: '{{ active }}' }\" x-cloak>\n    <div>\n        <div class=\"c-doc-tabs__tabs\" data-indexer=\"ignore\">\n          {{ foreach:tabs }}\n              <button type=\"button\" x-on:click=\"tab = '{{ key }}'\" :class=\"{ 'c-doc-tabs__active': tab === '{{ key }}' }\">{{ value }}</button>\n          {{ /foreach:tabs }}\n        </div>\n        {{ foreach:samples }}\n            <div class=\"tab-content\" x-show=\"tab === '{{ key }}'\">{{ value }}</div>\n        {{ /foreach:samples }}\n   </div>\n </div>\n"
  },
  {
    "path": "resources/views/tags/glide.antlers.html",
    "content": "{{ partial:header}}\n\n{{ content | toc:ids }}\n\n{{ if parameters }}\n<h2 id=\"parameters\">Parameters</h2>\n{{ /if }}\n\n{{ partial:details details=\"options\" }}\n\n<h3>Size, Crop, and Output</h3>\n{{ partial:details :details=\"shape\" }}\n\n<h3>Filters and Effects</h3>\n{{ partial:details :details=\"filters\" }}\n\n<h3>Other</h3>\n{{ partial:details :details=\"other\" }}\n\n{{ if variables }}\n    <h2 id=\"variables\">Variables</h2>\n    <p>These variables are only available within the <a href=\"#tag-pair\">tag pair</a>.</p>\n    {{ partial:variables }}\n{{ /if }}\n"
  },
  {
    "path": "resources/views/tags/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content }}\n<table>\n    <thead>\n        <tr>\n            <th>Type</th>\n            <th>Description</th>\n        </tr>\n    </thead>\n    {{ collection:tags }}\n    <tr>\n        <td>\n            <a href=\"{{ url }}\">{{ title }}</a>\n        </td>\n        <td>\n            {{ description | markdown ?? intro | markdown }}\n        </td>\n    </tr>\n    {{ /collection:tags }}\n</table>\n"
  },
  {
    "path": "resources/views/tips/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content }}\n<ul>\n{{ collection:tips }}\n    <li><h2><a href=\"{{ url }}\">{{ title }}</a></h2></li>\n{{ /collection:tips }}\n</ul>\n\n{{ $suggest = 'yes' }}\n{{ $hide_toc = 'yes' }}\n{{ push:scope_classes }}u-no-scroll-spy-toc-position{{ /push:scope_classes }}\n"
  },
  {
    "path": "resources/views/troubleshooting/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content }}\n<ul>\n{{ collection:troubleshooting }}\n    <li><h2><a href=\"{{ url }}\">{{ title }}</a></h2></li>\n{{ /collection:troubleshooting }}\n</ul>\n\n{{ $suggest = 'yes' }}\n{{ $hide_toc = 'yes' }}\n{{ push:scope_classes }}u-no-scroll-spy-toc-position{{ /push:scope_classes }}\n"
  },
  {
    "path": "resources/views/updates.antlers.html",
    "content": "<header>\n    <h1>{{ title }}</h1>\n    {{ intro }}\n</header>\n\n<section class=mt-8 w-full\">\n    <table>\n        <thead>\n            <th>Page</th>\n            <th>Section</th>\n            <th>Last Updated</th>\n        </thead>\n        <tbody>\n            {{ collection from=\"docs|tags|modifiers|variables|fieldtypes|screencasts\" sort=\"last_modified:desc\" limit=\"100\" title:not=\"Documentation\" scope=\"entry\" }}\n                <tr>\n                    <td class=\"w-1/2\"><a href=\"{{ github_commits_url }}\" target=\"_blank\">{{ entry:title ?? entry:slug|title }}</a></td>\n                    <td>{{ entry:collection | deslugify | title }}</td>\n                    <td x-text=\"dayjs('{{ entry:last_modified format=\"Y-m-d H:i\" }}').fromNow(true) + ' ago'\"></td>\n                </tr>\n            {{ /collection }}\n        </tbody>\n    </table>\n</section>\n"
  },
  {
    "path": "resources/views/variables/index.antlers.html",
    "content": "{{ partial:header }}\n\n{{ content }}\n<table>\n    <thead>\n        <tr>\n            <th>Type</th>\n            <th>Description</th>\n        </tr>\n    </thead>\n    {{ collection:variables }}\n    <tr>\n        <td>\n            <a href=\"{{ url }}\">{{ title }}</a>\n        </td>\n        <td>\n            {{ description | markdown ?? intro | markdown }}\n        </td>\n    </tr>\n    {{ /collection:variables }}\n</table>\n"
  },
  {
    "path": "routes/console.php",
    "content": "<?php\n\nuse Illuminate\\Foundation\\Inspiring;\nuse Illuminate\\Support\\Facades\\Artisan;\n\nArtisan::command('inspire', function () {\n    $this->comment(Inspiring::quote());\n})->purpose('Display an inspiring quote');\n"
  },
  {
    "path": "routes/redirects.php",
    "content": "<?php\n\nRoute::permanentRedirect('extending/actions', '/backend-apis/actions');\nRoute::permanentRedirect('extending/addons', '/addons/building-an-addon');\nRoute::permanentRedirect('extending/augmentation', '/frontend/augmentation#digging-deeper');\nRoute::permanentRedirect('extending/bard', '/fieldtypes/bard#extending-bard');\nRoute::permanentRedirect('extending/blink-cache', '/backend-apis/blink-cache');\nRoute::permanentRedirect('extending/breadcrumbs', '/control-panel/cp-navigation#breadcrumbs');\nRoute::permanentRedirect('extending/command-palette', '/control-panel/command-palette');\nRoute::permanentRedirect('extending/control-panel', '/control-panel/css-javascript');\nRoute::permanentRedirect('extending/cp-navigation', '/control-panel/cp-navigation');\nRoute::permanentRedirect('extending/data', '/backend-apis/data');\nRoute::permanentRedirect('extending/dictionaries', '/fieldtypes/dictionaries');\nRoute::permanentRedirect('extending/dirty-state-tracking', '/vue-components/dirty-state-tracking');\nRoute::permanentRedirect('extending/elevated-sessions', '/control-panel/elevated-sessions');\nRoute::permanentRedirect('extending/events', '/backend-apis/events');\nRoute::permanentRedirect('extending/field-actions', '/control-panel/field-actions');\nRoute::permanentRedirect('extending/fieldtypes', '/fieldtypes/build-a-fieldtype');\nRoute::permanentRedirect('extending/hooks', '/backend-apis/hooks');\nRoute::permanentRedirect('extending/javascript', '/vue-components/overview');\nRoute::permanentRedirect('extending/js-events', '/vue-components/js-events');\nRoute::permanentRedirect('extending/js-hooks', '/vue-components/js-hooks');\nRoute::permanentRedirect('extending/keyboard-shortcuts', '/control-panel/keyboard-shortcuts');\nRoute::permanentRedirect('extending/lifecycle', '/advanced-topics/lifecycle');\nRoute::permanentRedirect('extending/markdown', '/frontend/markdown');\nRoute::permanentRedirect('extending/modals', 'https://ui.statamic.dev/?path=/docs/overlays-modal--docs');\nRoute::permanentRedirect('extending/modifiers', '/modifiers/modifiers');\nRoute::permanentRedirect('extending/permissions', '/control-panel/permissions');\nRoute::permanentRedirect('extending/progress', '/vue-components/progress');\nRoute::permanentRedirect('extending/publish-components', '/control-panel/publish-forms');\nRoute::permanentRedirect('extending/publish-forms', '/control-panel/publish-forms');\nRoute::permanentRedirect('extending/query-scopes-and-filters', '/backend-apis/query-scopes-and-filters');\nRoute::permanentRedirect('extending/relationship-fieldtypes', '/fieldtypes/relationship-fieldtypes');\nRoute::permanentRedirect('extending/repositories', '/backend-apis/repositories');\nRoute::permanentRedirect('extending/search', '/frontend/search#digging-deeper');\nRoute::permanentRedirect('extending/slugs', '/vue-components/slugs');\nRoute::permanentRedirect('extending/stacks', 'https://ui.statamic.dev/?path=/docs/overlays-stack--docs');\nRoute::permanentRedirect('extending/tags', '/tags/building-a-tag');\nRoute::permanentRedirect('extending/testing-in-addons', '/addons/testing');\nRoute::permanentRedirect('extending/toast-notifications', '/control-panel/toast-notifications');\nRoute::permanentRedirect('extending/ui-components', 'http://ui.statamic.dev');\nRoute::permanentRedirect('extending/utilities', '/control-panel/utilities');\nRoute::permanentRedirect('extending/vite-in-addons', '/addons/vite-tooling');\nRoute::permanentRedirect('extending/vue-components', '/vue-components/overview');\nRoute::permanentRedirect('extending/widgets', '/widgets/building-a-widget');\nRoute::permanentRedirect('tips/configuring-update-scripts', '/knowledge-base/tips/configuring-update-scripts');\nRoute::permanentRedirect('tips/creating-users-by-hand', '/knowledge-base/tips/creating-users-by-hand');\nRoute::permanentRedirect('tips/digital-ocean-spaces-for-asset-container', '/knowledge-base/tips/digital-ocean-spaces-for-asset-container');\nRoute::permanentRedirect('tips/gdpr-considerations', '/knowledge-base/tips/gdpr-considerations');\nRoute::permanentRedirect('tips/laravel-nova', '/knowledge-base/tips/laravel-nova');\nRoute::permanentRedirect('tips/load-templates-dynamically-based-on-the-url', '/knowledge-base/tips/load-templates-dynamically-based-on-the-url');\nRoute::permanentRedirect('tips/localizing-navigation', '/knowledge-base/tips/localizing-navigation');\nRoute::permanentRedirect('tips/manually-resetting-a-user-password', '/knowledge-base/tips/manually-resetting-a-user-password');\nRoute::permanentRedirect('tips/pushing-related-entries-to-an-algolia-index', '/knowledge-base/tips/pushing-related-entries-to-an-algolia-index');\nRoute::permanentRedirect('tips/storing-users-somewhere-custom', '/knowledge-base/tips/storing-users-somewhere-custom');\nRoute::permanentRedirect('tips/taxonomies-by-hand', '/knowledge-base/tips/taxonomies-by-hand');\nRoute::permanentRedirect('tips/setting-default-listing-columns', '/knowledge-base/tips/setting-default-listing-columns');\nRoute::permanentRedirect('tips/storing-laravel-users-in-files', '/knowledge-base/tips/storing-laravel-users-in-files');\nRoute::permanentRedirect('tips/disabling-cp-authentication', '/knowledge-base/tips/disabling-cp-authentication');\nRoute::permanentRedirect('tips/localizing-entries', '/knowledge-base/tips/localizing-entries');\nRoute::permanentRedirect('tips/reserved-words', '/knowledge-base/tips/reserved-words');\nRoute::permanentRedirect('tips/using-an-independent-authentication-guard', '/knowledge-base/tips/using-an-independent-authentication-guard');\nRoute::permanentRedirect('tips/content-security-policy', '/knowledge-base/tips/content-security-policy');\nRoute::permanentRedirect('tips/recursive-nav-examples', '/knowledge-base/tips/recursive-nav-examples');\nRoute::permanentRedirect('tips/building-your-own-entries-repository', '/knowledge-base/tips/building-your-own-entries-repository');\nRoute::permanentRedirect('tips/how-to-enable-statamic-pro', '/knowledge-base/tips/how-to-enable-statamic-pro');\nRoute::permanentRedirect('tips/importing-content', '/knowledge-base/tips/importing-content');\nRoute::permanentRedirect('tips/optimizing-assets', '/knowledge-base/tips/optimizing-assets');\nRoute::permanentRedirect('tips/git-workflow', '/knowledge-base/tips/git-workflow');\nRoute::permanentRedirect('tips/zero-downtime-deployments', '/knowledge-base/tips/zero-downtime-deployments');\nRoute::permanentRedirect('tips/change-timezone-to-utc', '/knowledge-base/tips/change-timezone-to-utc');\nRoute::permanentRedirect('tips/converting-from-single-to-multi-site', '/knowledge-base/tips/converting-from-single-to-multi-site');\nRoute::permanentRedirect('tips/localizing-globals', '/knowledge-base/tips/localizing-globals');\nRoute::permanentRedirect('tips/storing-content-in-a-database', '/knowledge-base/tips/storing-content-in-a-database');\nRoute::permanentRedirect('tips/storing-users-in-a-database', '/knowledge-base/tips/storing-users-in-a-database');\nRoute::permanentRedirect('tips/timezones', '/knowledge-base/tips/timezones');\nRoute::permanentRedirect('tips/excluding-the-control-panel-from-maintenance-mode', '/knowledge-base/tips/excluding-the-control-panel-from-maintenance-mode');\nRoute::permanentRedirect('upgrade-guide/3-0-to-3-1', '/getting-started/upgrade-guide/3-0-to-3-1');\nRoute::permanentRedirect('upgrade-guide/3-1-to-3-2', '/getting-started/upgrade-guide/3-1-to-3-2');\nRoute::permanentRedirect('upgrade-guide/3-2-to-3-3', '/getting-started/upgrade-guide/3-2-to-3-3');\nRoute::permanentRedirect('upgrade-guide/3-3-to-3-4', '/getting-started/upgrade-guide/3-3-to-3-4');\nRoute::permanentRedirect('upgrade-guide/3-4-to-4-0', '/getting-started/upgrade-guide/3-4-to-4-0');\nRoute::permanentRedirect('upgrade-guide/4-to-5', '/getting-started/upgrade-guide/4-to-5');\nRoute::permanentRedirect('upgrade-guide/5-to-6', '/getting-started/upgrade-guide/5-to-6');\nRoute::permanentRedirect('addons', '/addons/overview');\nRoute::permanentRedirect('antlers', '/frontend/antlers');\nRoute::permanentRedirect('assets', '/frontend/assets');\nRoute::permanentRedirect('augmentation', '/frontend/augmentation');\nRoute::permanentRedirect('upgrade-guide/bard-v1-to-v2', '/getting-started/upgrade-guide/bard-v1-to-v2');\nRoute::permanentRedirect('blade-form-fields', '/frontend/blade-form-fields');\nRoute::permanentRedirect('blade', '/frontend/blade');\nRoute::permanentRedirect('blueprints', '/content-modeling/blueprints');\nRoute::permanentRedirect('caching', '/advanced-topics/caching');\nRoute::permanentRedirect('cli', '/advanced-topics/cli');\nRoute::permanentRedirect('code-of-conduct', '/knowledge-base/code-of-conduct');\nRoute::permanentRedirect('collections', '/content-modeling/collections');\nRoute::permanentRedirect('computed-values', '/content-modeling/computed-values');\nRoute::permanentRedirect('conditional-fields', '/control-panel/conditional-fields');\nRoute::permanentRedirect('conditions', '/tags/conditions');\nRoute::permanentRedirect('configuration', '/getting-started/configuration');\nRoute::permanentRedirect('content-managers-guide', '/knowledge-base/content-managers-guide');\nRoute::permanentRedirect('content-queries', '/frontend/content-queries');\nRoute::permanentRedirect('contributing', '/knowledge-base/contributing');\nRoute::permanentRedirect('contribution-guide', '/knowledge-base/contribution-guide');\nRoute::permanentRedirect('controllers', '/frontend/controllers');\nRoute::permanentRedirect('core-concepts', '/getting-started/core-concepts');\nRoute::permanentRedirect('cp-translations', '/control-panel/cp-translations');\nRoute::permanentRedirect('customizing-the-cp-nav', '/control-panel/customizing-the-cp-nav');\nRoute::permanentRedirect('dashboard', '/control-panel/dashboard');\nRoute::permanentRedirect('data-inheritance', '/content-modeling/data-inheritance');\nRoute::permanentRedirect('debugging', '/advanced-topics/debugging');\nRoute::permanentRedirect('deploying', '/getting-started/deploying');\nRoute::permanentRedirect('installing/digital-ocean', '/getting-started/installing/digital-ocean');\nRoute::permanentRedirect('deploying/digitalocean', '/getting-started/deploying/digitalocean');\nRoute::permanentRedirect('installing/docker', '/getting-started/installing/docker');\nRoute::permanentRedirect('email', '/advanced-topics/email');\nRoute::permanentRedirect('fields', '/content-modeling/fields');\nRoute::permanentRedirect('fieldsets', '/content-modeling/fieldsets');\nRoute::permanentRedirect('fieldtypes', '/fieldtypes/overview');\nRoute::permanentRedirect('forms', '/frontend/forms');\nRoute::permanentRedirect('deploying/fortrabbit', '/getting-started/deploying/fortrabbit');\nRoute::permanentRedirect('from-wordpress-to-statamic', '/knowledge-base/from-wordpress-to-statamic');\nRoute::permanentRedirect('frontend', '/frontend/overview');\nRoute::permanentRedirect('git-automation', '/control-panel/git-automation');\nRoute::permanentRedirect('globals', '/content-modeling/globals');\nRoute::permanentRedirect('graphql', '/frontend/graphql');\nRoute::permanentRedirect('image-manipulation', '/frontend/image-manipulation');\nRoute::permanentRedirect('installing', '/getting-started/installing');\nRoute::permanentRedirect('javascript-frameworks', '/frontend/javascript-frameworks');\nRoute::permanentRedirect('upgrade-guide/laravel-7-to-8', '/getting-started/upgrade-guide/laravel-7-to-8');\nRoute::permanentRedirect('deploying/laravel-cloud', '/getting-started/deploying/laravel-cloud');\nRoute::permanentRedirect('installing/laravel-forge-1-click', '/getting-started/installing/laravel-forge-1-click');\nRoute::permanentRedirect('deploying/laravel-forge', '/getting-started/deploying/laravel-forge');\nRoute::permanentRedirect('deploying/workflow', '/knowledge-base/tips/git-workflow');\nRoute::permanentRedirect('installing/laravel-herd', '/getting-started/installing/laravel-herd');\nRoute::permanentRedirect('installing/laravel', '/getting-started/installing/laravel');\nRoute::permanentRedirect('licensing', '/getting-started/licensing');\nRoute::permanentRedirect('installing/linode', '/getting-started/installing/linode');\nRoute::permanentRedirect('live-preview', '/control-panel/live-preview');\nRoute::permanentRedirect('installing/local', '/getting-started/installing/local');\nRoute::permanentRedirect('modifiers', '/modifiers/overview');\nRoute::permanentRedirect('multi-site', '/control-panel/multi-site');\nRoute::permanentRedirect('multi-user-collaboration', '/control-panel/multi-user-collaboration');\nRoute::permanentRedirect('navigation', '/content-modeling/navigation');\nRoute::permanentRedirect('deploying/netlify', '/getting-started/deploying/netlify');\nRoute::permanentRedirect('oauth', '/advanced-topics/oauth');\nRoute::permanentRedirect('deploying/ploi', '/getting-started/deploying/ploi');\nRoute::permanentRedirect('preferences', '/control-panel/preferences');\nRoute::permanentRedirect('protecting-content', '/frontend/protecting-content');\nRoute::permanentRedirect('quick-start-guide', '/getting-started/quick-start-guide');\nRoute::permanentRedirect('recent-updates', '/');\nRoute::permanentRedirect('relationships', '/content-modeling/relationships');\nRoute::permanentRedirect('release-schedule-support-policy', '/knowledge-base/release-schedule-support-policy');\nRoute::permanentRedirect('requirements', '/getting-started/requirements');\nRoute::permanentRedirect('rest-api', '/frontend/rest-api');\nRoute::permanentRedirect('revisions', '/content-modeling/revisions');\nRoute::permanentRedirect('routing', '/content-modeling/routing');\nRoute::permanentRedirect('scheduling', '/advanced-topics/scheduling');\nRoute::permanentRedirect('search', '/frontend/search');\nRoute::permanentRedirect('sites-api', '/advanced-topics/sites-api');\nRoute::permanentRedirect('stache', '/advanced-topics/stache');\nRoute::permanentRedirect('static-caching', '/advanced-topics/static-caching');\nRoute::permanentRedirect('structures', '/content-modeling/structures');\nRoute::permanentRedirect('tags', '/tags/all-tags');\nRoute::permanentRedirect('taxonomies', '/content-modeling/taxonomies');\nRoute::permanentRedirect('tips', '/knowledge-base/tips');\nRoute::permanentRedirect('troubleshooting', '/knowledge-base/troubleshooting');\nRoute::permanentRedirect('installing/ubuntu', '/getting-started/installing/ubuntu');\nRoute::permanentRedirect('updating', '/getting-started/updating');\nRoute::permanentRedirect('upgrade-guide', '/getting-started/upgrade-guide');\nRoute::permanentRedirect('users', '/control-panel/users');\nRoute::permanentRedirect('upgrade-guide/v2-to-v3', '/getting-started/upgrade-guide/v2-to-v3');\nRoute::permanentRedirect('validation', '/content-modeling/validation');\nRoute::permanentRedirect('deploying/vercel', '/getting-started/deploying/vercel');\nRoute::permanentRedirect('view-models', '/frontend/view-models');\nRoute::permanentRedirect('views', '/frontend/views');\nRoute::permanentRedirect('upgrade-guide/vue-2-to-3', '/getting-started/upgrade-guide/vue-2-to-3');\nRoute::permanentRedirect('white-labeling', '/control-panel/white-labeling');\nRoute::permanentRedirect('widgets', '/widgets/overview');\nRoute::permanentRedirect('yaml', '/advanced-topics/yaml');\nRoute::permanentRedirect('reference/fieldtypes', '/fieldtypes/all-fieldtypes');\nRoute::permanentRedirect('reference/modifiers', '/modifiers/all-modifiers');\nRoute::permanentRedirect('reference/repositories', '/backend-apis/resource-apis');\nRoute::permanentRedirect('reference/tags', '/tags/all-tags');\nRoute::permanentRedirect('reference/variables', '/variables/all-variables');\nRoute::permanentRedirect('reference/widgets', '/widgets/all-widgets');\nRoute::permanentRedirect('repositories/asset-container-repository', '/backend-apis/resource-apis/asset-container-repository');\nRoute::permanentRedirect('repositories/asset-repository', '/backend-apis/resource-apis/asset-repository');\nRoute::permanentRedirect('repositories/collection-repository', '/backend-apis/resource-apis/collection-repository');\nRoute::permanentRedirect('repositories/entry-repository', '/backend-apis/resource-apis/entry-repository');\nRoute::permanentRedirect('repositories/form-repository', '/backend-apis/resource-apis/form-repository');\nRoute::permanentRedirect('repositories/form-submission-repository', '/backend-apis/resource-apis/form-submission-repository');\nRoute::permanentRedirect('repositories/global-repository', '/backend-apis/resource-apis/global-repository');\nRoute::permanentRedirect('repositories/site-repository', '/backend-apis/resource-apis/site-repository');\nRoute::permanentRedirect('repositories/taxonomy-repository', '/backend-apis/resource-apis/taxonomy-repository');\nRoute::permanentRedirect('repositories/term-repository', '/backend-apis/resource-apis/term-repository');\nRoute::permanentRedirect('repositories/user-group-repository', '/backend-apis/resource-apis/user-group-repository');\nRoute::permanentRedirect('repositories/user-repository', '/backend-apis/resource-apis/user-repository');\nRoute::permanentRedirect('repositories/user-role-repository', '/backend-apis/resource-apis/user-role-repository');\nRoute::permanentRedirect('screencasts/creating-your-first-user', 'https://www.youtube.com/watch?v=KuiPocGq3L8');\nRoute::permanentRedirect('screencasts/home-page-templates-layouts', 'https://www.youtube.com/watch?v=leI3qRhzHLQ');\nRoute::permanentRedirect('screencasts/installation', 'https://www.youtube.com/watch?v=zuKZQNUYSf8');\nRoute::permanentRedirect('screencasts/npm-mix-tailwind', 'https://www.youtube.com/watch?v=FJMPeyvmVUE');\nRoute::permanentRedirect('screencasts/building-a-nav', 'https://www.youtube.com/watch?v=POgIsLeWGGQ');\nRoute::permanentRedirect('screencasts/index-page', 'https://www.youtube.com/watch?v=N_FO6xrBEtY');\nRoute::permanentRedirect('screencasts/partials', 'https://www.youtube.com/watch?v=Ddz6mD-jT7E');\nRoute::permanentRedirect('screencasts/related-entries', 'https://www.youtube.com/watch?v=WWbsM5u9afc');\nRoute::permanentRedirect('screencasts/scaffolding-a-collection', 'https://www.youtube.com/watch?v=WtelkWCDqE4');\nRoute::permanentRedirect('screencasts/show-page', 'https://www.youtube.com/watch?v=tHLhZskiblc');\nRoute::permanentRedirect('screencasts/the-blueprint', 'https://www.youtube.com/watch?v=cs_jL6fCaA8');\nRoute::permanentRedirect('screencasts/adding-custom-taxonomy-fields', 'https://www.youtube.com/watch?v=DNU8yH9dnro');\nRoute::permanentRedirect('screencasts/taxonomies-overview', 'https://www.youtube.com/watch?v=vssXeEC118M');\nRoute::permanentRedirect('screencasts/taxonomies-templates', 'https://www.youtube.com/watch?v=wQTqcYRLQbM');\nRoute::permanentRedirect('screencasts/search-forms-and-results', 'https://www.youtube.com/watch?v=WJmEVjJYmCY');\nRoute::permanentRedirect('extending', '/');\nRoute::permanentRedirect('guides', '/');\nRoute::permanentRedirect('reference', '/');\nRoute::permanentRedirect('screencasts', 'https://www.youtube.com/statamic');\nRoute::permanentRedirect('troubleshooting/asset-permissions', '/knowledge-base/troubleshooting/asset-permissions');\nRoute::permanentRedirect('troubleshooting/composer-and-github-authentication', '/knowledge-base/troubleshooting/composer-and-github-authentication');\nRoute::permanentRedirect('troubleshooting/fixing-issues-with-global-composer-packages', '/knowledge-base/troubleshooting/fixing-issues-with-global-composer-packages');\nRoute::permanentRedirect('troubleshooting/listing-performance', '/knowledge-base/troubleshooting/listing-performance');\nRoute::permanentRedirect('troubleshooting/missing-control-panel-assets', '/knowledge-base/troubleshooting/missing-control-panel-assets');\nRoute::permanentRedirect('troubleshooting/command-not-found-statamic', '/knowledge-base/troubleshooting/command-not-found-statamic');\nRoute::permanentRedirect('troubleshooting/assets-missing-urls', '/knowledge-base/troubleshooting/assets-missing-urls');\nRoute::permanentRedirect('troubleshooting/email-not-sending', '/knowledge-base/troubleshooting/email-not-sending');\nRoute::permanentRedirect('troubleshooting/control-panel-page-expired', '/knowledge-base/troubleshooting/control-panel-page-expired');\nRoute::permanentRedirect('troubleshooting/required-php-extensions', '/knowledge-base/troubleshooting/required-php-extensions');\n\nRoute::permanentRedirect('collections-and-entries', '/content-modeling/collections');\nRoute::permanentRedirect('template-engines', '/frontend/blade');\nRoute::permanentRedirect('entries', '/content-modeling/collections#entries');\nRoute::permanentRedirect('git-integration', '/control-panel/git-automation');\nRoute::permanentRedirect('installation', '/getting-started/installing');\nRoute::permanentRedirect('blade-templates', '/frontend/blade');\nRoute::permanentRedirect('fieldtypes/partial', '/content-modeling/blueprints#importing-fieldsets');\nRoute::permanentRedirect('cascade', '/content-modeling/data-inheritance');\nRoute::permanentRedirect('content-api', '/frontend/rest-api');\nRoute::permanentRedirect('using-front-end-frameworks', '/frontend/javascript-frameworks');\nRoute::permanentRedirect('content-queries/{slug}', '/backend-apis/resource-apis/{slug}');\nRoute::permanentRedirect('repositories', '/backend-apis/resource-apis');\nRoute::permanentRedirect('new-antlers-parser', '/frontend/antlers');\nRoute::permanentRedirect('tips/storing-entries-in-a-database', '/knowledge-base/tips/storing-content-in-a-database');\nRoute::permanentRedirect('account-api-sites', '/advanced-topics/sites-api');"
  },
  {
    "path": "routes/web.php",
    "content": "<?php\n\nuse App\\Http\\Controllers\\LlmsTxtController;\nuse App\\Http\\Controllers\\DocsMarkdownController;\nuse Statamic\\Facades\\Entry;\n\nRoute::get('llms.txt', LlmsTxtController::class);\nRoute::get('{any}.md', DocsMarkdownController::class)->where('any', '.*');\n\nRoute::statamic('search-results', 'search', ['hide_sidebar' => true]);\nRoute::statamic('sitemap.xml', 'sitemap', ['content_type' => 'xml', 'layout' => 'sitemap']);\n\nRoute::get('versions.json', fn () => config('docs.versions'));\n\nRoute::get('/from/{id?}', function ($id = null) {\n    if (! $id) {\n        return redirect()->to('/');\n    }\n\n    $entry = Entry::find($id);\n\n    if (! $entry) {\n        return redirect()->to('/');\n    }\n\n    return redirect()->to($entry->url());\n});\n\nrequire __DIR__.'/redirects.php';\n"
  },
  {
    "path": "server.php",
    "content": "<?php\n\n/**\n * Laravel - A PHP Framework For Web Artisans\n *\n * @author   Taylor Otwell <taylor@laravel.com>\n */\n$uri = urldecode(\n    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)\n);\n\n// This file allows us to emulate Apache's \"mod_rewrite\" functionality from the\n// built-in PHP web server. This provides a convenient way to test a Laravel\n// application without having installed a \"real\" web server software here.\nif ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {\n    return false;\n}\n\nrequire_once __DIR__.'/public/index.php';\n"
  },
  {
    "path": "storage/app/.gitignore",
    "content": "*\n!public/\n!.gitignore\n"
  },
  {
    "path": "storage/framework/.gitignore",
    "content": "config.php\nroutes.php\nschedule-*\ncompiled.php\nservices.json\nevents.scanned.php\nroutes.scanned.php\ndown\n"
  },
  {
    "path": "storage/framework/cache/.gitignore",
    "content": "*\n!data/\n!.gitignore\n"
  },
  {
    "path": "storage/framework/sessions/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "storage/framework/testing/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "storage/framework/views/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "storage/logs/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "storage/statamic/.gitignore",
    "content": "*\n!.gitignore"
  },
  {
    "path": "tests/CreatesApplication.php",
    "content": "<?php\n\nnamespace Tests;\n\nuse Illuminate\\Contracts\\Console\\Kernel;\nuse Illuminate\\Foundation\\Application;\n\ntrait CreatesApplication\n{\n    /**\n     * Creates the application.\n     */\n    public function createApplication(): Application\n    {\n        $app = require __DIR__.'/../bootstrap/app.php';\n\n        $app->make(Kernel::class)->bootstrap();\n\n        return $app;\n    }\n}\n"
  },
  {
    "path": "tests/Feature/ExampleTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\n// use Illuminate\\Foundation\\Testing\\RefreshDatabase;\nuse Tests\\TestCase;\n\nclass ExampleTest extends TestCase\n{\n    /**\n     * A basic test example.\n     */\n    public function test_the_application_returns_a_successful_response(): void\n    {\n        $response = $this->get('/');\n\n        $response->assertStatus(200);\n    }\n}\n"
  },
  {
    "path": "tests/TestCase.php",
    "content": "<?php\n\nnamespace Tests;\n\nuse Illuminate\\Foundation\\Testing\\TestCase as BaseTestCase;\n\nabstract class TestCase extends BaseTestCase\n{\n    use CreatesApplication;\n}\n"
  },
  {
    "path": "tests/Unit/ExampleTest.php",
    "content": "<?php\n\nnamespace Tests\\Unit;\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass ExampleTest extends TestCase\n{\n    /**\n     * A basic test example.\n     */\n    public function test_that_true_is_true(): void\n    {\n        $this->assertTrue(true);\n    }\n}\n"
  },
  {
    "path": "vite.config.js",
    "content": "import { defineConfig } from 'vite';\nimport laravel from 'laravel-vite-plugin';\nimport tailwindcss from \"@tailwindcss/vite\";\nimport vue from '@vitejs/plugin-vue';\nimport path from 'path';\n\nexport default defineConfig({\n    plugins: [\n        tailwindcss(),\n        laravel({\n            input: [\n                \"resources/css/style.css\",\n                \"resources/js/site.js\",\n            ],\n        }),\n        vue(),\n    ],\n    server: {\n        fs: {\n            allow: [\"..\"],\n        },\n    },\n    css: {\n        devSourcemap: true,\n    },\n    resolve: {\n        alias: {\n            vue: 'vue/dist/vue.esm-bundler.js',\n            '@': path.resolve(__dirname, 'vendor/statamic/cms/resources/js')\n        },\n    }\n});\n"
  }
]