diff --git a/content/collections/docs/5-to-6.md b/content/collections/docs/5-to-6.md new file mode 100644 index 000000000..104831469 --- /dev/null +++ b/content/collections/docs/5-to-6.md @@ -0,0 +1,385 @@ +--- +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] +``` + +### Breadcrumbs +**Affects apps or addons displaying breadcrumbs in the Control Panel.** + +Breadcrumbs are now generated from items in the Control Panel navigation, rather than needing to be passed into views manually. + +You should remove references to the `Breadcrumb` class in your code, as well as the `` Vue component. + +``` php +use Statamic\CP\Breadcrumbs; // [tl! remove:start] + +$crumbs = Breadcrumbs::make([ + ['text' => 'First', 'url' => '/first'], + ['text' => 'Second', 'url' => '/second'], +]); + +return view('myview', ['crumbs' => $crumbs]);// [tl! remove:end] +return view('myview'); // [tl! add] +``` + +``` blade + {{-- [tl! remove] --}} +``` + +``` vue + + +``` + +To learn more about customizing breadcrumbs, please refer to the [CP Navigation documentation](/extending/cp-navigation#breadcrumbs). + +## 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/addons.md b/content/collections/docs/addons.md index b49387712..d0fa6e223 100644 --- a/content/collections/docs/addons.md +++ b/content/collections/docs/addons.md @@ -19,7 +19,7 @@ You can use Composer to install any addon: composer require vendor/package ``` -The command can be found on the addon's page in the Control Panel or the Statamic Marketplace. +The command can be found on the addon's page in the [Statamic Marketplace](https://statamic.com/addons). :::tip Some first party addons – such as the Static Site Generator or Eloquent Driver - have their own dedicated commands which will be noted on the same pages. @@ -49,6 +49,4 @@ You can choose which edition is installed by entering it into your `config/stata 'addons' => [ 'vendor/package' => 'pro', // e.g., 'jezzdk/statamic-google-maps' => 'pro' ] -``` - -Or, by choosing it from an addon's details view in the Control Panel. \ No newline at end of file +``` \ No newline at end of file diff --git a/content/collections/docs/git-automation.md b/content/collections/docs/git-automation.md index 163a43e51..036d465a5 100644 --- a/content/collections/docs/git-automation.md +++ b/content/collections/docs/git-automation.md @@ -54,6 +54,7 @@ You are free to define the tracked paths to be considered when staging and commi 'paths' => [ base_path('content'), base_path('users'), + resource_path('addons'), resource_path('blueprints'), resource_path('fieldsets'), resource_path('forms'), diff --git a/content/collections/docs/multi-site.md b/content/collections/docs/multi-site.md index 672a8c18d..d7896704d 100644 --- a/content/collections/docs/multi-site.md +++ b/content/collections/docs/multi-site.md @@ -135,10 +135,6 @@ APP_URL=http://mysite.test Each site has a `locale` used to format region-specific data (like date strings, number formats, etc). This should correspond to the server's locale. By default, Statamic will use English – United States (`en_US`). -:::tip -To see the list of installed locales on your system or server, run the command `locale -a`. -::: - ### Language Statamic's control panel has been translated into more than a dozen languages. The language translations files live in `resources/lang`. 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..521374154 100644 --- a/content/collections/docs/users.md +++ b/content/collections/docs/users.md @@ -223,3 +223,35 @@ 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..4356e842c --- /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..be7cb2111 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 @@ -538,20 +548,74 @@ if ($addon->edition() === 'pro') { You don't need to check whether a license is valid, Statamic does that automatically for you. ::: +## Settings -## Update Scripts +Laravel config files are great for storing application settings, but they're not ideal for settings you might want users to edit through the Control Panel. -You may register update scripts to help your users migrate data, etc. when new features are added or breaking changes are introduced. +You can register a settings blueprint in your addon to give users a friendly interface for managing settings. Drop a blueprint file in `resources/blueprints/settings.yaml` or register it in your service provider like this: + +```php +public function bootAddon() +{ + $this->registerSettingsBlueprint([ + 'tabs' => [ + 'main' => [ + 'sections' => [ + [ + 'display' => __('API'), + 'fields' => [ + [ + 'handle' => 'api_key', + 'field' => ['type' => 'text', 'display' => 'API Key', 'validate' => 'required'], + ], + // ... + ], + ], + ], + ], + ], + ]); +} +``` -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: +Your addon's settings page will show up in the Control Panel under **Tools -> Addons**. Pretty convenient. -``` php -protected $updateScripts = [ - \Acme\Example\Updates\UpdatePermissions::class, -]; +You can even reference config options (and by extension environment variables) in your settings blueprint using Antlers, like so: `{{ config:app:url }}`. + +Settings are stored as YAML files in `resources/addons` by default, but can be moved to the database if you prefer. Just run the `php please install:eloquent-driver` command and you're all set. + +You can retrieve the settings using the `Addon` facade: + +```php +use Statamic\Facades\Addon; + +$addon = Addon::get('vendor/package'); + +// Getting settings... +$addon->settings()->get('api_key'); + +$addon->settings()->all(); +$addon->settings()->raw(); // Doesn't evaluate Antlers + +// Setting values... +$addon->settings()->set('api_key', '{{ config:services:example:api_key }}'); + +$addon->settings()->set([ + 'website_name' => 'My Awesome Site', + 'api_key' => '{{ config:services:example:api_key }}', +]); + +// Saving... +$addon->settings()->save(); ``` -Then in your update script, you can extend `UpdateScript` and implement the necessary methods: +## Update Scripts + +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, 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/breadcrumbs.md b/content/collections/extending-docs/breadcrumbs.md deleted file mode 100644 index 103564d92..000000000 --- a/content/collections/extending-docs/breadcrumbs.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: CP Breadcrumbs -intro: | - At the top of most pages in the control panel, you will see a title with breadcrumbs sitting above. Statamic provides - ways to generate these links without you having to worry about manually generating the HTML. -stage: 1 -id: a96676fe-0ec4-41f5-9205-2fe47988addb ---- -``` php -use Statamic\CP\Breadcrumbs; - -$crumbs = Breadcrumbs::make([ - ['text' => 'First', 'url' => '/first'], - ['text' => 'Second', 'url' => '/second'], -]); - -return view('myview', ['crumbs' => $crumbs]); -``` - -``` blade - -``` - -``` vue - - -``` diff --git a/content/collections/extending-docs/cp-navigation.md b/content/collections/extending-docs/cp-navigation.md index 576a83cfd..d7524bd25 100644 --- a/content/collections/extending-docs/cp-navigation.md +++ b/content/collections/extending-docs/cp-navigation.md @@ -71,6 +71,30 @@ Note that the `Nav` facade is `Statamic\Facades\CP\Nav`. There's another Nav facade _without_ the CP namespace, and it's for the front-end ["Navs"](/navigation) feature. ::: +### Breadcrumbs + +Breadcrumbs are displayed at the top of the Control Panel, allowing users to navigate back to previous pages. Statamic automatically generates these breadcrumbs from the CP navigation. + +However, breadcrumbs support a few additional options that can be set using the `extra()` method on a `NavItem`: + +```php +Nav::extend(function ($nav) { + $nav->content('Store') + ->route('store.index') + ->icon('shopping-cart') + ->extra([ // [tl! focus:start] + 'breadcrumbs' => [ + // Create button + 'create_label' => 'Create Product', + 'create_url' => cp_route('store.products.create'), + + // Configure button + 'configure_url' => cp_route('store.settings'), + ], + ]); // [tl! focus:end] +}); +``` + ## Adding Children Maybe we have `Products` and `Orders`, which we want to display as children under the `Store` item. To do this, we'll add a `children()` call to the parent nav item: diff --git a/content/collections/extending-docs/events.md b/content/collections/extending-docs/events.md index 4c9512041..fd73b910b 100644 --- a/content/collections/extending-docs/events.md +++ b/content/collections/extending-docs/events.md @@ -41,6 +41,30 @@ If you're creating an addon, you can quickly [register event listeners or subscr ## Available Events +### AddonSettingsSaved +`Statamic\Events\AddonSettingsSaved` + +Dispatched after an addon's settings have been saved. + +``` php +public function handle(AddonSettingsSaved $event) +{ + $event->addonSettings; +} +``` + +### AddonSettingsSaving +`Statamic\Events\AddonSettingsSaving` + +Dispatched before an addon's settings are saved. You can return `false` to prevent them from being saved. + +``` php +public function handle(AddonSettingsSaving $event) +{ + $event->addonSettings; +} +``` + ### AssetContainerBlueprintFound `Statamic\Events\AssetContainerBlueprintFound` @@ -1258,6 +1282,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/tags/form-create.md b/content/collections/tags/form-create.md index 23d856a9e..3aa879ce0 100644 --- a/content/collections/tags/form-create.md +++ b/content/collections/tags/form-create.md @@ -26,7 +26,7 @@ parameters: - name: csrf type: boolean - description: When `false`, the hidden `name="_token"` attribute won't be added to the form so you can use other ways of providing the token. Defaults to `true`. + description: When `false`, the hidden `name="_token"` attribute won't be added to the form so you can use other ways of providing the token. Defaults to `true`. - name: files type: boolean @@ -45,7 +45,7 @@ variables: name: fields type: array description: > - An array of available fields for [dynamic rendering](#dynamic-rendering). + An array of available fields for [dynamic rendering](#dynamically-rendering-fields). - name: errors type: array @@ -106,30 +106,30 @@ Here we'll be creating a form to submit an entry in a `contact` form. ``` ::tab blade ```blade - - @if (count($errors) > 0) -
- @foreach ($errors as $error) - {{ $error }}
- @endforeach -
- @endif + - @if ($success) -
- {{ $success }} -
- @endif + @if (count($errors) > 0) +
+ @foreach ($errors as $error) + {{ $error }}
+ @endforeach +
+ @endif - - + @if ($success) +
+ {{ $success }} +
+ @endif - - + + + + + + + -
``` :: @@ -147,7 +147,7 @@ You can also use the shorthand syntax for `form:create in="contact"`. ::tab blade ```blade - ... + ... ``` :: @@ -173,16 +173,14 @@ When you need to render a form that's selected via the [Form Fieldtype](/fieldty ::tab antlers ```antlers -{{ form:create in="{ form_fieldtype:handle }" }} +{{ form:create :in="form_fieldtype:handle" }} ... {{ /form:create }} ``` ::tab blade ```blade - - ... + + ... ``` :: @@ -191,7 +189,7 @@ This way you can let Control Panel users select which form should be used on an ### Dynamically Rendering Fields -Instead of hardcoding individual fields, you may loop through the `fields` array to render your blueprint's fields in a dynamic fashion. +Instead of hardcoding individual fields, you may loop through the `fields` array using the [form:fields](/tags/form-fields) tag to render your blueprint's fields in a dynamic fashion. ::tabs @@ -199,7 +197,7 @@ Instead of hardcoding individual fields, you may loop through the `fields` array ```antlers {{ form:contact }} - {{ fields }} + {{ form:fields }}
- {{ /fields }} + {{ /form:fields }} @@ -222,40 +220,43 @@ Instead of hardcoding individual fields, you may loop through the `fields` array ```blade - @foreach ($fields as $field) -
- -
{!! $field['field'] !!}
- @if ($field['error']) -

{{ $field['error'] }}

- @endif -
- @endforeach + +
+ +
{{ $field['field'] }}
+ @if ($field['error']) +

{{ $field['error'] }}

+ @endif +
+
- +
``` :: -Each item in the `fields` array contains the following data configurable in the form's blueprint. +Each item in this `fields` array contains the following data: #### Fields Array Variables | Variable | Type | Description | |---|---| --- | -| `handle` | string | System name for the field | -| `display` | string | User-friendly field label | +| `display` | string | User-friendly field label configured in blueprint | +| `instructions` | string | User-friendly instructions label configured in blueprint | +| `field` | string | [Pre-rendered field HTML](#pre-rendered-field-html) based on the fieldtype | | `type` | string | Name of the [fieldtype](/fieldtypes) | -| ` field` | string | [Pre-rendered field HTML](#pre-rendered-field-html) based on the fieldtype | +| `handle` | string | Blueprint handle for the field | +| `name` | string | Input name for submitting the field | +| `value` | mixed | Field value, which respects both `old` and `default` | +| `default` | mixed | Default field value as assigned by blueprint | +| `old` | mixed | Old field value from an unsuccessful submission | | `error` | string | Error message from an unsuccessful submission | -| `old` | array | Contains user input from an unsuccessful submission | -| `instructions` | string | User-friendly instructions label | | `validate` | array | Contains an array of validation rules | | `width` | string | Width of the field assigned in the blueprint | @@ -278,21 +279,21 @@ This approach, combined with the [blueprint editor](/blueprints), will give you ::tab antlers ```antlers -{{ fields }} +{{ form:fields }}
{{ field }}
-{{ /fields }} +{{ /form:fields }} ``` ::tab blade ```blade -@foreach ($fields as $field) -
- - {!! $field['field'] !!} -
-@endforeach + +
+ + {{ $field['field'] }} +
+
``` :: @@ -324,31 +325,30 @@ If you have defined multiple sections in your form's blueprint, you can loop ove {{ sections }}
{{ display }} - {{ fields }} + {{ form:fields }} ... - {{ /fields }} + {{ /form:fields }}
{{ /sections }} - + {{ /form:contact }} ``` ::tab blade ```blade - @foreach($sections as $section) -
- {{ $section['display'] }} - - @foreach ($section['fields'] as $field) - ... - @endforeach -
- @endforeach + @foreach($sections as $section) +
+ {{ $section['display'] }} + + ... + +
+ @endforeach - +
``` @@ -363,7 +363,7 @@ Each item in the `sections` array contains the following data configurable in th | `fields` | array | An array of [fields](#dynamically-rendering-fields) defined within that section | -## Conditional Fields 🆕 +## Conditional Fields You may conditionally show and hide fields by utilizing the [conditional fields](/conditional-fields#overview) settings in your form's blueprint editor. Once configured, by including the necessary front-end scripts and enabling JavaScript on the `form:create` tag, all of the conditional logic will Just Work™. @@ -394,10 +394,8 @@ The next step is to enable the Alpine JS driver via the `js="alpine"` parameter. ``` ::tab blade ```blade - - ... + + ... ``` :: @@ -412,7 +410,7 @@ Finally, you will need to wire up the fields. With Alpine, this is done using `x ::tab antlers ```antlers -