diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md new file mode 100644 index 000000000..af2f2fb92 --- /dev/null +++ b/content/collections/docs/5-to-6.md @@ -0,0 +1,342 @@ +--- +id: 9a013ab0-bd21-42e1-84ea-fecd052466e9 +blueprint: page +title: 'Upgrade from 5 to 6' +intro: 'A guide for upgrading from 5 to 6. For most sites (those running Laravel > 10), the process will take less than 5 minutes.' +template: page +--- +## Overview + +First 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. + +### Upgrade using Composer + +In your `composer.json`, change the `statamic/cms` requirement: + +```json +"statamic/cms": "^5.0" // [tl!--] +"statamic/cms": "^6.0" // [tl!++] +``` + +Then run: + +``` shell +composer update statamic/cms --with-dependencies +``` + +## High impact changes + +### PHP and Laravel support +**Affects apps using PHP < 8.2 or Laravel < 11.** + +- The minimum version of PHP is now 8.2. +- The minimum version of Laravel is now 11. + +We highly recommend upgrading all the way to Laravel 12 and PHP 8.4. + +:::tip +If 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 🤘). +::: + +### Vue 3 +**Affects apps or addons that use Vue.** + +We have upgraded the Control Panel's version of Vue.js from 2 to 3. + +To keep this upgrade guide manageable, we have a [dedicated page for upgrading from Vue 2 to Vue 3](/upgrade-guide/vue-2-to-3). + +If you do not have any custom Vue components in your app, or in your own addons, you can skip this. + +### Timezones +**Affects apps using dated collections or date fields** + +**If your `timezone` setting in `config/app.php` is set to `UTC`, then nothing will change for you.** + +Dates 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. + +This applies to dated entries or date fields. + +For 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! + +```php +// config/app.php +'timezone' => 'America/New_York', +``` +```yaml +# an-entry.md +my_date_field: '2025-03-06 22:00' +``` +```php +$entry->my_date_field; +// 5.x: Carbon { 2025-03-06 22:00 America/New_York } [tl! --] +// 6.x: Carbon { 2025-03-07 03:00 UTC } [tl! ++] +``` + +::tabs +::tab antlers +```antlers +{{ my_date_field | iso_format('JJJJ') }} +5.x: Thursday, March 6, 2025 10:00 PM {{# [tl! --] #}} +6.x: Friday, March 7, 2025 3:00 AM {{# [tl! ++] #}} +``` +::tab blade +```blade +{{ Statamic::modify($my_date_field)->iso_format('JJJJ') }} +5.x: Thursday, March 6, 2025 10:00 PM {{-- [tl! --] --}} +6.x: Friday, March 7, 2025 3:00 AM {{-- [tl! ++] --}} +``` +:: + +It'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`: + +```php +'localize_dates_in_modifiers' => true, // [tl! ++] +``` + +This settings _should_ have been automatically set to `true` by Statamic during the upgrade, but you should confirm it. + +#### Control Panel +Dates in the Control Panel are now localized to the user's operating system timezone, rather than the application timezone. + +For 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. + +This was a common cause of confusion, which was one of the main reasons for all these changes. + +Now in Statamic 6, the date you pick will be the date in **your** timezone. + +There is nothing for you to change except your expectations when working with dates, and instructing your clients about it. + +#### REST API & GraphQL +Dates will now be returned by Statamic's REST API and GraphQL API in UTC, allowing you to localize them as needed on your frontend. + +#### Changing your app timezone +It'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. + +Statamic 6 **does not** require that you change your timezone to UTC. But if you *want* to, we have provided a way to automate it. + +[Read how to change your timezone to UTC](/tips/change-timezone-to-utc). + +## Medium impact changes + +### Carbon 3 + +Support 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). + +If 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. + +You _may_ need to updates your templates to account for these changes. + +### Globals + +We 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. + +#### Single site installs: Variables are now stored separately from the global set config +Global Variables are now stored separately from the global set's config, allowing config and content to be properly separated. + +Instead 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`. + +**Before:** +```yaml +# content/globals/seo.yaml + +title: SEO +data: + meta_description: Synthwave nostalgia with soaring sax and heartfelt vibes. + meta_image: the-midnight.jpg +``` + +**After:** +```yaml +# content/globals/seo.yaml + +title: SEO +``` + +```yaml +# content/globals/default/seo.yaml + +meta_description: Synthwave nostalgia with soaring sax and heartfelt vibes. +meta_image: the-midnight.jpg +``` + +_This change may have been performed automatically by Statamic during the upgrade process._ + +**Note:** This change _doesn't_ affect multi-sites or sites storing global variables in the database, since they're already stored separately. + +#### Multi-sites: Localized sites are now determined by the `sites` array +Previously, 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. + +Now, 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. + +```yaml +# content/globals/seo.yaml + +title: SEO +sites: + en: null + fr: en # Localized from en + de: null # No origin +``` + +_This change may have been performed automatically by Statamic during the upgrade process._ + +**Note:** This change _doesn't_ affect single-site installs. + +#### Events +Previously, 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. + +This 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.test/extending/events#globalvariablessaving), [`GlobalVariablesCreated`](https://statamic.dev.test/extending/events#globalvariablescreated) and [`GlobalVariablesSaved`](https://statamic.dev.test/extending/events#globalvariablessaved) events. + +#### Removed methods on `GlobalSet` class +The `addLocalization` and `removeLocalization` methods have been removed from the `GlobalSet` class. + +If you were calling these methods in your app, you should update your code to call `save` and `delete` on the `Variables` class instead. + +```php +$globalSet->addLocalization($globalSet->makeLocalization('en')->data(['foo' => 'bar'])); // [tl! remove] +$globalSet->removeLocalization('en'); // [tl! remove] + +$globalSet->in('en')->data(['foo' => 'bar'])->save(); // [tl! add] +$globalSet->in('en')->delete(); // [tl! add] +``` + +## Low impact changes + +### Added columns to the `users` table +**Affects apps storing users in the database.** +If you're storing users in the database, you will need to add three columns to the `users` table in order to support Statamic's [two-factor authentication feature](/users#two-factor-authentication). You can add the columns using a migration: + +```php +text('two_factor_secret')->nullable(); + $table->text('two_factor_recovery_codes')->nullable(); + $table->timestamp('two_factor_confirmed_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn(['two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at']); + }); + } +}; +``` + +You can run the migration by running `php artisan migrate`. + +You should also add a cast for the `two_factor_confirmed_at` column in your `User` model: + +```php +protected function casts(): array +{ + return [ + 'email_verified_at' => 'datetime', + 'preferences' => 'json', + 'two_factor_confirmed_at' => 'datetime', // [tl! add] + ]; +} +``` + +_This change may have been performed automatically by Statamic during the upgrade process._ + +### Moment.js has been removed +**Affects addons and custom code directly using moment.js** +If 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: + +```js +moment().seconds(); // [tl! --] +new Date().getSeconds(); // [tl! ++] +``` + +You should search for `moment` or `$moment` references in your code and replace appropriately. + +[Here is a good resource](https://github.com/you-dont-need/You-Dont-Need-Momentjs) on how to migrate away from Moment.js. + +### Wildcard tags +**Affects apps using the `{{ session }}`, `{{ cookie }}`, `{{ nav }}` and `{{ redirect }}` tags.** + +In previous versions of Statamic, these tags accepted a wildcard value, allowing you to pass a handle or key directly: + +```antlers +{{ session:foo }} +``` + +Here, `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. + +In 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: + +```antlers +{{ session :handle="foo" }} +``` + +This ensures that `foo` is interpreted as a variable rather than a fixed string. + +### Site methods +**Affects addons using the `Site::setConfig()` method.** + +The `Site::setConfig()` method was deprecated in Statamic 5. It has now been removed. You should use the `Site::setSites()` method instead: + +```php +Site::setConfig([ // [tl! remove:5] + 'sites' => [ + 'english' => ['name' => 'English', 'locale' => 'en_US', 'url' => '/en'], + 'french' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => '/fr'], + ], +]); + +Site::setSites([ // [tl! add:3] + 'english' => ['name' => 'English', 'locale' => 'en_US', 'url' => '/en'], + 'french' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => '/fr'], +]); +``` + +### CP Nav Items: `->active()` method removed +**Affects apps or addons adding nav items to the Control Panel.** + +When 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. However, 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: + +```php +Nav::extend(function ($nav) { + $nav->create(ucfirst($type)) + ->section('SEO') + ->route("ecommerce.orders.index") + ->active("ecommerce/orders") // [tl! --] + ->icon($defaults->first()['type_icon']); +}); +``` + +### Relate tag has been removed + +The `relate` tag left over from Statamic 2 has been removed. You can safely remove it and rely on [augmentation](/augmentation) instead. + +```antlers +{{ relate:products }} {{# [tl! remove:2] #}} + {{ title }} +{{ /relate:products }} + +{{ products }} {{# [tl! add:2] #}} + {{ title }} +{{ /products }} +``` + +## Zero impact changes + +... + diff --git a/content/collections/docs/requirements.md b/content/collections/docs/requirements.md index 97b7939b3..3ccdfa8f4 100644 --- a/content/collections/docs/requirements.md +++ b/content/collections/docs/requirements.md @@ -9,7 +9,7 @@ blueprint: page To run Statamic you'll need a server meeting the following requirements. These are standard defaults (at minimum) for most modern hosting platforms. -- PHP 8.1 or above +- PHP 8.2 or above - BCMath PHP Extension - Ctype PHP Extension - Exif PHP Extension diff --git a/content/collections/docs/ubuntu.md b/content/collections/docs/ubuntu.md index 6d672d259..e3d940d56 100644 --- a/content/collections/docs/ubuntu.md +++ b/content/collections/docs/ubuntu.md @@ -13,7 +13,7 @@ To install Statamic on an Ubuntu instance you will need the following: - An Ubuntu 22.04 or 20.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) - A server with at least 1GB memory - A valid domain name pointed to your server and SSL certificate in place -- PHP 8.1+ +- PHP 8.2+ ## Update Packages diff --git a/content/collections/docs/upgrade-guide.md b/content/collections/docs/upgrade-guide.md index 348d0cc17..f5517422b 100644 --- a/content/collections/docs/upgrade-guide.md +++ b/content/collections/docs/upgrade-guide.md @@ -5,6 +5,7 @@ template: page id: f12f8ba3-19ff-48cb-a07b-653b05082d7e blueprint: page --- +- [5.0 to 6.0](/upgrade-guide/5-to-6) - [4.0 to 5.0](/upgrade-guide/4-to-5) - [3.4 to 4.0](/upgrade-guide/3-4-to-4-0) - [3.3 to 3.4](/upgrade-guide/3-3-to-3-4) diff --git a/content/collections/docs/users.md b/content/collections/docs/users.md index f5c4dc487..d12c212ed 100644 --- a/content/collections/docs/users.md +++ b/content/collections/docs/users.md @@ -223,3 +223,37 @@ You can configure impersonation in `config/statamic/users.php`, like setting the In 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/). Learn how to [configure OAuth](/oauth) on your site. + +## Two-Factor Authentication + +Statamic includes first-party support for two-factor authentication (2FA), providing an extra layer of account security. + +Once 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. + +To 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. + +2FA is optional by default, but you can enforce it for specific roles via configuration: + +```php +// config/statamic/users.php + +'two_factor' => [ + 'enforced_roles' => [ + // Enforce for everyone + '*', + + // Enforce for super users + 'super_users', + + // Enforce for a specific role + 'marketing_managers', + 'user_admin', + ], +], +``` + +:::warning +Statamic uses your `APP_KEY` to encrypt the two-factor authentication secret and recovery codes. + +You 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). +::: diff --git a/content/collections/docs/vue-2-to-3.md b/content/collections/docs/vue-2-to-3.md new file mode 100644 index 000000000..efc457d72 --- /dev/null +++ b/content/collections/docs/vue-2-to-3.md @@ -0,0 +1,305 @@ +--- +id: c3553d05-d1a8-453a-a59b-7e67dd2412a4 +blueprint: page +title: 'Upgrade from Vue 2 to Vue 3' +intro: 'A guide for upgrading Vue 2 to 3.' +template: page +--- +## Overview + +As part of the Statamic 6 release, Vue was upgraded to version 3. + + +## Vite + +```shell +npm remove @vitejs/plugin-vue2 +npm install -D @vitejs/plugin-vue +npm install -D vite-plugin-externals +``` + +```js +import path from 'path'; // [tl! ++] +import laravel from 'laravel-vite-plugin' +import { defineConfig, loadEnv } from 'vite' +import vue from '@vitejs/plugin-vue2'; // [tl! --] +import vue from '@vitejs/plugin-vue'; // [tl! ++] +import { viteExternalsPlugin } from 'vite-plugin-externals' // [tl! ++] + +export default defineConfig(({ command, mode }) => { + const env = loadEnv(mode, process.cwd(), ''); + return { + plugins: [ + laravel({ + refresh: true, + input: ['resources/js/cp.js'] + }), + vue(), + viteExternalsPlugin({vue: 'Vue', pinia: 'Pinia'}) // [tl! ++] + ], + resolve: { // [tl! ++:start] + alias: { + 'statamic': path.resolve(__dirname, './vendor/statamic/cms/resources/js/exports.js'), + }, + } // [tl! ++:end] + } +}); +``` + +## Fieldtypes + +### Mixins + +Mixins will now need to be explicitly imported. Assuming you've added `resolve.alias.statamic` to your `vite.config.js` explained above, you should be able to add the following to your fieldtype: + +```js +import { Fieldtype } from 'statamic'; // [tl! ++] + +export default { + mixins: [Fieldtype], + data() { + return { + // + } + } +} +``` + +### Events + +If you are manually emitting an `input` event from within a fieldtype, you should change it to `update:value`. + +```js +this.$emit('input', foo); // [tl! --] +this.$emit('update:value', foo); // [tl! ++] +``` + +Tip: you should try to instead use `this.update()`. + +```js +this.$emit('input', foo); // [tl! --] +this.update(foo); // [tl! ++] +``` + + + +## Props, events, and v-model + +Vue 3 changes how v-model works. + + +### Fieldtypes +To 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. + +Note that this is only if you are _using a fieldtype component_ from within another component. + +```vue + + v-model:value="foo" +/> +``` + +### Components that no longer support v-model + +If you were using `v-model`, you must change to the appropriate prop and event: + +```vue + + :value="foo" + @input="foo = $event" +/> +``` + +| Component | Prop | Event | +|-------------------------|-----------|-------------| +| `` | `to` | `@slugified` | +| `` | `values` | `@updated` | + +### Components that support v-model +If 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`. + +```vue + + @input="foo = $event" + :model-value="foo" + @update:model-value="foo = $event" +/> +``` + +| Component | Notes | +|----------------|--------------------------------------| +| `` | | +| `` | This is from the vue-select package. | + +## Slots + + +## Dropdown List + +## Bard + +Since 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. + +You might be using these methods if you have a custom Bard toolbar button (via `this.bard`) or Prosemirror mark/node element (via `vm`). + +```js +bard.$on(...); // [tl! --] +bard.$off(...); // [tl! --] +bard.$once(...); // [tl! --] +bard.events.on(...); // [tl! ++] +bard.events.off(...); // [tl! ++] +bard.events.once(...); // [tl! ++] +``` + +## Fieldtypes + +Components should be registered using the `$components` API rather than directly through `Statamic`: + +```js +Statamic.component('my-fieldtype', MyFieldtype); // [tl! --] +Statamic.$component.register('my-fieldtype', MyFieldtype); // [tl! ++] +``` + +## Vuex to Pinia + +Vuex has been removed in favor of Pinia. The `store` itself will now be provided to components. + +```js +{ + inject: [ + 'storeName', // [tl! --] + 'store', // [tl! ++] + ], + methods: { + myMethod() { + const values = this.$store.state.publish[this.storeName].values; // [tl! --] + const values = this.store.values; // [tl! ++] + } + } +} +``` + +If you were dispatching actions or committing mutations, you will now call methods on the `store` directly. + +```js +this.$store.dispatch(`publish/${this.storeName}/doSomething`), arg); // [tl! --] +this.store.doSomething(arg); // [tl! ++] + + +this.$store.commit(`publish/${this.storeName}/doSomething`), arg); // [tl! --] +this.store.doSomething(arg); // [tl! ++] +``` + +If you were adding your own Vue 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. + +```js +// In bootstrapping... [tl! --:start] +Statamic.$store.registerModule(['publish', 'myStore'], { + state: { foo: 'bar' }, + mutations: { + doSomething(payload) { + // + } + }, + actions: { + doSomething(context, payload) { + context.commit('doSomething', payload); + } + } +}); + +// In component... +const foo = this.$store.state.publish.myStore.foo; +this.$store.dispatch('publish/myStore/doSomething', 'example'); // [tl! --:end] + + +// mystore.js... [tl! ++:start] +import { defineStore } from 'pinia'; +const useMyStore = defineStore('myStore', { + state: { foo: 'bar' }, + actions: { + doSomething() { + // + } + } +}); + +// In component... +import { useMyStore } from './mystore'; +const store = useMyStore(); +const foo = store.foo; +store.doSomething('example'); // [tl! ++:end] +``` + +### Field actions + +Similarly, field actions were previously provided with the Vuex store through the `store` property. Now it will be an instance of the Pinia store itself. + +```js +Statamic.$fieldActions.add('text-fieldtype', { + title: 'Example', + run: ({ store, storeName }) => { + const values = store.state.publish[storeName].values; // [tl! --] + const values = store.values; // [tl! ++] + } +}) +``` + +## Bard Tiptap API + +Previously 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: + +```js +const { Node, Mark, Extension } = Statamic.$bard.tiptap.core; // [tl! --:start] + +Statamic.$bard.addExtension(() => { + return [ + Node.create({...}), + Mark.create({...}), + Extension.create({...}), + ] +}) // [tl! --:end] + +Statamic.$bard.addExtension(({ tiptap }) => { // [tl! ++:start] + const { Node, Mark, Extension } = tiptap.core; + + return [ + Node.create({...}), + Mark.create({...}), + Extension.create({...}), + ] +}) // [tl! ++:end] +``` + +If you were importing/exporting the component, you should change to a "factory" function that accepts the Tiptap API and returns the component. For example: + +```js +import TextColor from './TextColor.js'; // [tl! --:start] +Statamic.$bard.addExtension(() => TextColor); + +// TextColor.js +const { Mark } = Statamic.$bard.tiptap.core; +export default Mark.create({}); // [tl! --:end] + +import TextColor from './TextColor.js'; // [tl! ++:start] +Statamic.$bard.addExtension(({ tiptap }) => TextColor(tiptap)); + +// TextColor.js +export default function (tiptap) { + const { Mark } = tiptap.core; + return Mark.create({}); +} // [tl! ++:end] +``` + +## Removals + +A 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. + +- We had a `String.includes()` polyfill that has been removed since it's widely supported now. +- Underscore.js mixins `objMap`, `objFilter`, and `objReject` have been removed. +- `resource_url` and `file_icon` methods are no longer available in Vue templates but are still available as global functions. +- The deprecated `$slugify` function has been removed in favor of the `$slug` API. +- The `v-focus` directive has been removed. diff --git a/content/collections/extending-docs/addons.md b/content/collections/extending-docs/addons.md index da16468b8..578d9de42 100644 --- a/content/collections/extending-docs/addons.md +++ b/content/collections/extending-docs/addons.md @@ -189,7 +189,7 @@ After the composer package has been brought in, Statamic will automatically acti ### Post-install commands {#post-install-commands} -By default the `vendor:publish` command will be run for you after `statamic:install`, letting your assets be automatically published. +By default, the `vendor:publish` command will be run for you after `statamic:install`, letting your assets be automatically published. However, you can run other commands or custom code too using the `afterInstalled` method: @@ -205,15 +205,9 @@ public function bootAddon() ## Registering Components -::: tip -Statamic v5.28.0 and v5.29.0 introduced the concept of "autoloading" for most addon components. +Statamic will autoload _most_ of your addon's components, as long as they're in the right place and named correctly. -For example: as long as your tags live in `src/Tags` and your fieldtypes live in `src/Fieldtypes`, they will be automatically registered by Statamic, without you needing to register them manually. - -If your addon supports Statamic v5.28.0 or lower, you should continue registering components manually, like shown below. Otherwise, you can let Statamic do its thing 😎. -::: - -You may register your various addon components by adding their class names to corresponding arrays: +However, you can still register them manually in your service provider if you need to: ``` php protected $tags = [ @@ -237,7 +231,6 @@ protected $widgets = [ protected $commands = [ // ]; - ``` ## Assets @@ -320,7 +313,24 @@ ln -s /path/to/addons/example/resources public/vendor/package ### Registering Routes -You may register three types of routes in your service provider. +Addons can register three types of routes: + +* Control Panel routes +* Action routes +* Web routes + +To keep things organized, we recommend keeping your routes in separate files. + +``` files theme:serendipity-light +/ + src/ + routes/ + cp.php + actions.php + web.php +``` + +If 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: ``` php protected $routes = [ @@ -330,12 +340,6 @@ protected $routes = [ ]; ``` -::: tip -As of Statamic v5.29.0, addon routes following the convention shown above will be automatically registered by Statamic. - -If your addon supports Statamic v5.29 or lower, you should continue registering your route files manually, like shown below. Otherwise, you can let Statamic do its thing 😎. -::: - #### Control Panel Routes Control Panel routes will be automatically prefixed by `/cp` (or whatever URL the control panel has been configured to use) and will have authorization applied. @@ -363,7 +367,7 @@ When referencing a controller in a route, it will automatically be namespaced to ``` ``` php -Route::get('/', 'ExampleController@index'); // Acme\Example\ExampleController +Route::get('/', [ExampleController::class, 'index']); // Acme\Example\ExampleController ``` If you'd prefer not to have separate route files, you can write routes in your service provider's `bootAddon` method. @@ -458,31 +462,37 @@ return view('custom::foo'); ## Events -::: tip -Statamic v5.35.0 introduced the concept of "autoloading" for event listeners. +Statamic 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. -As long as your event listener lives in `src/Listeners` and the event is typehinted in the listener's `handle` or `__invoke` method, they will be automatically registered by Statamic, without you needing to register them manually. +``` php +use Acme\Example\Events\OrderShipped; -Subscribers will also be autoloaded, as long as they live in `src/Subscribers`. +class SendShipmentNotification +{ + public function handle(OrderShipped $event) + { + // + } +} +``` -If your addon supports below Statamic v5.33.0, you should continue registering events and subscribers manually, like shown below. Otherwise, you can let Statamic do its thing 😎. -::: +Subscribers will also be autoloaded, as long as they live in `src/Subscribers`. -You may register any number of event listeners or subscribers the same way you would in a traditional Laravel application's EventServiceProvider – by using a `$listen` or `$subscribes` array: +If your addon's listeners or subscribers live elsewhere, you may register them manually in your service provider: ``` php protected $listen = [ - 'Acme\Example\Events\OrderShipped' => [ - 'Acme\Example\Listeners\SendShipmentNotification', + \Acme\Example\Events\OrderShipped::class => [ + \Acme\Example\Listeners\SendShipmentNotification::class, ], ]; protected $subscribe = [ - 'Acme\Example\Listeners\UserEventSubscriber', + \Acme\Example\Listeners\UserEventSubscriber::class, ]; ``` -Consult the [Laravel event documentation](https://laravel.com/docs/events) to learn how to define events, listeners, and subscribers. +To learn more about defining events, listeners and subscribers, please consult the [Laravel event documentation](https://laravel.com/docs/events). ## Scheduling @@ -543,15 +553,9 @@ You don't need to check whether a license is valid, Statamic does that automatic You may register update scripts to help your users migrate data, etc. when new features are added or breaking changes are introduced. -For example, maybe you've added a new permission and want to automatically give all of your existing form admins that new permission. To do this, first register your update script in your addon's service provider: - -``` php -protected $updateScripts = [ - \Acme\Example\Updates\UpdatePermissions::class, -]; -``` +For example, maybe you've added a new permission and want to automatically give all of your existing form admins that new permission. -Then in your update script, you can extend `UpdateScript` and implement the necessary methods: +To do this, create a class which extends the `UpdateScript` class and implement the necessary methods: ``` php use Statamic\UpdateScripts\UpdateScript; diff --git a/content/collections/extending-docs/events.md b/content/collections/extending-docs/events.md index 4c9512041..f259161bd 100644 --- a/content/collections/extending-docs/events.md +++ b/content/collections/extending-docs/events.md @@ -1258,6 +1258,66 @@ public function handle(TermSaved $event) } ``` +### TwoFactorAuthenticationChallenged +`Statamic\Events\TwoFactorAuthenticationChallenged` + +Dispatched when the two-factor authentication challenge is presented to a user. + +``` php +public function handle(TwoFactorAuthenticationChallenged $user) +{ + $event->user; +} +``` + +### TwoFactorAuthenticationDisabled +`Statamic\Events\TwoFactorAuthenticationDisabled` + +Dispatched when a user disables two-factor authentication. + +``` php +public function handle(TwoFactorAuthenticationDisabled $user) +{ + $event->user; +} +``` + +### TwoFactorAuthenticationEnabled +`Statamic\Events\TwoFactorAuthenticationEnabled` + +Dispatched when a user enables two-factor authentication. + +``` php +public function handle(TwoFactorAuthenticationEnabled $user) +{ + $event->user; +} +``` + +### TwoFactorAuthenticationFailed +`Statamic\Events\TwoFactorAuthenticationFailed` + +Dispatched when a user fails the two-factor authentication challenge. + +``` php +public function handle(TwoFactorAuthenticationFailed $user) +{ + $event->user; +} +``` + +### TwoFactorRecoveryCodeReplaced +`Statamic\Events\TwoFactorRecoveryCodeReplaced` + +Dispatched when one of a user's two-factor authentication recovery codes is replaced. + +``` php +public function handle(TwoFactorRecoveryCodeReplaced $user) +{ + $event->user; +} +``` + ### UserBlueprintFound `Statamic\Events\UserBlueprintFound` diff --git a/content/collections/extending-docs/permissions.md b/content/collections/extending-docs/permissions.md index 2559e2d59..c7bd6ba05 100644 --- a/content/collections/extending-docs/permissions.md +++ b/content/collections/extending-docs/permissions.md @@ -158,3 +158,51 @@ Permission::extend(function () { ); }); ``` + + +## Overriding Policies + +You may override policies by registering a binding in your AppServiceProvider. + +```php +public function register() +{ + $this->app->bind( + \Statamic\Policies\EntryPolicy::class, + \App\Policies\CustomEntryPolicy::class + ); +} +``` + +```php +class CustomEntryPolicy extends \Statamic\Policies\EntryPolicy +{ + public function edit($user, $entry) + { + // ... + } +} +``` + +Keep 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: + +```php +class CustomEntryPolicy extends \Statamic\Policies\EntryPolicy +{ + public function before($user, $ability) // [tl! **:start] + { + if ($ability === 'edit') { + // Returning null here will allow the method to be called. + return null; + } + + return parent::before($user, $ability); + } // [tl! **:end] + + + public function edit($user, $entry) + { + // ... + } +} +``` diff --git a/content/collections/fieldtypes/date.md b/content/collections/fieldtypes/date.md index 20b678a6b..cc624aec7 100644 --- a/content/collections/fieldtypes/date.md +++ b/content/collections/fieldtypes/date.md @@ -19,7 +19,7 @@ options: name: format type: string description: | - How the date should be stored, using the [PHP date format](https://www.php.net/manual/en/datetime.format.php) + 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. - name: full_width type: boolean @@ -74,6 +74,10 @@ date_range: end: 2019-11-22 ``` +Dates are stored in your application's timezone. + +The 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. + ## Templating Date 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`. @@ -171,4 +175,12 @@ When using Blade, you may also call the `->isoFormat` method on Carbon instances :: +## Timezones + +Dates are stored in your application timezone, then converted before being displayed to users. + +For more information on how Statamic handles timezones, please review our [Timezones](/tips/timezones) guide. + + + [carbon]: https://carbon.nesbot.com/docs/ diff --git a/content/collections/modifiers/format.md b/content/collections/modifiers/format.md index 5d5997681..ecaf70436 100644 --- a/content/collections/modifiers/format.md +++ b/content/collections/modifiers/format.md @@ -28,6 +28,12 @@ event_date: April 15 2016 2016-04-15 ``` +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: + ## Parameters ### Day @@ -87,7 +93,7 @@ event_date: April 15 2016 | `I`  | Whether or not the date is in daylight saving time | `1` if Daylight Saving Time, `0` otherwise. | | `O` | Difference to Greenwich time (GMT) without colon between hours and minutes | `+0200` | | `P` | Difference to Greenwich time (GMT) with colon between hours and minutes | `+02:00` | -| `p` | The same as `P`, but returns `Z` instead of `+00:00` (available as of PHP 8.0.0) | `+02:00` | +| `p` | The same as `P`, but returns `Z` instead of `+00:00` | `+02:00` | | `T` | Timezone abbreviation, if known; otherwise the GMT offset. | `EST`, `MDT`, `+05` | | `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` | diff --git a/content/collections/modifiers/format_translated.md b/content/collections/modifiers/format_translated.md index 4d8ea05b7..e53ecce0f 100644 --- a/content/collections/modifiers/format_translated.md +++ b/content/collections/modifiers/format_translated.md @@ -30,4 +30,10 @@ Assuming your site's locale is `fr_FR`: mercredi 28 février 2024 ``` -[carbon]: http://carbon.nesbot.com \ No newline at end of file +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: + +[carbon]: http://carbon.nesbot.com diff --git a/content/collections/modifiers/is_after.md b/content/collections/modifiers/is_after.md index 92ed5b711..87880b5e7 100644 --- a/content/collections/modifiers/is_after.md +++ b/content/collections/modifiers/is_after.md @@ -34,3 +34,9 @@ true true false ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_before.md b/content/collections/modifiers/is_before.md index b3f74630a..d6a678707 100644 --- a/content/collections/modifiers/is_before.md +++ b/content/collections/modifiers/is_before.md @@ -35,4 +35,8 @@ false true ``` +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_between.md b/content/collections/modifiers/is_between.md index 0dbecc8d0..1cb248139 100644 --- a/content/collections/modifiers/is_between.md +++ b/content/collections/modifiers/is_between.md @@ -30,3 +30,9 @@ end_date: December 1 2015 ```html true ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_future.md b/content/collections/modifiers/is_future.md index 9f73e9bd6..c5dd0ab6e 100644 --- a/content/collections/modifiers/is_future.md +++ b/content/collections/modifiers/is_future.md @@ -31,3 +31,9 @@ another_date: November 2030 false true ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_leap_year.md b/content/collections/modifiers/is_leap_year.md index 16f399ba1..054af6de9 100644 --- a/content/collections/modifiers/is_leap_year.md +++ b/content/collections/modifiers/is_leap_year.md @@ -31,3 +31,9 @@ another_date: November 2017 true false ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_past.md b/content/collections/modifiers/is_past.md index cfb2ee99d..003a36267 100644 --- a/content/collections/modifiers/is_past.md +++ b/content/collections/modifiers/is_past.md @@ -30,3 +30,9 @@ another_date: November 2019 true false ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_today.md b/content/collections/modifiers/is_today.md index 774b4b10c..086b4bc29 100644 --- a/content/collections/modifiers/is_today.md +++ b/content/collections/modifiers/is_today.md @@ -27,3 +27,9 @@ date: January 1, 2000 ```html false ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_tomorrow.md b/content/collections/modifiers/is_tomorrow.md index bd9ba95ca..d943b6bb6 100644 --- a/content/collections/modifiers/is_tomorrow.md +++ b/content/collections/modifiers/is_tomorrow.md @@ -27,3 +27,9 @@ date: January 1, 2000 ```html false ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_weekday.md b/content/collections/modifiers/is_weekday.md index 80a34ba0c..e6e1b9932 100644 --- a/content/collections/modifiers/is_weekday.md +++ b/content/collections/modifiers/is_weekday.md @@ -28,3 +28,9 @@ date: December 25 2015 ```html true ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_weekend.md b/content/collections/modifiers/is_weekend.md index a2355f13c..161dee5ae 100644 --- a/content/collections/modifiers/is_weekend.md +++ b/content/collections/modifiers/is_weekend.md @@ -28,3 +28,9 @@ date: December 25 2015 ```html false ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/is_yesterday.md b/content/collections/modifiers/is_yesterday.md index 17b8fad77..62250d2b7 100644 --- a/content/collections/modifiers/is_yesterday.md +++ b/content/collections/modifiers/is_yesterday.md @@ -27,3 +27,9 @@ date: January 1, 2000 ```html false ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/iso_format.md b/content/collections/modifiers/iso_format.md index 097c788c1..fbe92bc71 100644 --- a/content/collections/modifiers/iso_format.md +++ b/content/collections/modifiers/iso_format.md @@ -62,4 +62,10 @@ And this on your French site: Check out the [complete list of available macro-formats](https://carbon.nesbot.com/docs/#available-macro-formats). +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: + [carbon]: http://carbon.nesbot.com diff --git a/content/collections/modifiers/modify_date.md b/content/collections/modifiers/modify_date.md index eb12efe93..555ce283c 100644 --- a/content/collections/modifiers/modify_date.md +++ b/content/collections/modifiers/modify_date.md @@ -37,3 +37,9 @@ April 1, 2000 :::tip As 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. ::: + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/relative.md b/content/collections/modifiers/relative.md index a54eda0e1..a64627a39 100644 --- a/content/collections/modifiers/relative.md +++ b/content/collections/modifiers/relative.md @@ -40,3 +40,9 @@ future_date: October 1 2024 1 year from now 1 year ``` + +:::warning +By default, when using a modifier on a date variable, it will be operating on the UTC date rather than the localized date. + +Please refer to our [Timezones](/tips/timezones) guide for more information. +::: diff --git a/content/collections/modifiers/timezone.md b/content/collections/modifiers/timezone.md index 872de0e3d..3068c0ee5 100644 --- a/content/collections/modifiers/timezone.md +++ b/content/collections/modifiers/timezone.md @@ -32,8 +32,7 @@ Tue, 27 Jan 2015 11:00:00 -0500 Wed, 28 Jan 2015 03:00:00 +1100 ``` -Using no parameter will simply use the timezone defined in your system settings. This is useful if your date value -already contains a timezone, and you want to display it in the system timezone. +Using 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. ```yaml when: Tue, 27 Jan 2015 16:00:00 +0000 # Date in UTC diff --git a/content/collections/tips/change-timezone-to-utc.md b/content/collections/tips/change-timezone-to-utc.md new file mode 100644 index 000000000..cbbdb70e8 --- /dev/null +++ b/content/collections/tips/change-timezone-to-utc.md @@ -0,0 +1,38 @@ +--- +id: 7af5ee6c-234a-48eb-b02b-c72b52562618 +blueprint: tips +title: 'How to change your timezone to UTC' +intro: "Statamic 6 introduces improved timezone handling. Using UTC as your timezone is a best practice." +template: page +related_entries: + - 7dfba904-8a74-40e1-b507-51cd2b5f6123 +--- +It's best practice to keep your app's timezone set to UTC. + +If you've had to change it in the past (maybe to workaround timezone issues in Statamic), you may now change it back to UTC. + +:::warning +Before continuing, you will want to make a backup of your content and/or database. + +Additionally, you may consider **not** changing your timezone for existing sites. While it is a best practice, it's not a requirement. +::: + +## Migration command +You can use our migration command which will update date fields throughout your content: + +```bash +php please migrate-dates-to-utc America/New_York +``` + +You should replace `America/New_York` with whatever timezone you want to convert dates _from_ (e.g. your old app timezone). + +If you're storing content in a database, or outside of version control, you will need to run this command after deploying to production/staging. + +Once complete, you may change your app's timezone in `config/app.php`: + +```php +// config/app.php + +'timezone' => 'America/New_York', // [tl! remove] +'timezone' => 'UTC', // [tl! add] +``` diff --git a/content/collections/tips/converting-from-single-to-multi-site.md b/content/collections/tips/converting-from-single-to-multi-site.md index e4bc20043..716c967f3 100644 --- a/content/collections/tips/converting-from-single-to-multi-site.md +++ b/content/collections/tips/converting-from-single-to-multi-site.md @@ -57,7 +57,6 @@ Now you'll need to update your default site content file & folder structure, so - 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`) - Add a `sites` array to the root structure's yaml file with each site you want the structure to be available in. 3. For each global set: - - Take the values inside the `data` array, and move them to the top level in a file in a subdirectory named after the default site's handle. (eg. `content/globals/pages.yaml` to `content/globals/default/pages.yaml`) - Add a `sites` array to the root global's yaml file with each site you want the global to be available in. At 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. diff --git a/content/collections/tips/storing-users-in-a-database.md b/content/collections/tips/storing-users-in-a-database.md index 40ad63043..c90981148 100644 --- a/content/collections/tips/storing-users-in-a-database.md +++ b/content/collections/tips/storing-users-in-a-database.md @@ -19,7 +19,7 @@ If you installed Statamic using the `statamic new` command, or created a project Statamic comes with an Eloquent driver to make the transition as seamless as possible. 1. Ensure you have a [database configured](https://laravel.com/docs/database#configuration). -1. In your user model, cast the preferences column to json. +1. In your `User` model, add casts for the `preferences` and `two_factor_confirmed_at` columns: ```php class User extends Authenticatable { @@ -27,6 +27,7 @@ Statamic comes with an Eloquent driver to make the transition as seamless as pos { // [tl! focus] return [ // [tl! focus] 'preferences' => 'json', // [tl! ++] [tl! focus] + 'two_factor_confirmed_at' => 'datetime', // [tl! ++] [tl! focus] ]; // [tl! focus] } // [tl! focus] @@ -70,6 +71,9 @@ Statamic comes with an Eloquent driver to make the transition as seamless as pos $table->json('preferences')->nullable(); $table->timestamp('last_login')->nullable(); $table->string('password')->nullable()->change(); + $table->text('two_factor_secret')->nullable(); + $table->text('two_factor_recovery_codes')->nullable(); + $table->timestamp('two_factor_confirmed_at')->nullable(); }); Schema::create('role_user', function (Blueprint $table) { diff --git a/content/collections/tips/timezones.md b/content/collections/tips/timezones.md new file mode 100644 index 000000000..7b9699f78 --- /dev/null +++ b/content/collections/tips/timezones.md @@ -0,0 +1,89 @@ +--- +id: 2080f786-9c04-4916-b77a-c62202ec4f07 +title: 'Timezones' +intro: "Every developer's worst nightmare." +template: page +related_entries: + - 7dfba904-8a74-40e1-b507-51cd2b5f6123 +--- + +> **This guide is only relevant to sites running Statamic 6 and above.** + +## UTC + +It's best practice to store dates in the UTC timezone for consistency, and to convert the timezone just before displaying it. + +We recommend leaving your timezone set to UTC. Statamic will convert dates to the timezone defined in `config/statamic/system.php` when appropriate. + +```php +'display_timezone' => 'America/New_York', +``` + +By default, or when this is not explicitly set, it will also be `UTC`. + + +## Templating + +Within templates, any dates will be [Carbon](https://carbon.nesbot.com) instances in the UTC timezone. + +When converting a Carbon instance to a string, it will be automatically converted to your configured display timezone. For example: + +```antlers +{{ date }} "December 25th, 2020" +```` + +Since 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. + +### Modifiers + +For example, the `format` modifier will receive a UTC date: + +```yaml +date: '2020-12-25 03:00' # This is UTC +``` + +```antlers +{{ date | format('Y-m-d H:i') }} "2020-12-25 03:00" +``` + +Since 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. + +There are two options for this: +1. Apply the `timezone` modifier (or `tz` for short) before passing it along: + ```antlers + {{ date | tz | format(...) }} "2020-12-24 20:00" + ``` +2. Opt to convert dates in date modifiers in `config/statamic/system.php`: + ```php + 'localize_dates_in_modifiers' => true, + ``` + ```antlers + {{ date | format(...) }} "2020-12-24 20:00" + ``` + _Note that this option will only convert dates when using date-related modifiers like `format`, `days_ago`, etc._ + +### Tags + +If a tag _needs_ a Carbon instance in your display timezone, you can modify it before passing it: + +::tabs +::tab antlers +```antlers +{{ my_tag :date="date|tz" }} +``` +::tab blade +```blade +{{ Statamic::tag('my_tag')->date( + $date->tz(config('statamic.system.display_timezone')) +) }} +``` +:: + +Although, a tag shouldn't be expecting this of you. + +## Custom Routes +If you're passing Carbon instances into templates yourself (eg. from a custom route), you should make sure they're all in UTC. + +Under 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. + +You 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. diff --git a/content/collections/variables/now.md b/content/collections/variables/now.md index f3866d964..8819a890c 100644 --- a/content/collections/variables/now.md +++ b/content/collections/variables/now.md @@ -5,7 +5,10 @@ types: - system title: Now --- -The current date/time. If you use it on its own, it will be formatted using the default time format. If you pass it into a tag parameter or modifier, it will be treated as the `Carbon` instance. + +The 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. + +When you pass it to a tag parameter, or modifier, it will be treated as a UTC [`Carbon`](https://carbon.nesbot.com/) instance. ::tabs diff --git a/content/trees/collections/docs.yaml b/content/trees/collections/docs.yaml index 8319e3588..848e012ff 100644 --- a/content/trees/collections/docs.yaml +++ b/content/trees/collections/docs.yaml @@ -18,6 +18,10 @@ tree: entry: e077f513-45c1-4eff-ba87-210340dd6f54 - entry: 91e8f239-2f99-47bc-b4dd-3518cd3e36ae + - + entry: 9a013ab0-bd21-42e1-84ea-fecd052466e9 + - + entry: c3553d05-d1a8-453a-a59b-7e67dd2412a4 - entry: ec130472-4f44-4e7e-8dce-71d0c93e8fef -