From d2b67a9a385d35a1827f694c6018745954ebfdb9 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 24 Jan 2025 16:45:30 -0500 Subject: [PATCH 01/18] Stub out upgrade guides --- content/collections/docs/5-to-6.md | 62 +++++++++++++++++++++++ content/collections/docs/upgrade-guide.md | 1 + content/collections/docs/vue-2-to-3.md | 10 ++++ content/trees/collections/docs.yaml | 4 ++ 4 files changed, 77 insertions(+) create mode 100644 content/collections/docs/5-to-6.md create mode 100644 content/collections/docs/vue-2-to-3.md diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md new file mode 100644 index 000000000..834c9b5d5 --- /dev/null +++ b/content/collections/docs/5-to-6.md @@ -0,0 +1,62 @@ +--- +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. + + +## Medium impact changes + +... + +## Low impact changes + +... + +## Zero impact changes + +... + diff --git a/content/collections/docs/upgrade-guide.md b/content/collections/docs/upgrade-guide.md index 9759889a5..8251e81b1 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/vue-2-to-3.md b/content/collections/docs/vue-2-to-3.md new file mode 100644 index 000000000..dac0f2069 --- /dev/null +++ b/content/collections/docs/vue-2-to-3.md @@ -0,0 +1,10 @@ +--- +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. diff --git a/content/trees/collections/docs.yaml b/content/trees/collections/docs.yaml index c3131fca0..4a471fab0 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 - From 3ac58a9312bce06aaa4df51c6319958f98638b3b Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 11 Feb 2025 12:21:18 +0000 Subject: [PATCH 02/18] Statamic 6 requires PHP 8.2 or above --- content/collections/docs/requirements.md | 2 +- content/collections/docs/ubuntu.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content/collections/docs/requirements.md b/content/collections/docs/requirements.md index ddfec00db..be7bce06f 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 From fb52c757b399f04b868677a96a0475d0d66fbcb2 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 11 Feb 2025 12:21:52 +0000 Subject: [PATCH 03/18] Remove PHP version note We don't support < 8.2 now, so this note is no longer applicable. --- content/collections/modifiers/format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/collections/modifiers/format.md b/content/collections/modifiers/format.md index 5d5997681..c89826912 100644 --- a/content/collections/modifiers/format.md +++ b/content/collections/modifiers/format.md @@ -87,7 +87,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` | From cbf19a5782d9c2e2d25a512d0ddb55eaa330b616 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Wed, 26 Feb 2025 14:42:36 +0000 Subject: [PATCH 04/18] Add "Carbon 3" to the upgrade guide --- content/collections/docs/5-to-6.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index 834c9b5d5..be0eeeb8f 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -47,10 +47,15 @@ To keep this upgrade guide manageable, we have a [dedicated page for upgrading f If you do not have any custom Vue components in your app, or in your own addons, you can skip this. - ## 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. ## Low impact changes From 4e2ca81585824fa9fb54e7ff59f5ef1e08833770 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 28 Feb 2025 16:38:21 -0500 Subject: [PATCH 05/18] wip --- content/collections/docs/vue-2-to-3.md | 295 +++++++++++++++++++++++++ 1 file changed, 295 insertions(+) diff --git a/content/collections/docs/vue-2-to-3.md b/content/collections/docs/vue-2-to-3.md index dac0f2069..efc457d72 100644 --- a/content/collections/docs/vue-2-to-3.md +++ b/content/collections/docs/vue-2-to-3.md @@ -8,3 +8,298 @@ 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. From 448edc2525ef2804291db0c056035796688de880 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 11 Mar 2025 20:16:20 +0000 Subject: [PATCH 06/18] [6.x] UTC Dates (#1637) Co-authored-by: Jason Varga --- content/collections/docs/5-to-6.md | 66 ++++++++++++++ content/collections/fieldtypes/date.md | 12 ++- content/collections/modifiers/format.md | 6 ++ .../modifiers/format_translated.md | 8 +- content/collections/modifiers/is_after.md | 6 ++ content/collections/modifiers/is_before.md | 4 + content/collections/modifiers/is_between.md | 6 ++ content/collections/modifiers/is_future.md | 6 ++ content/collections/modifiers/is_leap_year.md | 6 ++ content/collections/modifiers/is_past.md | 6 ++ content/collections/modifiers/is_today.md | 6 ++ content/collections/modifiers/is_tomorrow.md | 6 ++ content/collections/modifiers/is_weekday.md | 6 ++ content/collections/modifiers/is_weekend.md | 6 ++ content/collections/modifiers/is_yesterday.md | 6 ++ content/collections/modifiers/iso_format.md | 6 ++ content/collections/modifiers/modify_date.md | 6 ++ content/collections/modifiers/relative.md | 6 ++ content/collections/modifiers/timezone.md | 3 +- .../tips/change-timezone-to-utc.md | 38 ++++++++ content/collections/tips/timezones.md | 89 +++++++++++++++++++ content/collections/variables/now.md | 5 +- 22 files changed, 304 insertions(+), 5 deletions(-) create mode 100644 content/collections/tips/change-timezone-to-utc.md create mode 100644 content/collections/tips/timezones.md diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index be0eeeb8f..708d9ae78 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -47,6 +47,72 @@ To keep this upgrade guide manageable, we have a [dedicated page for upgrading f 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. + +#### 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 diff --git a/content/collections/fieldtypes/date.md b/content/collections/fieldtypes/date.md index 707b0037d..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,6 +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 c89826912..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 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/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 From ab8fc138a333560617cc29bd95a8cbacac22c411 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 13 Mar 2025 09:49:49 -0400 Subject: [PATCH 07/18] Explain overriding policies and how super users work --- .../collections/extending-docs/permissions.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) 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) + { + // ... + } +} +``` From fb6b85e5b061a20e3362425e1ca3d6fb1da4ddfe Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Mar 2025 10:45:00 -0400 Subject: [PATCH 08/18] =?UTF-8?q?=F0=9F=91=8B=20moment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/collections/docs/5-to-6.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index 708d9ae78..71fd8f098 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -125,7 +125,18 @@ You _may_ need to updates your templates to account for these changes. ## Low impact changes -... +### 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. ## Zero impact changes From 6260813c6ffab7e32b91360151b799cdd054ebc8 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 21 Mar 2025 17:32:11 +0000 Subject: [PATCH 09/18] Mention that the APIs will always return UTC dates --- content/collections/docs/5-to-6.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index 71fd8f098..b4b6682b1 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -106,6 +106,9 @@ 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. From 3d539f3689f404862acac72002caf9324fdb1eb2 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 24 Mar 2025 18:04:03 +0000 Subject: [PATCH 10/18] Addon components are auto-registered now. Manually registering stuff is really only needed if you're doing something weird. --- content/collections/extending-docs/addons.md | 78 ++++++++++---------- 1 file changed, 41 insertions(+), 37 deletions(-) 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; From 6096c22797eb0d793ca4410d69bd025d8d0d9d5a Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 24 Mar 2025 18:55:18 +0000 Subject: [PATCH 11/18] Document deprecations --- content/collections/docs/5-to-6.md | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index b4b6682b1..c387fb4fa 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -141,6 +141,54 @@ You should search for `moment` or `$moment` references in your code and replace [Here is a good resource](https://github.com/you-dont-need/You-Dont-Need-Momentjs) on how to migrate away from Moment.js. +### 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 ... From 9ba08718c629d4b73f87ba23e482f7d59b1d4d13 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 25 Mar 2025 15:54:11 +0000 Subject: [PATCH 12/18] Document wildcard tags change --- content/collections/docs/5-to-6.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index c387fb4fa..edafbc120 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -141,6 +141,25 @@ You should search for `moment` or `$moment` references in your code and replace [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.** From e994d342d5aeb9a7b9e805fc1960d05b562c3598 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 7 Apr 2025 10:26:04 +0100 Subject: [PATCH 13/18] [6.0] Separate globals config and content (#1643) --- content/collections/docs/5-to-6.md | 74 +++++++++++++++++++ .../converting-from-single-to-multi-site.md | 1 - 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index edafbc120..90398dd7e 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -126,6 +126,80 @@ If you're using any of Statamic's `months_ago`, `weeks_ago`, `days_ago`, `hours_ 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 ### Moment.js has been removed 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. From ceb36ace4d1ad86cf82d46961208229b01ccc40b Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 24 Apr 2025 17:57:27 +0100 Subject: [PATCH 14/18] Document events --- content/collections/extending-docs/events.md | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) 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` From 2d49fcfbc32edb9f1b2eb49a4d6bd627de51b24f Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 24 Apr 2025 18:01:27 +0100 Subject: [PATCH 15/18] Mention cast & two factor columns in database users guide --- content/collections/tips/storing-users-in-a-database.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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) { From 69dea0e45adc37931e6d0042d2d86c2b01e8b4c8 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 24 Apr 2025 18:15:27 +0100 Subject: [PATCH 16/18] Mention needing to add two factor database columns in the upgrade guide Should be covered by an upgrade script though. --- content/collections/docs/5-to-6.md | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index 90398dd7e..ac9e692d3 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -202,6 +202,60 @@ $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. 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: From 6a2738f3a9b583e582cd64853b8228ef8fa3f0d2 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 24 Apr 2025 18:45:18 +0100 Subject: [PATCH 17/18] Document two-factor authentication on users page --- content/collections/docs/5-to-6.md | 2 +- content/collections/docs/users.md | 32 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md index ac9e692d3..af2f2fb92 100644 --- a/content/collections/docs/5-to-6.md +++ b/content/collections/docs/5-to-6.md @@ -204,7 +204,7 @@ $globalSet->in('en')->delete(); // [tl! add] ### 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. You can add the columns using a migration: +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 [ + '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). +::: \ No newline at end of file From 13a24ec2317b8caafb731e2595a5ce507d826b32 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 24 Apr 2025 18:54:07 +0100 Subject: [PATCH 18/18] wip --- content/collections/docs/users.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/content/collections/docs/users.md b/content/collections/docs/users.md index 674157d68..d12c212ed 100644 --- a/content/collections/docs/users.md +++ b/content/collections/docs/users.md @@ -226,7 +226,9 @@ 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. +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. @@ -254,4 +256,4 @@ To enable 2FA, head to your **Profile** in the Control Panel. Scan the QR code w 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). -::: \ No newline at end of file +:::